2. 中断控制器硬件:GIC v3/v4 架构概述、中断分发与路由、中断触发方式
在 ARM 多核系统中,通用中断控制器(Generic Interrupt Controller, GIC) 是连接外设中断源与 CPU 核心的硬件中枢。GIC v3/v4 是当前主流 ARMv8-A 和 ARMv9-A 平台的标准中断控制器,其设计直接影响中断延迟、亲和性路由和虚拟化支持。本节从架构层次、中断分发/路由机制以及触发方式三个维度展开分析。
2.1 GIC v3/v4 架构概述
GIC v3 相比 v2 引入了 独立的路由表(Redistributor)、消息信号中断(MSI) 的本地支持以及 对大于 8 个 PE(Processing Element) 的可扩展性。GIC v4 则在 v3 基础上增加了 直接注入虚拟中断(Direct Virtual Interrupt Injection) 能力,显著降低虚拟机中断处理的宿主机开销。
下表对比了 GIC v2、v3、v4 的关键差异:
| 特性 | GIC v2 | GIC v3 | GIC v4 |
|---|---|---|---|
| 最大 PE 数 | ≤ 8 | ≥ 256 | ≥ 256 |
| 中断路由方式 | 固定 CPU 接口 | 基于 Redistributor 路由 | 基于 Redistributor + vPE 路由 |
| MSI 支持 | 需外部桥接 | 原生支持(通过 ITS) | 原生支持 + 虚拟 MSI |
| 虚拟中断注入 | 软件模拟(List Register) | 软件模拟(List Register) | 硬件直接注入(Doorbell) |
| 中断标识范围 | ID 0-1019 | ID 0-1023(扩展至 16-bit) | ID 0-1023(扩展至 16-bit) |
下图展示了 GIC v3/v4 的组件层次与数据流关系:
2.2 中断分发与路由
GIC v3/v4 的中断分发遵循 中断类型 → Distributor/ITS → Redistributor → CPU Interface → PE 的路径。每种中断类型具有不同的路由策略:
- SGI(Software Generated Interrupt): 由 PE 通过写 ICC_SGI1R_EL1 寄存器直接发送给目标 PE 的 Redistributor,不经过 Distributor。
- PPI(Private Peripheral Interrupt): 每个 PE 私有,直接路由到对应 PE 的 Redistributor,无需全局分发。
- SPI(Shared Peripheral Interrupt): 由 Distributor 根据 中断路由表(Interrupt Routing Table, IRT) 分发到指定 PE 组的 Redistributor。
- LPI(Locality-specific Peripheral Interrupt): 通过 ITS 进行地址翻译和路由,支持 基于消息的中断(MSI),可精确路由到任意 PE 或 vPE。
下表总结了四种中断类型的路由属性:
| 中断类型 | ID 范围 | 路由目标 | 路由控制寄存器 | 典型用途 |
|---|---|---|---|---|
| SGI | 0-15 | 指定 PE 或 PE 组 | ICC_SGI1R_EL1 | 核间通信(IPI) |
| PPI | 16-31 | 所属 PE 自身 | GICR_ICFGR0/1 | 本地定时器、PMU |
| SPI | 32-1019 | Distributor 路由表指定 PE | GICD_IROUTER | 外设 DMA、网络控制器 |
| LPI | 8192-65535 | ITS 翻译后路由至任意 PE | ITS 命令表 + GICR_PROPBASER | PCIe MSI/MSI-X |
下图展示了 SPI 和 LPI 两种典型中断的路由流程对比:
2.3 中断触发方式:电平与边沿
GIC v3/v4 支持两种中断触发方式,通过 GICD_ICFGR(Distributor 级) 或 GICR_ICFGR(Redistributor 级) 寄存器配置:
- 电平触发(Level-sensitive): 中断信号保持高电平(或低电平)直到被清除。GIC 在每次时钟采样时检测电平状态。适用于 共享中断线 和 需要硬件握手的设备。
- 边沿触发(Edge-triggered): 中断信号在上升沿或下降沿被锁存。GIC 只记录跳变事件,即使信号恢复也不会丢失中断。适用于 脉冲型中断源 和 MSI。
下表从多个维度对比两种触发方式:
| 对比维度 | 电平触发 | 边沿触发 |
|---|---|---|
| 信号检测 | 持续采样电平状态 | 检测上升/下降沿跳变 |
| 中断丢失风险 | 低(电平持续有效) | 高(脉冲过窄可能丢失) |
| 共享中断线 | 支持(需轮询中断状态寄存器) | 不支持(无法区分共享源) |
| 中断清除方式 | 必须清除设备端中断源,否则持续触发 | 自动清除(GIC 记录事件后即可) |
| 典型应用 | GPIO、I2C、UART | PCIe MSI/MSI-X、定时器、DMA 完成 |
| GIC 配置寄存器 | ICFGR 对应位 = 0b00 | ICFGR 对应位 = 0b01 |
下图展示了电平触发与边沿触发在 GIC 内部的采样与挂起逻辑:
在 Linux 内核中,中断触发方式的配置通过设备树或 ACPI 表描述,驱动开发者可通过 irq_set_irq_type() 或 request_threaded_irq() 的 IRQF_TRIGGER_* 标志指定:
/* 电平触发中断注册示例 */
static irqreturn_t my_isr(int irq, void *dev_id)
{
/* 必须清除设备中断源 */
writel(0x1, DEVICE_INT_CLEAR_REG);
return IRQ_HANDLED;
}
/* 边沿触发中断注册示例 */
static irqreturn_t my_edge_isr(int irq, void *dev_id)
{
/* 无需清除设备,GIC 自动处理 */
return IRQ_HANDLED;
}
/* 注册时指定触发方式 */
ret = request_irq(irq_num, my_isr,
IRQF_TRIGGER_HIGH, /* 电平触发:高电平 */
"my_device", dev_id);
ret = request_irq(irq_num, my_edge_isr,
IRQF_TRIGGER_RISING, /* 边沿触发:上升沿 */
"my_device_edge", dev_id);
/proc/interrupts 查看中断触发次数和亲和性;使用 perf stat -e irq_vectors:* 统计 GIC 中断向量分布;对于电平触发中断,若 /proc/interrupts 计数持续增长而设备未产生新中断,说明中断源未正确清除。