TCP 拥塞控制:从 Jacobson 的 AIMD 到 CUBIC 与 BBR
内容提要
本文梳理TCP拥塞控制的演进,先澄清慢启动、快速恢复、BBRv3等常见误解,再依据RFC 5681与Linux源码讲解Reno规则,用Chiu–Jain模型解释AIMD,推导Mathis公式,并介绍CUBIC、BBRv1及BBRv3草案,最后通过模拟比较三者的性能与公平性。
延伸解读
常见误解的澄清
文章指出几个广泛流传的错误:慢启动并非每个ACK窗口翻倍,而是每个ACK加一个报文段,翻倍发生在每个RTT;快速恢复不是Jacobson 1988年论文的成果,而是1990年4.3BSD Reno引入;Linux主线内核至今仍使用BBRv1,BBRv3仅存在于Google分支和IETF草案;BBR是否抢占CUBIC带宽取决于瓶颈缓冲区大小。这些澄清有助于读者避免被不准确的信息误导。
AIMD的数学基础与Reno的局限
Chiu-Jain模型证明,加性增加乘性减少(AIMD)是同时收敛到效率和公平的最快规则。Mathis公式则揭示Reno的两个结构性问题:吞吐与RTT成反比,导致RTT不公平;高带宽时延积需要极低丢包率,例如10Gbit/s、100ms路径要求丢包率低于2.2e-10,否则恢复时间长达数十分钟。这些分析解释了CUBIC等高速TCP出现的动机。
CUBIC与BBR的设计权衡
CUBIC用三次函数替代线性增长,窗口增长基于真实时间而非RTT,改善了RTT公平性,并通过Reno友好区保持与Reno的兼容。BBR则基于带宽和RTT估计,旨在避免bufferbloat,但模拟显示其在浅缓冲区对CUBIC不公平,在深缓冲区份额受限于在途数据上限,且对随机丢包不敏感。两者各有适用场景,选择需考虑缓冲区深度、延迟敏感性和公平性要求。
模拟结果的启示与局限
文章通过包级模拟比较了Reno、CUBIC和简化BBRv1。结果显示:Reno在缓冲区小于BDP时利用率不足;CUBIC排队延迟随缓冲区线性增长;BBR排队延迟低但利用率约97%。BBR与CUBIC竞争时,浅缓冲区BBR占优,深缓冲区CUBIC反超。但模拟省略了BBRv1的丢包恢复和监管器模型,且BBRv3尚未在主线实现,因此结论不能直接外推到真实网络或BBRv3。
Q&A
TCP慢启动是每收到一个ACK窗口就翻倍吗?
不是。RFC 5681规定慢启动阶段每收到一个确认新数据的ACK,cwnd增加min(N, SMSS),即每个ACK只加一个报文段;窗口翻倍发生在每个RTT,因为一个RTT内会收到约cwnd/SMSS个ACK。
快速恢复是Jacobson在1988年提出的吗?
不是。Jacobson 1988年的论文列出了七个算法,其中包括快速重传,但没有快速恢复。快速恢复出自1990年的4.3BSD Reno,来源是Jacobson 1990年4月30日发到end2end-interest邮件列表的“Modified TCP Congestion Avoidance Algorithm”。
Linux主线内核现在默认使用BBRv3吗?
不是。截至Linux v7.2,主线net/ipv4/tcp_bbr.c仍是2016年发表的BBRv1,默认拥塞控制算法仍是CUBIC。BBRv3只存在于Google的内核分支和IETF的实验性草案draft-ietf-ccwg-bbr中。
BBR总是会抢走CUBIC的带宽吗?
不一定。BBR与CUBIC竞争时的带宽份额主要取决于瓶颈缓冲区的大小。浅缓冲区(如0.5到1倍BDP)时BBR占优,份额约0.86到0.89;深缓冲区(如8倍BDP)时CUBIC占优,BBR份额降至约0.36。
CUBIC的窗口增长与RTT有关吗?
在Reno友好区之外,CUBIC的窗口增长只取决于真实时间,与RTT无关。其窗口函数为W_cubic(t) = C(t - K)^3 + W_max,其中K是窗口回到W_max所需的时间,与RTT无关。因此不同RTT的流在稳态下窗口接近,模拟中RTT 20ms与80ms的CUBIC流吞吐比接近1:1。
为什么Reno在高带宽时延积网络中效率低?
Reno的吞吐与RTT成反比,且高带宽时延积需要极低的丢包率。根据Mathis公式,吞吐BW ≈ 1.22*MSS/(RTT*sqrt(p))。例如10 Gbit/s、100 ms、1500字节报文,填满管道需要约83,333个包,要求丢包率p ≤ 2.2×10^-10,约每46亿个包才允许丢一个;一次丢包后从W/2爬回W要约69分钟。
BBRv3草案与BBRv1有哪些主要区别?
BBRv3草案(draft-ietf-ccwg-bbr-06)相对v1的主要变化包括:STARTUP步调增益从2.885改为4ln2≈2.77,窗口增益从2.885改为2;DRAIN步调增益从0.35改为0.5;丢包率超过2%时停止探测并按Beta=0.7下调上限;引入长期和短期在途数据上限,巡航时留15%余量;PROBE_RTT每5秒一次,窗口降到0.5倍BDP;带宽探测间隔取2到3秒随机值与约62到63个RTT中的较小者;PROBE_BW改为四个状态(DOWN、CRUISE、REFILL、UP)。
在Linux上如何查看和切换TCP拥塞控制算法?
可以用sysctl查看当前算法和可用算法:sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_available_congestion_control。切换全局默认:modprobe tcp_bbr后sysctl -w net.ipv4.tcp_congestion_control=bbr。单个连接可用TCP_CONGESTION套接字选项选择,例如setsockopt(socket.IPPROTO_TCP, socket.TCP_CONGESTION, b"reno")。请求未加载的算法会返回ENOENT。