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() (编辑:佛山站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |