1. 中断基础概念
中断(Interrupt)是嵌入式实时系统中处理器与外部世界异步交互的核心机制。它允许硬件设备在需要服务时主动通知处理器,从而避免处理器轮询(Polling)带来的CPU资源浪费。理解中断的基础概念是掌握中断处理驱动开发的基石。
1.1 中断定义与分类
中断是一种硬件或软件触发的电信号,它使处理器暂停当前正在执行的程序,保存现场(Context),转而执行一个预先定义好的中断服务程序(Interrupt Service Routine, ISR),执行完毕后再恢复现场,继续执行被中断的程序。
核心要点:中断的本质是“异步事件驱动”——处理器不需要主动查询设备状态,设备通过中断线“通知”处理器。
中断按触发来源可分为以下几类:
| 分类 | 触发源 | 典型示例 | 特点 |
|---|---|---|---|
| 硬件中断 | 外部设备(外设) | UART接收数据、定时器溢出、GPIO边沿触发 | 异步、随机、与处理器时钟无关 |
| 软件中断 | CPU执行指令 | 系统调用(SWI/SVC)、断点指令(BKPT) | 同步、由程序主动触发 |
| 异常 | CPU内部错误或特殊状态 | 除零错误、未定义指令、页故障 | 同步、通常不可屏蔽 |
实时系统视角:在RTOS中,硬件中断的响应延迟(Interrupt Latency)是衡量系统实时性的关键指标。中断定义中的“保存现场”和“恢复现场”操作耗时,直接影响中断延迟。
1.2 中断向量表(Interrupt Vector Table, IVT)
中断向量表是一张存储在内存固定地址的表格,表中每一项存放着对应中断服务程序(ISR)的入口地址。当处理器接收到中断信号并完成仲裁后,会通过中断号(Vector Number)索引IVT,跳转到对应的ISR执行。
IVT的结构因处理器架构而异:
- ARM Cortex-M系列:向量表起始地址默认为0x00000000,前16个为系统异常向量,从第16个开始为外部中断向量。向量表项存放的是函数地址(而非跳转指令)。
- x86架构:实模式下IVT位于0x00000~0x003FF(256个中断向量,每个4字节);保护模式下使用中断描述符表(IDT),每个描述符8字节,包含段选择子和偏移量。
- RISC-V:使用机器模式中断向量表(mtvec寄存器指向),支持直接向量模式和向量化模式。
常见陷阱:在ARM Cortex-M上,向量表第0项存放的是栈顶指针(MSP),而非ISR地址。如果开发者在链接脚本中错误放置向量表偏移,会导致系统上电后无法正确初始化堆栈,直接进入HardFault。
1.3 中断号(Interrupt Number)
中断号是系统为每个中断源分配的唯一的数字标识符。处理器和中断控制器使用中断号来区分不同的中断请求,并索引中断向量表找到对应的ISR。
中断号的分配规则:
- 系统异常:通常占用最低的几个编号(如ARM Cortex-M的1~15号),用于复位、NMI、硬故障等。
- 外部中断:从某个基数开始连续编号(如ARM Cortex-M从16号开始),每个外设中断源占用一个唯一编号。
- 软件中断:由操作系统或应用程序通过写中断控制器寄存器触发,编号通常位于外部中断之后或由软件指定。
| 架构 | 中断号范围 | 外部中断起始号 | 最大中断数 |
|---|---|---|---|
| ARM Cortex-M3/M4 | 1 ~ 240 | 16 | 240(含系统异常) |
| ARM Cortex-A72 | 0 ~ 987 | 32(PPI) | 988(GICv3) |
| x86 APIC | 0 ~ 255 | 32(IRQ0~IRQ15) | 256 |
| RISC-V(CLINT) | 0 ~ 15(机器模式) | 3(MEI) | 16 |
驱动开发提示:在Linux设备树(Device Tree)中,中断号通过
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH> 这样的属性指定。其中42是相对于GIC SPI(共享外设中断)基址的偏移量,驱动开发者需要查阅芯片手册确认每个外设的中断号映射。
1.4 中断控制器(GIC)简介
通用中断控制器(Generic Interrupt Controller, GIC)是ARM架构中用于管理中断的核心硬件单元。它负责接收来自各个外设的中断请求,进行优先级仲裁、分发和屏蔽,最终将中断信号发送给指定的CPU核心。GIC是构建多核、实时性要求高的嵌入式系统的关键组件。
GIC的架构经历了多个版本迭代,目前主流的是GICv2和GICv3:
| 特性 | GICv2 | GICv3 | GICv4 |
|---|---|---|---|
| CPU接口 | 内存映射寄存器(CPU Interface) | 系统寄存器(SysReg)访问 | 系统寄存器 + 虚拟化扩展 |
| 中断类型 | SGI/PPI/SPI/LPI | SGI/PPI/SPI/LPI + 扩展SPI | 增加虚拟中断(vLPI) |
| 最大中断数 | 1020 | 988(SPI)+ 扩展 | 与GICv3相同 |
| 多核支持 | 支持,但亲和性配置有限 | 增强亲和性路由(GICR) | 支持虚拟化直通 |
| 实时性 | 中等,优先级分组固定 | 高,支持抢占和优先级掩码 | 高,支持虚拟中断直接注入 |
GIC内部的核心组件包括:
- 分发器(Distributor, GICD):全局管理所有中断源的使能、优先级、触发方式(边沿/电平)、目标CPU亲和性。
- CPU接口(CPU Interface, GICC):每个CPU核心独享,负责向核心发送中断信号、应答中断、完成中断。
- 重分发器(Redistributor, GICR):GICv3引入,每个CPU核心对应一个GICR,管理PPI和SGI的配置,减少全局分发器的竞争。
- 中断翻译服务(ITS, GIC ITS):GICv3可选组件,用于管理LPI(本地特定中断)的映射和路由,支持MSI(消息信号中断)。
实时性关键点:GIC的优先级分组(Priority Group)和抢占(Preemption)机制直接影响中断嵌套行为。在GICv2中,通过二进制点(Binary Point)寄存器控制抢占阈值;GICv3则支持更灵活的优先级掩码(Priority Mask)和运行优先级(Running Priority),允许高优先级中断抢占低优先级中断服务程序,这对实时操作系统(RTOS)的中断管理至关重要。
1.5 中断处理流程概览
一个完整的中断处理流程从硬件信号触发到软件ISR执行完毕,通常包含以下步骤:
- 中断请求(IRQ):外设将中断信号发送到中断控制器(如GIC)的引脚或消息通道。
- 中断仲裁:GIC分发器根据优先级、使能状态和目标CPU配置,选择最高优先级的中断,将其路由到目标CPU核心。
- 中断通知:GIC CPU接口向CPU核心发送IRQ信号(或FIQ信号,在ARM中用于快速中断)。
- 现场保存:CPU硬件自动压栈(Push)当前程序的上下文(PC、PSR、LR、R0-R3、R12等),并切换到中断模式(或Handler模式)。
- 向量读取:CPU从中断向量表中读取对应中断号的ISR入口地址。
- ISR执行:CPU跳转到ISR执行。ISR通常分为顶半部(Top Half,快速响应)和底半部(Bottom Half,延迟处理),这部分将在后续章节详细展开。
- 中断完成:ISR执行完毕后,CPU执行中断返回指令(如ARM的
BX LR或MOVS PC, LR),恢复现场,继续执行被中断的程序。
驱动开发实践:在Linux内核中,中断处理流程的步骤④~⑤由
handle_IRQ()或__handle_domain_irq()函数完成。驱动开发者注册的中断处理函数(通过request_irq())在步骤⑤中被调用。理解这个流程有助于定位中断丢失、延迟过高或死锁等问题。