2. 中断基础:硬件中断与软中断的概念、区别与触发机制
中断是操作系统响应异步事件的底层机制,也是网络收包流程的起点。理解硬件中断(Hard IRQ)与软中断(SoftIRQ)的分工与协作,是掌握 NAPI 轮询优化的前提。本节从概念、触发流程到差异对比,建立完整的中断知识框架。
2.1 硬件中断(Hard IRQ)
硬件中断由外设(如网卡、磁盘控制器)通过中断控制器向 CPU 发送的电信号触发。CPU 在每条指令执行结束后检查中断线,若检测到中断请求,则保存当前上下文,跳转到中断向量表(IDT)中注册的处理函数。
关键特征:
- 异步性:与 CPU 当前执行流无关,由硬件事件随机触发。
- 优先级高:CPU 响应后立即屏蔽同级或低级中断,保证实时性。
- 执行上下文受限:在中断上下文中运行,不能睡眠、不能持有锁、不能访问用户空间。
- 处理时间极短:典型要求 < 10μs,否则会导致中断延迟增大或丢包。
在 Linux 网络收包路径中,网卡收到数据帧后通过 PCIe 总线向中断控制器发送 MSI-X 中断(Message Signaled Interrupts),中断控制器将中断路由到特定 CPU 核心,触发注册的 net_interrupt() 处理函数。
2.2 软中断(SoftIRQ)
软中断是 Linux 内核实现的 下半部机制(Bottom Half),用于处理硬件中断中无法完成的耗时操作。软中断在 do_softirq() 或内核线程 ksoftirqd 中执行,运行在 软中断上下文,允许被硬件中断抢占,但自身不可睡眠。
| 软中断编号 | 符号名称 | 用途 | 优先级 |
|---|---|---|---|
| 0 | HI_SOFTIRQ |
高优先级 tasklet | 最高 |
| 1 | TIMER_SOFTIRQ |
定时器处理 | 高 |
| 2 | NET_TX_SOFTIRQ |
网络发送完成处理 | 中 |
| 3 | NET_RX_SOFTIRQ |
网络接收处理(核心) | 中 |
| 4 | BLOCK_SOFTIRQ |
块设备 I/O 完成 | 中 |
| 5 | IRQ_POLL_SOFTIRQ |
中断轮询 | 低 |
| 6 | TASKLET_SOFTIRQ |
普通 tasklet | 低 |
网络收包关键软中断:NET_RX_SOFTIRQ(编号 3)是 NAPI 轮询机制的执行载体。硬件中断处理程序仅做“唤醒”操作——标记 NET_RX_SOFTIRQ 位,真正的数据包处理(协议栈解析、skb 分发)全部在软中断中完成。
2.3 硬件中断与软中断的核心区别
| 对比维度 | 硬件中断(Hard IRQ) | 软中断(SoftIRQ) |
|---|---|---|
| 触发源 | 硬件设备(网卡、磁盘、定时器) | 内核代码主动触发(raise_softirq()) |
| 执行上下文 | 中断上下文(不可睡眠、不可调度) | 软中断上下文(可被硬中断抢占,不可睡眠) |
| 优先级 | 最高,CPU 立即响应 | 低于硬件中断,高于普通进程 |
| 处理时间 | 必须极短(< 10μs) | 可稍长(通常 < 1ms) |
| 并发性 | 同一中断线在 CPU 上串行 | 同一类型软中断可在多 CPU 上并行 |
| 典型操作 | ACK 设备、拷贝寄存器、触发软中断 | 协议栈处理、skb 队列操作、NAPI 轮询 |
| 内核线程 | 无,直接由 CPU 中断门进入 | ksoftirqd/n(每个 CPU 一个) |
2.4 触发机制与执行路径
Linux 内核中软中断的触发与执行遵循以下路径:
- 硬件中断触发:网卡产生中断信号 → 中断控制器路由到 CPU → CPU 执行
do_IRQ()→ 调用注册的handler(如ixgbe_msix_clean())。 - 标记软中断:handler 中调用
__raise_softirq_irqoff(NET_RX_SOFTIRQ),在irq_cpustat_t中设置对应位。 - 软中断执行时机:
- 中断返回前:
irq_exit()中调用invoke_softirq(),若满足条件则直接执行do_softirq()。 - ksoftirqd 内核线程:若软中断执行时间过长或 pending 位持续存在,唤醒
ksoftirqd/n线程处理。 - 显式调用:如
local_bh_enable()释放软中断锁时检查并执行。
- 中断返回前:
- 网络软中断处理:
net_rx_action()被调用,遍历 NAPI 设备列表执行轮询(poll),将数据帧送入协议栈。
性能陷阱:如果硬件中断处理程序执行时间过长(例如在 handler 中直接处理数据包),会导致中断延迟飙升、CPU 被长时间占用、其他设备中断丢失。正确的设计是 handler 仅做“最小必要操作”,将数据包处理全部推入软中断。
2.5 中断亲和性与 CPU 负载均衡
通过设置 /proc/irq/{IRQ_NUM}/smp_affinity,可以将特定硬件中断绑定到指定 CPU 核心。网络收包场景中,通常将不同网卡队列的中断绑定到不同 CPU,实现 RSS(Receive Side Scaling) 负载均衡。
# 查看网卡中断号
cat /proc/interrupts | grep eth0
# 将中断 78 绑定到 CPU 0-1(十六进制 0x3)
echo 3 > /proc/irq/78/smp_affinity
# 查看当前绑定
cat /proc/irq/78/smp_affinity
中断亲和性直接影响软中断的 CPU 分布:硬件中断在哪个 CPU 上执行,对应的 NET_RX_SOFTIRQ 也会在同一 CPU 上被触发。因此,合理的中断绑定可以避免 CPU 缓存抖动,提升收包吞吐量。
2.6 中断上下文的性能约束
理解中断上下文的限制,对后续 NAPI 轮询设计至关重要:
- 不可睡眠:中断上下文不在进程调度范围内,调用
schedule()或等待锁会导致内核 panic。 - 栈空间有限:中断栈通常只有 4KB~16KB,不能进行深层递归或大内存分配。
- 中断屏蔽:执行硬件中断处理程序时,本地 CPU 会屏蔽同级及更低级中断,过长的处理会阻塞其他设备中断。
- 时间敏感:实时系统要求硬件中断处理时间 < 10μs,软中断处理时间 < 1ms。
实践要点:在编写或调试网卡驱动时,应确保 ndo_poll() 回调(NAPI 轮询函数)在软中断上下文中执行,而非硬件中断处理程序。硬件中断处理程序只做三件事:
- 读取设备状态寄存器确认中断来源。
- 关闭该队列的硬件中断(NAPI 机制要求)。
- 调用
napi_schedule()触发NET_RX_SOFTIRQ。