进程的状态与转换

这一节的考法非常固定:给出一个状态转换,判断其是否可能发生。所以真正要掌握的不是”有五个状态”这种清单,而是每条边为什么存在、以及那两条不存在的边为什么不存在。

机制

三个基本状态是被”缺什么”分出来的

一个进程在任意时刻,无非三种处境:正在用 CPU、能用但轮不到、想用也没用。这就是三个基本状态:

运行态——正在 CPU 上执行。

就绪态——已经获得了除 CPU 之外的一切所需资源,万事俱备只欠 CPU。

阻塞态——在等某个事件(I/O 完成、拿到某个资源、等一个信号),此时即使把 CPU 白送给它也没用,因为它无事可做。

请注意划分它们的那把尺子只有一把:缺的是不是 CPU。

  • 缺 CPU,别的都不缺 → 就绪
  • 缺的是别的东西 → 阻塞
  • 什么都不缺,正在跑 → 运行

这把尺子能解决绝大多数判断题。比如”进程因为时间片用完被换下来”,它缺的是 CPU,所以进就绪队列而不是阻塞队列。

再加上生命周期两端的创建态(正在申请 PCB 和资源,还没准备好)和结束态(正在善后,PCB 还没回收),就是五状态模型。

四条合法的转换边

flowchart LR
    C(创建态):::edge --> |"初始化完成"| R
    R(就绪态):::core --> |"被调度<br/>(唯一入口)"| N(运行态):::core
    N --> |"时间片到<br/>被抢占"| R
    N --> |"主动请求<br/>得不到满足"| B(阻塞态):::core
    B --> |"事件完成<br/>(别人唤醒)"| R
    N --> |"正常 / 异常结束"| E(结束态):::edge
    B -.-> |"✗ 不存在"| N
    R -.-> |"✗ 不存在"| B

    classDef core fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
    classDef edge fill:#f1f5f9,stroke:#94a3b8,color:#334155
    linkStyle 6,7 stroke:#dc2626,stroke-width:2px,color:#dc2626

四条边分别是:

  1. 就绪 → 运行:被调度程序选中。这是进入运行态的唯一途径。
  2. 运行 → 就绪:时间片用完,或被更高优先级的进程抢占。进程本身没出问题,只是暂时让出 CPU。
  3. 运行 → 阻塞:进程主动执行阻塞原语。比如它调用 read() 要读磁盘,数据还没到位,它自己主动把自己挂到阻塞队列上。
  4. 阻塞 → 就绪:等待的事件发生了,由别的执行流(中断处理程序、释放资源的那个进程)执行唤醒原语把它放回就绪队列。

主动与被动:方向性千万别记反

第 3 条和第 4 条有一个非常重要的不对称,这是理解后面所有陷阱题的钥匙:

运行→阻塞是进程自己主动做的;阻塞→就绪是别人被动帮它做的。

为什么必须是这样?因为一个进程只有在正在运行的时候才能执行指令,才能”主动”做任何事。而它一旦阻塞,就不再占有 CPU,它连一条指令都执行不了,当然不可能自己把自己唤醒。必须有外力。

这个不对称直接推出两条不存在的边。

两条不存在的边

阻塞 → 运行:不存在。

阻塞的进程被唤醒后,只能先回到就绪队列排队,等调度程序再次选中它才能上 CPU。它不能插队直达运行态。

道理很直白:唤醒它的那个执行流(比如中断处理程序)无权决定 CPU 接下来给谁——那是调度程序的职责。而且此刻可能有优先级更高的进程正在就绪队列里等着,凭什么让刚醒的这个先跑?

就绪 → 阻塞:不存在。

就绪态的进程没有在执行,它没有机会去请求任何资源,也就无从”得不到满足”而阻塞。要阻塞,必须先被调度上 CPU,在运行过程中发出请求,然后才可能因请求不满足而转入阻塞。

疑问点:就绪进程上了 CPU 却仍缺资源,会怎样

进程由就绪态转入运行态后,若仍缺少所需资源,是否会被重新挂入阻塞队列? 操作系统既然始终维护各类资源的可用数量,这种情形是否还会出现?

这个问题触及”为什么就绪→阻塞不存在”的实质,需分两层回答。

第一层:就绪态的定义已经排除了这种情况。 就绪的含义是”除 CPU 外一切资源都已到手”。如果它还缺别的资源,那它按定义就不该在就绪队列里,而应该在阻塞队列里。所以”就绪上了 CPU 却发现没资源”这个场景,在已经持有的资源上不会发生。

第二层:但它可以请求新资源。 进程上了 CPU 之后继续往下执行,完全可能发出一个新的请求(比如现在要打印了)。如果这个新资源拿不到,它就阻塞——但注意此时它的路径是 运行 → 阻塞,而不是”就绪 → 阻塞”。它是先跑起来了,再阻塞的。

至于 OS 是否始终维护资源数:是的,OS 通过信号量、资源分配表等结构记录每类资源的可用数量。但这不能消除阻塞,只是让”能不能拿到”这个判断有据可依。资源就那么多,拿不到就是拿不到。

挂起态:一个正交的维度

挂起态常被误当成”第六个状态”塞进上面那张图,这会让人很乱。正确的理解是:挂起是另一个维度。

  • 原来的三态问的是:缺的是什么(缺 CPU / 缺事件 / 什么都不缺)
  • 挂起问的是:是否驻留内存(在内存为活动;被换出到外存为静止)

两个维度正交,于是组合出四种:活动就绪、静止就绪、活动阻塞、静止阻塞。

引入挂起是为了在内存紧张时把一些进程整个换到外存去,腾出内存。这就和第 3 章的对换技术接上了。被挂起的进程即使等待的事件完成了,也只能变成”静止就绪”,必须先被换回内存才有资格竞争 CPU。

边界

”任何时刻都只有一个进程处于运行态”——这句话对吗

疑问点:"只有一个"与"有且仅有一个"哪个表述正确

命题:“在单处理机系统中,任何时刻都只有一个进程处于运行态。” 参考答案称该命题错误,理由是死锁时没有进程运行。 那么是否应将其改述为”有且仅有一个”?“只有一个”在中文里似乎已可涵盖 0 个或 1 个。

这道题的措辞确实值得较真,但上述修正方向是反的,需要彻底钉死。

准确的表述是:单处理机下运行态进程数至多为 1,即 0 个或 1 个。

  • 正常情况下是 1 个。
  • 但当所有进程都阻塞(比如全在等 I/O),或者发生死锁时,就绪队列为空,运行态进程数是 0。这时 CPU 跑的是闲逛进程,而闲逛进程通常不被算作用户进程。

所以:

  • “任何时刻至多有一个” ✅ 正确
  • “任何时刻有且仅有一个” ❌ 错误(漏了 0 的情况)

因此把命题改成”有且仅有一个”,反而是把对的改成错的。“只有一个”在中文里确实容易读成”恰好一个”,但在考试语境中它对应的是”至多一个”这一正确命题。处理此类措辞题的通用做法是先追问:0 个的情形是否存在? 在单处理机且全部阻塞或死锁的场景下,答案是存在。

就绪队列里最多能有几个

疑问点:调度的瞬间,就绪队列里到底有几个

  1. 在单处理机系统中,若同时存在 10 个进程,则处于就绪队列中的进程最多有()个?

疑问在于:既然进程是先被放入就绪队列、再被取出投入运行,那么在取出之前的那一瞬间,队列中不是应当有 10 个进程吗?

答案是 9。

这个追问抓住了一个真实的时序细节,但结论仍是 9。原因在于题目问的是”进程状态”,而不是”某一瞬间队列数据结构中的元素个数”。

一个进程一旦被调度程序选中,它的状态就已经变成运行态了。状态的改变是通过修改 PCB 里的状态字段完成的,这个修改和”把它从就绪队列摘下来”是同一个原语里的动作,对外不可分割。所以不存在一个合法的观测时刻,让某个进程同时既是运行态又留在就绪队列里。

换一个角度更清楚:10 个进程中必有 1 个在运行(否则 CPU 空转,题目默认系统正常工作),其余 9 个至多全部就绪。故就绪队列最多 9 个。

如果题目改成”最多有几个进程处于阻塞态”,答案就是 10——因为可以全部阻塞,此时无人运行,这正好又印证了上一条”运行态可以是 0 个”。

释放一台打印机,会改变谁的状态

疑问点:释放一台打印机,唤醒一个还是全部

  1. 一个进程释放了一台打印机,它可能改变()的状态。 A.自身进程 B.输入/输出进程 C.另一个等待打印机的进程 D.所有等待打印机的进程

疑问在于:为何不是 D——等待打印机的进程不应全部由阻塞态转为就绪态吗?

答案是 C,不是 D。

关键在于释放的是”一台”打印机。打印机是互斥共享的临界资源,一台只能给一个进程用。所以这一台被释放出来,只够唤醒一个等待者。

唤醒原语的语义是”从该资源的等待队列队首取出一个进程,把它变成就绪态”,剩下的进程继续阻塞着等下一次释放。

关联辨析: notify 与 notifyAll

Java 中 notify() 唤醒一个等待线程,notifyAll() 唤醒全部。notifyAll 之所以在工程上常被推荐,恰恰是因为它不精确——它唤醒所有等待者重新竞争,依靠 while 循环重新检查条件来兜底,代价是惊群效应。

而教材中”释放一台打印机”是资源语义:可用资源只有一份,唤醒多个没有意义,被唤醒者也拿不到资源。因此它对应 notify 而非 notifyAll。

判据:本次释放出的资源够几个进程使用,就唤醒几个。

对照速查

转换是否存在触发者主动/被动
就绪 → 运行✅调度程序—
运行 → 就绪✅时间片到 / 被抢占被动
运行 → 阻塞✅进程自己调用阻塞原语主动
阻塞 → 就绪✅中断处理程序 / 其他进程唤醒被动
阻塞 → 运行❌—必须先回就绪排队
就绪 → 阻塞❌—没上 CPU 就无从请求资源
问法单处理机下的答案
运行态进程最多几个1
运行态进程最少几个0(全阻塞或死锁)
n 个进程时就绪队列最多几个n − 1
n 个进程时阻塞态最多几个n

考点

  • 判断某条状态转换是否可能(阻塞→运行、就绪→阻塞是标准错误选项)
  • “至多一个”与”有且仅有一个”的措辞辨析
  • n 个进程时各状态的最多/最少个数
  • 释放一份资源唤醒一个而非全部
  • 挂起态与阻塞态是两个正交维度

链接