UDP 端口和传输控制块管理

UDP提供了无连接通信,且不对传送数据包进行可靠性保证,不用对数据进行超时重传,也不用对数据进行应答(ACK),适合于一次传输少量数据,UDP传输的可靠性由应用层负责. 这里主要分析UDP 传输控制块(sock)的管理, port获取, udp查找传输控制块.

 

1. UDP sock管理

sock在内核中表示了通信双方的所有信息,接收和发送数据,都要找到对应的sock, 为了加快查找速度,udp实现了两个hash 表。

hash 表初始化

void __init udp_table_init(struct udp_table *table, const char *name)

{

unsigned int i;

/*分配分配hash表结构udp_hslot,这里乘以2,表示 分配两种类型的hash表*/

table->hash = alloc_large_system_hash(name,2 * sizeof(struct udp_hslot),uhash_entries,21,0,&table->log,&table->mask,UDP_HTABLE_SIZE_MIN,64 * 1024);

/*第二类hash表内存,mask表示hash表的个数*/

table->hash2 = table->hash + (table->mask + 1);

/*初始化对应的hash 表 */

for (i = 0; i <= table->mask; i++) {

INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);

table->hash[i].count = 0;

spin_lock_init(&table->hash[i].lock);

}

for (i = 0; i <= table->mask; i++) {

INIT_HLIST_NULLS_HEAD(&table->hash2[i].head, i);

table->hash2[i].count = 0;

spin_lock_init(&table->hash2[i].lock);

}

}

目前内核中log=11,mask=2048-1 ; 也即每种hash表有2048个hash表

udp 根据port 去进行hash运算

hash布局如下:


 

 

2 sock hash运算


 

在用户空间进场调用bind接口时,如指定port,则直接根据port找到对应的hash表,如果port==0,则随机选取一个port,并进行hash运行

int udp_lib_get_port(struct sock *sk, unsigned short snum,

int (*saddr_comp)(const struct sock *sk1,

const struct sock *sk2),

unsigned int hash2_nulladdr)

{

struct udp_hslot *hslot, *hslot2;

struct udp_table *udptable = sk->sk_prot->h.udp_table;

int error = 1;

struct net *net = sock_net(sk);

if (!snum) { /*处理port=0的情况 */

int low, high, remaining;

unsigned int rand;

unsigned short first, last;

DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN);

/* 获取系统端口的范围*/

inet_get_local_port_range(net, &low, &high);

remaining = (high - low) + 1;


 

/*获取随机数,这样可以获取到随机的port */

rand = prandom_u32();

first = reciprocal_scale(rand, remaining) + low;

/*把rand变成hash表大小的奇数倍,这样可以遍历hash表中的所有port,并且不会产生连续的port ,可以结合上面hash表布局图来看*/

rand = (rand | 1) * (udptable->mask + 1);

last = first + udptable->mask + 1;

do{ /*获取第一个hash表 */

hslot = udp_hashslot(udptable, net, first);

/*用于port是否使用的标记 */

bitmap_zero(bitmap, PORTS_PER_CHAIN);

spin_lock_bh(&hslot->lock);

/*标记hash表中的port是否被使用,一个bit对应一个port */

udp_lib_lport_inuse(net, snum, hslot, bitmap, sk,

saddr_comp, udptable->log);


 

snum = first;

do {

if (low <= snum && snum <= high &&

!test_bit(snum >> udptable->log, bitmap) &&

!inet_is_local_reserved_port(net, snum))

goto found;

snum += rand; /*如果snum被使用,则获取下一个port,这里加上奇数倍hash表大小,可以遍历hash表中的所有port,直到snum回到first */

} while (snum != first);

spin_unlock_bh(&hslot->lock);

} while (++first != last);/ *进行下一个hast表的遍历 */

goto fail;

} else { /*这里处理指定port的类型,一般用于服务端 */

hslot = udp_hashslot(udptable, net, snum);

spin_lock_bh(&hslot->lock);

if (hslot->count > 10) {/*如果hslot的个数大于10,则优先在slot2中查找,这样可以加快速度 */

int exist;

/*下面对slot2的遍历查找,与上面类似 */

unsigned int slot2 = udp_sk(sk)->udp_portaddr_hash ^ snum;

slot2 &= udptable->mask;

hash2_nulladdr &= udptable->mask;

hslot2 = udp_hashslot2(udptable, slot2);

if (hslot->count < hslot2->count)

goto scan_primary_hash;

exist = udp_lib_lport_inuse2(net, snum, hslot2,/*判读snum释放被使用 */

sk, saddr_comp);

if (!exist && (hash2_nulladdr != slot2)) {

hslot2 = udp_hashslot2(udptable, hash2_nulladdr);

exist = udp_lib_lport_inuse2(net, snum, hslot2,

sk, saddr_comp);

}

if (exist)

goto fail_unlock;

else

goto found;

}

scan_primary_hash:

if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk,

saddr_comp, 0))

goto fail_unlock;

}

found: /* 当找到一个port后,把对应的sock,添加到hast表*/

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);}


 

 

3. sock查找

当ip层接收到数据后,根据源IP,目的IP, 目的port去查找对应的sock,实现函数为__udp4_lib_lookup

sock同时加入了两个hash表,查找时,如果hash1的个数大于10,则优先从hash2中查找,只要弄懂hash过程,从hash中查找则相对简单。


 


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值