加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_阳江站长网 (https://www.0662zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux内核分析 - 网络[十二]:UDP模块 - socket

发布时间:2016-06-05 14:30:36 所属栏目:Linux 来源:站长网
导读:内核版本:2.6.34 这部分内容在于说明socket创建后如何被内核协议栈访问到,只关注两个问题:sock何时插入内核 表的,sock如何被内核访问的。对于核心的sock的

sock->ops指向inet_dgram_ops,然后创建sk,sk->proto指向udp_prot,注意这里分配的大小是struct udp_sock,而不仅仅是struct sock大小。

sock->ops = answer-

>ops;     
……     
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);

然后设置inet的一些参数,这里直接将sk类型转换为 inet,因为在sk_alloc()中分配的是struct udp_sock结构大小,返回的是struct sock,利用了第一个成员的特性,三者之间的 关系如下图:

inet = inet_sk(sk);     
…..     
inet->inet_id = 0;

Linux内核分析 - 网络[十二]:UDP模块 - socket

此时sock和sk都已经分配了空间,再设置sock与sk关系,即sock->sk=sk, 并做一些初始化操作,如sk的队列初始化。初后调用sk_prot->init(),inet_dgram_ops->init()为NULL,这里没做任何 事情。

sock_init_data(sock, sk);     
if (sk->sk_prot->init) {     
 err = sk->sk_prot->init(sk);     
 if (err)     
  sk_common_release(sk);     
}

当创建的是一个SOCK_RAW类型的socket时,还会额外执行下列语句。当协议值赋给inet->inet_num与inet- >inet_sport,然后sk->sk_prot->hash(sk)将sk插入到内核的sock表中,使用的索引值是协议号。这个可以这样理解 ,如果创建的是UDP或TCP的socket,它们是标准的套接字,用[sip, sport, tip, tport]这样的四元组来查找,socket()时还缺 少这些信息,还不能插入到内核的sock表中。但如果创建的是RAW的socket,它只属于某一特定协议,查找它使用的应是协议号 而不是套接字的四元组,因此,socket()时就通过hash()插入到内核sock表中。

if (SOCK_RAW == sock->type) {     
 inet->inet_num = protocol;     
 if (IPPROTO_RAW == protocol)     
  inet->hdrincl = 1;     
}     
if (inet->inet_num) {     
 inet->inet_sport = htons(inet->inet_num);     
 sk->sk_prot->hash(sk);     
}

那么sock是在什么时候插入到内核表中的,答案是sk->sk_prot->get_port()函数,对于UDP来讲,它指向 udp_v4_get_port()函数,根据服务器和客户端的行为不同,bind()和sendto()都会调用到get_port(),也就是说,在bind()或 sendto()调用时,sock才被插入到内核表中。
bind() 绑定地址

sys_bind() -> sock- >ops->bind() -> inet_bind() -> sk->sk_prot->get_port()

sk- >sk_prot是udp_prot,这里实际调用udp_v4_get_port()函数。

sendto() 发送到指定地址

sys_sendto() -> sock_sendmsg() -> __sock_sendmsg()() -> sock->ops->sendmsg()

由于创建的是udp socket,因此sock->ops指向inet_dgram_ops,sendmsg()实际调用inet_sendmsg()函数。该 函数中的有如下语句:

if (!inet_sk(sk)->inet_num && 

inet_autobind(sk))     
 return -EAGAIN;

客户端在执行sendto()前仅仅执行了socket()操作,此时inet_num=0,因此执行了inet_autobind() ,该函数会调用sk->sk_prot->get_port()。从而回到了udp_v4_get_port()函数,它会将sk插入到内核表udp_table中。

下面重点看下插入sk的函数udp_v4_get_port():

udp_v4_get_port() 插入sk到内核表udptable中

哈希值hash2_nulladdr由[INADDR_ANY, snum]得到,hash2_partial由[inet_rcv_saddr, 0]得到,即前者用本地 端口作哈希,后者用本地地址作哈希。udp_portaddr_hash存储后者的值hash2_partial,便于计算最后的哈希值。

unsigned int hash2_nulladdr = udp4_portaddr_hash(sock_net(sk), INADDR_ANY, 

snum);     
unsigned int hash2_partial = udp4_portaddr_hash(sock_net(sk), inet_sk(sk)->inet_rcv_saddr, 0);     
udp_sk(sk)->udp_portaddr_hash = hash2_partial;

最后调用udp_lib_get_port(),ipv4_rcv_saddr_equal()是比 较地址是否相等的函数,snum是本地端口,hash2_nulladdr是由它得到的哈杀值,sk是要插入的表项。

return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal, hash2_nulladdr);

udp_lib_get_port()

(编辑:应用网_阳江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!