2. 中断处理流程:CPU中断响应、中断上下文切换、中断处理函数执行
硬中断(Hard IRQ)的处理并非瞬时完成,而是由CPU按照严格的硬件-软件协同协议分阶段执行。理解这一流程是进行亲缘性调优的前提——只有知道中断在哪一刻被分发、哪一刻被阻塞、哪一刻完成,才能精准地将中断绑定到最合适的CPU核心上。
2.1 中断处理三阶段总览
一次完整的硬中断处理可拆解为三个阶段:CPU中断响应(硬件自动完成)、中断上下文切换(保存现场)、中断处理函数执行(ISR运行)。下表对比了各阶段的核心特征:
| 阶段 | 触发者 | 执行环境 | 是否可被其他中断抢占 | 耗时 |
|---|---|---|---|---|
| CPU中断响应 | 硬件(中断控制器+CPU微架构) | CPU微码/硬件状态机 | 否(硬件自动屏蔽同级及更低级中断) | 纳秒级 |
| 中断上下文切换 | 内核entry代码(汇编) | 内核栈(中断栈) | 部分可(视中断优先级和CPU标志位) | 微秒级 |
| 中断处理函数执行 | 内核C代码(ISR) | 中断上下文(不可睡眠) | 可被更高优先级中断抢占 | 微秒~毫秒级 |
调优启示:亲缘性调优主要影响的是第一阶段——中断控制器将中断信号路由到哪个CPU。但后两个阶段的效率(尤其是上下文切换时的TLB/缓存污染)决定了绑定后是否真正受益。
2.2 阶段一:CPU中断响应(硬件自动完成)
当网卡收到数据包后,通过PCIe总线向中断控制器(如x86的APIC或ARM的GIC)发送中断消息。中断控制器根据预先配置的中断亲缘性掩码(IRQ affinity mask),将中断信号仅发送到掩码中指定的CPU核心。该CPU在完成当前指令后,立即执行以下硬件操作:
- 保存关键寄存器:CPU自动将当前指令指针(RIP)、代码段(CS)、标志寄存器(RFLAGS)压入内核栈。
- 切换栈指针:从用户栈或内核进程栈切换到中断栈(Interrupt Stack),防止中断嵌套导致栈溢出。
- 屏蔽同级中断:硬件自动设置CPU的IF(Interrupt Flag)或PIC的IMR(Interrupt Mask Register),阻止同优先级或更低优先级的中断再次触发。
- 跳转到中断向量表:根据中断号查找IDT(Interrupt Descriptor Table),跳转到对应的内核入口函数。
关键指标:从网卡发出中断信号到CPU开始执行第一条内核指令,通常耗时 50~200ns。这个延迟受CPU频率、缓存命中率和中断控制器路由速度影响。
2.3 阶段二:中断上下文切换(保存现场)
CPU跳转到IDT入口后,执行内核汇编代码(如x86的entry_64.S中的common_interrupt)。这一阶段的核心任务是保存被中断任务的完整上下文,以便ISR执行完毕后能无缝恢复原任务。
- 保存通用寄存器:将rax、rbx、rcx、rdx、rsi、rdi、r8~r15等通用寄存器压入中断栈。
- 保存段寄存器和标志位:包括cs、ds、es、fs、gs以及rflags。
- 保存中断号:将中断向量号作为参数传递给后续的C函数。
- 切换页表(可选):如果中断发生在用户态,需要切换到内核页表;如果中断发生在内核态且使用同一组页表,则跳过此步以节省开销。
- 调用do_IRQ():最终调用C语言编写的通用中断分发函数
do_IRQ()或handle_irq()。
性能陷阱:上下文切换阶段会污染CPU的L1指令缓存和数据缓存。如果频繁将中断绑定到某个CPU,该CPU的缓存命中率可能因频繁的上下文切换而下降。实测表明,每10万次中断/秒的上下文切换,会导致该CPU的L1缓存命中率下降约2~5%。
2.4 阶段三:中断处理函数执行(ISR运行)
do_IRQ()根据中断号查找全局中断描述符表(irq_desc),调用注册在该中断线上的中断处理函数(ISR)。对于网卡多队列场景,每个队列通常注册独立的ISR,分别处理对应队列的数据包。
ISR的执行环境具有以下约束:
- 不可睡眠:ISR运行在中断上下文中,不能调用可能导致睡眠的函数(如
kmalloc(GFP_KERNEL)、mutex_lock())。 - 不可触发缺页中断:ISR访问的内存必须已经映射在页表中,否则会导致双重中断(double fault)。
- 执行时间应极短:ISR执行期间,同CPU上的其他中断(同优先级或更低优先级)被屏蔽。长时间ISR会导致中断延迟抖动(jitter)。
典型的网卡ISR执行流程如下:
- 读取中断状态寄存器:确认中断来源(哪个队列产生了中断)。
- 禁用该队列中断:防止同一队列在ISR处理期间再次触发中断(避免活锁)。
- 调度NAPI软中断:将数据包处理任务交给
net_rx_action()软中断,ISR本身只做最轻量的通知。 - 写中断结束寄存器(EOI):通知中断控制器当前中断已处理完毕,允许后续中断到达。
- 返回:执行
iretq指令,恢复被中断任务的上下文。
调优实战要点:在亲缘性调优中,我们通常将同一网卡队列的硬中断ISR和NAPI软中断绑定到同一个CPU核心,以最大化L1/L2缓存的热度。如果硬中断和软中断分散在不同CPU上,会导致缓存行 bouncing,吞吐量下降10~30%。
2.5 中断处理流程中的关键性能指标
在调优实践中,需要关注以下指标来评估中断处理流程的健康度:
| 指标 | 测量位置 | 健康值 | 异常值(需调优) |
|---|---|---|---|
| 中断响应延迟(IRQ latency) | 从设备发出中断到CPU开始执行ISR | < 1μs | > 5μs(可能中断风暴或亲缘性错误) |
| ISR执行时间 | ISR函数开始到返回 | < 10μs | > 50μs(ISR中做了过多工作) |
| 中断栈使用率 | 内核统计(/proc/interrupts 间接观察) | < 30% | > 70%(可能栈溢出风险) |
| 同一CPU上的中断频率 | /proc/interrupts 按CPU列统计 | 各CPU均衡或按设计分布 | 单个CPU中断率超过其他CPU 3倍以上 |
实战检查命令:使用
cat /proc/interrupts | grep eth0 查看网卡各队列的中断分布;使用 cat /proc/softirqs | grep NET_RX 查看软中断分布。如果硬中断和软中断不在同一CPU上,说明需要调整RPS(Receive Packet Steering)或亲缘性设置。