3. ARM GIC 中断控制器:GIC v2/v3 架构、中断类型与中断亲和性
在嵌入式 Linux 系统中,GPIO 中断的最终路由与分发由 ARM 通用中断控制器(Generic Interrupt Controller, GIC)完成。GIC 是连接外设中断源与 CPU 核心的硬件中枢,其架构直接决定了中断的响应延迟、负载均衡能力以及多核系统的可扩展性。本章深入剖析 GIC v2 与 v3 的核心差异、中断类型(SPI、PPI、SGI)以及中断亲和性(IRQ Affinity)的配置原理。
3.1 GIC v2 架构:经典多核中断分发
GIC v2 是 ARM Cortex-A9/A15 等处理器广泛采用的中断控制器架构,由两个主要逻辑单元组成:分发器(Distributor)和CPU 接口(CPU Interface)。
- 分发器(GICD):负责全局中断管理,包括中断使能、优先级分组、中断类型配置(电平/边沿触发)、中断目标 CPU 选择(亲和性)。
- CPU 接口(GICC):每个 CPU 核心拥有独立的 CPU 接口,负责向本核发送中断信号、应答中断(EOI)、屏蔽/取消屏蔽中断。
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) |
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 刷新 |
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 服务中写入配置 |
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
/proc/interrupts 中对应中断号的计数是否增加。如果计数始终为 0,可能是 GIC 配置错误、中断类型不匹配(电平/边沿)或设备树中中断号映射错误。
3.6 本章小结
ARM GIC 中断控制器是 GPIO 中断路由的核心硬件。GIC v2 通过分发器统一管理中断,适合 8 核以内的系统;GIC v3 通过 Redistributor 和 ITS 支持大规模多核和虚拟化。SPI 是 GPIO 中断的主要类型,PPI 用于本地外设,SGI 用于核间通信。中断亲和性允许将中断绑定到指定 CPU,是实现负载均衡和实时性的关键手段。掌握 GIC 架构和中断类型,是编写高效 GPIO 中断驱动的必备基础。