3. 中断号与IRQ:中断号分配、IRQ编号、动态与静态中断号映射

中断号(Interrupt Number)是操作系统为每个硬件中断源分配的整数标识符,是驱动与中断控制器(Interrupt Controller)之间沟通的“门牌号”。在Linux内核中,中断号分为硬件中断号(HW IRQ)和软件中断号(Linux IRQ),两者通过映射机制关联。本章深入剖析中断号的分配策略、IRQ编号规则以及动态/静态映射的实现原理。

3.1 中断号的基本概念

中断号在系统中有两个层面的含义:

  • 硬件中断号(HW IRQ):由芯片厂商定义,对应中断控制器(如GIC、IOAPIC)的物理输入引脚或SPI ID。
  • 软件中断号(Linux IRQ):内核抽象出的逻辑中断号,驱动通过request_irq()注册时使用的参数。
关键理解:驱动开发者通常只与软件中断号(Linux IRQ)打交道,硬件中断号由中断控制器驱动或设备树自动转换。

3.2 中断号分配方式

Linux内核支持三种中断号分配方式,适用于不同硬件平台和场景:

分配方式 描述 典型平台 优点 缺点
静态分配 中断号在编译时或设备树中固定指定 ARM、RISC-V 简单、可预测 灵活性差,易冲突
动态分配 内核运行时动态分配空闲中断号 x86、PCIe设备 避免冲突,支持热插拔 中断号不固定,调试稍复杂
混合分配 部分固定,部分动态 嵌入式SoC 兼顾稳定性和灵活性 管理复杂度增加
工程实践:在嵌入式Linux中,设备树(Device Tree)是静态分配中断号的主要载体。例如:interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; 表示使用SPI中断,硬件ID为42。

3.3 IRQ编号与中断域

现代Linux内核引入了中断域(irq_domain)机制,用于管理硬件中断号到Linux IRQ的映射。每个中断控制器对应一个中断域,域内维护一张映射表。

中断域(irq_domain)映射架构 硬件中断源 HW IRQ 0..N 中断控制器 GIC / IOAPIC irq_domain 映射表 Linux IRQ 软件中断号 驱动 request_irq() 物理连接 注册 映射 绑定 映射关系示例 HW IRQ 42 → Linux IRQ 74 HW IRQ 53 → Linux IRQ 85 HW IRQ 67 → Linux IRQ 91

3.4 动态中断号映射

动态映射是irq_domain的核心能力,内核提供三种映射类型:

  • 线性映射(Linear):预分配固定大小的数组,适用于中断源数量已知且较少的场景。
  • 树形映射(Tree):使用基数树(radix tree)管理,适用于中断源数量大且稀疏的场景。
  • 无映射(No Map):硬件中断号直接作为Linux IRQ,适用于x86等固定映射平台。
注意事项:动态映射虽然灵活,但可能导致中断号在每次启动时不同。调试时建议通过/proc/interrupts查看当前映射关系。

3.5 静态中断号映射

静态映射通常用于嵌入式系统,中断号在设备树或板级文件中硬编码。典型流程如下:

  1. 设备树中通过interrupts属性指定硬件中断号。
  2. 内核解析设备树,调用irq_of_parse_and_map()建立映射。
  3. 驱动通过platform_get_irq()获取Linux IRQ。
/* 设备树示例:UART设备中断 */
uart0: serial@10000000 {
    compatible = "vendor,uart";
    reg = <0x10000000 0x1000>;
    interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
};

/* 驱动代码获取中断号 */
int irq_num;
irq_num = platform_get_irq(pdev, 0);
if (irq_num < 0) {
    dev_err(&pdev->dev, "failed to get IRQ\n");
    return irq_num;
}

/* 注册中断处理 */
ret = request_irq(irq_num, uart_isr, IRQF_TRIGGER_HIGH,
                  "uart_irq", dev_id);

3.6 中断号映射对比总结

特性 静态映射 动态映射
中断号确定性 固定,可预测 每次启动可能不同
适用平台 ARM、RISC-V嵌入式 x86、PCIe、热插拔设备
配置方式 设备树或板级文件 内核运行时自动分配
调试难度 低 中(需查看/proc/interrupts)
资源占用 低(预分配数组) 中(基数树或链表)
灵活性 低 高

3.7 工程案例:查看中断号映射

在实际开发中,通过/proc/interrupts可以查看当前系统的中断号映射状态:

# cat /proc/interrupts
           CPU0       CPU1
 16:      12345      67890     GICv3  42  uart0
 17:          0          0     GICv3  53  mmc0
 18:        987        654     GICv3  67  eth0

输出字段说明:

  • 第一列:Linux IRQ号(16、17、18)。
  • CPU列:各CPU处理的中断次数。
  • 中断控制器:GICv3。
  • 硬件中断号:42、53、67。
  • 设备名称:uart0、mmc0、eth0。
调试技巧:当驱动无法获取中断时,首先检查/proc/interrupts中是否存在对应设备。若不存在,说明设备树或中断控制器驱动未正确注册映射。

3.8 本章小结

中断号是驱动与中断子系统交互的桥梁。理解静态与动态映射的差异,掌握irq_domain的工作原理,能够帮助开发者快速定位中断相关的问题。在嵌入式Linux中,设备树是静态映射的主要工具;而在x86/PCIe场景下,动态映射提供了更好的灵活性和可扩展性。