2. Linux中断子系统架构:从硬件中断到CPU响应,再到内核中断服务例程(ISR)的完整路径
本章深入剖析Linux中断子系统的完整架构,从硬件引脚上的电平变化,到CPU的异常响应,再到内核中注册的中断服务例程(ISR)被调用执行的完整路径。理解这一路径是掌握中断处理与NAPI轮询协同设计的基础。
2.1 中断子系统整体架构分层
Linux中断子系统采用分层设计,从上到下依次为:硬件层、中断控制器层、CPU异常处理层、通用中断框架层以及设备驱动层。每一层都封装了特定的职责,并通过标准接口与上下层交互。
核心观点: 中断路径的本质是“硬件事件 → 软件处理”的桥接过程。每一层都在减少CPU的轮询负担,提高事件响应效率。
2.2 硬件中断信号到CPU的传递路径
当外设产生中断时,信号传递遵循以下物理路径:
- 外设断言中断线: 设备通过其INT引脚拉高或拉低电平,向中断控制器发送请求。
- 中断控制器仲裁与路由: 控制器根据优先级、亲和性设置,决定将中断送达哪个CPU核心。
- CPU核心接收中断信号: CPU在指令边界检查INTR引脚或LAPIC的IRQ线,若中断未被屏蔽,则进入中断响应周期。
- CPU自动保存关键上下文: 硬件自动将EFLAGS/RFLAGS、CS、EIP/RIP压栈,并加载中断门描述符中的段选择子和偏移量。
| 阶段 | 典型延迟 | 影响因素 |
|---|---|---|
| 外设信号建立 | 10~100 ns | 总线协议、电气特性 |
| 中断控制器仲裁 | 50~500 ns | 优先级比较、路由表查找 |
| CPU响应与上下文保存 | 100~300 ns | 流水线冲刷、微码操作 |
| 内核入口代码执行 | 200~1000 ns | 页表切换、栈切换 |
2.3 CPU响应中断:从向量表到入口代码
CPU响应中断后,硬件强制跳转到中断向量表(x86的IDT,ARM的异常向量表)中对应的表项。该表项包含一个门描述符,指向内核的中断入口代码。
架构差异提示: x86架构使用IDT(中断描述符表),每个中断向量对应一个门描述符;ARM架构使用异常向量表(Exception Vector Table),通常位于0xFFFF0000或0x00000000。RISC-V则使用mtvec/stvec寄存器指向中断入口。
2.4 内核中断服务例程(ISR)的执行路径
CPU进入内核入口代码后,软件逻辑开始接管。以下是ISR被调用的完整路径:
- 保存完整寄存器上下文: 入口代码(如x86的
common_interrupt)将通用寄存器、段寄存器压栈,建立pt_regs结构。 - 调用中断流处理函数: 根据中断号查找
irq_desc,调用对应的handle_irq流处理函数(如handle_edge_irq、handle_level_irq)。 - 执行已注册的ISR: 流处理函数遍历
irq_desc->action链表,依次调用每个irqaction中的handler函数指针。 - 中断结束(EOI)与返回: ISR执行完毕后,流处理函数向中断控制器发送EOI命令,允许后续中断到达。最后执行
iret或eret指令恢复上下文。
关键约束: ISR运行在中断上下文中,必须快速完成(通常建议在微秒级)。ISR中禁止调用可能导致睡眠的函数(如
mutex_lock、kmalloc(GFP_KERNEL))。对于耗时操作,应使用中断下半部(Softirq/Tasklet/Workqueue)或NAPI轮询机制。
2.5 中断描述符(irq_desc)与中断流处理
内核使用irq_desc结构体管理每个中断号的状态与处理逻辑。该结构体是中断子系统的核心数据结构。
| 字段 | 类型 | 作用 |
|---|---|---|
irq_data |
struct irq_data |
包含中断号、芯片私有数据、中断类型(电平/边沿) |
handle_irq |
irq_flow_handler_t |
中断流处理函数指针,决定中断处理策略 |
action |
struct irqaction * |
指向已注册ISR的链表头,支持共享中断 |
status |
unsigned int |
中断状态标志(IRQ_INPROGRESS、IRQ_DISABLED等) |
depth |
unsigned int |
中断禁用嵌套深度,用于disable_irq/enable_irq配对 |
中断流处理函数是连接通用框架与具体ISR的桥梁。常见流处理函数包括:
- handle_simple_irq: 用于无需硬件控制器的简单中断,直接调用ISR。
- handle_level_irq: 用于电平触发中断,在ISR执行期间屏蔽中断源,防止重复触发。
- handle_edge_irq: 用于边沿触发中断,处理时需注意丢失的边沿事件。
- handle_fasteoi_irq: 用于支持EOI寄存器的中断控制器,如ARM GIC v2/v3。
2.6 中断路径中的关键数据结构关系
整个中断路径涉及多个关键数据结构,它们之间的协作关系如下:
- irq_domain: 将硬件中断号(hwirq)映射到Linux IRQ号,是中断控制器与通用框架的桥梁。
- irq_chip: 封装中断控制器的底层操作(mask/unmask/ack/eoi),由具体硬件驱动实现。
- irqaction: 代表一个设备驱动注册的中断处理请求,包含handler、dev_id、flags等。
- pt_regs: 保存中断发生时的CPU寄存器状态,用于恢复现场。
路径总结: 硬件中断信号 → 中断控制器路由 → CPU向量表跳转 → 内核入口代码保存上下文 → 通用中断框架查找irq_desc → 流处理函数调用irqaction链表中的ISR → ISR执行 → EOI → 恢复上下文返回。
2.7 中断路径性能关键点
从硬件到ISR的完整路径中,以下环节对性能影响最大:
| 环节 | 性能开销 | 优化方向 |
|---|---|---|
| 中断控制器路由 | 中等 | 使用中断亲和性(IRQ affinity)绑定到特定CPU,避免跨核转发 |
| CPU上下文保存 | 高 | 使用快速系统调用(如x86的sysenter)减少保存量;ARM的v7/v8支持硬件自动保存 |
| irq_desc查找 | 低 | 使用radix树或直接数组索引,O(1)查找 |
| ISR执行 | 取决于驱动 | 将耗时操作推迟到下半部;使用NAPI轮询合并中断 |
| EOI与返回 | 低 | 使用EOI寄存器直接写操作,避免函数调用开销 |