1. 中断基础概念:中断定义、中断向量表、中断上下文与进程上下文区别

1.1 中断的定义与本质

中断(Interrupt)是硬件或软件向处理器发送的一种异步信号,用于通知处理器发生了需要立即关注的事件。中断机制是现代操作系统实现并发处理和实时响应的核心基石。

核心要点:中断的本质是CPU执行流的临时转移——CPU暂停当前正在执行的程序,保存现场,跳转到预先注册的中断服务程序(ISR)执行,执行完毕后恢复现场,继续执行被中断的程序。

中断的主要分类:

分类维度 类型 触发方式 典型示例
触发来源 硬件中断 外部设备通过中断控制器向CPU发送电信号 GPIO按键、UART接收、DMA完成
触发来源 软件中断 CPU执行特定指令主动触发 系统调用(SWI/SVC)、异常(除零)
触发信号类型 电平触发 中断线保持高/低电平直到CPU响应 按键长按检测
触发信号类型 边沿触发 中断线电平跳变(上升沿/下降沿)时触发 GPIO脉冲计数
可屏蔽性 可屏蔽中断 可通过CPU的IF标志位屏蔽 大部分外设中断
可屏蔽性 不可屏蔽中断 CPU必须立即响应,无法屏蔽 硬件看门狗、电源故障
Android BSP实战提示:在Android设备中,触摸屏、传感器、WiFi/BT等外设中断大多使用边沿触发方式,以减少误触发和功耗。调试中断风暴问题时,首先检查中断触发类型配置是否正确。

1.2 中断向量表(Interrupt Vector Table, IVT)

中断向量表是存储在固定内存地址的一张跳转表,表中每个条目对应一个中断号,存储着该中断对应的中断服务程序入口地址。当CPU收到中断信号时,硬件自动根据中断号查表,跳转到对应的ISR执行。

中断向量表结构(ARM Cortex-A 系列) 中断向量表(IVT) 向量0:复位 向量1:未定义指令 向量2:软件中断(SVC) 向量3:预取中止 向量4:数据中止 向量5:IRQ(外设中断) 向量6:FIQ(快速中断) 中断号 中断控制器 (GIC-400) 中断号 → 向量偏移 优先级裁决 IRQ/FIQ CPU Core 保存现场(PC/CPSR/寄存器) 查向量表 → 跳转ISR 执行中断服务程序 恢复现场 → 返回原程序 Android BSP中,GIC(Generic Interrupt Controller)负责将外设中断映射到IRQ向量,驱动通过request_irq()注册ISR
关键注意:在ARM架构中,中断向量表可以位于低地址(0x00000000)或高地址(0xFFFF0000),由系统控制寄存器(SCTLR)的V位控制。Android/Linux内核通常将向量表映射到高地址,以避免与用户空间低地址冲突。

1.3 中断上下文 vs 进程上下文

理解中断上下文与进程上下文的区别,是编写高质量中断驱动的基础。两者在可执行操作、调度能力、栈空间等方面存在本质差异。

对比维度 中断上下文 进程上下文
定义 CPU正在执行中断服务程序(ISR)时的运行环境 CPU正在执行用户进程或内核线程时的运行环境
调度能力 不可调度,ISR执行期间禁止进程切换 可调度,可被更高优先级进程抢占
可睡眠 ❌ 绝对禁止睡眠/阻塞 ✅ 可以调用可能睡眠的函数(如kmalloc(GFP_KERNEL))
使用的栈 中断栈(通常4KB~8KB,固定大小) 进程内核栈(通常8KB~16KB)
可访问用户空间 ❌ 不能访问用户空间内存 ✅ 可以通过copy_from_user/copy_to_user访问
持有锁 只能使用自旋锁(spin_lock),不能使用互斥锁 可以使用互斥锁(mutex)、信号量等
执行时间要求 必须极短(微秒级),否则导致系统响应延迟 相对宽松,可执行耗时操作
内核标识 current宏指向被中断进程的task_struct(不可用) current宏指向当前进程的task_struct(可用)
中断上下文 vs 进程上下文:执行流对比 时间轴 进程上下文 用户进程 / 内核线程 ✅ 可睡眠、可调度 ✅ 可访问用户空间 中断发生 中断上下文 中断服务程序(ISR) ❌ 禁止睡眠、禁止调度 ❌ 不能访问用户空间 返回 中断处理流程(Linux内核) 硬件中断到达 保存现场 执行ISR(中断上下文) 恢复现场 整个ISR执行期间,CPU处于中断上下文,不可睡眠、不可调度、不可访问用户空间

1.4 中断上下文编程约束

在中断上下文中编程,必须严格遵守以下约束,否则会导致内核崩溃或系统死锁:

  • 禁止睡眠:不能调用任何可能导致睡眠的函数,如msleep()、wait_event()、mutex_lock()、kmalloc(GFP_KERNEL)等。
  • 禁止调度:不能主动调用schedule()或触发调度点。
  • 禁止访问用户空间:不能使用copy_from_user()/copy_to_user(),因为此时current指向的进程上下文不可靠。
  • 锁的使用:只能使用自旋锁(spin_lock),且必须关中断(spin_lock_irqsave)以避免死锁。
  • 执行时间:ISR应尽可能短,通常建议在几十微秒内完成。耗时操作应推迟到底半机制(Tasklet/Workqueue/Threaded IRQ)中处理。
Android BSP实战经验:在调试触摸屏驱动时,如果ISR中执行了I2C读取操作(可能睡眠),会导致内核报错:BUG: sleeping function called from invalid context。正确的做法是在ISR中仅做标志位设置和数据读取,将I2C通信放到中断线程化(request_threaded_irq)中处理。

1.5 中断上下文检测方法

在Linux内核中,可以通过以下API检测当前是否处于中断上下文:

#include <linux/hardirq.h>

/* 返回非0表示当前在中断上下文 */
if (in_interrupt()) {
    pr_info("当前处于中断上下文\n");
    /* 只能使用自旋锁,不能睡眠 */
    spin_lock(&my_lock);
} else {
    pr_info("当前处于进程上下文\n");
    /* 可以使用互斥锁 */
    mutex_lock(&my_mutex);
}

/* 更精确的检测 */
if (in_irq())     /* 正在执行硬件中断处理 */
if (in_softirq()) /* 正在执行软中断 */
if (in_nmi())     /* 正在执行NMI处理 */
调试技巧:在驱动开发中,如果怀疑某个函数被错误地在中断上下文中调用,可以在函数入口添加WARN_ON(in_interrupt()),内核会在中断上下文调用时打印栈回溯,帮助快速定位问题。

1.6 本章小结

知识点 核心要点
中断定义 异步事件通知机制,CPU暂停当前任务转去执行ISR
中断向量表 存储中断号到ISR入口地址的映射表,硬件自动查表跳转
中断上下文 ISR执行环境:不可睡眠、不可调度、不可访问用户空间
进程上下文 普通进程/线程执行环境:可睡眠、可调度、可访问用户空间
编程约束 中断上下文使用自旋锁,进程上下文使用互斥锁