你在浏览器里敲了个回车,数据穿越了 7 层协议、3 个内核队列、2 次 DMA,最后从网线飞出去。
网络协议栈是 Linux 最复杂的子系统之一。一条消息从 send() 到网线,经历 socket -> 传输层 -> 网络层 -> 邻居子系统 -> netfilter -> qdisc -> 驱动 -> 硬件。不懂全路径,调优就是瞎蒙。
应用层 -> socket -> 系统调用 -> Socket 层 -> 传输层(TCP/UDP)-> 网络层(IP+路由+netfilter) -> 邻居子系统(ARP) -> qdisc-> 设备驱动(Ring Buffer + NAPI) -> 硬件(NIC + DMA)
send() -> tcp_sendmsg() -> 拷贝到 sk_buff -> tcp_push() -> IP 路由 -> Netfilter 钩子 -> ARP 查找 -> qdisc 入队 -> 驱动 xmit -> DMA -> 网线
网卡 -> DMA -> Ring Buffer -> 硬中断 -> NAPI -> 软中断 -> Netfilter -> IP -> TCP -> socket 队列 -> recvmsg() -> 用户态
每个 TCP socket 有独立的发送/接收缓冲区。发送缓冲区满时 send() 阻塞——这是背压机制。
cat /proc/sys/net/core/wmem_default # 默认 16KBcat /proc/sys/net/core/wmem_max # 最大 128KBcat /proc/sys/net/ipv4/tcp_wmem # min default maxcat /proc/sys/net/ipv4/tcp_rmem
BDP = 带宽 x RTT。缓冲区 >= BDP 才能跑满带宽。1Gbps x 100ms = 12.5MB。
echo "4096 131072 25000000" > /proc/sys/net/ipv4/tcp_rmemecho "4096 131072 25000000" > /proc/sys/net/ipv4/tcp_wmemecho 25000000 > /proc/sys/net/core/rmem_maxecho 25000000 > /proc/sys/net/core/wmem_max
SYN -> SYN+ACK -> ACK,内核通过 SYN Queue 和 Accept Queue 管理。队列满则丢连接。
net.core.somaxconn = 65535 # Accept Queue 长度net.ipv4.tcp_max_syn_backlog = 65535 # SYN Queue 长度netstat -s | grep overflow # 查看队列溢出
攒数据到 MSS 才发。交互式场景引入 200ms 延迟,应禁用:
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
BBR 在 0.1% 丢包时吞吐几乎不受影响,cubic 下降 90%。
04
PART 04:Netfilter + qdisc + NAPI
iptables 规则线性遍历。1000 条规则吞吐下降 30%,5000 条下降 60%。
优化:ipset 替代多条规则、nftables 哈希查找、eBPF/XDP 完全绕过。
Linux 默认 fq_codel。每个流独立队列,延迟超标时主动丢包(解决 BufferBloat)。
高吞吐场景下传统中断方式导致 CPU 被淹没。NAPI 批量轮询收包,减少中断次数。
RSS 多队列 + 中断亲和性将中断分散到多 CPU:
ethtool -L eth0 combined 8echo 1 > /proc/irq/N/smp_affinity # 分配中断到 CPU 0ethtool -C eth0 rx-usecs 1 # 低延迟模式
1. 有丢包吗? ethtool -S eth0 | grep drop netstat -s | grep -E "overflow|drop"2. 延迟在哪? ss -ti | grep -E "rtt|retrans"3. CPU 瓶颈? cat /proc/interrupts | grep eth0 perf top -e net:*
Nginx 10Gbps 带宽利用率 30%。排查:Ring Buffer 太小(增大)、中断集中(开 RSS)、qdisc 丢包(增大队列)、iptables 8000 条规则(ipset 替代)。结果提升到 85%。
网络调优 = BDP 缓冲区 + TCP_NODELAY + BBR + RSS 多队列 + fq_codel + ipset/nftables。七层协议每层都可能卡,每层都有解。
GRO(Generic Receive Offload) 将多个小包合并为一个大 skb,减少协议栈处理次数。TSO(TCP Segment Offload) 让网卡硬件完成 TCP 分段,减少 CPU 开销。
ethtool -k eth0 | grep -E "gro|gso|tso"# GRO 开启(默认),CPU 降低 30%ethtool -K eth0 gro on# TSO 开启(大文件传输场景)ethtool -K eth0 tso on
某些虚拟化环境(VMware ESXi)和网络抓包场景需要关闭 GRO,否则 tcpdump 看不到单个小包。
网络收包的后半段在软中断(softirq)中执行。软中断过多会导致 ksoftirqd 进程占用大量 CPU。
# 查看软中断统计cat /proc/softirqs | grep NET# 如果 NET_RX 大幅增长,说明软中断处理压力大# 需要 RSS 多队列或升级网卡
软中断的 CPU 时间占比可以通过 /proc/softirqs 观察。如果 NET_RX 占用了大量 CPU 时间且集中在单核,说明需要打开 RSS 多队列。
END