2. Linux中断子系统架构:中断控制器(GIC)、中断描述符、中断请求(IRQ)域
中断子系统是Linux内核响应硬件事件的神经中枢。理解其核心组件——中断控制器(GIC)、中断描述符(irq_desc)以及中断请求域(IRQ Domain)——是进行驱动中断开发与性能调优的基石。本章将深入剖析这三层架构的协作关系与数据结构。
2.1 中断控制器(GIC):硬件中断的交通枢纽
通用中断控制器(Generic Interrupt Controller, GIC)是ARM体系结构下标准的中断管理单元。它负责接收来自外设的物理中断信号,进行优先级仲裁,并最终将中断分发给指定的CPU核心。
GIC主要分为两个逻辑块:分发器(Distributor) 和 CPU接口(CPU Interface)。
| 组件 | 功能描述 | 关键寄存器/特性 |
|---|---|---|
| 分发器 (Distributor) | 集中管理所有中断源,控制中断的使能、优先级、触发方式(电平/边沿)和目标CPU。 | ICDISER (使能)、ICDICFR (配置)、ICDIPTR (目标CPU) |
| CPU接口 (CPU Interface) | 每个CPU核心私有,负责向该CPU发送中断请求,并处理中断确认(ACK)和结束(EOI)信号。 | ICC_IAR (中断确认)、ICC_EOIR (中断结束)、ICC_PMR (优先级掩码) |
性能调优要点: 在SMP系统中,通过配置ICDIPTR寄存器将中断亲和性绑定到特定CPU,可以有效避免多核间的缓存抖动,是中断负载均衡的核心手段。
2.2 中断描述符(irq_desc):内核的中断对象模型
在内核中,每个硬件中断源都对应一个 struct irq_desc 结构体。这个结构体是中断子系统的核心对象,包含了中断的所有状态、处理函数指针以及统计信息。
// include/linux/irqdesc.h (简化结构)
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs; // 每个CPU的中断统计
irq_flow_handler_t handle_irq; // 高层中断流处理函数
struct irqaction *action; // 中断动作链表(驱动注册的handler)
unsigned int status_use_accessors;
struct proc_dir_entry *dir; // /proc/irq/ 目录
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp; // 对齐到缓存行,防止伪共享
关键字段解读:
- handle_irq:标准流处理函数(如 handle_level_irq, handle_edge_irq),由GIC驱动在初始化时设置。
- action:链表,挂载了通过
request_irq()注册的驱动中断处理程序。 - kstat_irqs:Per-CPU变量,用于记录中断发生次数,是排查中断风暴的第一手数据来源。
2.3 中断请求域(IRQ Domain):硬件中断号到Linux IRQ号的映射
IRQ Domain 是连接硬件中断号(HW IRQ)与Linux虚拟中断号(Linux IRQ)的桥梁。由于设备树或ACPI描述的硬件中断号与内核使用的IRQ号是独立的,Domain层提供了灵活的映射机制。
IRQ Domain 支持三种主要的映射操作:
- 线性映射(Linear):预分配一个固定大小的数组,适用于中断源数量固定且较少的场景。
- 树形映射(Tree):使用基数树(radix tree)管理,适用于中断源数量大且稀疏的场景。
- 不映射(Nomap):硬件中断号直接作为Linux IRQ号,用于简单的嵌入式系统。
| 映射类型 | 数据结构 | 适用场景 | 查找复杂度 |
|---|---|---|---|
| 线性映射 | 数组 (irq_desc[]) | 中断源少且连续 (如GPIO控制器) | O(1) |
| 树形映射 | 基数树 (radix_tree) | 中断源多且稀疏 (如PCIe MSI/MSI-X) | O(log n) |
| 不映射 | 直接使用 | 硬件IRQ与Linux IRQ一致 | O(1) |
调优陷阱: 在中断密集型设备(如万兆网卡)中,如果使用线性映射且中断号范围过大,会导致irq_desc数组占用大量内存。此时应优先考虑树形映射或动态分配。
2.4 三者协作:从硬件中断到驱动处理
当外设触发中断时,完整的软件处理路径如下:
- GIC分发器 接收中断信号,根据配置选择目标CPU。
- 目标CPU的 GIC CPU接口 触发IRQ异常,CPU跳转到异常向量表。
- 内核汇编入口保存现场,调用
handle_arch_irq(),该函数读取ICC_IAR获取硬件中断号。 - 通过 IRQ Domain 将硬件中断号转换为 Linux IRQ号。
- 根据Linux IRQ号索引 irq_desc,调用其
handle_irq流处理函数。 - 流处理函数调用
action链表中驱动注册的handler。 - 驱动处理完成后,写
ICC_EOIR通知GIC中断处理结束。
性能关键路径: 步骤4的IRQ Domain查找和步骤5的irq_desc访问是中断响应延迟的主要软件开销。使用线性映射和确保irq_desc结构体缓存行对齐(____cacheline_internodealigned_in_smp)是优化这两步的常用手段。