TCP定时器之超时重传定时器

每条TCP连接都会维护一个超时重传定时器,该定时器是TCP保证可靠性的一个非常重要的手段,一旦该定时器超时,那么就会重传还未收到ACK的报文。这篇笔记就来看看该定时器相关的代码实现。1. 相关数据结构struct inet_connection_sock {... //icsk_retransmit_timer的超时时刻,jiffies超过该值时定时器超时 unsigned long ...
摘要由CSDN通过智能技术生成

每条TCP连接都会维护一个超时重传定时器,该定时器是TCP保证可靠性的一个非常重要的手段,一旦该定时器超时,那么就会重传还未收到ACK的报文。这篇笔记就来看看该定时器相关的代码实现。

1. 相关数据结构

struct inet_connection_sock {
   
...
	//icsk_retransmit_timer的超时时刻,jiffies超过该值时定时器超时
	unsigned long		  icsk_timeout;
	//超时重传定时器、持续定时器(还有其它)
 	struct timer_list	  icsk_retransmit_timer;
...
	//拥塞状态
	__u8			  icsk_ca_state;
	//发生超时重传的次数。当退出LOSS状态时该计数器清零
	__u8			  icsk_retransmits;
	//icsk_retransmit_timer定时器函数可以处理4个定时器,
	//当前应该处理哪个事件也需要区分,0表示没有事件需要处理
	__u8			  icsk_pending;
...
};

2. 初始化

超时重传定时器是在socket()系统调用执行过程中初始化的,具体在创建TCB后调用tcp_v4_sock_init()初始化其TCP相关字段时完成的,代码如下:

static int tcp_v4_init_sock(struct sock *sk)
{
   
...
	tcp_init_xmit_timers(sk);
...
}

void tcp_init_xmit_timers(struct sock *sk)
{
   
	//超时重传定时器的定时器处理函数为tcp_write_time()
	inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
				  &tcp_keepalive_timer);
}

/*
 * Using different timers for retransmit, delayed acks and probes
 * We may wish use just one timer maintaining a list of expire jiffies
 * to optimize.
 */
//该函数安装三个定时器
void inet_csk_init_xmit_timers(struct sock *sk,
			       void (*retransmit_handler)(unsigned long),
			       void (*delack_handler)(unsigned long),
			       void (*keepalive_handler)(unsigned long))
{
   
	struct inet_connection_sock *icsk = inet_csk(sk);

	setup_timer(&icsk->icsk_retransmit_timer, retransmit_handler,
			(unsigned long)sk);
	setup_timer(&icsk->icsk_delack_timer, delack_handler,
			(unsigned long)sk);
	setup_timer(&sk->sk_timer, keepalive_handler, (unsigned long)sk);
	//由于同一个定时器函数可以处理多个定时器,它们也需要进行区分,pending参数表示
	//当前需要处理的是哪个定时事件,0表示没有事件需要处理
	icsk->icsk_pending = icsk->icsk_ack.pending = 0;
}

//setup_timer()仅仅是初始化定时器参数,并没有启动定时器
static inline void setup_timer(struct timer_list * timer,
				void (*function)(unsigned long),
				unsigned long data)
{
   
	timer->function = function;
	//data参数就是TCB指针
	timer->data = data;
	init_timer(timer);
}

3. 启动定时器

启动定时器由函数inet_csk_reset_xmit_timer()完成。

/*
 *	Reset the retransmission timer
 */
@what: 表示要复位的定时器,对于超时重传定时器,该值为ICSK_TIME_RETRANS;
@when: 表示该定时器再过几个滴答超时;
@max_when: when可取的最大值,如果指定when超过了max_when,那么只取max_when.
static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,
					     unsigned long when,
					     const unsigned long max_when)
{
   
	struct inet_connection_sock *icsk = inet_csk(sk);

	//矫正when参数
	if (when > max_when) {
   
		when = max_when;
	}
	//关注what为ICSK_TIME_RETRANS的情况
	if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0) {
   
		//将事件记录到icsk_pending中表示启动的定时器是超时重传定时器
		icsk->icsk_pending = what;
		//记录超时时间点到icsk_timeout
		icsk->icsk_timeout = jiffies + when;
		//重新启动定时器
		sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
	} else if (what == ICSK_TIME_DACK) {
   
		icsk->icsk_ack.pending |= ICSK_ACK_TIMER;
		icsk->icsk_ack.timeout = jiffies + when;
		sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);
	}
}

void sk_reset_timer(struct sock *sk, struct timer_list* timer,
		    unsigned long expires)
{
   
	//定时器启动期间会持有TCB的引用,防止其被释放
	if (!mod_timer(timer, expires))
		sock_hold(sk);
}

3.1 启动时机

超时重传定时器会在如下几种情况下激活:

  1. 发送了第一包新数据(第一次发送或者之间发送的已经全部被确认);
  2. 路径MTU探测失败后;
  3. 发现接收端将SACK确认过的数据丢掉后。

4. 超时处理

RTO超时后,由定时器函数tcp_write_timer()开始处理.

在整个超时处理过程中,当前只关注连接态的处理。

4.1 tcp_write_timer()

static void tcp_write_timer(unsigned
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值