2. 网络I/O模型演进:从轮询(Polling)到中断驱动,再到混合模型(NAPI)
网络I/O模型是网卡驱动性能的底层骨架。从早期的轮询到经典的中断驱动,再到Linux内核创新的NAPI混合模型,每一次演进都围绕一个核心矛盾:CPU利用率与网络吞吐量之间的权衡。理解这一演进路径,是后续调优中断合并与NAPI权重的基础。
2.1 轮询模型:以CPU换延迟
在早期操作系统或嵌入式裸机环境中,网卡驱动采用轮询(Polling)方式工作。CPU不断读取网卡状态寄存器,检查是否有新数据包到达。
核心特征:CPU 100%占用,无中断开销,但无法处理其他任务。
| 指标 | 轮询模型 | 适用场景 |
|---|---|---|
| 延迟 | 极低(微秒级) | 实时系统、高频交易 |
| CPU利用率 | 100%(空转浪费) | 专用硬件、单任务环境 |
| 吞吐量 | 受限于CPU频率 | 低负载、确定性场景 |
2.2 中断驱动模型:解放CPU,但引入新问题
随着多任务操作系统普及,中断驱动(Interrupt-Driven)模型成为主流。网卡在数据包到达时通过硬件中断通知CPU,CPU暂停当前任务,执行中断服务程序(ISR)处理数据。
关键改进:CPU只在有数据时被唤醒,空闲时可执行其他进程,大幅提升系统整体利用率。
中断风暴(Interrupt Storm):当网络流量超过某个阈值,中断频率高到CPU无法处理其他任务,系统吞吐量反而下降。这是驱动调优必须避免的状态。
2.3 NAPI混合模型:中断+轮询的优雅结合
NAPI(New API)是Linux内核在2.4.20版本引入的混合I/O模型,由David S. Miller等人设计。其核心思想:低负载用中断,高负载切轮询。
NAPI核心机制:中断关闭 + 轮询调度。第一个数据包触发中断后,驱动关闭中断并注册轮询回调,后续数据包通过轮询批量处理,处理完毕再重新开启中断。
2.4 三种模型对比总结
| 特性 | 轮询模型 | 中断驱动模型 | NAPI混合模型 |
|---|---|---|---|
| CPU空闲利用率 | 100%(空转) | 接近0%(可调度) | 接近0%(可调度) |
| 低负载延迟 | 极低 | 低(中断响应) | 低(中断响应) |
| 高负载吞吐量 | 受限于CPU频率 | 中断风暴,吞吐下降 | 线性增长,无风暴 |
| 中断次数 | 0 | 每个数据包1次 | 可调(合并后) |
| 缓存污染 | 低 | 高(频繁上下文切换) | 低(批量处理) |
| Linux内核支持 | 非标准驱动 | 传统驱动 | 所有现代驱动 |
2.5 NAPI关键数据结构
NAPI的核心数据结构是 napi_struct,每个NAPI实例对应一个轮询队列。驱动在初始化时注册poll回调函数和权重(weight)。
/* 内核源码:include/linux/netdevice.h */
struct napi_struct {
struct list_head poll_list; /* 轮询链表 */
unsigned long state; /* NAPI状态标志 */
int weight; /* 轮询权重(单次最大收包数) */
int (*poll)(struct napi_struct *, int); /* 轮询回调 */
struct net_device *dev; /* 关联网络设备 */
struct list_head dev_list; /* 设备链表 */
// ... 其他字段
};
权重(weight)是NAPI调优的核心参数。它决定了每次轮询回调最多处理多少个数据包。默认值通常为64,但实际调优需要根据CPU频率、网卡速率、包大小综合计算。
2.6 从模型到调优:NAPI权重的影响
NAPI权重直接影响CPU在中断和轮询之间的切换频率:
- 权重过小:轮询很快退出,频繁回到中断模式,中断次数增加,CPU开销上升。
- 权重过大:轮询占用CPU时间过长,导致其他任务延迟增大,甚至触发软锁检测。
- 理想值:使每次轮询刚好处理完一个调度周期内到达的数据包,既不浪费中断,也不过度占用CPU。