
[Android稳定性] 第036篇 [原理篇] 【深入内核】理解中断上下文、进程上下文以及进程调度之间的关系
iliuqi一、三者概念的梳理
1.1 进程上下文(Process Context)
当内核代码是在为一个具体进程执行某项任务(比如响应系统调用)时,就是在“进程上下文”。
- 是普通用户或内核线程运行的上下文。
- 可以被调度、休眠、阻塞。
- 拥有完整的进程信息(
task_struct
)。 - 可以执行阻塞操作,比如
sleep()
、mutex_lock()
、schedule()
。
✔️ 能被调度、能睡眠、可切换 CPU、参与 CFS 调度
1.2 中断上下文
在 Linux 内核中,中断上下文(interrupt context) 是指内核在响应硬件中断或软中断(如 tasklet、softirq)时运行的上下文。这种上下文不是在代表某个用户进程执行代码,而是在处理外部或内部事件时抢占当前 CPU 的执行流运行的代码。
1.2.1 ✅ 中断上下文的特点
特征 | 描述 |
---|---|
❌不能睡眠 | 绝不能调用可能导致阻塞的函数,比如schedule() 、mutex_lock() |
❌没有用户上下文 | 不是为任何用户进程服务,current 指针虽然存在,但不能用于调度或睡眠 |
✅响应快速 | 中断处理函数要尽可能短,避免阻塞整个系统 |
✅可以抢占进程上下文 | 中断可以打断用户态或内核态代码 |
✅可以嵌套 | 高优先级中断可以嵌套低优先级中断(根据中断控制器配置)arm平台不支持中断嵌套 |
1.2.2 ✅ 中断上下文的类型
- 硬中断(Hard IRQ)
- 是由硬件触发的,比如网卡收到数据、串口完成发送等。
- 使用
request_irq()
注册处理函数。 - 入口是
do_IRQ()
→irq_handler_t
。
- 软中断(SoftIRQ)
- 内核中定义的一种中断延迟机制,用于将某些耗时的中断处理移出硬中断。
- 在
do_softirq()
中调度,比如网络协议栈处理。
- Tasklet
- 更轻量级的软中断,用于驱动程序将中断工作推迟执行。
- 比如网卡驱动在硬中断中收包,然后用 tasklet 处理数据包。
1.2.3 ✅ 为什么不能在中断上下文中睡眠?
中断上下文是“抢占”而来的,它没有独立的进程堆栈,也不是某个进程的调度实体。如果你在中断上下文里睡眠,就会导致:
- 系统调度器无法正确恢复中断处理后的上下文;
- 某些 CPU 核心会 hang;
- 触发
BUG()
或WARNING: sleeping function called from invalid context
等错误。
1.3 Linux 进程调度器(如 CFS)
- 只调度 进程上下文,不调度中断上下文。
- 中断发生时,会中断当前进程的执行,处理完后继续。
- 如果中断处理引起某个进程状态变化(如唤醒某个阻塞的进程),会在下次调度点由调度器进行切换。
二、三者之间的“关联性”
✅ 2.1 中断上下文触发调度事件
中断常用于 I/O、定时器等场景,可能导致以下行为:
行为 | 影响调度 |
---|---|
中断处理程序唤醒某个阻塞的进程(比如唤醒poll() ) |
是(设置need_resched ) |
定时器中断触发调度器周期调度(如 CFS 周期) | 是(周期性调度) |
硬件中断抢占当前进程执行 | 否(中断返回后,仍由调度器决定是否切换进程) |
💡 中断上下文本身不参与调度,但它可以触发调度。
中断上下文“不能被调度” ≠ 它“不能触发调度”
✅ 2.2 进程上下文中主动调用调度器
例如:
schedule()
:手动触发调度器,当前进程让出 CPU。mutex_lock()
:若锁不可用,会调用调度器阻塞当前进程。wait_event()
:等待事件触发,当前进程睡眠,唤醒后重新调度。
✅ 2.3 软中断 / Tasklet 是中间层
- 软中断SoftIRQ:在中断处理后尽快处理的数据包收发、调度等逻辑。
- Tasklet:软中断的封装形式,运行在 softirq 上下文中。
它们是中断和进程之间的桥梁,将部分不能在中断上下文中完成的任务延迟执行,同时不阻塞调度器。
三、总结对比标
特性 | 进程上下文 | 中断上下文 | SoftIRQ/Tasklet |
---|---|---|---|
是否有task_struct |
✅ 有 | ❌ 无 | ❌ 无 |
是否能睡眠 | ✅ 可 | ❌ 不可 | ❌ 不可 |
是否能被调度 | ✅ 可 | ❌ 不可 | ❌ 不可 |
优先级 | 低(相对中断) | 高 | 中等 |
能否触发调度器 | ✅ 可主动调度 | ✅ 可被动触发 | ✅ 可唤醒进程 |
四、举几个不能在中断上下文中调用的函数/行为
函数/行为 | 说明 |
---|---|
mutex_lock() /down() |
可能阻塞,绝对禁止 |
schedule() |
主动调度,只有进程上下文可用 |
copy_to_user() /copy_from_user() |
可能睡眠,且依赖用户地址空间 |
msleep() /usleep_range() |
显式睡眠,不可调用 |
kmalloc(GFP_KERNEL) |
可能睡眠,需改用GFP_ATOMIC |
vfs_* /file_operations 中的阻塞 I/O |
禁止,可能阻塞 |
总结一句话:
中断上下文里不要睡觉(sleep),也不要等锁(block),否则系统会直接💥炸掉或 hang。
五、总结
在中断上下文中绝对禁止使用可能引起睡眠或阻塞的函数
如果我们遇到某个 kernel hang
或 watchdog timeout
造成的死机问题,可以怀疑是某个中断 handler 没有及时退出或调用了阻塞函数。
可以查看这个案例:[Android稳定性] 第033篇 [问题篇] 在中断上下文进行进程调度造成系统卡死
这个案例就是一个典型的在中断处理函数中pm8941_pwrkey_irq
中调用了内核通知链去执行BIO的操作,schedule导致了进程阻塞。
评论
匿名评论隐私政策