一台双路服务器,100G 网卡直连对端,iperf3 在丢包为 0 的情况下怎么都跑不过 50Gbps,CPU 却早就烧到 90% 以上。这是我在云峦KeyarchOS 上做网络调优时遇到的一个现场。排查到最后,问题不在网卡、不在 PCIe 链路,而在内核处理每个数据包本身的成本。于是 BIG TCP 进入了我的视野,并且我在这套系统上完整验证了一个遍。这篇文章就是我自己的实践记录:BIG TCP 解决什么问题、原理是什么、在 KeyarchOS 上怎么开、开了以后到底值不值,以及过程中踩过的坑。
1. 先泼盆冷水:BIG TCP 不是让链路提速,而是让 CPU 少干活
很多人第一次听到 BIG TCP,会误以为它让网卡在物理链路上发更大的包,从而把 100G 跑满。实际完全不是这个概念。BIG TCP 改变的不是链路带宽,而是内核里“一个数据包能承载多少数据”的上限。这个上限从默认的 64KB 放大到 512KB 甚至更高,CPU 处理同样吞吐量时,面对的数据包数量就少了一个数量级。
1.1 我在 100G 网卡上看到的诡异瓶颈
当时的现场是这样的:两台配置接近的服务器直连,网卡都是支持 100G 的多队列网卡,PCIe、线缆、交换模块都确认过没有问题。跑 iperf3,不管怎么调整线程数、窗口大小、MTU,吞吐都卡在大约 45~50Gbps。最扎眼的指标是 CPU 占用:每个测试线程对应的 CPU 核心 softirq 占比非常高,整体 CPU 已经接近饱和,但网卡侧统计却显示远远没有到硬件上限。
后来用 perf top 看,热点集中在网络协议栈的收包路径、skb 分配释放、以及 GRO 相关函数上。也就是说,链路还有富余,CPU 先被数据包“淹没”了。为什么会被淹没?因为默认情况下,内核每次从网卡驱动拿到的包,经过 GRO 聚合后最大也就是 64KB 左右。100Gbps 全速跑起来,每秒要处理的包数量非常惊人,每个包即使只花几百纳秒的 CPU 时间,累加起来也足以把核心打满。
1.2 一句话理解 BIG TCP 的收益
BIG TCP 做的事情,简单说就是把“单次交给协议栈处理的包”从 64KB 级别放大到 512KB 甚至 1MB 级别。CPU 每处理一个 skb,都要做协议头解析、路由查找、队列操作、软中断调度等一堆事。如果一次能处理 512KB 的数据,处理相同数据量需要的包数量就降到原来的八分之一左右,CPU 开销自然大幅下降。
它适合的场景非常明确:大包长流、高带宽、CPU 已经成为瓶颈的数据中心业务,比如 AI 训练集群的分布式通信、大数据 shuffle、存储备份、跨机数据同步这些。不适合的场景也很明确:大量小消息、延迟敏感业务、依赖 IPv4-only 的老旧环境。这一点很重要,我后面专门讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从 64KB 到 512KB:BIG TCP 到底改了哪一层
想真正用好一个内核特性,不能只会上命令,还得知道它在数据路径上的位置。BIG TCP 的逻辑基础,是 Linux 网络栈里已经存在很多年的 GSO/GRO 机制。
2.1 发送路径上,TSO/GSO 已经帮 CPU 省了很多事
应用层通过 TCP 发送一大块数据时,内核不会直接把这块数据切成一个个 MTU 大小的包再交给网卡。现代网卡支持 TSO(TCP Segmentation Offload),也就是“发送分段卸载”。内核只把一个很大的待发送数据单元交给驱动,网卡自己把它切成符合 MTU 的多个帧发出去。如果没有 TSO,内核每发送一个 MTU 大小的包就要陷入一次协议栈处理,100G 场景下会直接把 CPU 打爆。
GSO 则是 TSO 的软件实现,用于网卡不支持 TSO 的情况。它在软件层把大块数据拆成多个 skb,但拆包的粒度仍然远大于 MTU,目的是让上层协议只处理一次“大包”。
2.2 接收路径上,GRO 把很多收包合并成一个收包
接收方向对应的机制是 GRO(Generic Receive Offload)。网卡收到一堆连续的小帧后,驱动会在软件层把它们合并成一个更大的 skb 再交给协议栈,这样上层只需要处理一个 skb,而不是几百个 skb。和发送方向一样,合并的上限在默认情况下也是 64KB。
没有 GRO 的话,100G 网卡满速收小包时,每秒中断和软中断能把 CPU 按在地上摩擦。所以很多人说“大包靠 TSO,收包靠 GRO”,这个说法基本准确。
2.3 IPv4 的 16 位长度字段,成了 BIG TCP 绕不过去的墙
为什么默认上限是 64KB?因为传统 IPv4 报文头里的
