想象你走进一家热闹的餐厅,厨师面前贴着一排小票:有的菜刚下单(原材料还没准备),有的菜已备好料正等着下锅,锅里正炒着的那道菜占用了厨师的全部注意力,还有一道菜在等蒸锅空出来。每一张小票都记录着“哪道菜、料备到哪一步、火候到几成”。这就是进程世界的缩影——操作系统就是这家餐厅的总调度,它管理的不是菜谱(程序),而是一张张“正在做菜的进度单”(进程)。 CPU 只有一个(或者少数几个),但餐厅里同时有几十桌客人在点菜。厨师不能真的同时炒十道菜,只能炒一下这道、再炒一下那道,靠“记住每道菜炒到哪一步”来来回回切换。操作系统也是如此:它把每个正在运行的程序包装成“进程”,用一块叫做 PCB 的“小票”记录进程的进度,并让进程在新建、就绪、运行、阻塞、终止五个状态之间流转。这篇文章,我们就用“餐厅后厨”这一幅画面,把进程是什么、它有哪些状态、状态之间怎么转换,一次讲透。

岗位关联

理解进程与状态转换,是理解操作系统如何“同时”运行无数程序的基石。对 AI 从业者而言,无论是分析多进程推理服务、排查 GPU 任务排队等待,还是读懂训练框架的分布式调度日志,你看到的每一个"running / waiting / sleep"背后,都是这套状态机在运转。

目录
  1. 可视化演示
  2. 进程基本概念
  3. 进程控制块
  4. 进程生命周期
  5. 状态转换机制
  6. 进程主要特征
  7. 进程与线程
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示操作系统进程的核心概念:五状态转换图(就绪/运行/等待/新建/终止)、PCB进程控制块结构、以及fork()进程创建与写时复制(CoW)机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

'+captions.state+'

进程基本概念

客人点了一份宫保鸡丁,后厨的菜谱架上放着《宫保鸡丁做法》。菜谱本身不会动、不会饿、不会被烫到;真正在灶台上切鸡丁、调芡汁、翻锅的那套“动作”才是活的东西。操作系统把“正在发生的一整套做菜动作”取了个名字——进程(Process);而那份永远不会变的菜谱,就是程序(Program)。

用一句话定义:进程是程序在一个数据集合上的一次执行过程。拆开看有三个要素:一是“程序”,即那本菜谱;二是“数据集合”,即此刻备好的鸡丁、花生米、调料;三是“执行”,即 CPU 正在推进的那一行代码。你可以把它想象成“程序 + 数据 + CPU 现场”三样东西的合体。

进程与程序的区别可以打一张对比表:程序是静态的,存在磁盘里,像一本合上的菜谱;进程是动态的,有出生、有运行、有死亡,像灶台上正被翻炒的一道菜。同一个程序可以同时对应多个进程——十桌客人都点宫保鸡丁,就同时有十份“做宫保鸡丁”的进程,每份的进度各不相同。

回到厨房:菜谱放在架子上十年也不会自己变成菜,只有厨师拿起它、备好料、开火翻炒,才构成一个“进程”。所以操作系统的任务从来不是管理菜谱,而是管理每一个正在灶台上的做菜流程——为它们登记、排队、换锅、熄火。理解了这一点,后面的状态转换就顺理成章了。

进程=程序+数据集合+执行现场\text{进程} = \text{程序} + \text{数据集合} + \text{执行现场}
进程不是软件本身,而是软件“正在被运行”这件事。就像菜谱不是菜,做菜才是。
  1. 程序项进程必须依托一段可执行代码而存在,没有程序就没有进程。
    程序=指令序列\text{程序} = \text{指令序列}
  2. 数据集合项进程处理的数据是独立的,不同进程有各自的输入、输出与中间变量。
    数据集合={d1,d2,…,dn}\text{数据集合} = \{d_1, d_2, \dots, d_n\}
  3. 执行现场项进程拥有自己的程序计数器与寄存器状态,记录“菜做到哪一步”。
    执行现场=(PC,寄存器集合)\text{执行现场} = (\text{PC}, \text{寄存器集合})

进程控制块

厨师同时炒三口锅时,为什么不会搞混?因为他手边贴着三张小票,上面写着“宫保鸡丁:鸡丁已切、花生已炸、还剩最后收汁”。这张小票在后厨里至关重要——一旦厨师被叫去处理另一道菜,回来扫一眼小票就能无缝续炒。操作系统里,这张小票叫进程控制块(Process Control Block,PCB)。

PCB 是操作系统为每个进程建立的一张“订单卡片”,它是进程存在的唯一标志。一个进程从被创建到终止,PCB 始终跟随;进程被换下 CPU 时,它的进度就“存”在 PCB 里;下次被调度时,操作系统把 PCB 里的进度“恢复”出来。所以说,PCB 是进程的身份证,也是进程的存档点。

PCB 里记了什么?首先是进程标识符 PID,相当于小票上的菜名和桌号,系统靠它区分每一个进程;其次是进程状态,标明它此刻是就绪、运行还是阻塞;再是程序计数器,记录下一条要执行的指令地址,也就是“菜做到哪一步”;还有通用寄存器、内存分配信息、打开的文件列表等资源清单,相当于“这道菜占用了哪口锅、哪把勺”。

一张张 PCB 汇总起来,就形成了操作系统的“订单墙”。调度器不看菜谱、不闻菜香,只看小票上的状态和进度,就能决定谁上灶、谁回队。这也是为什么 PCB 被称为“进程实体的一部分”——没有 PCB 的进程,操作系统根本无从管理。

PCB=(PID, 状态, PC, 寄存器, 资源列表)\text{PCB} = (\text{PID},\ \text{状态},\ \text{PC},\ \text{寄存器},\ \text{资源列表})
PCB 里的每一项,都是操作系统“续炒”这道菜时需要的全部现场信息。
  1. PID 项进程的唯一编号,类似订单号,创建时分配、终止时回收。
    PID∈N+\text{PID} \in \mathbb{N}^+
  2. 状态项记录进程当前处于哪个生命周期阶段,是调度器决策的依据。
    状态∈{新建,就绪,运行,阻塞,终止}\text{状态} \in \{\text{新建}, \text{就绪}, \text{运行}, \text{阻塞}, \text{终止}\}
  3. PC 项程序计数器保存下一条指令的地址,是“进度条”的核心。
    PC=下一条指令的内存地址\text{PC} = \text{下一条指令的内存地址}
  4. 资源项登记进程占用的内存、文件、外设,便于回收与隔离。
    资源={内存段,文件描述符,设备}\text{资源} = \{\text{内存段}, \text{文件描述符}, \text{设备}\}

进程生命周期

一道菜从客人点单到上桌,经历:下单(创建)→ 备好料等灶口(就绪)→ 上灶翻炒(运行)→ 等蒸锅/等调料(阻塞)→ 出菜(终止)。操作系统的进程也遵循同样的“五态一生”:新建态、就绪态、运行态、阻塞态、终止态。

  • 新建态(New):小票刚打出来,进程正在被创建,还没进厨房。
  • 就绪态(Ready):所有材料备齐,只等一口空灶;进程已具备运行条件,只差 CPU。
  • 运行态(Running):正占着 CPU 执行指令,是全厨房唯一真正“动手”的状态。
  • 阻塞态(Blocked):运行中需要等待某事件(如 I/O 完成),于是暂时让出 CPU 去等待。
  • 终止态(Terminated):菜已出,小票作废,进程结束并等待系统回收资源。

关键认知:只有一个 CPU 时,同一时刻最多只有一个进程处于运行态;但可以有很多进程同时处于就绪态和阻塞态。就绪是“万事俱备,只欠 CPU”,阻塞是“CPU 给了我我也干不了,因为我在等别的东西”。

后厨的节奏由此变得井然有序:点单的小票先“新建”,备好料进“就绪队列”,轮到灶口才“运行”,等蒸锅时“阻塞”,出菜后“终止”。每一张小票的一生,都是这五个状态的一次完整旅行。理解五态,等于拿到了阅读一切操作系统状态监控数据的钥匙。

S∈{新建, 就绪, 运行, 阻塞, 终止}S \in \{\text{新建},\ \text{就绪},\ \text{运行},\ \text{阻塞},\ \text{终止}\}
五态模型是理解一切进程管理的基础,后续的挂起态、多级队列,都是在这五态上做的扩展。
  1. 创建分配 PID、建立 PCB、装入程序代码,进程从无到有。
    创建:∅→新建态\text{创建}: \varnothing \rightarrow \text{新建态}
  2. 就绪创建完成后进入就绪队列,等待调度器分配 CPU。
    新建态→就绪态\text{新建态} \rightarrow \text{就绪态}
  3. 运行调度器从就绪队列选中一个进程,赋予它 CPU 的使用权。
    就绪态→运行态\text{就绪态} \rightarrow \text{运行态}
  4. 阻塞运行中遇到 I/O 请求或等待事件,进程主动让出 CPU。
    运行态→阻塞态\text{运行态} \rightarrow \text{阻塞态}
  5. 终止进程执行完毕或出现致命错误,系统回收资源并删除 PCB。
    运行态→终止态\text{运行态} \rightarrow \text{终止态}

状态转换机制

厨房里最忙的人不是炒菜的厨师,而是排菜的“调度员”:他面前有两条队——“已备好料的就绪队”和“在等食材/等锅的阻塞队”。一个进程为什么从运行变就绪?因为时间片用完被换下;为什么从运行变阻塞?因为它在等 I/O;为什么从阻塞变就绪?因为它等的事件完成了。这些“为什么”,就是状态转换的驱动事件。

核心转换关系可以总结为四条弧线:就绪→运行是调度器分配 CPU;运行→就绪是时间片到期被抢占;运行→阻塞是进程主动请求 I/O;阻塞→就绪是 I/O 完成被唤醒。特别提醒:阻塞态不能直接进运行态,必须先回就绪队重新排队——就像等蒸锅的菜,蒸锅空出来只是回到“可上灶”的队列,不能直接抢正在炒菜的灶口。

进程中“换人”的这个动作,专业术语叫上下文切换(Context Switch):把当前进程的 PC、寄存器等现场保存进它的 PCB,再把下一个进程 PCB 里的现场恢复到寄存器。这正是“厨师放下 A 菜、抄起 B 菜的小票、立刻续炒”的底层实现。

理解状态转换的关键,是把每条弧线都和一个“事件”绑定:调度的钟声、时间片的哨声、I/O 完成的铃响。下次你在 top 命令里看到进程处于 S(sleeping)状态,不妨想想它正在哪条弧线上——是刚发出 I/O 请求,还是在等待某个事件完成。

δ:(就绪→调度运行)∨(运行→时间片到就绪)∨(运行→I/O阻塞)∨(阻塞→事件完成就绪)\delta: (\text{就绪} \xrightarrow{\text{调度}} \text{运行}) \vee (\text{运行} \xrightarrow{\text{时间片到}} \text{就绪}) \vee (\text{运行} \xrightarrow{\text{I/O}} \text{阻塞}) \vee (\text{阻塞} \xrightarrow{\text{事件完成}} \text{就绪})
状态转换永远是“事件驱动”的,没有事件,就没有转换。
  1. 调度事件就绪队列队首的进程被调度器选中,从就绪变为运行。
    就绪→dispatch运行\text{就绪} \xrightarrow{\text{dispatch}} \text{运行}
  2. 时间片到进程用完了分配给它的 CPU 时间,被强制换下。
    运行→timeout就绪\text{运行} \xrightarrow{\text{timeout}} \text{就绪}
  3. 等待事件进程请求数据或等待外设,进入阻塞队列。
    运行→block阻塞\text{运行} \xrightarrow{\text{block}} \text{阻塞}
  4. 唤醒事件等待的 I/O 完成,进程状态变为就绪。
    阻塞→wakeup就绪\text{阻塞} \xrightarrow{\text{wakeup}} \text{就绪}
  5. 切换执行上下文切换完成两个进程现场的保存与恢复。
    CPU=PCBnew; 现场old→PCBold\text{CPU} = \text{PCB}_{new};\ \text{现场}_{\text{old}} \rightarrow \text{PCB}_{\text{old}}

进程主要特征

进程有四个招牌特征,可以借后厨一一对应:动态性——进程是“正在做菜”,有创建有消亡,而菜谱永远静止;并发性——多个进程在一段时间内交替推进,像厨师在三口锅之间来回翻炒;独立性——每个进程有自己的地址空间和资源,A 菜炒糊了不影响 B 菜;异步性——各进程以不可预知的速度推进,谁先谁后全看调度,像客人无法預知自己这桌上菜的确切时刻。

这四个特征不是理论空谈,它们直接决定了操作系统怎么设计。因为并发,所以需要 PCB 保存进度;因为独立,所以进程间不能随便访问对方内存,需要隔离机制;因为异步,所以需要队列和状态机来管理不确定性。

在资源管理上,进程还有一个重要身份:进程是系统进行资源分配和调度的基本单位。内存、文件、设备,都以进程为单位来分配和回收。就像餐厅里,灶台、蒸锅、调料架的配额是下发给“每一张小票”的。

记住这四个特征,就能回答面试官最爱问的一道题:“程序和进程有什么区别?”——程序是静态的、永久的,进程是动态的、暂时的;程序可被多个进程共享,进程不能脱离程序而存在。后厨里的菜谱可以传三代,但任何一次“做菜”都只是一顿饭的时间。

进程特征={动态性, 并发性, 独立性, 异步性}\text{进程特征} = \{\text{动态性},\ \text{并发性},\ \text{独立性},\ \text{异步性}\}
这四个特征相互支撑:动态是前提,并发是表现,独立是保障,异步是结果。
  1. 动态性进程有完整的生命周期,由创建而产生、由调度而运行、由终止而消亡。
    进程:创建→运行→撤销\text{进程}: \text{创建} \rightarrow \text{运行} \rightarrow \text{撤销}
  2. 并发性多个进程轮流使用 CPU,宏观上同时推进,微观上交替执行。
    并发:P1∥P2⇒P1⊕P2\text{并发}: P_1 \parallel P_2 \Rightarrow P_1 \oplus P_2
  3. 独立性各进程拥有独立的内存空间与资源,互不干扰。
    地址空间A∩地址空间B=∅\text{地址空间}_A \cap \text{地址空间}_B = \varnothing
  4. 异步性进程推进速度不可预知,但结果应当确定。
    时序不可预测⇒结果可预测\text{时序不可预测} \Rightarrow \text{结果可预测}

进程与线程

一道复杂的菜(比如佛跳墙)往往由一个厨师和两个帮厨共同完成:厨师掌勺,帮厨切菜、看火。他们共享同一本菜谱、同一个操作台,但各干各的活。操作系统把这个思路实体化了:线程(Thread)就是进程内的一条执行流,一个进程可以包含多个线程,它们共享进程的代码、数据和资源,却拥有各自独立的程序计数器和栈。

为什么需要线程?因为进程太重了——创建进程要分配独立的资源、切换时要保存整个现场,就像为一道菜单独开一个厨房。而线程仿佛“同一厨房里的多个厨师”,创建快、切换轻,还能天然共享资源(同一份食材,不需要复制三份)。因此现代操作系统把进程定位为“资源分配的基本单位”,把线程定位为“CPU 调度的基本单位”。

进程与线程的对比:进程之间相互隔离,一个进程崩溃一般不影响其他进程;而同一进程内的线程共享地址空间,一个线程越界可能拖垮整个进程。资源方面,进程拥有独立的内存、文件、设备;线程只拥有自己的栈和寄存器,其余全部共享。

后厨的比喻在这里达到圆满:进程是“一家餐厅”的完整配置(店面、食材、执照),线程是店里忙碌的厨师。开一家新餐厅代价高昂,但在同一家店里多雇一个帮厨却便宜得多。这也解释了为什么如今高性能服务器动辄“多线程”——在高并发的 AI 推理服务里,一个进程内的多个线程共享模型权重,比开多个进程划算得多。

进程=资源容器+至少一个线程;线程=进程内可调度的执行流\text{进程} = \text{资源容器} + \text{至少一个线程}; \quad \text{线程} = \text{进程内可调度的执行流}
把进程想成一家餐厅,线程就是店里各司其职的厨师和服务员,共享店面与食材,但各有各的活儿。
  1. 线程定义线程是进程内的一个执行单元,是系统独立调度和分派 CPU 的基本单位。
    线程⊆进程,∣线程∣≥1\text{线程} \subseteq \text{进程},\quad |\text{线程}| \geq 1
  2. 共享资源同一进程的线程共享代码段、数据段、堆和打开的文件。
    资源线程1=资源线程2=进程资源\text{资源}_{\text{线程1}} = \text{资源}_{\text{线程2}} = \text{进程资源}
  3. 独立现场线程拥有自己的 PC、寄存器和栈,保证各自执行路径独立。
    现场线程=(PCi,栈i)≠现场线程j\text{现场}_{\text{线程}} = (\text{PC}_i, \text{栈}_i) \neq \text{现场}_{\text{线程}_j}
  4. 关系总结进程是资源分配单位,线程是 CPU 调度单位。
    调度单位=线程≪进程=资源单位\text{调度单位} = \text{线程} \ll \text{进程} = \text{资源单位}

AI 应用场景

场景一:大模型推理服务的并发调度

阶段:模型部署与推理服务

解决的问题:当多个用户同时请求大模型推理时,如果按传统方式每个请求开一个进程,内存开销巨大(每个进程都要加载一份模型权重);用进程+线程的思维,一个进程内创建多个线程,共享常驻内存中的模型参数,让 GPU 排队调度更高效。

具体案例:某 AI 公司用 vLLM 部署 7B 模型,推理进程常驻显存,每个请求由一个工作线程处理。观察 nvidia-smi 时你会看到进程状态在 R(运行)与 S(休眠等待 I/O)之间跳动——这正是“运行态→阻塞态→就绪态”的实时写照。当一个请求等待 GPU 计算结果返回时,该线程阻塞,CPU 立即调度其他线程处理新请求。

场景二:训练任务的状态监控与故障诊断

阶段:模型训练与资源管理

解决的问题:大模型训练时,数据加载(I/O)常常成为瓶颈,导致 GPU 空转。用进程状态模型来诊断:如果训练进程长期处于阻塞态,说明它在等数据;如果频繁在就绪和运行间切换,说明 CPU 在抢时间片。理解了状态转换,就能精准定位是 I/O 慢还是 CPU 不足。

具体案例:训练工程师用 top 查看进程状态列,发现 D(不可中断睡眠,即阻塞在磁盘 I/O)占比超高,于是改用 DataLoader 多进程预取数据,把“等待数据”的阻塞态时间转化为“计算+加载并行”,训练吞吐提升 38%。

场景三:微服务架构中的进程隔离设计

阶段:AI 应用架构设计

解决的问题:AI 平台常把不同模型服务拆成独立进程部署,利用“进程独立性强”的特性做故障隔离——一个模型服务崩溃不会拖垮整个平台;同时利用“线程共享资源”的特性,在单进程内处理海量并发推理请求。

具体案例:某推荐系统平台把 CTR 模型、向量检索、重排模型分别部署为独立进程,进程间通过消息队列通信;每个进程内部用线程池处理请求。一次线上事故中,重排进程因内存泄漏被杀掉重启,其余进程毫发无损——进程的独立性成了系统稳定的最后一道防线。

Python 演示

python82 行
"""进程五态转换模拟器:演示就绪、运行、阻塞、终止之间的转换。"""import timefrom collections import deque  class PCB:    """进程控制块:相当于后厨的点单小票。"""     def __init__(self, pid, name):        self.pid = pid        self.name = name        self.state = "NEW"        self.pc = 0          # 程序计数器:菜做到哪一步        self.total_ticks = 4  # 总共需要 4 个时间片  class Scheduler:    """简化调度器:管理就绪队列与阻塞队列。"""     def __init__(self):        self.ready_queue = deque()        self.blocked_queue = deque()        self.running = None     def create(self, pid, name):        p = PCB(pid, name)        p.state = "READY"        self.ready_queue.append(p)        print(f"[创建] {name}(pid={pid}) 进入就绪队列")        return p     def tick(self):        if self.running is None and self.ready_queue:            self.running = self.ready_queue.popleft()            self.running.state = "RUNNING"            print(f"[调度] {self.running.name} 上 CPU 运行")         if self.running is None:            print("[空闲] 没有可运行的进程")            return         self.running.pc += 1        if self.running.pc >= self.running.total_ticks:            print(f"[完成] {self.running.name} 执行完毕 (终止)")            self.running.state = "TERMINATED"            self.running = None            return         if self.running.pc == 2:            print(f"[阻塞] {self.running.name} 请求 I/O, 进入阻塞队列")            self.running.state = "BLOCKED"            self.blocked_queue.append(self.running)            self.running = None            return         print(f"[时间片] {self.running.name} 用完时间片, 回到就绪队列")        self.running.state = "READY"        self.ready_queue.append(self.running)        self.running = None     def wakeup(self):        if self.blocked_queue:            p = self.blocked_queue.popleft()            p.state = "READY"            self.ready_queue.append(p)            print(f"[唤醒] {p.name} I/O 完成, 回到就绪队列")  def main():    sched = Scheduler()    sched.create(1, "train_worker")    sched.create(2, "data_loader")    for i in range(12):        print(f"\n--- 第 {i + 1} 个时间片 ---")        sched.tick()        if i == 3:            sched.wakeup()        time.sleep(0.3)  if __name__ == "__main__":    main()
目的:用 60 行代码模拟进程从创建、就绪、运行、阻塞到终止的完整生命周期,直观展示“时间片到期回就绪”与“I/O 请求进阻塞”两条典型转换弧线。
  1. class PCB:用一张小票抽象进程控制块,记录 pid、状态和程序计数器。
  2. self.ready_queue = deque()就绪队列用双端队列实现,先进先出调度。
  3. def create(self, pid, name):创建进程并放入就绪队列,对应“新建态→就绪态”。
  4. self.running = self.ready_queue.popleft()从就绪队列取一个进程上 CPU,对应“调度”。
  5. if self.running.pc == 2:模拟进程运行一段时间后请求 I/O,进入阻塞态。
  6. sched.wakeup()模拟 I/O 完成,把阻塞进程唤醒回就绪队列。

常见误区

误区认为进程和程序是同一个东西,“我写了一个 Python 脚本,那它就是一个进程”。

正解程序是静态的指令集合,像一本菜谱;进程是程序被加载到内存、获得资源并开始执行后的动态实体,像“正在灶台上做这道菜”这件事。一个程序可以对应多个进程(同一脚本被启动三次就是三个进程),同一进程的代码段也可能被多个进程共享。判断标准很简单:有 PID、有状态、有 PCB 的才是进程。

为何常见:日常开发中我们常说“跑一个程序”,给人的错觉是程序自己会跑;其实真正跑起来的是操作系统为它创建的进程。

误区看到进程处于阻塞态,就以为它“死机”了或者不再往前推进。

正解阻塞态是进程主动或因 I/O 被迫让出 CPU,去等待某个事件(键盘输入、磁盘读取、网络包到达)。事件完成后进程会回到就绪态,等待再次被调度。它更像“菜在等蒸锅”,而不是“厨房关门”。区分阻塞与终止的关键:阻塞的进程还有未来,终止的进程已经销毁。

为何常见:top 命令里大量进程显示 S 状态,新手容易误读为系统卡死;其实这正是操作系统高效利用 CPU 的表现——等待时不占 CPU。

误区认为“运行变就绪”和“运行变阻塞”都是被动的、被系统打断的,因此没必要区分。

正解两者本质不同。时间片到期是被抢占(preempt),进程本身没做错什么,只是配额用完,回就绪队重新排队;而阻塞是进程主动要求等待 I/O 或某个事件,原因在进程自身。可以这样记:时间片到期是“到点下班”,阻塞是“我得等个快递”。状态机里这两条弧线方向完全不同,调度器对它们的处理方式也截然不同。

为何常见:两者都导致进程离开 CPU,表象相似,初学者容易混淆“被动让出”与“主动等待”。

误区以为“并发”就是“并行”,觉得只要进程多,就能同时跑在多个核上。

正解并行(parallel)确实要求多核同时执行;而并发(concurrent)是逻辑上的同时,单个核上多个进程靠时间片轮流执行。经典的五态模型讨论的正是单核视角下进程的宏观并发。多核只是多了几个“运行态”名额,状态机和转换规则完全不变。就像餐厅里有两位厨师,也只是两口锅同时在炒,每口锅依然要遵守“一锅一菜”的铁律。

为何常见:日常用语里“并发”和“并行”经常混用,加上多核宣传深入人心,容易忽略单核轮流执行这个基础事实。

面试题

Q1. 进程与程序的本质区别是什么?

单选题 · easy

  • A.进程是静态的,程序是动态的
  • B.程序是静态的,进程是动态的← 正确答案
  • C.两者都是静态的
  • D.两者都是动态的

答案:B。程序是存放在存储介质上的指令集合,像一本不会自己变动的菜谱;进程是程序在数据集合上的一次执行过程,有创建、运行、终止,像灶台上正在被翻炒的菜。因此“静态对程序、动态对进程”才是正确的对应关系。

Q2. 一个进程被创建后,首先进入哪个状态?

单选题 · easy

  • A.运行态
  • B.阻塞态
  • C.就绪态← 正确答案
  • D.终止态

答案:C。进程创建完成后会先进入就绪态,等待调度器分配 CPU。它不能直接从新建跳到运行,就像备好料的菜必须先进“等灶口”的队列,而不能直接抢正在炒菜的锅。只有被调度器选中后才会从就绪态转为运行态。

Q3. 下列哪个状态转换是“时间片用完”触发的?

单选题 · easy

  • A.运行态 → 就绪态← 正确答案
  • B.就绪态 → 运行态
  • C.运行态 → 阻塞态
  • D.阻塞态 → 就绪态

答案:A。时间片用完时进程被剥夺 CPU,从运行态回到就绪态排队,相当于“到点下班回队重排”。B 是调度器主动分配 CPU,C 是进程主动请求 I/O,D 是等待的事件完成。只有 A 对应被动的时间片到期抢占。

Q4. 一个运行中的进程发出 I/O 请求,它的状态变化是?

单选题 · mid

  • A.运行态 → 就绪态
  • B.运行态 → 阻塞态← 正确答案
  • C.阻塞态 → 就绪态
  • D.就绪态 → 运行态

答案:B。进程运行中请求 I/O 时,它无法继续执行,必须等待 I/O 完成,因此进入阻塞态。它不会先回就绪态,因为就绪态意味着“万事俱备只欠 CPU”,而此刻它缺的是数据而非 CPU。I/O 完成后它才从阻塞态回到就绪态。

Q5. PCB(进程控制块)中不需要记录下列哪项信息?

单选题 · mid

  • A.进程标识符 PID
  • B.程序计数器 PC
  • C.进程当前状态
  • D.另一份菜谱的完整内容← 正确答案

答案:D。PCB 相当于后厨的点单小票,记录 PID(哪道菜)、PC(做到哪一步)、状态(在等什么)、资源占用(占着哪口锅)。它不需要保存其他程序的代码——那是被共享的程序本体,不归单一进程独有,类似小票上不需要抄写完整菜谱。

Q6. 下列关于就绪态与阻塞态的对比,正确的有?

多选题 · mid

  • A.就绪态进程只缺 CPU← 正确答案
  • B.阻塞态进程可能缺 CPU 也可能缺数据
  • C.就绪态进程可以直接运行
  • D.阻塞态进程必须回到就绪态才能运行← 正确答案

答案:AD。就绪态的一切资源都已齐备,只差被调度器选中获得 CPU,所以 A 正确。阻塞态进程是在等某个事件(数据、I/O),它即使有 CPU 也无法推进,所以阻塞态必须等事件完成后回到就绪态、再经调度才能运行,D 正确。阻塞态并不“缺 CPU”,它缺的是等待的事件,B 错误;就绪态也不能“直接运行”,必须经过调度器分配 CPU,C 错误。

Q7. 上下文切换主要完成的工作是?

单选题 · mid

  • A.把当前进程的小票撕掉
  • B.保存当前进程现场到 PCB,并恢复下一个进程的现场← 正确答案
  • C.为新进程重新编译代码
  • D.清空内存中的所有数据

答案:B。上下文切换是“厨师换菜”的底层实现:先把当前进程的 PC、寄存器等现场保存进它的 PCB,再把下一个进程 PCB 里的现场恢复到寄存器,这样新进程能无缝续跑。它不销毁 PCB、不编译代码、不清空内存,只是交换“存档”。

Q8. 关于进程的“并发性”,下列说法正确的是?

单选题 · hard

  • A.多个进程必须同时在不同 CPU 上运行
  • B.多个进程在一段时间内交替执行,宏观上同时推进← 正确答案
  • C.多个进程互相等待,谁也不运行
  • D.只有一个进程能存在于系统中

答案:B。并发是逻辑上的同时:在单核 CPU 上,多个进程靠时间片快速交替,宏观上好像同时运行,微观上任意时刻只有一个进程在运行。并行(A)才要求多核同时执行。C 是死锁场景,D 与多道程序设计矛盾。

Q9. 下列哪些转换在经典五态模型中是合法的?

多选题 · hard

  • A.就绪态 → 运行态← 正确答案
  • B.阻塞态 → 运行态
  • C.运行态 → 阻塞态← 正确答案
  • D.阻塞态 → 就绪态← 正确答案

答案:ACD。经典五态模型中合法的转换包括:就绪→运行(调度)、运行→就绪(时间片到)、运行→阻塞(等待 I/O)、阻塞→就绪(I/O 完成)、运行→终止。阻塞态不能直接进运行态,因为等待事件完成后必须先回到就绪队列排队,就像等蒸锅的菜蒸好后要重新排“上灶队”,不能直接抢正在炒菜的锅。

Q10. 下面关于多线程与进程关系的描述,正确的有?

多选题 · hard

  • A.进程是资源分配的基本单位← 正确答案
  • B.线程是 CPU 调度的基本单位← 正确答案
  • C.同一进程内的线程共享进程的代码和数据← 正确答案
  • D.不同进程内的线程可以共享同一段栈空间

答案:ABC。经典结论:进程是资源分配的基本单位(A 对),线程是系统独立调度和分派 CPU 的基本单位(B 对)。同一进程的多个线程共享代码段、数据段和堆,但各自拥有独立的栈和寄存器(C 对)。不同进程的地址空间相互隔离,它们的线程栈也彼此独立,不能共享同一段栈空间(D 错)。