进程与线程
考点定位
- 大纲章节:进程与线程(概念、组成、状态转换、控制、通信、线程模型);处理机调度(时机、切换、三级调度、典型算法);同步与互斥(临界区、实现方法、信号量、经典同步问题);死锁(条件、预防、避免、检测、解除)
- 常见题型:选择题(状态转换、调度算法计算、死锁条件辨析)+ 大题两巨头(PV 信号量同步应用题、银行家算法/死锁分析题),调度算法还常出表格化计算小题
- 考查频率:极高(OS 部分大题的最主要出处,几乎每年综合应用题二选一落在本章)
- 工程师读者注意:你写过 pthread、用锁无数,但考试要的是信号量语义的严格陈述与P 操作次序的死锁分析、银行家算法的表格化推演——工程直觉只帮你理解,答题必须按模板
- 前置知识:操作系统概述(内核态/中断是理解进程切换的基础)
一、进程的概念与组成
1.1 进程定义与 PCB
定义(考点口径):进程是程序在一个数据集合上的一次运行过程,是系统进行资源分配和调度(未引入线程时)的独立单位。程序是静态的指令集合,进程是动态的执行过程——进程有生命周期(创建→运行→消亡),程序可以长期保存。
进程实体的组成(进程映像):
PCB 的内容(简答背诵清单,四大类):
- 进程描述信息:进程标识符 PID、用户标识符 UID
- 进程控制和管理信息:进程当前状态、优先级、代码入口地址、程序在外存的地址、进入内存时间、已用 CPU 时间
- 资源分配清单:代码段/数据段/堆栈段指针、打开文件列表、使用的 I/O 设备
- 处理机相关信息(处理机上下文):通用寄存器、PC、PSW、用户栈指针——进程切换时保存/恢复的就是这部分
组织方式:线性方式、链接方式(按状态挂成就绪队列/阻塞队列等)、索引方式。
1.2 进程状态与转换(五态模型,高频图题)
逐边分析(选择题按"边"命题,每条边的触发者与方向必须精确):
不存在的转换(高频判断):就绪→阻塞、阻塞→运行、阻塞→终止(严格五态口径下阻塞进程不能直接终止需先就绪——408 教材口径,判断题按"阻塞→运行不可能"为准即可)。
引入挂起态(七态模型):内存不足或用户/系统需要时,进程被换出到外存对换区。
- 就绪 → 静止就绪(挂起就绪):激活(active)前不能被调度
- 阻塞 → 静止阻塞(挂起阻塞):即使等待的事件发生,仍需激活回到活动阻塞/就绪
- 区分阻塞(等待事件)与挂起(换出内存):阻塞是等待 I/O 或资源,挂起是内存管理行为,两个正交维度
1.3 进程控制(原语)
进程控制用原语实现——原语是执行期间不允许中断的原子操作,靠关中断指令实现(关中断是特权指令,故原语运行在内核态)。
进程切换的过程(动态过程,分步静态图 + 文字推演占位,待动画化):
- 第 0 步:触发切换的事件(时钟中断 / 主动阻塞 / 更高优先级就绪)
- 第 1 步:保存当前进程的处理机上下文(寄存器、PC、PSW)到其 PCB
- 第 2 步:修改其 PCB 状态并移入相应队列(就绪/阻塞)
- 第 3 步:调度程序按算法从就绪队列选新进程
- 第 4 步:恢复新进程 PCB 中的上下文,更新其状态为运行态
- 第 5 步:CPU 执行新进程(可能需切换页表基址/地址空间,见内存管理章)
注意:"调度"与"切换"不同——调度是决定谁上(选择),切换是换上去(执行);广义调度含选择与切换。切换有成本(纯开销),切换过于频繁降低效率。
1.4 进程通信(IPC)
选择题高频结论:共享内存最快(因为少一次内核中转拷贝);管道是共享存储的一种特殊形式(共享缓冲区);消息传递可适用于分布式(共享内存不行)。
二、线程
2.1 线程概念
定义:线程是进程中的一个执行流,是 CPU 调度和分派的基本单位;引入线程后,进程仅作为资源分配的基本单位(408 标准口径:引入线程后,处理机调度单位是线程,资源分配单位是进程)。
线程自身基本不拥有系统资源,只保有运行必需的一点点:线程 ID、PC、寄存器组、栈。同一进程内的线程共享进程的地址空间(代码段、数据段、打开文件),但各自私有栈与寄存器。
线程的属性(选择题):轻型实体、独立调度分派单位、可并发执行、共享进程资源。同进程内线程切换不引起进程切换(不换地址空间),开销小;跨进程线程切换需换地址空间,开销大。
2.2 多线程模型(用户级/内核级/组合,高频对比)
映射关系(选择送分点):多对一(用户级)、一对一(内核级,如 Linux pthread、Windows)、多对多。
2.3 线程状态与组织
线程同样有就绪、运行、阻塞三态(及创建/终止),转换规则与进程类似。线程组织:TCB(线程控制块,含线程 ID、寄存器、栈指针、状态)挂入进程的线程队列;同进程线程切换不改变进程状态。
关键判定清单:
- 进程切换(换地址空间)必然涉及线程切换;线程切换未必是进程切换。
- 用户级线程的切换不需要内核介入,可在用户态完成;内核级线程的切换需要。
- "同一进程内的线程切换不需要保存/恢复整个进程的地址空间"——正确。
- 进程间通信的所有机制线程都能用之外,同进程线程还可直接读写共享全局变量通信(因此更需同步)。
三、处理机调度
3.1 三级调度
3.2 调度时机、方式与切换过程
不能进行进程调度与切换的情况(高频多选,背清单):
- 处理中断/异常的过程中(中断处理不属于进程上下文)
- 进程在操作系统内核临界区中(如就绪队列本身正在被修改)
- 其他完全不可抢占的内核关键路径上
可以调度切换的时机归纳:当前进程主动放弃(阻塞/终止)、被动失去 CPU(时间片到、被抢占、更高优先级到达)。
调度方式:
- 非抢占(不可剥夺):一旦 CPU 分给进程,直到完成或阻塞才让出。实现简单、开销小,但紧急任务无法及时响应。
- 抢占(可剥夺):按某种原则(优先级/短作业/时间片)剥夺当前进程 CPU。可保证紧急任务及时响应,切换开销大。
进程切换的完整过程见 1.3 节切换原语。注意"调度 ≠ 切换":狭义调度只做"选进程",切换才是"换进程",切换包含上下文保存/恢复(纯开销,无有效计算)。
3.3 调度算法性能指标(计算题基础)
设作业 到达时间 、开始时间 、完成时间 、运行时间 :
- 周转时间 (含等待)
- 带权周转时间 (越接近 1 越好)
- 平均周转时间 ;平均带权周转时间
- 等待时间 = 周转时间 − 运行时间(纯排队);响应时间 = 首次获得 CPU 时刻 − 到达时刻
- 吞吐量 = 单位时间完成的作业数;CPU 利用率 = 忙碌时间 / 总时间
3.4 典型调度算法(必考,含计算套路)
SJF 最优性口径:在所有作业同时到达(或非抢占情形按给定到达序列局部最优)时,SJF 平均等待时间/平均周转时间最小——选择题直接考这个结论。
计算题标准流程(甘特图法):画时间轴甘特图 → 每个作业标完成时刻 → 逐个算 → 求平均。RR 计算注意:按时间片边界推进,同刻到达与转回的入队次序按题面约定(一般新到达先入队、用完的排其后——以题面为准)。
例(模拟题):作业 1/2/3 到达时刻 0/1/2,运行时间 8/4/9。
- FCFS 甘特图:1(0–8),2(8–12),3(12–21)。,。
- 抢占式 SJF(SRTF):0–1 运行 1(剩 7);1 时刻 2 到达(4 < 7)抢占,1–5 运行 2;5–12 运行 1(完成于 12);12–21 运行 3。,优于 FCFS。
四、同步与互斥
4.1 基本概念
- 临界资源:一段时间内只允许一个进程使用的资源(打印机、共享变量等)。
- 临界区:进程中访问临界资源的那段代码。进入临界区前申请(进入区),退出时释放(退出区)。
- 同步:多个协作进程因工作次序需要而相互等待、传递信息("你先我后");互斥:多个进程因独占临界资源而相互排斥("一进一出")。互斥可视为特殊同步。
- 同步机制四准则(必背):空闲让进、忙则等待、有限等待(不能饿死)、让权等待(进不去应释放 CPU,避免忙等——软件方法均不满足此条)。
4.2 互斥实现方法
软件方法(理解思想,选择考缺陷):
硬件方法:
硬件方法共同缺点:忙等待;且 TS/Swap 的 while 期间不释放 CPU。
4.3 信号量机制(PV 操作,大题核心)
整型信号量:整数 S,wait: while(S<=0); S--;,signal: S++;——仍忙等,不满足让权等待。
记录型信号量(考试默认):
P/V 语义严格陈述(答题必背):
- P(wait):S.value 减 1;若减后 S.value < 0,则该进程阻塞并进入该信号量的等待队列。
- V(signal):S.value 加 1;若加后 S.value ≤ 0(说明仍有等待者),从等待队列唤醒一个进程。
- S.value 的物理意义: 表示当前可用资源数量; 时 表示等待该资源的进程数; 表示资源刚好用完且无等待。
- P 操作的次序不可随意交换:先 P 互斥再 P 同步资源会造成死锁(见下文死锁陷阱);V 操作次序相对不敏感(不影响安全性,但影响唤醒时机)。
- P、V 操作(对同一信号量)必须成对出现;P 可能阻塞、V 可能唤醒,二者本身均为原语。
4.4 用信号量实现互斥与同步(前驱关系)
- 互斥:临界资源设
semaphore mutex = 1;,进入区P(mutex),退出区V(mutex)。 - 同步(保证 a 操作先于 b 操作):
semaphore S = 0;先方做完 a 后V(S),后方 b 前P(S)。 - 前驱关系图:每条边一个初值 0 的信号量,前操作完成后对所有出边 V,后操作开始前对所有入边 P。
4.5 经典同步问题(PV 大题模板,完整建模)
PV 大题解题模板(背下来套用):
- 定义信号量并说明每个的物理意义(互斥量初值 1;资源量初值 = 资源数;同步量初值 0)
- 写初值理由(一句话/个)
- 写伪代码:每个进程一个代码块,P/V 与动作交错,同一信号量的 P(mutex) 紧贴临界区
- 验证:检查 P 次序(先资源后互斥)、P/V 配对、V 的位置(完成动作后立即 V)
生产者-消费者(多类资源的 P 次序死锁陷阱)
缓冲区容量 ,生产者放入,消费者取出,缓冲区是临界资源。
死锁陷阱验证:若生产者写成 P(mutex); P(empty);——当缓冲区满(empty=0)时,生产者拿到 mutex 后阻塞在 P(empty);消费者想取产品却因拿不到 mutex 而阻塞——互相等待,死锁。故对资源的 P 必须在对互斥的 P 之前。V 操作的两个次序交换不会死锁(可自证)。
读者-写者(含写优先变体)
允许多读者同时读,写者与所有人互斥。
读优先(读者可无限插队,写者可能饥饿):
关键点:第一个读者负责加锁、最后一个负责解锁,中间读者直接进出;count 的自增/自减必须在互斥下进行。
写优先(写优先变体:有写者等待时阻止新读者进入,避免写者饿死)——加一个 w = 1 的信号量控制"进入读的资格":
(严格"读写公平"版本可再扩展,考试掌握上述两级即可。)
哲学家进餐(5 人 5 筷)
每支筷子一个信号量 chopstick[i] = 1。直接版(每人先拿左再拿右)存在全体同时拿左导致死锁的风险。正确解法任选其一:
- 至多允许 4 人同时拿筷:
semaphore room = 4;拿筷前P(room),放筷后V(room) - 奇数先拿左、偶数先拿右(破坏"同向竞争")
- 一次性拿两支(对拿筷过程整体互斥:
P(mutex); P(左); P(右); V(mutex);)
死锁根因:五人同时各持一支、各等一支——四要素齐备(互斥、请求保持、不可剥夺、循环等待),解法即破坏其一。
吸烟者问题
供应者轮流在桌上放"两样材料",三个吸烟者各持有一种材料,凑齐者取走卷烟并抽(后示意供应者再放)。
要点:供应者每次只放一组(offer 互斥由"只放一个"保证),用 finish 保证"一桌一组"。
五、死锁
5.1 死锁概念与四要素(必背)
死锁:多个进程因竞争资源而造成的一种互相等待的僵局,若无外力介入这些进程都无法推进。
产生死锁的四个必要条件(缺一不可,全背):
- 互斥:资源一次只能被一个进程使用
- 请求并保持(占有并等待):进程已保持至少一个资源,又提出新请求且被阻塞,但对已占资源保持不放
- 不可剥夺(不可抢占):资源只能由占有者用完自愿释放,不能被强行夺走
- 循环等待:存在进程-资源的循环等待链(注意:循环等待不一定死锁——若链外每类资源有多个实例、资源够分则不死锁;但死锁必有循环等待)
死锁、饥饿、活锁辨析:饥饿是进程长期得不到调度(不一定互等,如优先级调度下的低优先级进程);死锁必互等成环;活锁是进程不断改变状态却无进展。
5.2 死锁预防(静态,破坏四条件之一)
互斥条件一般不可破坏(临界资源本质),故预防不提破坏互斥。三个可破坏条件中,资源有序分配法最常考(判断"按序申请可预防死锁")。
5.3 死锁避免(动态):银行家算法(大题核心,完整流程)
数据结构( 个进程, 类资源):
- Available():当前可用各类资源数
- Max():各进程最大需求
- Need()= Max − Allocation:尚需
- Allocation():已分配
- Request():本次请求向量
安全性检查算法(推演流程):
- 初始化:Work = Available;Finish[i] = false(∀i)
- 找一个满足 Finish[i] = false 且 Need_i ≤ Work 的进程 i(找不到则转 4)
- 假装放行:Work = Work + Allocation_i;Finish[i] = true;记下 i,回到 2
- 若所有 Finish[i] = true,则安全(记录的顺序即安全序列);否则不安全
请求处理流程(进程 请求资源 Request_i):
- Request_i ≤ Need_i?否则出错(超过声明的最大需求)
- Request_i ≤ Available?否则等待
- 试探分配:Available −= Request_i;Allocation_i += Request_i;Need_i −= Request_i
- 执行安全性检查:安全则正式分配;不安全则回滚(恢复三组数据),令 等待
表格化推演示例(完整示范,模拟题):系统有 A/B/C 三类资源,总量 (10, 5, 7),当前状态:
Allocation 列和 = (7, 2, 5),故 Available = (10,5,7) − (7,2,5) = (3, 3, 2)。
安全性推演(Work 从 (3,3,2) 出发):
全部 Finish = true,安全序列 P1→P3→P0→P2→P4(不唯一,任给一个合法序列即可)。
追加请求示例:若 P1 请求 (1, 0, 2):① (1,0,2) ≤ Need_1 (1,2,2) ✓;② ≤ Available (3,3,2) ✓;③ 试探分配后 Available = (2,3,0),Allocation_1 = (3,0,2),Need_1 = (0,2,0);④ 重做安全检查可找到序列(如 P1→P3→P0→P2→P4),故可以分配。若 P4 请求 (3,3,0):试探后 Available = (0,0,2),任何进程的 Need 均无法满足(如 P0 需 (7,4,3) > (0,0,2)),不存在安全序列,拒绝并回滚,P4 等待。
5.4 死锁检测与解除
资源分配图:圆点=进程,方框=资源类(框内圆点=实例),请求边(进程→资源)、分配边(资源→进程)。
化简(检测)步骤:1) 找一个非孤立且非阻塞(其所有请求都能满足)的进程 P,它可运行完并释放全部资源 → 2) 删去 P 的所有请求边与分配边(P 变孤立点)→ 3) 重复;若能消去所有边(图完全化简),则无死锁;否则不能化简的进程即死锁进程。
死锁解除:1) 资源剥夺:从其他进程抢资源给死锁进程(被抢者回退);2) 撤销进程:按代价(优先级/已运行时间/还要多久)逐个撤销死锁进程直至破环,可全部撤销;3) 进程回退:让进程退到足以避开死锁的检查点(需记录历史信息)。
死锁处理四种策略对比(选择送分):
例题
例题 1(模拟题) 下列进程状态转换中,不可能发生的是( )。 A. 运行→就绪 B. 阻塞→就绪 C. 就绪→阻塞 D. 运行→阻塞
解:C。就绪态进程未占有 CPU,谈不上请求 I/O 而阻塞;阻塞只能由运行态进入(主动请求等待),唤醒只能到就绪态。
例题 2(模拟题) 下列关于管道通信的说法,错误的是( )。 A. 管道是内存中的固定大小缓冲区 B. 管道通信是半双工的,双向通信需两个管道 C. 写满缓冲区后写进程阻塞 D. 任意两个进程间都可以用 pipe() 建立的管道通信
解:D。匿名管道基于 fork 的描述符继承,限亲缘进程;无亲缘进程需命名管道/FIFO。
例题 3(模拟题) 作业到达与运行时间如下,采用非抢占 SJF 与 FCFS 分别求平均周转时间:J1(0, 7)、J2(2, 4)、J3(4, 1)、J4(5, 4)。
解:FCFS 次序 J1(0–7)、J2(7–11)、J3(11–12)、J4(12–16);,。SJF(0 时刻只有 J1):J1(0–7);7 时刻就绪 J2/J3/J4,选最短 J3(7–8);再选 J2(8–12);J4(12–16);,,更优。
例题 4(模拟题) 面包师问题变式:桌上有一盘容量为 1 的水果,爸爸放苹果,妈妈放橘子,女儿只吃苹果,儿子只吃橘子。用信号量给出同步方案。
解:
验证:盘容量 1 由 plate 保证互斥放置(放前 P、取后 V 成对);apple/orange 初值 0 保证"先放后取"次序;不存在"先占互斥再等资源"的次序问题(plate 即资源本身),无死锁。
例题 5(模拟题) 系统中有同类资源 12 个,进程 P1/P2/P3 各需最多 8/6/9 个,已分别持有 4/3/3 个。该状态是否安全?P3 再请求 2 个能否分配?
解:Available = 12 − 10 = 2。Need:P1 = 4,P2 = 3,P3 = 6。安全检查:P2 需 3 > 2 不行;P1 需 4 > 2 不行;P3 需 6 > 2 不行——无进程可满足,当前已不安全(实际此状态已死锁边缘:无人能完成)。若 P3 请求 2:试探后 Available = 0,更不安全,不能分配。(出题教训:先做安全检查判断当前态,再处理请求。)
常见坑
- P(mutex) 与 P(资源) 的次序:先资源后互斥,反了必死锁(生产者-消费者满缓冲场景);V 的次序不影响安全性。
- 阻塞→运行、就绪→阻塞不存在;阻塞进程由别人唤醒且唤醒后进就绪不是运行。
- 挂起 ≠ 阻塞:挂起是换出内存(内存维度的行为),阻塞是等待事件;静止阻塞收到事件后先回到(活动)阻塞/就绪,需先激活。
- 信号量 时 = 等待进程数; = 可用资源数。P 后 S<0 才阻塞(减 1 后判负);V 后 S≤0 才唤醒(说明队列里有人)。
- 记录型信号量不存在忙等(block/wakeup 实现让权等待);TS/Swap/单标志/Peterson 都忙等。让权等待只有记录型信号量满足。
- 调度算法饥饿判定:SJF、优先级调度会饥饿;FCFS、RR、HRRN 不会饥饿;多级反馈队列若设计得当(最低级轮转)不饥饿。
- RR 时间片的两端极端(过大=FCFS、过小=切换开销大)是判断题常客;"时间片越小越好"必错。
- 循环等待 ≠ 死锁(每类资源单实例时才等价);死锁四条件是"必要"条件,破坏任一即可预防。
- 银行家算法三步:先查 Need、再查 Available、试探分配后必须重做全局安全检查;只看请求本身能否满足就分配是典型错误。
- 安全序列不唯一,答任意一个合法序列即可;"不存在安全序列"才是不安全状态(不安全 ≠ 已死锁,只是可能死锁)。
- 进程是资源分配单位、线程是调度单位(引入线程后);"线程切换必须进入内核态"对内核级线程成立,对用户级线程不成立。
- 管道:半双工、亲缘进程、写满/读空阻塞;"全双工""任意进程"都是错误选项。
自测
- PCB 中哪部分信息用于进程切换时的现场保存与恢复?进程控制原语为什么必须用关中断实现原子性?
- 画出五态图并说明每条边的触发者(自己/其他进程/OS)。
- 某系统采用 RR(时间片 2,同刻"新到达者先入队、用完片者排其后"),作业 A/B/C 到达时刻 0/1/3,运行时间 4/3/2,画甘特图并求平均周转时间。
- 生产者-消费者中若将生产者的 P(empty) 与 P(mutex) 交换,构造一个死锁场景并说明其满足了死锁四要素中哪些。
- 用银行家算法判断:系统有同类资源 9 个,P1/P2/P3 的 Max 分别为 7/5/4,已分配 3/2/2,当前是否安全?给出推演。
自测答案与简析
- 处理机上下文(PC、PSW、通用寄存器、栈指针)。原语期间若被中断打断,进程管理数据结构(如就绪队列)会处于不一致的中间状态,故关中断保证原子性。
- 五态图见 1.2 节 Mermaid 图;创建→就绪与运行→终止由 OS 触发;就绪→运行由调度(OS)触发;运行→就绪由时钟中断/抢占(OS,被动);运行→阻塞由进程自身主动;阻塞→就绪由其他进程 V 操作或 I/O 中断(他人)触发。
- 逐片推演:0 时刻队 [A],A 用 0–2(剩 2);2 时刻 B(1 时刻已入队)在前,A 归队尾 → [B, A],B 用 2–4(剩 1);3 时刻 C 入队 → [A, C];4 时刻 B 归队尾 → [A, C, B],A 用 4–6(剩 0,完成);6–8 C(完成);8–9 B(完成)。完成时刻:A=6、B=9、C=8。周转:、、,。做 RR 题必须逐片列队画甘特图,"同刻到达与归队者的先后"按题面约定执行。
- 缓冲区满(empty=0)时:生产者 P(mutex) 成功后再 P(empty) 阻塞(持 mutex 等空位);消费者 P(mutex) 阻塞(等 mutex)——满足互斥、请求并保持(生产者持 mutex 等 empty)、不可剥夺(mutex 不能强抢)、循环等待(生产者等消费者放空位、消费者等生产者放 mutex),四要素全部满足,死锁。
- Available = 9 − (3+2+2) = 2。Need:P1 = 7−3 = 4,P2 = 5−2 = 3,P3 = 4−2 = 2。推演:P3(需 2 ≤ 2)✓ → Work = 2+2 = 4;P1(需 4 ≤ 4)✓ → Work = 4+3 = 7;P2(需 3 ≤ 7)✓ → Work = 9。全部完成,安全,安全序列 P3→P1→P2(不唯一)。