最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

TCP

运维笔记admin36浏览0评论

TCP

TCP

经典的Reno算法实现了三个拥塞函数,如下所示:

struct tcp_congestion_ops tcp_reno = {.flags      = TCP_CONG_NON_RESTRICTED,.name       = "reno",.owner      = THIS_MODULE,.ssthresh   = tcp_reno_ssthresh,.cong_avoid = tcp_reno_cong_avoid,.undo_cwnd  = tcp_reno_undo_cwnd,
};

TCP套接口在进入TCP_CA_CWR/Recovery/Loss拥塞状态时,将执行tcp_congestion_ops拥塞结构的成员函数ssthresh,更新慢启动阈值。如下所示,将拥塞窗口减半,但是,不低于2。

/* Slow start threshold is half the congestion window (min 2) */
u32 tcp_reno_ssthresh(struct sock *sk)
{const struct tcp_sock *tp = tcp_sk(sk);return max(tp->snd_cwnd >> 1U, 2U);
}

在拥接收到ACK确认报文时,调用cong_avoid指针函数,即tcp_reno_cong_avoid。如果当前拥塞窗口足够使用,不进行处理。如果TCP套接口处于慢启动阶段,由函数tcp_slow_start处理拥塞窗口的增长,返回值acked表示慢启动结束后,还剩余的确认报文数量,这部分剩余数量用于拥塞避免阶段的窗口处理,即函数tcp_cong_avoid_ai。

/* This is Jacobson's slow start and congestion avoidance. SIGCOMM '88, p. 328.*/
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
{struct tcp_sock *tp = tcp_sk(sk);if (!tcp_is_cwnd_limited(sk))return;/* In "safe" area, increase. */if (tcp_in_slow_start(tp)) {acked = tcp_slow_start(tp, acked);if (!acked)return;}/* In dangerous area, increase slowly. */tcp_cong_avoid_ai(tp, tp->snd_cwnd, acked);
}

在慢启动阶段,新的拥塞窗口等于原窗口加上ACK确认的报文数量,但是,如果结果值超过慢启动阈值ssthresh,超出部分不执行窗口增加操作,交由拥塞避免阶段算法处理。

/* Slow start is used when congestion window is no greater than the slow start* threshold. We base on RFC2581 and also handle stretch ACKs properly.* We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but* something better;) a packet is only considered (s)acked in its entirety to* defend the ACK attacks described in the RFC. Slow start processes a stretch* ACK of degree N as if N acks of degree 1 are received back to back except* ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and* returns the leftover acks to adjust cwnd in congestion avoidance mode.*/
u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)
{u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh);acked -= cwnd - tp->snd_cwnd;tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);return acked;
}

在拥塞避免阶段,如果拥塞计数snd_cwnd_cnt大于当前窗口,清空计数,将当前拥塞窗口加一(snd_cwnd)。反之,如果拥塞计数snd_cwnd_cnt小于当前窗口,将计数增加acked,如果增加之后,计数大于等于当前窗口w,新的拥塞窗口(snd_cwnd)增加值为计数除以当前窗口的整数值,余数还保存在窗口计数变量中(snd_cwnd_cnt)。拥塞避免阶段,对于每个ACK报文,窗口增长遵循以下公式:

c w n d + = 1 c w n d cwnd += \frac{1}{cwnd} cwnd+=cwnd1​

/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd (or alternative w),* for every packet that was ACKed.*/
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked)
{/* If credits accumulated at a higher w, apply them gently now. */if (tp->snd_cwnd_cnt >= w) {tp->snd_cwnd_cnt = 0;tp->snd_cwnd++;}tp->snd_cwnd_cnt += acked;if (tp->snd_cwnd_cnt >= w) {u32 delta = tp->snd_cwnd_cnt / w;tp->snd_cwnd_cnt -= delta * w;tp->snd_cwnd += delta;}tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_cwnd_clamp);
}

Reno处理拥塞撤销的函数tcp_reno_undo_cwnd,返回当前拥塞窗口和拥塞发生前窗口两者之间的最大值。

u32 tcp_reno_undo_cwnd(struct sock *sk)
{const struct tcp_sock *tp = tcp_sk(sk);return max(tp->snd_cwnd, tp->prior_cwnd);
}

内核版本 5.0

发布评论

评论列表(0)

  1. 暂无评论