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);
}
上一篇: nordic 52832 蓝牙广播名与UID的修改
下一篇: Zigbee协议学习记录