2. 网络收包路径全景:从网卡到应用的数据流

理解网络收包的全路径,是配置软中断CPU亲和性的前提。数据从物理网线到达用户态应用,依次经过DMA传输、硬中断、软中断和协议栈处理四个核心阶段。每个阶段都涉及特定的CPU资源消耗,只有厘清这些环节,才能精准定位亲和性优化的切入点。

核心认知: 收包路径中,硬中断和软中断是CPU开销最集中的两个阶段。硬中断负责快速通知,软中断负责实际的数据搬运与协议解析。将软中断绑定到特定CPU核心,可以显著提升缓存命中率并减少锁竞争。

2.1 收包路径四阶段总览

下图展示了从网卡到应用的数据流全景,包含DMA、硬中断、软中断和协议栈四个关键环节:

网络收包路径全景:从网卡到应用 ① 网卡接收 DMA写入Ring Buffer ② 硬中断 触发IRQ,唤醒ksoftirqd ③ 软中断 ksoftirqd处理数据 ④ 协议栈 IP/TCP/UDP解析 ⑤ 应用层 socket读取数据 关键优化点 软中断绑定到特定CPU核心 可提升缓存命中率30%~50% 各阶段CPU资源消耗特征 • DMA:不占用CPU,由网卡硬件完成 • 硬中断:短暂占用CPU,执行最小必要操作 • 软中断:主要CPU消耗阶段,执行协议栈处理 • 协议栈:在软中断上下文中完成,不额外调度

2.2 阶段一:DMA与Ring Buffer

当数据帧到达网卡时,网卡通过DMA(Direct Memory Access)直接将数据写入主内存中的Ring Buffer。此过程完全由硬件完成,不占用CPU周期。

组件 作用 CPU参与
网卡硬件 接收数据帧,解析MAC地址 否
DMA引擎 将数据从网卡FIFO搬运到内存 否
Ring Buffer 环形队列,存储sk_buff指针 否(分配时需CPU)
性能提示: Ring Buffer的大小直接影响丢包率。过小会导致DMA写入失败(丢包),过大会增加内存占用。建议根据网卡型号和流量特征调整 ethtool -g eth0 查看当前配置。

2.3 阶段二:硬中断(Hard IRQ)

DMA完成后,网卡通过PCIe总线向CPU发送硬中断信号。CPU收到中断后,立即暂停当前任务,执行中断处理程序(ISR)。

硬中断处理程序执行最小必要操作:

  • 确认中断来源(哪个网卡、哪个队列)
  • 禁止该中断线(防止嵌套)
  • 标记软中断需要处理(raise_softirq)
  • 唤醒ksoftirqd内核线程
硬中断处理流程 网卡触发IRQ CPU暂停当前任务 执行ISR 唤醒ksoftirqd 硬中断执行时间通常 < 10μs,仅做必要标记 中断亲和性(IRQ Affinity)决定哪个CPU处理硬中断
注意: 硬中断处理时间过长会阻塞其他中断,导致系统响应延迟。因此ISR必须快速返回,将繁重工作交给软中断。这也是NAPI(New API)的设计初衷——在流量高时合并中断,减少硬中断次数。

2.4 阶段三:软中断(SoftIRQ)

硬中断处理程序唤醒ksoftirqd内核线程后,软中断开始执行真正的数据包处理。软中断运行在软件中断上下文,可以响应其他硬中断,但不能睡眠。

软中断处理的核心任务:

  • 从Ring Buffer取出sk_buff(socket buffer)
  • 调用网卡驱动的poll方法批量处理数据包
  • 将数据包送入协议栈(IP层、TCP/UDP层)
  • 将处理后的数据放入socket接收队列
软中断类型 向量号 处理函数 主要工作
NET_TX_SOFTIRQ 1 net_tx_action 发送数据包
NET_RX_SOFTIRQ 0 net_rx_action 接收数据包(本课程重点)
软中断(NET_RX_SOFTIRQ)处理流程 ksoftirqd运行 调用net_rx_action 驱动poll方法 批量取包 协议栈处理(IP/TCP) 放入socket接收队列 软中断运行在哪个CPU核心,由ksoftirqd的CPU亲和性决定 默认情况下,ksoftirqd在触发硬中断的CPU上运行 通过配置亲和性,可将软中断迁移到指定核心

2.5 阶段四:协议栈处理

协议栈处理在软中断上下文中完成,不额外调度。主要步骤:

  1. 链路层: 检查MAC地址、VLAN标签,剥离以太网帧头
  2. 网络层: 检查IP头部,路由查找,处理分片重组
  3. 传输层: TCP/UDP头部校验,查找socket,处理流量控制
  4. 数据交付: 将数据放入socket的接收缓冲区,唤醒等待的应用进程
性能关键: 协议栈处理是CPU密集型的操作。每个数据包都需要进行多次校验、查找和拷贝。将软中断绑定到具有独立L2缓存的CPU核心,可以显著减少缓存未命中,提升吞吐量。实测表明,合理的亲和性配置可使收包性能提升20%~40%。

2.6 各阶段CPU资源消耗对比

阶段 CPU占用 执行上下文 可抢占 优化手段
DMA 0% 硬件 — 增大Ring Buffer
硬中断 < 5% 中断上下文 否 IRQ Affinity
软中断 60%~80% 软中断上下文 否 CPU亲和性(本课程核心)
协议栈 15%~30% 软中断上下文 否 GRO/LRO、RPS
应用读取 5%~10% 进程上下文 是 epoll、零拷贝

2.7 关键数据结构:sk_buff

sk_buff 是网络收包路径中最重要的数据结构,贯穿DMA到协议栈的整个过程。理解其结构有助于优化内存访问:

struct sk_buff {
    /* 链表指针 */
    struct sk_buff    *next;
    struct sk_buff    *prev;

    /* 协议层指针 */
    struct net_device *dev;
    unsigned int      len;          // 数据总长度
    unsigned int      data_len;     // 数据段长度
    __u16             protocol;     // 协议类型(ETH_P_IP等)

    /* 头部指针 */
    sk_buff_data_t    transport_header;  // 传输层头部
    sk_buff_data_t    network_header;    // 网络层头部
    sk_buff_data_t    mac_header;        // 链路层头部

    /* 数据指针 */
    unsigned char     *head;        // 缓冲区起始
    unsigned char     *data;        // 数据起始
    unsigned char     *tail;        // 数据结束
    unsigned char     *end;         // 缓冲区结束

    /* 其他字段 */
    struct sock       *sk;          // 关联的socket
    atomic_t          users;        // 引用计数
};
优化提示: sk_buff的分配和释放是收包路径中的高频操作。使用slab分配器预分配sk_buff缓存,可以减少内存分配开销。在亲和性配置中,确保软中断所在CPU的sk_buff缓存热,可减少跨CPU内存访问。

2.8 本章小结

网络收包路径是一个从硬件到软件的完整流水线:

  • DMA 负责数据搬运,不消耗CPU
  • 硬中断 快速通知CPU,执行最小操作
  • 软中断 承担主要处理工作,是性能优化的核心
  • 协议栈 在软中断上下文中完成数据解析与交付