Linux内核协议栈对于timewait状态的处理

最近在做操作系统升级时,发现升级后的系统处于TIME_WAIT状态的连接数明显增多(内核版本 2.6.18 -> 2.6.32)。

原因

2.6.18 与 2.6.32 的 diff 结果

net/ipv4/inet_timewait_sock.c

导致TIME_WAIT状态增多,正是由于

twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));

位置的改变。

具体分析

1. 关键数据结构

inet_timewait_death_row: 用于管理timewait控制块的数据结构,位置: include/net/inet_timewait_sock.h

此数据结构,可以分为两部分看,一部分处理 tw_recycle 开启时timewait块的快速回收,另一部分为未开启时用于等待时间较长的timewait块的回收。由于系统没有开启 tw_recycle , 因此我们主要关注等待时间较长的timewait块回收。

用于等待时间较长的主要成员变量:

int period : tw_timer 定时器的超时时间固定值为 TCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS,其中 TCP_TIMEWAIT_LEN 为 60 * HZ (60s),INET_TWDR_TWKILL_SLOTS 为 8。

u32 thread_slots : 用于标识未完成的timewait块的位图。

struct work_struct twkill_work : 分批删除(默认值为每次删除100个)cells中timewait块时的工作队列。

struct timer_list tw_timer : 定时器,每过 period,触发一次 inet_twdr_hangman()

以下是此数据结构的初始化:

inet_timewait_sock: 用于组成 tcp_timewait_sock 结构,其前部是 sock_common 的前部。位置: include/net/inet_timewait_sock.h

此数据结构暂时只需要知道 tw_substate 即可。 tw_substate : TCP状态迁移到 FIN_WAIT2TIME_WAIT 状态时,协议栈会用 timewait 块取代 tcp_sock 块,因为这两种状态都需要由定时器处理,超时立即释放。其对外状态都表现为 TIME_WAIT , 但其内部状态还是有分别,通过 tw_substate 进行区分。


2. timewait块释放时的逻辑

inet_twdr_hangman() 此函数是定时器到期时执行的函数,用于释放timewait块。

用于慢timewait块释放的逻辑可参考下图:

timewait


3. 结论

按照 2.6.18 中的逻辑,如果一次没有全部删除一个slot中的timewait控制块, twdr->slot 仍然会执行 + 1 操作。此时如果有一个tcp_sock进入FIN_WAIT2状态,则此时的timewait(tw_substate = fin_wait2)块会被放在上一个slot中,而此时有一个线程正在处理那个队列,因此会导致处于FIN_WAIT2状态的timewait块被提前释放,若此时对端的FIN分节到达,协议栈会回复一个RST分节。

为了修复此BUG,2.6.32 协议栈中修改了 twdr->slot + 1 的时机,每次必须完全释放一个slot中所有的timewait块后,才会进行 + 1 操作。这也就是说协议栈不保证在 TCP_TWKILL_PERIOD 周期内,移动一个格子,所以当系统繁忙时,会导致timewait块的等待时间大于 TCP_TIMEWAIT_LEN

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值