2. Linux中断子系统架构:从硬件到软件的中断传递路径,IRQ Domain与中断控制器

理解Linux中断子系统,需要从硬件外设触发中断信号开始,追踪其经过多个中断控制器、CPU接口,最终到达内核处理函数的完整路径。本章将拆解这一“中断传递链”,并重点介绍IRQ Domain——连接硬件中断号与Linux IRQ号的抽象层。

2.1 中断传递路径:硬件→软件全景

一个典型的中断从外设到CPU处理函数的路径,可以划分为四个阶段:

  1. 外设触发:设备(如网卡、磁盘控制器)通过其中断引脚(INTx/MSI/MSI-X)向中断控制器发送电气信号。
  2. 中断控制器级联:多个中断控制器(如GIC、IO-APIC)对中断进行优先级仲裁、屏蔽、路由,最终将中断信号送达CPU的IRQ引脚。
  3. CPU异常处理:CPU检测到IRQ引脚信号后,保存上下文,跳转到内核的异常向量表(entry.S)中的中断入口。
  4. 内核通用中断处理:从汇编入口进入do_IRQ()(x86)或handle_arch_irq()(ARM64),经过IRQ Domain映射,调用注册的中断处理函数(handler)。
关键概念:硬件中断号(hwirq)是中断控制器视角的编号;Linux IRQ号是内核软件视角的虚拟中断号。IRQ Domain负责两者之间的翻译。
中断传递路径:从硬件外设到CPU处理函数 外设设备 (网卡/磁盘/USB) INTx / MSI-X 中断控制器 GIC / IO-APIC 优先级仲裁 / 屏蔽 hwirq → 路由 CPU 核心 异常向量表 保存上下文 跳转 entry.S 内核 do_IRQ() IRQ Domain handler 说明: 硬件中断号(hwirq)在中断控制器阶段确定;Linux IRQ号在内核阶段通过 IRQ Domain 映射得到。 各阶段关键组件与数据结构 阶段 硬件/软件组件 关键数据结构 映射关系 外设 PCIe EP / 平台设备 pci_dev / platform_device — 中断控制器 GIC-400 / IO-APIC irq_chip / irq_domain hwirq → Linux IRQ CPU ARM Cortex-A / x86 pt_regs / irq_desc IRQ# → handler

2.2 中断控制器与级联拓扑

现代SoC和x86平台通常包含多级中断控制器。以ARM GICv3为例,其拓扑结构如下:

层级 控制器 功能 输出目标
L1 GIC Distributor (GICD) 全局中断使能/屏蔽、优先级分组、SPI路由 Redistributor
L2 GIC Redistributor (GICR) 每CPU本地中断(PPI/SGI)管理、唤醒逻辑 CPU Interface
L3 CPU Interface (GICC) 与CPU核直接交互,提供IRQ/FIQ信号 CPU Core
x86对比:x86平台使用IO-APIC(负责I/O设备中断)和Local APIC(每CPU本地中断管理),两者通过FSB(前端总线)或系统总线通信。IO-APIC将中断路由到指定CPU的Local APIC。

2.3 IRQ Domain:硬件中断号到Linux IRQ号的桥梁

IRQ Domain(struct irq_domain)是内核中断子系统最核心的抽象层之一。它解决了以下问题:

  • 硬件中断号(hwirq)由中断控制器定义,通常是线性或树状编号。
  • Linux IRQ号是全局唯一的整数,用于在/proc/interrupts和驱动中标识中断。
  • IRQ Domain提供map() / unmap() / translate()回调,实现hwirq → Linux IRQ的映射。

2.4 IRQ Domain的三种映射方式

内核支持三种映射模型,适用于不同的中断控制器硬件特性:

映射类型 适用场景 映射方式 示例
线性映射 (Linear) hwirq范围连续且较小 hwirq直接作为数组索引 GICv2 SPI (32~1019)
树形映射 (Tree) hwirq稀疏或范围很大 红黑树查找 GPIO中断控制器
不映射 (No Map) hwirq与Linux IRQ一一对应 直接使用hwirq作为IRQ号 某些嵌入式平台
性能注意:线性映射是O(1)查找,性能最优;树形映射在中断频繁发生时有红黑树遍历开销。在中断密集型场景(如高速网卡)中,应优先选择线性映射的中断控制器。
IRQ Domain 映射机制:hwirq → Linux IRQ 中断控制器硬件 hwirq 空间 hwirq 0: 定时器 hwirq 1: UART hwirq 2: 网卡 hwirq 3: DMA IRQ Domain struct irq_domain linear_revmap[] revmap_tree (红黑树) ops->map / translate Linux IRQ 空间 全局 IRQ 号 IRQ 16: 定时器 IRQ 17: UART IRQ 18: 网卡 IRQ 19: DMA hwirq Linux IRQ# 映射流程: 1. 中断控制器触发中断,携带 hwirq → 2. 内核调用 irq_find_mapping(domain, hwirq) → 3. 通过 linear_revmap 或 revmap_tree 查找 → 4. 返回对应的 Linux IRQ 号 → 5. 调用 irq_desc[IRQ]->handle_irq()

2.5 中断描述符与中断流处理

每个Linux IRQ号对应一个struct irq_desc,其中包含:

  • irq_chip:指向中断控制器的底层操作函数(mask/unmask/ack/eoi)。
  • handle_irq:高层中断流处理函数(如handle_level_irq、handle_edge_irq)。
  • action链表:挂载该中断的所有注册处理函数(支持共享中断)。
  • irq_data:包含hwirq、domain指针等映射信息。
调试技巧:查看 /proc/interrupts 可以观察每个IRQ号的触发次数和亲和性(CPU Affinity)。/proc/irq/{IRQ}/smp_affinity 可手动设置中断绑核。

2.6 中断传递路径的完整代码流程

以ARM64为例,中断从硬件到软件的关键代码路径如下:

// 1. 硬件触发 → CPU 异常向量
// arch/arm64/kernel/entry.S
el1_irq:
    // 保存上下文,读取 ESR_EL1 确认中断类型
    // 跳转到 handle_arch_irq

// 2. 架构相关入口
// arch/arm64/kernel/irq.c
void handle_arch_irq(struct pt_regs *regs)
{
    // 调用 GIC 驱动注册的 handle_irq
    gic_handle_irq(regs);
}

// 3. GIC 驱动读取中断号
// drivers/irqchip/irq-gic-v3.c
static void gic_handle_irq(struct pt_regs *regs)
{
    u32 irqnr = gic_read_iar();  // 读取硬件中断号
    // 通过 irq_find_mapping 转换为 Linux IRQ 号
    unsigned int irq = irq_find_mapping(gic->domain, irqnr);
    generic_handle_irq(irq);     // 调用中断流处理函数
}

// 4. 通用中断层
// kernel/irq/irqdesc.c
int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);
    desc->handle_irq(desc);      // 调用 handle_level_irq / handle_edge_irq
}

// 5. 中断流处理 → 调用 action 链表中的 handler
// kernel/irq/chip.c
void handle_level_irq(struct irq_desc *desc)
{
    // mask 中断 → 遍历 action 链表 → 调用 handler → unmask
    desc->irq_data.chip->irq_mask(desc->irq_data);
    handle_irq_event(desc);
    desc->irq_data.chip->irq_unmask(desc->irq_data);
}

2.7 本章小结

知识点 核心要点
中断传递路径 外设 → 中断控制器 → CPU异常向量 → do_IRQ() → IRQ Domain → handler
中断控制器级联 GIC Distributor → Redistributor → CPU Interface(ARM);IO-APIC → Local APIC(x86)
IRQ Domain 将硬件中断号(hwirq)映射为Linux IRQ号,支持线性/树形/不映射三种方式
irq_desc 每个IRQ号对应的描述符,包含chip、handle_irq、action链表
性能关键 线性映射O(1)最优;中断绑核通过smp_affinity控制