精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-07-05
一、前情回顾 上一节《socket地址绑定》中提到,应用程序传递过来的端口在内核中需要检查端口是否可用:
按照前面的例子来分析,这里是调用了tcp_prot结构变量中的get_prot函数指针,该函数位于net/ipv4/Inet_connection_sock.c中;这个函数比较长,也是我们今天要分析的重点;
二、端口的管理 1、端口管理数据结构 Linux内核将所有socket使用时的端口通过一个哈希表来管理,该哈希表存放在全局变量tcp_hashinfo中,通过tcp_prot变量的h成员引用,该成员是一个联合类型;对于tcp套接字类型,其引用存放在h. hashinfo成员中;下面是tcp_hashinfo的结构体类型:
端口管理相关的,目前可以只关注加注释的这三个成员,其中bhash为已经哈希表结构,bhash_size为哈希表的大小;所有哈希表中的节点内存都是在bind_bucket_cachep高速缓存中分配;
下面看一下inet_bind_hashbucket结构体:
inet_bind_hashbucket是哈希桶结构,lock成员是用于操作时对桶进行加锁,chain成员是相同哈希值的节点的链表;示意图如下:
2、默认端口的分配 当应用程序没有指定端口时(如socket客户端连接到服务端时,会由内核从可用端口中分配一个给该socket); 看看下面的代码(参见net/ipv4/Inet_connection_sock.c: inet_csk_get_port()函数):
这里,随机端口的范围是32768~61000;上面代码的逻辑如下: <!--[if !supportLists]-->1) <!--[endif]-->从[32768, 61000]中随机取一个端口rover; <!--[if !supportLists]-->2) <!--[endif]-->计算该端口的hash值,然后从全局变量tcp_hashinfo的哈希表bhash中取出相同哈希值的链表head; <!--[if !supportLists]-->3) <!--[endif]-->遍历链表head,检查每个节点的网络设备是否和当前网络设置相同,同时检查节点的端口是否和rover相同; <!--[if !supportLists]-->4) <!--[endif]-->如果相同,表明端口被占用,继续下一个端口;如果和链表head中的节点都不相同,则跳出循环,继续后面的逻辑;
inet_bind_bucket_foreach宏利用《创建socket》一文中提到的container_of宏来实现 的,大家可以自己看看;
3、端口重用 当应用程序指定端口时,参考下面的源代码:
此时同样会检查该端口有没有被占用;如果被占用,会检查端口重用(跳转到tb_found):
<!--[if !supportLists]-->1) <!--[endif]-->端口节点结构
前面提到的哈希桶结构中的chain链表中的每个节点,其宿主结构体是inet_bind_bucket,该结构体通过成员node链入链表; <!--[if !supportLists]-->2) <!--[endif]-->检查端口是否可重用 这里涉及到两个属性,一个是socket的sk_reuse,另一个是inet_bind_bucket的fastreuse; sk_reuse可以通过setsockopt()库函数进行设置,其值为0或1,当为1时,表示当一个socket进入TCP_TIME_WAIT状态(连接关闭已经完成)后,它所占用的端口马上能够被重用,这在调试服务器时比较有用,重启程序不用进行等待;而fastreuse代表该端口是否允许被重用: <!--[if !supportLists]-->l <!--[endif]-->当该端口第一次被使用时(owners为空),如果sk_reuse为1且socket状态不为TCP_LISTEN,则设置fastreuse为1,否则设置为0; <!--[if !supportLists]-->l <!--[endif]-->当该端口同时被其他socket使用时(owners不为空),如果当前端口能被重用,但是当前socket的sk_reuse为0或其状态为TCP_LISTEN,则将fastreuse设置为0,标记为不能重用; <!--[if !supportLists]-->3) <!--[endif]-->当不能重用时,再次检查冲突 此时会调用inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)再次检查端口是否冲突;回想《创建socket》一文中提到,创建socket成功后,要使用相应的协议来初始化socket,对于tcp协议来说,其初始化方法是net/ipv4/Tcp_ipv4.c:tcp_v4_init_sock(),其中就做了如下一步的设置:
下面看看这里再次检查冲突的代码:
上面函数的逻辑是:从owners中遍历绑定在该端口上的socket,如果某socket跟当前的socket不是同一个,并且是绑定在同一个网络设备接口上的,并且它们两个之中至少有一个的sk_reuse表示自己的端口不能被重用或该socket已经是TCP_LISTEN状态了,并且它们两个之中至少有一个没有指定接收IP地址,或者两个都指定接收地址,但是接收地址是相同的,则冲突产生,否则不冲突。 也就是说,不使用同一个接收地址的socket可以共用端口号,绑定在不同的网络设备接口上的socket可以共用端口号,或者两个socket都表示自己可以被重用,并且还不在TCP_LISTEN状态,则可以重用端口号。
4、新建inet_bind_bucket 当在bhash中没有找到指定的端口时,需要创建新的桶节点,然后挂入bhash中:
有兴趣的可以自己看看这段代码的实现,这里就不再展开了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-07-06
感谢楼主的辛勤劳动,希望多多发表此类文章哦,现在非常需要这方面的东西
|
|
返回顶楼 | |
发表时间:2011-07-06
hwbest 写道 感谢楼主的辛勤劳动,希望多多发表此类文章哦,现在非常需要这方面的东西
共同学习,以后会不定期更新blog,请关注。 |
|
返回顶楼 | |
浏览 3446 次