欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux tcp 慢启动重启

程序员文章站 2022-07-13 17:21:43
...

背景

在Linux内核实现了,tcp发送端在连接建立开始时,会通过慢启动流程来避免一次性发送太多的数据到网络,引起网络拥塞丢包,如果tcp连接建立好了之后,发送端正常发送数据,并且已经完成慢启动流程了,这时候发送端没有数据发送,进入idle空闲期,然后过了一段时间才继续发送,因为当前的tcp拥塞状态已经不处于慢启动阶段了,可能拥塞窗口值是比较大的,但是现在的网络环境可能也已经发生变化了,如果按当前的拥塞窗口直接发送数据,有可能会造成拥塞丢包,因此内核提供了sysctl_tcp_slow_start_after_idle参数,用来解决这种场景。

tcp_transmit_skb

发送端在每发送一个数据包时,都会记录当前的时间信息;

/* Congestion state accounting after a packet has been sent. */
static void tcp_event_data_sent(struct tcp_sock *tp,
				struct sock *sk)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	const u32 now = tcp_time_stamp;

	if (tcp_packets_in_flight(tp) == 0)
		tcp_ca_event(sk, CA_EVENT_TX_START);

	tp->lsndtime = now;

	/* If it is a reply for ato after last received
	 * packet, enter pingpong mode.
	 */
	if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato)
		icsk->icsk_ack.pingpong = 1;
}

tcp_sendmsg

tcp发送数据时,新申请一个数据包加入发送队列,进入skb_entail,然后调用tcp_slow_start_after_idle_check检查是否需要重新进入慢启动流程;

static void skb_entail(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);

	skb->csum    = 0;
	tcb->seq     = tcb->end_seq = tp->write_seq;
	tcb->tcp_flags = TCPHDR_ACK;
	tcb->sacked  = 0;
	__skb_header_release(skb);
	tcp_add_write_queue_tail(sk, skb);
	sk->sk_wmem_queued += skb->truesize;
	sk_mem_charge(sk, skb->truesize);
	if (tp->nonagle & TCP_NAGLE_PUSH)
		tp->nonagle &= ~TCP_NAGLE_PUSH;

	tcp_slow_start_after_idle_check(sk);
}

在tcp_slow_start_after_idle_check里,检查如果sysctl_tcp_slow_start_after_idle有开启,并且packets_out为0,并且不是采用bbr算法,则判断当前时间距离上一次发送时间是否超过一个rto,如果超过,则重新进入慢启动流程,调整拥塞窗口。

static inline void tcp_slow_start_after_idle_check(struct sock *sk)
{
	const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
	struct tcp_sock *tp = tcp_sk(sk);
	s32 delta;

	if (!sysctl_tcp_slow_start_after_idle || tp->packets_out ||
	    ca_ops->cong_control)
		return;
	delta = tcp_time_stamp - tp->lsndtime;
	//在tcp_event_data_sent里,每次新transmit一个新的数据包,都会同步更新
	//tp->lendtime为当前的时钟周期值,这里判断本次发送距离上一次发送如果超过
	//了一个rto,那说明是发送端在经历一段idle空闲期后,又重新发送数据了,这时候
	//网络状态可能也发生了变化了,为了避免一次性发送太多数据,
	//这里判断sysctl_tcp_slow_start_after_idle如果有配置,就重新按慢启动流程发送
	if (delta > inet_csk(sk)->icsk_rto)
		tcp_cwnd_restart(sk, delta);
}

 

相关标签: Linux网络协议栈