2. 网络协议栈基础回顾:数据包从网卡到应用层的完整路径(RX/TX路径)
在深入 GRO/GSO 卸载技术之前,必须牢固掌握数据包在内核协议栈中的完整生命周期。理解接收(RX)和发送(TX)路径上的每一个关键节点,是后续分析卸载点、瓶颈以及优化收益的前提。本章将拆解这两个路径,并聚焦于协议栈各层之间的数据交互与内存管理。
2.1 接收路径(RX):从线缆到 Socket
数据包到达网卡后,需要经过硬件捕获、内核处理、协议解析,最终交付给用户态应用。整个过程可分为四个主要阶段:
- 硬件接收与 DMA:网卡将帧写入预分配的内存缓冲区(Ring Buffer)。
- 中断处理与 NAPI:网卡触发中断,内核以轮询方式批量收包。
- 协议栈处理:从链路层逐层向上解析,直至传输层。
- Socket 交付:数据放入接收队列,唤醒等待的应用进程。
关键指标: 接收路径的延迟通常由“硬中断 → 软中断 → 协议处理”的流水线决定。NAPI 的引入将每次中断处理的收包数从 1 提升到 N,显著降低了高吞吐场景下的 CPU 开销。
2.2 发送路径(TX):从 Socket 到线缆
发送路径是接收路径的逆过程,但逻辑更为复杂,因为内核需要负责分片、校验和计算以及排队规则(Qdisc)。
- 系统调用:应用调用
send()或write(),数据从用户态拷贝到内核态。 - 协议封装:传输层添加 TCP/UDP 头,网络层添加 IP 头,链路层添加 MAC 头。
- Qdisc 排队:数据包进入传输队列,等待内核流量控制策略调度。
- 驱动发送:驱动将 sk_buff 映射到 DMA 区域,通知网卡发送。
- 发送完成中断:网卡发送完毕后触发中断,释放 sk_buff 和 DMA 缓冲区。
性能陷阱: TX 路径上的锁竞争(如 Qdisc 锁、设备队列锁)和过多的内存拷贝是常见的性能瓶颈。GSO 技术正是为了减少协议封装阶段的 CPU 消耗而设计的。
2.3 核心数据结构:sk_buff
在整个 RX/TX 路径中,struct sk_buff(套接字缓冲区)是贯穿始终的核心载体。它承载了从原始帧到协议头的所有元数据。
| 字段 | 作用 | 路径相关性 |
|---|---|---|
data / head |
指向数据缓冲区的起始和当前数据指针 | RX/TX 均需操作 |
len |
当前有效数据长度 | 协议层校验 |
protocol |
L3 协议类型(如 ETH_P_IP) | RX 路径分发 |
dev |
关联的网络设备 | RX/TX 路由 |
sk |
指向关联的 Socket | TX 路径绑定 |
destructor |
释放 sk_buff 时的回调函数 | 内存管理 |
优化视角: sk_buff 的分配与释放是协议栈的热点路径。GRO 通过合并多个 sk_buff 为一个巨型 sk_buff,减少了上层协议栈的处理次数;GSO 则推迟了 sk_buff 的分割,降低了协议封装阶段的 CPU 开销。
2.4 协议栈分层处理与卸载切入点
下表总结了各层在 RX/TX 路径上的主要职责,并标注了 GRO/GSO 的卸载切入点:
| 协议层 | RX 职责 | TX 职责 | 卸载切入点 |
|---|---|---|---|
| 链路层 (L2) | 帧校验、类型分发 | MAC 头封装 | — |
| 网络层 (L3) | IP 重组、路由查找 | IP 分片、路由选择 | GRO 可合并 IP 分片 |
| 传输层 (L4) | TCP 重组、校验和验证 | TCP 分段、校验和计算 | GRO 合并 TCP 段;GSO 推迟分段 |
| Socket 层 | 数据拷贝到用户态 | 数据从用户态拷贝 | — |
2.5 路径上的关键性能开销
在未启用卸载技术时,RX/TX 路径存在以下典型开销:
- 每包中断开销:传统模式下每个数据包触发一次中断,高 PPS(包每秒)场景下 CPU 被中断淹没。
- 协议头处理:每个包独立进行校验和计算、分片/重组、头部分析。
- sk_buff 元数据操作:大量小包的 sk_buff 分配、链表操作、引用计数更新。
- 内存带宽瓶颈:小包场景下,协议头占比高,有效数据吞吐受限。
GRO/GSO 的优化哲学: 通过“合并”减少上层协议栈的处理次数(RX 方向),或通过“推迟分割”减少协议封装的重复计算(TX 方向),从而将 CPU 从 per-packet 开销中解放出来,提升整体吞吐量。