2. 中断基础:硬件中断与软中断的概念、区别与触发机制

中断是操作系统响应异步事件的底层机制,也是网络收包流程的起点。理解硬件中断(Hard IRQ)与软中断(SoftIRQ)的分工与协作,是掌握 NAPI 轮询优化的前提。本节从概念、触发流程到差异对比,建立完整的中断知识框架。

硬件中断与软中断协同流程 硬件中断与软中断协同流程 硬件层 网卡设备 中断控制器 CPU 硬件中断 中断信号 IRQ 路由 内核软件层 中断处理程序 软中断调度 NET_RX_SOFTIRQ 触发注册的 handler 标记 softirq 位 ksoftirqd/do_softirq 说明:硬件中断处理必须极短(通常 < 10μs),仅做必要硬件操作后立即触发软中断,将数据包处理推迟到软中断上下文中完成。

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 中执行,运行在 软中断上下文,允许被硬件中断抢占,但自身不可睡眠。

Linux 内核预定义的软中断类型(部分)
软中断编号 符号名称 用途 优先级
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 内核中软中断的触发与执行遵循以下路径:

  1. 硬件中断触发:网卡产生中断信号 → 中断控制器路由到 CPU → CPU 执行 do_IRQ() → 调用注册的 handler(如 ixgbe_msix_clean())。
  2. 标记软中断:handler 中调用 __raise_softirq_irqoff(NET_RX_SOFTIRQ),在 irq_cpustat_t 中设置对应位。
  3. 软中断执行时机:
    • 中断返回前:irq_exit() 中调用 invoke_softirq(),若满足条件则直接执行 do_softirq()。
    • ksoftirqd 内核线程:若软中断执行时间过长或 pending 位持续存在,唤醒 ksoftirqd/n 线程处理。
    • 显式调用:如 local_bh_enable() 释放软中断锁时检查并执行。
  4. 网络软中断处理: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 缓存抖动,提升收包吞吐量。

中断亲和性与软中断分布 中断亲和性与软中断分布 网卡多队列 Queue 0 Queue 1 Queue 2 CPU 核心 CPU 0 CPU 1 CPU 2 软中断分布 NET_RX_SOFTIRQ on CPU 0 NET_RX_SOFTIRQ on CPU 1 NET_RX_SOFTIRQ on CPU 2

2.6 中断上下文的性能约束

理解中断上下文的限制,对后续 NAPI 轮询设计至关重要:

  • 不可睡眠:中断上下文不在进程调度范围内,调用 schedule() 或等待锁会导致内核 panic。
  • 栈空间有限:中断栈通常只有 4KB~16KB,不能进行深层递归或大内存分配。
  • 中断屏蔽:执行硬件中断处理程序时,本地 CPU 会屏蔽同级及更低级中断,过长的处理会阻塞其他设备中断。
  • 时间敏感:实时系统要求硬件中断处理时间 < 10μs,软中断处理时间 < 1ms。

实践要点:在编写或调试网卡驱动时,应确保 ndo_poll() 回调(NAPI 轮询函数)在软中断上下文中执行,而非硬件中断处理程序。硬件中断处理程序只做三件事:

  1. 读取设备状态寄存器确认中断来源。
  2. 关闭该队列的硬件中断(NAPI 机制要求)。
  3. 调用 napi_schedule() 触发 NET_RX_SOFTIRQ。