1. 中断控制器概述

1.1 中断控制器在计算机系统中的作用

中断控制器是计算机系统中连接外设与 CPU 的关键硬件单元,负责管理、仲裁和分发中断请求。在嵌入式系统与 Linux 内核启动流程中,中断控制器的正确初始化是系统能够响应外设事件、实现抢占式多任务与实时性的前提。

中断控制器承担以下核心职责:

  • 中断信号采集:接收来自多个外设(如 UART、Timer、GPIO、DMA)的硬件中断请求线(IRQ)。
  • 优先级仲裁:当多个中断同时到达时,根据预设优先级选择最高优先级的中断提交给 CPU。
  • 中断屏蔽与使能:允许软件按需开启或关闭特定中断源,实现精细化的中断控制。
  • 中断分发与路由:在多核系统中,将中断定向到指定的 CPU 核心,支持亲和性设置。
  • 中断状态管理:记录中断的挂起(Pending)、活动(Active)与完成(Completed)状态,供软件查询。
关键概念: 中断控制器是 CPU 与外设之间的“交通警察”。没有中断控制器,CPU 必须通过轮询方式检查每个外设状态,导致严重的性能浪费和实时性缺失。

1.2 中断控制器与 CPU 的交互方式

中断控制器与 CPU 之间的交互遵循一套标准的硬件握手协议,典型流程如下:

  1. 外设触发中断:外设将中断请求线(IRQ)置为有效电平或产生上升沿。
  2. 中断控制器锁存与仲裁:中断控制器检测到有效中断,锁存中断源编号,并根据优先级仲裁选出当前最高优先级中断。
  3. 向 CPU 发送中断信号:中断控制器通过专用中断引脚(如 nIRQ、nFIQ)或消息信号中断(MSI)通知 CPU。
  4. CPU 响应中断:CPU 在指令边界检查中断引脚,若中断使能且未屏蔽,则保存当前上下文(PC、PSR 等),跳转到中断向量表(Vector Table)对应的入口地址。
  5. 读取中断号:在中断服务例程(ISR)中,CPU 通过读取中断控制器的中断号寄存器(如 GICC_IAR)获取具体中断源。
  6. 执行中断处理:CPU 根据中断号调用对应的处理函数。
  7. 结束中断(EOI):处理完成后,CPU 向中断控制器写入中断结束标志(如 GICC_EOIR),通知控制器可以发送下一个中断。
交互要点: 现代中断控制器(如 GICv2/v3)支持 中断抢占 和 中断分组,允许高优先级中断打断低优先级中断的处理,这对实时操作系统(RTOS)和 Linux 的实时性至关重要。

下表总结了中断控制器与 CPU 交互中的关键信号与寄存器:

交互阶段 信号/寄存器 方向 说明
中断请求 nIRQ / nFIQ 控制器 → CPU ARM 架构中普通中断与快速中断请求线
中断号获取 GICC_IAR CPU → 控制器 读取当前最高优先级中断的 ID
中断结束 GICC_EOIR CPU → 控制器 写入中断 ID 以通知处理完成
中断使能 GICD_ISENABLER CPU → 控制器 使能特定中断源
中断屏蔽 GICD_ICENABLER CPU → 控制器 屏蔽特定中断源
中断控制器与 CPU 交互流程 中断控制器与 CPU 交互流程 外设 (Peripheral) 中断控制器 (GIC) CPU 核心 中断请求 (IRQ) nIRQ/nFIQ 交互步骤: ① 外设产生中断请求,发送到 GIC 的 SPI/PPI 中断线 ② GIC 进行优先级仲裁,选择最高优先级中断 ③ GIC 通过 nIRQ 引脚向 CPU 发送中断信号 ④ CPU 读取 GICC_IAR 寄存器获取中断号 ⑤ CPU 执行对应的中断服务例程 (ISR) ⑥ CPU 写入 GICC_EOIR 寄存器,通知中断处理完成 ⑦ GIC 清除该中断的 Active 状态,允许下一个中断

1.3 中断控制器的发展历史

中断控制器从简单的可编程中断控制器(PIC)演进到如今功能强大的通用中断控制器(GIC),其发展历程反映了计算机系统对中断处理能力需求的不断提升。

时代 控制器类型 代表芯片 核心特性 局限性
1980s PIC (可编程中断控制器) Intel 8259A 8 个中断输入、优先级级联、向量中断 最多级联 64 个中断,无多核支持
1990s APIC (高级可编程中断控制器) Intel I/O APIC + Local APIC 支持 SMP、中断重定向、MSI x86 架构专用,ARM 平台不适用
2000s GICv1 (通用中断控制器 v1) ARM GIC-400 支持 8-1020 个中断源、SMP、优先级分组 不支持虚拟化,中断号空间有限
2010s GICv2 ARM Cortex-A15/A7 集成 支持 CPU 接口虚拟化、安全扩展、TrustZone 中断路由灵活性不足
2015+ GICv3/v4 ARM Cortex-A72/A76/A78 支持 LPI、ITS、Direct Injection、虚拟化增强 硬件复杂度高,需要操作系统深度适配
注意: 在 Linux 内核启动流程中,中断控制器的初始化阶段必须根据硬件版本选择对应的驱动代码。例如,GICv2 使用 drivers/irqchip/irq-gic.c,而 GICv3 使用 drivers/irqchip/irq-gic-v3.c。错误的中断控制器驱动会导致系统无法响应任何中断,造成启动卡死。
中断控制器发展历程 中断控制器发展历程 8259A PIC 1980s APIC 1990s GICv1 2000s GICv2 2010s GICv3/v4 2015+ 演进趋势: 单核 → 多核 SMP → 虚拟化 → 大规模中断 (LPI) → 中断直接注入 (Direct Injection)

1.4 中断控制器在 Linux 内核启动中的角色

在 Linux 内核启动的早期阶段,中断控制器的初始化是 arch_init_irq() 或 init_IRQ() 流程的核心任务。其初始化顺序直接影响后续所有设备驱动的中断注册与响应能力。

典型的中断控制器初始化流程包括:

  • 设备树解析:从 DTB 中读取中断控制器节点,获取寄存器基地址、中断类型、CPU 接口信息。
  • 寄存器映射:通过 ioremap() 将中断控制器的 MMIO 地址空间映射到内核虚拟地址空间。
  • 硬件初始化:配置中断分发器(Distributor)和 CPU 接口(CPU Interface),设置默认优先级、中断分组、使能控制。
  • 中断域创建:建立 irq_domain,将硬件中断号映射到 Linux 的 IRQ 编号。
  • 中断控制器注册:调用 irq_chip 注册函数,将中断控制器操作集(如 irq_mask、irq_unmask、irq_ack)挂入内核中断子系统。
启动要点: 在 Linux 内核启动的 start_kernel() 函数中,中断控制器的初始化发生在 setup_arch() 之后、time_init() 之前。这意味着在系统时钟和调度器初始化之前,中断控制器必须已经就绪,否则系统无法接收任何硬件中断。

下表总结了不同中断控制器在 Linux 内核中的对应驱动文件与初始化函数:

中断控制器 内核驱动路径 初始化函数 设备树 compatible
8259A PIC (x86) arch/x86/kernel/i8259.c init_8259A() N/A (x86 legacy)
GICv2 (ARM) drivers/irqchip/irq-gic.c gic_of_init() arm,cortex-a15-gic
GICv3 (ARM64) drivers/irqchip/irq-gic-v3.c gic_v3_of_init() arm,gic-v3
RISC-V PLIC drivers/irqchip/irq-plic.c plic_init() riscv,plic0
学习建议: 阅读 Linux 内核源码时,重点关注 drivers/irqchip/ 目录下的驱动文件。每个中断控制器驱动都实现了 struct irq_chip 和 struct irq_domain_ops,这是理解中断控制器与内核中断子系统交互的关键。