Linux内核分析 - 网络[十二]:UDP模块 - socket
|
如果snum==0,即没有绑定本地端口,此时执行if部分代码段,这种情况一般发 生在客户端使用socket,此时内核会为它选择一个未使用的端口,下面来看下内核选择临时端口的策略。 在说明下列参数含义前要先弄清楚udptable中hash公式:(num + net_hash_mix(net)) & mask,net_hash_mix(net) 返回一般为0,hash公式可简写为num&mask。即本地端口对udptable大小取模。因此表项是循环、均匀地分布在hash表中的 。假设udptable大小为8,现插入16个表项,结果会如下图:
声明bitmap数组,大小为udp_table每个键值最多存储的表项,即最大端口号/哈希表大小。端口号的值规定范围是1- 65536,而哈希表一般大小是256,因此实际分配bitmap[8]。low和high代表可用本地端口的下限和上限;remaining代表位于low 和high间的端口号数目。用随机值rand生成first,注意它是unsigned short类型,16位,表示起始查找位置;last表示终止查 找位置,first和last相差表大小保证了所有键值都会被查询一次。随机值rand最后处理成哈希表大小的奇数倍,之所以要是奇 数倍,是为了保证哈希到同一个键值的所有端口号都能被遍历,可以试着1开始,每次+2和每次+3,直到回到1,所遍历的数有哪 些不同,就会明白rand处理的意义。 DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); inet_get_local_port_range(&low, &high); remaining = (high - low) + 1; rand = net_random(); first = (((u64)rand * remaining) >> 32) + low; rand = (rand | 1) * (udptable->mask + 1); last = first + udptable->mask + 1; 使用first值作为端口号,从udptable的hash表中找到hslot项,重置bitmap 数组全0,调用函数udp_lib_lport_inuse()遍历hslot项的所有表项,将所有已经使用的sport对应于bitmap的位置置1。
do {
hslot = udp_hashslot(udptable, net, first);
bitmap_zero(bitmap, PORTS_PER_CHAIN);
spin_lock_bh(&hslot->lock);
udp_lib_lport_inuse(net, snum, hslot, bitmap, sk,
addr_comp, udptable->log);
此时bitmap中包含了所有哈希到hslot的端口的使用情况,下面要做的就是从first 位置开始,每次递增rand(保证哈希值不变),查找符合条件的端口:端口在low~high的可用范围内;端口还没有被占用。do{} while循环的判断条件snum!=first和snum+=rand一起保证了所有哈希到hslot的端口号都会被遍历到。如果找到了可用端口号, 即跳出,执行插入sk的操作,否则++first,查找下一个键值,直到fisrt==last,表明所有键值都已轮循一遍,仍没有结果,则 退出,sk插入失败。
snum = first;
do {
if (low <= snum && snum <= high &&
!test_bit(snum >> udptable->log, bitmap))
goto found;
snum += rand;
} while (snum != first);
spin_unlock_bh(&hslot->lock);
} while (++first != last);
goto fail;
流程图:
当没有在当前内核udp_table中找到匹配项时,执行插入新sk的操作。首先给sk 参数赋值:inet_num, udp_port_hash, udp_portaddr_hash。然后将sk加入到hash表和hash2表中,并增加相应计数。
found:
inet_sk(sk)->inet_num = snum;
udp_sk(sk)->udp_port_hash = snum;
udp_sk(sk)->udp_portaddr_hash ^= snum;
if (sk_unhashed(sk)) {
sk_nulls_add_node_rcu(sk, &hslot->head);
hslot->count++;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
spin_lock(&hslot2->lock);
hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
&hslot2->head);
hslot2->count++;
spin_unlock(&hslot2->lock);
}
sock如何被内核访问 创建的udp socket成功后,当使用该socket与外部通信时,协议 栈会收到发往该socket的udp报文。 udp_rcv() -> __udp4_lib_rcv() -> __udp4_lib_lookup() (编辑:应用网_镇江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

![Linux内核分析 - 网络[十二]:UDP模块 - socket](/uploads/allimg/c160205/145460415042S0-3U46.gif)
![Linux内核分析 - 网络[十二]:UDP模块 - socket](/uploads/allimg/c160205/145460415106410-42X9.gif)
