1. 中断控制器概述
1.1 中断控制器在计算机系统中的作用
中断控制器是计算机系统中连接外设与 CPU 的关键硬件单元,负责管理、仲裁和分发中断请求。在嵌入式系统与 Linux 内核启动流程中,中断控制器的正确初始化是系统能够响应外设事件、实现抢占式多任务与实时性的前提。
中断控制器承担以下核心职责:
- 中断信号采集:接收来自多个外设(如 UART、Timer、GPIO、DMA)的硬件中断请求线(IRQ)。
- 优先级仲裁:当多个中断同时到达时,根据预设优先级选择最高优先级的中断提交给 CPU。
- 中断屏蔽与使能:允许软件按需开启或关闭特定中断源,实现精细化的中断控制。
- 中断分发与路由:在多核系统中,将中断定向到指定的 CPU 核心,支持亲和性设置。
- 中断状态管理:记录中断的挂起(Pending)、活动(Active)与完成(Completed)状态,供软件查询。
关键概念: 中断控制器是 CPU 与外设之间的“交通警察”。没有中断控制器,CPU 必须通过轮询方式检查每个外设状态,导致严重的性能浪费和实时性缺失。
1.2 中断控制器与 CPU 的交互方式
中断控制器与 CPU 之间的交互遵循一套标准的硬件握手协议,典型流程如下:
- 外设触发中断:外设将中断请求线(IRQ)置为有效电平或产生上升沿。
- 中断控制器锁存与仲裁:中断控制器检测到有效中断,锁存中断源编号,并根据优先级仲裁选出当前最高优先级中断。
- 向 CPU 发送中断信号:中断控制器通过专用中断引脚(如 nIRQ、nFIQ)或消息信号中断(MSI)通知 CPU。
- CPU 响应中断:CPU 在指令边界检查中断引脚,若中断使能且未屏蔽,则保存当前上下文(PC、PSR 等),跳转到中断向量表(Vector Table)对应的入口地址。
- 读取中断号:在中断服务例程(ISR)中,CPU 通过读取中断控制器的中断号寄存器(如 GICC_IAR)获取具体中断源。
- 执行中断处理:CPU 根据中断号调用对应的处理函数。
- 结束中断(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 → 控制器 | 屏蔽特定中断源 |
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。错误的中断控制器驱动会导致系统无法响应任何中断,造成启动卡死。
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,这是理解中断控制器与内核中断子系统交互的关键。