2. 中断向量基础:中断向量号、IRQ编号、中断描述符、中断控制器层级关系
在深入 MSI 中断配置之前,必须厘清中断子系统中的几个核心概念及其层级关系。中断向量号、IRQ 编号、中断描述符以及中断控制器构成了从硬件信号到软件处理的完整链路。理解这些基础元素是正确配置 MSI 中断向量的前提。
2.1 核心概念定义
下表总结了中断子系统中三个最基础的概念及其在 x86 架构下的典型特征:
| 概念 | 英文 | 本质 | 典型范围 | 分配时机 |
|---|---|---|---|---|
| 中断向量号 | Vector Number | CPU IDT 表索引,直接对应中断服务例程入口 | 0–255(x86) | 系统启动 / CPU 初始化 |
| IRQ 编号 | IRQ Number | 软件抽象层使用的逻辑中断号,与硬件中断线解耦 | 0–15(传统 PIC),0–223(IOAPIC) | 设备枚举 / 驱动加载 |
| 中断描述符 | irq_desc | 内核管理每个 IRQ 的数据结构,包含处理函数、状态、芯片操作 | 每个 IRQ 对应一个 | IRQ 注册时动态分配 |
关键区分:中断向量号是 CPU 硬件层面的概念,IRQ 编号是内核软件抽象层的概念。MSI 中断配置中,驱动通常只与 IRQ 编号打交道,而内核负责将 IRQ 映射到具体的向量号。
2.2 中断控制器层级关系
现代 x86 系统通常包含多个中断控制器,形成层级结构。典型的层级关系如下:
- I/O APIC(I/O Advanced Programmable Interrupt Controller):位于芯片组或 CPU 外部,负责收集设备中断请求,将其格式化为中断消息发送给 Local APIC。MSI 中断直接通过 PCIe 总线写入 I/O APIC 或 CPU 的 Local APIC。
- Local APIC(本地高级可编程中断控制器):集成在每个 CPU 核心内部,接收来自 I/O APIC 的中断消息,将其转换为中断向量号,触发 CPU 中断处理。
- 传统 PIC(8259A):在较新系统中通常仅用于兼容性,级联在 I/O APIC 之下,处理 legacy 中断。
MSI 中断路径:设备 → PCIe 总线 → 内存写事务(MSI 消息) → Local APIC → CPU 中断处理。此路径跳过了 I/O APIC 的引脚路由,直接到达 CPU,延迟更低。
2.3 中断向量分配流程
从 IRQ 编号到中断向量号的映射由内核中断子系统完成,流程如下:
- 步骤 1:驱动调用
request_irq()或request_threaded_irq(),传入 IRQ 编号和处理函数。 - 步骤 2:内核为指定 IRQ 分配或查找对应的
irq_desc结构体。 - 步骤 3:内核调用中断控制器芯片的
irq_chip.irq_startup()或irq_chip.irq_enable()回调。 - 步骤 4:中断控制器(如 IOAPIC)分配一个空闲的中断向量号,并写入 I/O APIC 的 Redirection Table Entry(RTE)。
- 步骤 5:CPU 的 Local APIC 根据向量号在 IDT 中查找对应的中断门描述符,执行处理函数。
2.4 中断描述符结构
irq_desc 是内核管理中断的核心数据结构,其关键字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
irq_data |
struct irq_data | 包含中断控制器私有数据、芯片操作指针、层级信息 |
action |
struct irqaction * | 指向中断处理函数链表的头节点 |
status_use_accessors |
unsigned int | 中断状态标志,如 IRQ_DISABLED、IRQ_INPROGRESS |
depth |
unsigned int | disable 嵌套深度,用于管理中断使能/禁止 |
chip |
struct irq_chip * | 中断控制器操作函数集(mask/unmask/ack/set_type 等) |
2.5 中断控制器层级关系图
下图展示了从 MSI 设备到 CPU 的中断传递路径及各层级控制器的关系:
2.6 中断向量号分配示例
以下代码展示了驱动在 MSI 中断使能过程中如何获取 IRQ 编号,并观察内核如何将其映射到向量号:
/* 驱动侧:分配 MSI 中断向量 */
int irq_num;
struct pci_dev *pdev = /* 获取 PCIe 设备指针 */;
/* 步骤1:使能 MSI,获取 IRQ 编号 */
irq_num = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (irq_num < 0) {
dev_err(&pdev->dev, "MSI vector allocation failed\n");
return irq_num;
}
/* 步骤2:注册中断处理函数 */
ret = request_irq(irq_num, my_msi_handler, 0, "my_msi_device", dev);
if (ret) {
pci_free_irq_vectors(pdev);
return ret;
}
/* 步骤3:内核内部自动完成 IRQ → 向量号映射 */
/* 可通过 /proc/interrupts 查看映射关系 */
/* cat /proc/interrupts | grep my_msi_device */
注意事项:驱动不应直接操作中断向量号。向量号的分配由中断控制器驱动和内核核心代码完成,驱动只需通过 IRQ 编号进行注册和使能操作。直接操作向量号会导致系统不稳定或中断冲突。
2.7 层级关系总结
中断向量号、IRQ 编号、中断描述符和中断控制器之间的层级关系可以概括为:
- 硬件层:中断向量号是 CPU 硬件直接识别的 IDT 索引,由中断控制器(IOAPIC/Local APIC)在 RTE 中配置。
- 抽象层:IRQ 编号是内核为驱动提供的逻辑中断标识,驱动通过
request_irq()使用此编号。 - 管理层:中断描述符
irq_desc是内核管理每个 IRQ 的数据结构,包含状态、处理函数链和芯片操作。 - 路由层:中断控制器层级(IOAPIC → Local APIC)负责将设备中断请求路由到正确的 CPU 核心,并完成 IRQ 到向量号的转换。