1. NAPI 概述:背景与动机
1.1 传统中断方式的瓶颈
在早期 Linux 网络设备驱动中,每当一个数据包到达网卡(NIC),硬件会触发一个 IRQ(中断请求),CPU 立即暂停当前任务,保存上下文,执行中断服务程序(ISR)将数据包从硬件 FIFO 拷贝到内核协议栈。这种“每包必中断”的模式在低速网络(10/100 Mbps)下表现良好,但当链路速率提升到 1 Gbps 甚至 10 Gbps 时,中断风暴成为性能杀手。
| 瓶颈维度 | 传统中断方式表现 | 后果 |
|---|---|---|
| 中断频率 | 每包触发一次 IRQ,10 Gbps 下可达 14.88 Mpps(64 字节包) | CPU 被中断淹没,用户态进程几乎得不到时间片 |
| 上下文切换开销 | 每次中断需保存/恢复寄存器、TLB、Cache 状态 | Cache 污染严重,有效吞吐量下降 30%~50% |
| 中断亲和性 | 中断可能在不同 CPU 间漂移 | Cache 局部性被破坏,跨 NUMA 访问延迟增加 |
| 活锁(Livelock) | 高负载下 CPU 100% 忙于处理中断,无法处理上层协议 | 系统吞吐量归零,网络看似“死机” |
⚠ 活锁场景: 当网络负载超过 CPU 的中断处理能力时,每个新中断都会打断正在处理上一个包的中断下半部(softirq),导致没有任何一个包能被完整送入协议栈。系统看起来“忙碌”,但实际吞吐量为 0。
1.2 NAPI 的核心思想:中断 + 轮询混合
NAPI(New API)由 Alexey Kuznetsov 在 Linux 2.4.20 中引入,核心思想是:用一次中断唤醒轮询,用轮询批量处理后续数据包。它打破了“每包必中断”的僵化模式,将中断处理与轮询处理有机结合。
🔑 核心公式: NAPI 吞吐量 ≈ 中断延迟 + (批量大小 × 单包处理时间) / 批量大小。当批量足够大时,中断开销被均摊到每个包上趋近于 0。
1.3 NAPI 的工作流程
- 中断触发: 网卡收到第一个数据包,硬件产生 IRQ,驱动 ISR 被调用。
- 关闭中断: ISR 立即关闭该网卡接收中断,并将设备注册到 CPU 的 softirq 轮询列表(poll_list)中。
- 调度 softirq: 触发 NET_RX_SOFTIRQ,内核在 softirq 上下文中调用驱动注册的 poll 回调函数。
- 轮询收包: poll 函数从硬件 FIFO 或 DMA ring 中批量读取数据包,每次最多处理 budget 个包(默认 64)。
- 退出条件:
- 如果包被取完(FIFO 空),则从 poll_list 移除,重新开启中断。
- 如果达到 budget 但仍有包,则保留在 poll_list 中,等待下一次 softirq 继续轮询。
1.4 NAPI 的关键数据结构
| 数据结构 | 所在文件 | 作用 |
|---|---|---|
struct napi_struct |
include/linux/netdevice.h |
NAPI 实例,包含 poll 回调、设备指针、状态标志、poll_list 节点 |
poll_list |
softirq 上下文 |
每个 CPU 维护一个链表,挂载所有待轮询的 napi_struct |
budget |
net/core/dev.c |
每次 softirq 允许处理的最大包数(默认 300,每个设备最多 64) |
NAPI_STATE_SCHED |
include/linux/netdevice.h |
标志位,表示该 NAPI 实例已被调度到 poll_list 中 |
💡 设计哲学: NAPI 将“中断频率”与“数据包到达率”解耦。无论网络负载多高,中断频率上限被限制在
HZ / budget 量级(例如 1000 Hz / 64 ≈ 15.6 KHz),远低于 14.88 Mpps 的包到达率。
1.5 NAPI 的适用场景
- 高速网络(≥1 Gbps): 批量收包优势明显,吞吐量提升 2~5 倍。
- 低延迟场景: 首次包仍由中断触发,延迟与纯中断方式相当。
- 虚拟化环境: vhost-net、virtio 等虚拟网卡驱动广泛使用 NAPI。
- 不适用场景: 极低负载(如 IoT 设备,每秒几包),轮询反而浪费 CPU。
1.6 NAPI 的演进历史
| 内核版本 | 里程碑 |
|---|---|
| 2.4.20 | Alexey Kuznetsov 首次提交 NAPI 补丁 |
| 2.6.0 | NAPI 成为默认网络收包路径 |
| 2.6.24 | 支持多队列网卡的 RPS(Receive Packet Steering) |
| 3.8 | 引入 busy poll 机制,进一步降低轮询延迟 |
| 4.5 | NAPI 支持 GRO(Generic Receive Offload),批量合并小包 |
| 5.0+ | XDP(eXpress Data Path)与 NAPI 协同工作 |
📊 性能数据: 在 10 Gbps 测试中,传统中断方式在 64 字节小包下吞吐量仅 1.2 Gbps(CPU 100%),而 NAPI 轮询方式可达 9.8 Gbps(CPU 占用约 60%)。NAPI 将中断开销从每包 ~2 μs 降低到每批 ~0.1 μs。