3. ARM GIC 中断控制器:GIC v2/v3 架构、中断类型与中断亲和性

在嵌入式 Linux 系统中,GPIO 中断的最终路由与分发由 ARM 通用中断控制器(Generic Interrupt Controller, GIC)完成。GIC 是连接外设中断源与 CPU 核心的硬件中枢,其架构直接决定了中断的响应延迟、负载均衡能力以及多核系统的可扩展性。本章深入剖析 GIC v2 与 v3 的核心差异、中断类型(SPI、PPI、SGI)以及中断亲和性(IRQ Affinity)的配置原理。

核心要点:GIC v3 是 v2 的演进,主要解决了多核扩展性(超过 8 核)和虚拟化支持问题。在嵌入式 Linux 中,GPIO 中断通常映射为 SPI(共享外设中断),而每个 CPU 核的本地定时器、PMU 等则使用 PPI(私有外设中断)。

3.1 GIC v2 架构:经典多核中断分发

GIC v2 是 ARM Cortex-A9/A15 等处理器广泛采用的中断控制器架构,由两个主要逻辑单元组成:分发器(Distributor)和CPU 接口(CPU Interface)。

  • 分发器(GICD):负责全局中断管理,包括中断使能、优先级分组、中断类型配置(电平/边沿触发)、中断目标 CPU 选择(亲和性)。
  • CPU 接口(GICC):每个 CPU 核心拥有独立的 CPU 接口,负责向本核发送中断信号、应答中断(EOI)、屏蔽/取消屏蔽中断。
GIC v2 架构:分发器与 CPU 接口 GIC v2 架构:分发器与 CPU 接口 外设中断源 GPIO 控制器 分发器 (GICD) 中断使能/屏蔽 优先级分组 目标 CPU 选择 CPU 接口 0 CPU 接口 1 CPU 0 CPU 1 说明:GIC v2 分发器统一管理所有中断,通过 GICD_ITARGETSR 寄存器设置每个中断的目标 CPU。
实战提示:在 GIC v2 中,分发器通过内存映射寄存器(如 GICD_ISENABLER、GICD_ICPENDR)控制中断状态。Linux 内核中的 irq-gic.c 驱动直接操作这些寄存器。

3.2 GIC v3 架构:可扩展性与虚拟化增强

GIC v3 针对大规模多核系统(超过 8 核)和虚拟化场景进行了重新设计,引入了 重新分配器(Redistributor) 和 ITS(Interrupt Translation Service) 等新组件。

特性 GIC v2 GIC v3
最大 CPU 核心数 8 核 无限制(通过 Redistributor 扩展)
中断路由 分发器统一路由 每个核心拥有独立 Redistributor
虚拟化支持 有限(需要外部虚拟化扩展) 原生支持(LPI 中断、ITS)
中断优先级 8 位(实际使用 5 位) 8 位(支持更多优先级级别)
寄存器访问 内存映射(MMIO) 内存映射 + 系统寄存器(ICC_*_ELn)
GIC v3 架构:Redistributor 与 ITS GIC v3 架构:Redistributor 与 ITS 外设中断源 GPIO 控制器 ITS 中断翻译服务 Redistributor 0 PPI/SGI/LPI 管理 Redistributor 1 PPI/SGI/LPI 管理 CPU 0 CPU 1 说明:GIC v3 中,每个 CPU 核心拥有独立的 Redistributor,负责管理 PPI、SGI 和 LPI 中断。 ITS 负责将设备中断(LPI)翻译为 Redistributor 可识别的格式,支持虚拟化直通。
注意:GIC v3 的 Redistributor 通过系统寄存器(如 ICC_IAR0_EL1、ICC_EOIR0_EL1)与 CPU 交互,不再完全依赖 MMIO。这要求内核驱动在启动时正确识别 GIC 版本。

3.3 中断类型:SPI、PPI、SGI

GIC 将中断分为三种类型,每种类型在硬件路由、触发方式和软件处理上存在显著差异。

中断类型 全称 中断 ID 范围 目标 CPU 典型用途
SPI Shared Peripheral Interrupt 32 ~ 1019(GIC v2 为 32 ~ 988) 可路由到任意一个或多个 CPU GPIO 中断、UART、I2C、SPI 等外设中断
PPI Private Peripheral Interrupt 16 ~ 31 固定绑定到特定 CPU 核心 本地定时器、PMU(性能监视单元)、L2 缓存控制器
SGI Software Generated Interrupt 0 ~ 15 由软件触发,可定向到指定 CPU 或广播 核间通信(IPI)、任务调度、TLB 刷新
中断类型与路由关系 中断类型与路由关系 SPI (共享外设中断) ID 32~1019 PPI (私有外设中断) ID 16~31 SGI (软件生成中断) ID 0~15 GIC 分发器 / Redistributor 路由决策 优先级仲裁 亲和性配置 CPU 0 CPU 1 CPU 2
GPIO 中断映射:在设备树中,GPIO 控制器通常通过 interrupts = 指定 SPI 中断。例如:interrupts = ; 表示 GPIO 中断号为 42,上升沿触发。

3.4 中断亲和性:将中断绑定到指定 CPU

中断亲和性(IRQ Affinity)允许将特定中断路由到指定的 CPU 核心,从而实现负载均衡、减少缓存抖动或满足实时性要求。在 Linux 中,通过 /proc/irq/IRQ_NUM/smp_affinity 文件配置。

3.4.1 亲和性配置原理

  • 位掩码表示:每个中断的亲和性由一个位掩码(bitmask)表示,每一位对应一个 CPU 核心。例如,0x01 表示仅 CPU 0,0x03 表示 CPU 0 和 CPU 1。
  • 硬件支持:GIC v2 通过分发器的 GICD_ITARGETSR 寄存器设置目标 CPU;GIC v3 通过 Redistributor 的 ICD_IROUTER 寄存器设置。
  • 内核接口:irq_set_affinity() 函数用于在驱动中动态设置亲和性。
// 示例:将 IRQ 42 绑定到 CPU 0 和 CPU 2
// 位掩码:CPU0 (bit0) + CPU2 (bit2) = 0x05
echo 0x05 > /proc/irq/42/smp_affinity

// 查看当前亲和性
cat /proc/irq/42/smp_affinity
// 输出:00000000,00000000,00000000,00000005

3.4.2 亲和性配置流程

步骤 操作 说明
1 确定中断号 通过 cat /proc/interrupts 查看 GPIO 中断对应的 IRQ 号
2 计算位掩码 根据目标 CPU 核心编号计算十六进制掩码
3 写入 smp_affinity echo 0x05 > /proc/irq/42/smp_affinity
4 验证生效 再次查看 /proc/interrupts,确认中断计数分布到目标 CPU
5 持久化配置 在 /etc/rc.local 或 systemd 服务中写入配置
中断亲和性配置流程 中断亲和性配置流程 步骤 1 确定中断号 步骤 2 计算位掩码 步骤 3 写入 smp_affinity 步骤 4 验证生效 步骤 5 持久化配置 说明:中断亲和性配置在实时系统(RT)中尤为重要,可将高优先级中断绑定到专用 CPU 核心, 避免与其他中断或进程争抢 CPU 时间,从而降低延迟抖动。

3.5 实战:查看 GPIO 中断的 GIC 信息

在嵌入式 Linux 系统中,可以通过以下命令查看 GPIO 中断的 GIC 相关信息:

# 查看所有中断的分布情况(包括 GPIO 中断)
cat /proc/interrupts

# 输出示例(部分):
#           CPU0       CPU1       CPU2       CPU3
# 42:        1234        0          0          0     GICv3  42  Edge  gpio_irq

# 查看中断 42 的详细属性
cat /proc/irq/42/smp_affinity
# 输出:00000000,00000000,00000000,00000001  (仅 CPU0)

# 查看中断控制器类型
cat /proc/irq/42/chip_name
# 输出:GICv3
调试技巧:如果 GPIO 中断没有按预期触发,首先检查 /proc/interrupts 中对应中断号的计数是否增加。如果计数始终为 0,可能是 GIC 配置错误、中断类型不匹配(电平/边沿)或设备树中中断号映射错误。

3.6 本章小结

ARM GIC 中断控制器是 GPIO 中断路由的核心硬件。GIC v2 通过分发器统一管理中断,适合 8 核以内的系统;GIC v3 通过 Redistributor 和 ITS 支持大规模多核和虚拟化。SPI 是 GPIO 中断的主要类型,PPI 用于本地外设,SGI 用于核间通信。中断亲和性允许将中断绑定到指定 CPU,是实现负载均衡和实时性的关键手段。掌握 GIC 架构和中断类型,是编写高效 GPIO 中断驱动的必备基础。