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架构中,中断向量表可以位于低地址(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(可用) |
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执行环境:不可睡眠、不可调度、不可访问用户空间 |
| 进程上下文 | 普通进程/线程执行环境:可睡眠、可调度、可访问用户空间 |
| 编程约束 | 中断上下文使用自旋锁,进程上下文使用互斥锁 |