Linux tcp 慢启动重启

背景

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

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值