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

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

发布时间:2016-04-19 00:43:42 所属栏目:Linux 来源:网络整理
导读:内核版本:2.6.34 这部分内容在于说明socket创建后如何被内核协议栈访问到,只关注两个问题:sock何时插入内核 表的,sock如何被内核访问的。对于核心的sock的

如果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个表项,结果会如下图:

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

声明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;

流程图:

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

当没有在当前内核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()

(编辑:佛山站长网)

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

热点阅读