Linux内核分析 - 网络[十二]:UDP模块 - socket
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; 此时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才被插入到内核表中。 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() (编辑:应用网_阳江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |