preemption 与内核抢占
这页讲的是:一个任务正在 CPU 上跑时,并不意味着它一定能一直跑到自己愿意停下。学这页的重点,是理解 preemption 关心的是“谁可以在什么时候把当前执行打断并换成别的执行”,它直接决定系统响应性、延迟和很多并发规则的成立条件。
这块是什么
preemption 与内核抢占,讲的是 Linux 在运行某段代码时,什么时候允许当前执行被更高优先级任务、中断返回后的调度决策或其他时机打断,并把 CPU 交给别的执行路径。它关注的是“当前这段执行能不能被插队”,而不只是“系统最后会调度谁”。
可以把它理解成:你正在柜台前办业务,不代表中途永远不会被更紧急的事情插进来。关键不是有没有排队,而是什么情况下允许插队、插了之后现场会不会乱。
它负责什么
决定执行是否能被中途切走
- 当前任务跑着时不一定天然独占 CPU
- 系统要定义哪些位置允许切换
- 让高优先级工作有机会更快得到响应
- 把“能不能插队”做成正式规则
影响延迟与响应性
- 如果长时间不允许抢占,别的任务可能一直等
- 如果到处都可随时抢占,又会抬高管理复杂度
- 系统要在响应速度和实现约束之间找平衡
- 让延迟预算不是拍脑袋决定
约束并发代码怎么写
- 一段代码会不会在中途被切走,会直接影响它能不能安全访问共享状态
- 有些临界区不仅怕多核并发,也怕本核执行上下文被换走
- 让同步规则不只考虑“别人会不会同时跑”
- 还要考虑“自己会不会突然不在这个点继续跑”
为什么它不是“调度器顺手切一下”
| 如果想得太简单 | 会怎样 | 真正关键在哪 |
|---|
| 觉得调度器想换就换 | 会忽略很多执行位置根本不能随便被打断。 | 抢占不是随意切换,而是受上下文和临界区约束。 |
| 把它和时间片耗尽画等号 | 会漏掉中断返回、优先级变化和内核路径本身的可抢占性。 | 时间片只是触发切换的一种来源,不是全部。 |
| 只看单核顺序逻辑 | 会低估抢占对锁、局部状态和时序假设的影响。 | 很多并发错误并不是同时跑,而是被打断后顺序假设失效。 |
| 觉得关闭抢占只是优化细节 | 会看不见它常常在保护很短但关键的上下文假设。 | 抢占开关本身就是执行环境的一部分。 |
关键概念
| 概念 | 现在怎么理解 |
|---|
| preemption | 当前执行在尚未主动结束时,被系统切走并换成别的执行路径的能力。 |
| 可抢占点 | 系统允许检查并发生切换的时机,不是任何地方都能随便换。 |
| 关闭抢占 | 暂时要求当前这小段执行别在本核被换走,用来保护局部时序假设。 |
| 调度延迟 | 更合适的任务已经想跑,但因为暂时不能切换而被延后的时间。 |
| 执行上下文 | 当前是普通任务、中断后半部还是别的环境,会直接决定抢占规则。 |
为什么重要
- 它解释了为什么系统响应性不只取决于“总会不会调度到”,还取决于“最早什么时候能切过去”。
- 很多锁设计、每 CPU 本地状态和中断/软中断约束,都默认了某些位置不会被随便换走。
- 理解这层后,你会更容易把调度、中断、锁和实时性要求串起来。
- 它让你看到:执行不是只有“谁在跑”,还有“这段跑着的代码允许被怎样打断”。
常见误解
- 误解一:抢占就是多任务的别名。实际上它讲的是切换发生的时机和允许条件。
- 误解二:只要有锁就不用关心抢占。实际上有些问题和共享并发无关,而和本核执行连续性有关。
- 误解三:抢占越积极越好。实际上过度可抢占也会带来额外管理代价和更复杂的约束。
它不负责什么
- 它不单独决定最终选谁运行,那是调度器更大的职责。
- 它不等于中断本身;中断是事件来源,抢占讨论的是执行切换许可。
- 它不自动解决并发正确性,锁、RCU 和生命周期规则仍然各自重要。
和其他模块的关系
| 相关模块 | 关系 |
|---|
| 调度器 | 调度器决定候选任务,而抢占决定当前何时真的能把 CPU 交出去。 |
| 锁与并发控制 | 很多临界区既怕并发访问,也怕执行中途被换走打破局部假设。 |
| 中断与下半部 | 硬件事件和后续处理常会改变系统何时检查、何时允许切换。 |
| 实时性与延迟 | 系统想把响应时间压低时,往往会非常关心可抢占性边界。 |
读完这页后,你应该能回答
- 为什么“调度器最终会切换”并不等于“当前马上就能切换”?
- 为什么抢占规则会直接影响延迟、响应体感和并发代码写法?
- 为什么关闭抢占保护的常常不是大块共享资源,而是很短的执行连续性假设?
后面适合继续问:关闭中断、关闭抢占和拿自旋锁最容易混在哪?为什么同样是单核本地状态,有时关抢占就够,有时还得关中断?PREEMPT_RT 想改善的到底是哪类延迟?