1. 中断基础概念

中断是嵌入式系统与 Linux 内核中处理异步事件的硬件-软件协作机制。当外设或 CPU 内部需要立即处理的事件发生时,中断信号打断当前执行流,跳转到预先注册的处理函数,完成后再恢复原执行流。理解中断的定义、向量表、优先级与嵌套是后续框架注册与性能验证的基石。

1.1 中断定义

中断(Interrupt)是一种由硬件或软件触发的信号,通知处理器暂停当前任务,转而执行一个特定的中断服务程序(ISR, Interrupt Service Routine)。中断的核心目的是提高系统实时性与资源利用率,避免 CPU 轮询等待。

中断 vs 轮询对比
特性 中断驱动 轮询方式
CPU 利用率 高,无事时休眠或执行其他任务 低,持续占用 CPU 检查状态
响应延迟 微秒级,事件触发即响应 取决于轮询周期,通常毫秒级
功耗 低,可进入低功耗模式 高,CPU 持续活跃
实现复杂度 较高,需配置中断控制器与向量表 简单,仅需循环读取寄存器
适用场景 实时性要求高、事件稀疏 事件频繁、延迟容忍度高
关键点:在 Linux 内核中,中断处理分为上半部(Top Half,ISR 中执行)和下半部(Bottom Half,如 tasklet、workqueue、软中断),上半部要求快速、不可阻塞,下半部处理耗时工作。

1.2 中断向量表

中断向量表(Interrupt Vector Table, IVT)是一张存储在固定内存地址的跳转表,每个表项对应一个中断号,存放该中断对应的 ISR 入口地址。当 CPU 响应中断时,硬件自动根据中断号从向量表中取出地址并跳转执行。

在 ARM Cortex-M 系列中,向量表起始地址通常为 0x00000000(或通过 VTOR 寄存器重定位),前 16 个为系统异常,之后为外设中断。x86 架构使用 IDT(Interrupt Descriptor Table),每个表项 8 字节,包含段选择子与偏移量。

中断向量表结构示意(ARM Cortex-M)
ARM Cortex-M 中断向量表示例 偏移地址 中断号 异常/中断类型 ISR 入口地址 0x00000000 0 堆栈指针初始值 0x20001000 0x00000004 1 Reset 0x08000101 0x00000008 2 NMI 0x08000201 0x0000000C 3 HardFault 0x08000301 ...... 0x00000040 16 UART1 0x08001001 0x00000044 17 TIM2 0x08001101 0x00000048 18 GPIOA 0x08001201 每个表项 4 字节,共 256 项(0~255),前 16 项为系统异常,16 之后为外设中断
Linux 内核视角:在 x86 上,Linux 启动时通过 setup_idt() 初始化 IDT,中断门使用 DPL=0(内核态)或 DPL=3(用户态)。ARM 平台通过设备树或 request_irq() 动态注册中断处理函数,内核维护 irq_desc 数组管理每个中断号的状态与回调。

1.3 中断优先级

中断优先级决定了当多个中断同时发生时,CPU 优先响应哪一个。优先级通常由中断控制器(如 ARM GIC、x86 APIC、NVIC)的寄存器配置,数值越小优先级越高(ARM Cortex-M 采用 0 为最高优先级)。

优先级分组(Priority Grouping)允许将优先级位拆分为抢占优先级(Preempt Priority)和子优先级(Subpriority),实现更灵活的嵌套控制。

ARM Cortex-M NVIC 优先级分组示例(3 位用于抢占,1 位用于子优先级)
优先级值(8 位) 抢占优先级(3 位) 子优先级(1 位) 实际抢占行为
0x00 (0) 000 0 最高抢占,可打断所有
0x10 (16) 001 0 可打断优先级 2~7 的中断
0x20 (32) 010 0 中等优先级
0x30 (48) 011 0 较低优先级
0xE0 (224) 111 0 最低优先级,不可打断任何中断
注意:在 Linux 内核中,中断优先级通常由硬件中断控制器管理,软件层面不直接修改优先级(除非通过 irq_set_priority() 等平台相关 API)。内核更关注 IRQ 的 irq_chip 和 irq_domain 映射,优先级配置属于板级 BSP 代码。

1.4 中断嵌套

中断嵌套(Interrupt Nesting)是指一个中断服务程序正在执行时,另一个更高优先级的中断到达,CPU 暂停当前 ISR,转而执行更高优先级的 ISR,完成后返回被暂停的 ISR 继续执行。嵌套深度受硬件栈空间和中断控制器支持的限制。

中断嵌套的流程如下:

  1. 低优先级中断触发:CPU 保存现场(PC、PSR、寄存器),跳转到对应 ISR。
  2. 高优先级中断到达:中断控制器向 CPU 发出更高优先级的请求。
  3. 硬件判断:若新中断优先级高于当前 ISR 的优先级(且中断使能),CPU 再次保存当前 ISR 的上下文。
  4. 执行高优先级 ISR:完成后执行中断返回指令(如 ERET 或 BX LR)。
  5. 恢复低优先级 ISR:CPU 恢复之前保存的上下文,继续执行。
中断嵌套流程 SVG 图
中断嵌套执行流程 时间 主程序(Main Loop) 低优先级中断触发 低优先级 ISR(如 UART) 执行中 ... 高优先级中断到达 高优先级 ISR(如 TIM2) 执行完成,返回 恢复低优先级 ISR 低优先级 ISR 继续执行并返回 返回主程序 主程序继续执行 嵌套深度受硬件栈空间限制,ARM Cortex-M 硬件自动压栈,无需软件保存现场
性能影响:中断嵌套会增加延迟抖动(Jitter),因为高优先级中断会抢占低优先级 ISR。在实时系统中,应合理分配优先级,避免高优先级 ISR 执行时间过长,同时确保低优先级 ISR 不会被无限期推迟。Linux 内核通过 local_irq_disable() 和 spin_lock_irqsave() 在临界区屏蔽中断,防止嵌套导致的数据竞争。

1.5 中断处理流程总结

一个完整的中断生命周期包含以下阶段:

  • 触发:外设或软件设置中断挂起位。
  • 仲裁:中断控制器根据优先级选择最高优先级中断。
  • 响应:CPU 保存现场,查找向量表,跳转 ISR。
  • 执行:ISR 执行必要操作(通常为读取数据、清除中断标志)。
  • 返回:执行中断返回指令,恢复现场,继续原执行流。
中断生命周期 SVG 图
中断生命周期 触发 仲裁 响应 执行 返回 各阶段说明: • 触发:外设置位中断挂起寄存器,或软件执行中断指令(如 SVC)。 • 仲裁:中断控制器比较所有挂起中断的优先级,选出最高者。 • 响应:CPU 自动压栈(PC、PSR、LR、R0-R3、R12),读取向量表获取 ISR 地址。 • 执行:ISR 运行,通常包含读取 FIFO、清除中断标志、触发下半部。 • 返回:执行异常返回指令(如 BX LR 带 EXC_RETURN),硬件出栈恢复现场。
常见陷阱:在 ISR 中调用可能导致睡眠的函数(如 mutex_lock()、kmalloc(GFP_KERNEL))会触发内核调度,导致系统死锁或崩溃。ISR 中应仅使用原子操作或 GFP_ATOMIC 标志。Linux 内核提供 in_interrupt() 宏检查当前是否在中断上下文中。