合法的转换只有五条:就绪→运行、运行→就绪、运行→阻塞、阻塞→就绪、运行→僵尸。最有用的一条是【阻塞→运行不合法】——醒来不等于立刻跑,只是有了排队资格。
4.6 立住了五个状态,这一节把状态之间的路画出来,并且重点讲【哪些路不通、为什么不痛】——不合法的那几条才是理解的试金石。
一、五条合法的路
就绪 → 运行:被调度器选中。这是唯一一条【进入 CPU】的路。 运行 → 就绪:时间片用完,或者被更高优先级的抢占。这是分时的核心动作。 运行 → 阻塞:自己主动让出(等 IO、等锁、sleep)。注意是主动的。 阻塞 → 就绪:等的事件发生了。注意终点是【就绪】不是【运行】。 运行 → 僵尸:自己退出或被信号杀死。这条路没有回头。
二、三条不合法,比合法的更有信息量
阻塞 → 运行:不合法。醒来只是【有了资格】,还得排队。这条最容易被想错——很多人以为事件一发生进程就接着跑。
就绪 → 阻塞:不合法。没在跑,没资格去等 IO——IO 请求是【跑起来之后】才发出去的。
僵尸 → 就绪:不合法。死不能复生。僵尸的唯一出路是被 wait 掉。
把这三条记牢,你看调度日志、看 ps 状态跳变时,就不会做出错误推断。
三、为什么【醒来不能直接跑】
因为 CPU 只有一个(或几个),而醒来的可能有很多个。 如果事件一发生就让它跑,那就是【谁醒谁插队】——调度器就失去了对【谁先跑】的控制权。 插队的后果:一个爱睡爱醒的进程能反复抢占 CPU,把老实干活的饿死。 所以必须经过就绪队列,让调度器统一决定顺序。代价是【唤醒延迟】——醒了还得等排到。
四、真实系统里多出来的路
运行 → 停止(SIGSTOP/调试器),停止 → 就绪(SIGCONT)。
阻塞 → 被唤醒 → 又被抢:事件来了,但 CPU 忙着,它白醒一场回队尾。
不可中断睡眠(D):这条路连信号都进不去,是唯一一条【外力无效】的路。
这些额外路径的存在,说明状态机是【内核实现】而不是【教材抽象】——教材画五条,是因为这五条足以解释绝大部分现象。
五、动手 Demo:状态转换——哪些转换合法
先把五条合法的路列出来,再把三条不合法的一条条说明为什么。 这一节的产出是一张【能贴在墙上的表】。
legal = {
("就绪", "运行"): "被调度器选中",
("运行", "就绪"): "时间片用完 / 被抢占",
("运行", "阻塞"): "等 IO、等锁、sleep",
("阻塞", "就绪"): "等待的事件发生了",
("运行", "僵尸"): "自己退出或被杀死",
}
bad = [("阻塞", "运行"), ("就绪", "阻塞"), ("僵尸", "就绪")]
实跑输出:
--------------------------------------------------------------------
状态转换——哪些转换合法
--------------------------------------------------------------------
① 合法的转换就这五条
从 到 触发原因
--------------------------------------
就绪 运行 被调度器选中
运行 就绪 时间片用完 / 被抢占
运行 阻塞 等 IO、等锁、sleep
阻塞 就绪 等待的事件发生了
运行 僵尸 自己退出或被杀死
② 不合法的转换,才是这一节的重点
从 到 为什么不合法
-------------------------------------------------
阻塞 运行 必须先进就绪队列排队,不能插队
就绪 阻塞 没在跑,没法去等 IO
僵尸 就绪 死不能复生
**【阻塞 → 运行】不合法这一条,是理解调度的关键**:
**醒来不等于立刻跑**,它只是【有了排队资格】——
**什么时候真跑,由调度器说了算。**
五条合法转换里,只有一条是【进入 CPU】的——所以一个进程能跑多久、什么时候能跑,完全由【就绪 → 运行】这一步说了算。
三条非法转换里,【阻塞 → 运行】最重要:它把【唤醒】与【被调度】分开,这正是调度器能控制顺位的前提。
注意【运行 → 阻塞】是主动的:进程自己调 read、自己 sleep。主动让出与被动抢占,是两回事——前者是它知道要等,后者是别人要跑。
📋 在真 Linux 上看状态跳变:watch -n 0.2 'ps -o stat,cmd -p <pid>',或者 cat /proc/<pid>/status | grep State 反复读。一个在做 IO 的进程,你会看到 S 与 R 来回跳。
30 秒实验
- 起一个
while true; do echo x; sleep 1; done,用watch ps看它的状态在 R/S 之间跳。 kill -STOP <pid>然后ps -o stat看它变 T;再kill -CONT看它回 S。cat /proc/<pid>/wchan——它告诉你这个进程在等什么(内核函数名)。
常见误区
- 【IO 完成了进程就接着跑】。它只是从等待队列挪到就绪队列。什么时候真跑,得排队。
- 【被抢占和主动让出是一回事】。主动让出(等 IO)是自己知道要等;抢占是时间片到了或者别人优先级更高。两者对缓存的影响不同。
- 【僵尸能被 kill 掉】。僵尸已经死了,没有任何代码在跑,信号无处可送。只能靠父进程
wait。
自测三问(点开对答案)
1. 五条合法转换是什么?哪一条是【唯一进入 CPU】的路? —— 就绪→运行、运行→就绪、运行→阻塞、阻塞→就绪、运行→僵尸。**只有【就绪→运行】能进 CPU。** 2. 为什么【阻塞→运行】不合法?这条的意义是什么? —— 醒来只是有了排队资格。**这条保证调度器掌握【谁先跑】的决定权**,否则爱醒的进程能反复插队。 3. 【运行→阻塞】是谁发起的?和【运行→就绪】有什么区别? —— 前者由进程自己发起(调 read/sleep,主动让出);后者是时间片用完或被抢占(被动)。- ☐ 4.8 空闲状态
相关笔记
- 进程的状态 — 五个状态码
- 空闲状态 — 没人的时候谁在跑
- 进程睡眠 — 主动让出的实战
- 🐧 Linux是怎样工作的 · 索引