2. Linux 中断子系统架构:中断控制器、中断描述符、中断处理流程
中断子系统是 Linux 内核响应硬件事件的神经中枢。本章从全局视角拆解其三大核心组件:中断控制器(Interrupt Controller)、中断描述符(Interrupt Descriptor)以及中断处理流程(Interrupt Handling Flow)。理解这三者的协作关系,是后续掌握 tasklet 与工作队列的前提。
2.1 中断控制器:硬件与内核的桥梁
中断控制器负责收集外设的中断请求,进行优先级仲裁,并通知 CPU。在嵌入式系统中,常见的中断控制器包括 GIC(Generic Interrupt Controller,ARM)、APIC(x86)和 INTC(RISC-V)。
内核通过 struct irq_chip 抽象中断控制器的硬件操作,包括中断使能、屏蔽、确认和触发方式配置。
| 中断控制器类型 | 典型架构 | 关键特性 |
|---|---|---|
| GIC v2/v3/v4 | ARM Cortex-A/R | 支持 SPI、PPI、SGI;支持亲和性分配 |
| APIC / x2APIC | x86 | 支持 I/O APIC 与本地 APIC;MSI 中断 |
| PLIC / CLINT | RISC-V | 平台级中断控制器;上下文切换开销低 |
实践提示:在设备树中,
interrupt-parent 属性指向中断控制器节点,interrupts 属性指定中断号与触发类型。驱动开发中,务必确认中断控制器驱动已正确注册。
2.2 中断描述符:内核的中断管理单元
每个中断源在内核中对应一个 struct irq_desc 描述符。该结构体是中断管理的核心,包含中断状态、处理函数链表、芯片操作指针和线程化标志。
// include/linux/irqdesc.h (简化结构)
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_chip *irq_chip; // 中断控制器操作
struct irqaction *action; // 处理函数链表
unsigned int status_use_accessors;
const char *name;
struct proc_dir_entry *dir;
unsigned int irq;
// ... 其他字段
};
中断描述符通过 irq_to_desc(irq) 宏快速查找,内核启动时根据硬件拓扑预分配。每个描述符的 action 链表支持多个处理函数共享同一中断号(共享中断)。
关键字段说明:
irq_chip:指向中断控制器的硬件操作函数集。action:挂载该中断的所有处理函数,以单向链表组织。status_use_accessors:记录中断是否被屏蔽、是否正在处理等状态。
2.3 中断处理流程:从硬件触发到软件执行
一次完整的中断处理分为三个阶段:硬件阶段、内核入口阶段和执行阶段。理解这个流程是编写高效中断处理程序的基础。
- 硬件阶段:外设拉高中断线,中断控制器仲裁后向 CPU 发送 IRQ 信号。
- 内核入口阶段:CPU 保存上下文,跳转到
irq_handler入口,调用handle_irq_desc()。 - 执行阶段:根据中断描述符的
action链表,依次执行注册的处理函数。处理函数分为顶半部(快速响应)和底半部(延迟处理)。
性能陷阱:顶半部执行期间,当前中断线被屏蔽(取决于触发类型),过长的顶半部会直接导致中断延迟增大。建议顶半部只做最小必要操作,将耗时工作推迟到底半部(tasklet / 工作队列)。
2.4 中断处理流程中的关键函数调用链
从 CPU 接收到中断信号到执行具体处理函数,内核内部经历了一系列精心设计的函数调用。以下是 ARM64 架构下的典型调用链:
中断触发
→ 异常向量表 entry (arch/arm64/kernel/entry.S)
→ handle_arch_irq() (跳转到中断控制器入口)
→ gic_handle_irq() (GIC 驱动)
→ handle_domain_irq()
→ __handle_domain_irq()
→ generic_handle_irq_desc()
→ desc->handle_irq() (流控处理函数)
→ handle_edge_irq() / handle_level_irq()
→ handle_irq_event()
→ __handle_irq_event_percpu()
→ action->handler() (驱动注册的处理函数)
关键点:
desc->handle_irq是流控处理函数,根据中断触发类型(边沿/电平)选择不同策略。handle_irq_event负责遍历 action 链表,依次调用所有注册的处理函数。- 处理函数返回
IRQ_HANDLED或IRQ_NONE,内核据此判断是否发生伪中断。
2.5 中断处理流程的同步与并发考量
中断上下文与进程上下文存在本质区别,驱动开发中必须遵守以下规则:
| 特性 | 中断上下文(顶半部) | 进程上下文 |
|---|---|---|
| 可睡眠 | ❌ 禁止 | ✅ 允许 |
| 持有锁 | 只能使用 spinlock | 可以使用 mutex |
| 调度 | 不可调度 | 可被调度 |
| 访问用户空间 | ❌ 禁止 | ✅ 允许 |
常见错误:在顶半部中使用
kmalloc(GFP_KERNEL) 或 mutex_lock() 会导致内核崩溃。顶半部只能使用 GFP_ATOMIC 分配内存,并使用 spinlock 保护共享数据。
2.6 本章小结
中断子系统架构围绕“中断控制器 → 中断描述符 → 处理流程”三条主线展开:
- 中断控制器抽象硬件差异,提供统一的芯片操作接口。
- 中断描述符是每个中断号在内核中的“档案”,管理处理函数与状态。
- 处理流程从硬件触发到软件执行,严格区分顶半部与底半部,确保系统实时性。