操作系统模块。对应全局地图高频因果链第 2 条(互斥同步链)。 依赖模块:OS-0(中断与内核态)、OS-1(进程/线程与状态转换);向后引出 OS-4 死锁。
核心问题
为什么需要这一套机制?
多道程序让多个进程并发执行、共享资源,但指令是交错执行的。一个看似原子的操作 x += 1 实际是“取 x → 加 1 → 写回”三条指令,两个线程交错执行会丢失更新(2016 年 30 题即考此:共享变量 x 的 x+=1 与 x+=2 必须互斥,而各自的局部变量 a、b 不需要)。
如果没有任何机制:
- 共享数据的最终结果依赖于不可控的交错顺序(竞争条件),程序失去确定性;
- 打印机、缓冲区等独占资源会被同时使用,输出混乱、数据覆盖。
所以需要回答两个层次的问题:
- 互斥:同一时刻只允许一个进程进入访问临界资源的代码段(解决“不能同时”);
- 同步:多个进程之间存在执行先后顺序,必须能“等对方做完某件事我再做”(解决“有先有后”)。
互斥是特殊的同步(同步的等待条件是“临界区空闲”),二者共用同一套机制,但PV 的配对位置不同:互斥的 P、V 在同一进程内夹住临界区;同步的 P、V 分布在不同进程之间传递事件(来源:复习资料 P3 §四)。
概念体系
flowchart TD
A["并发进程共享资源<br/>→ 竞争条件"] --> B["临界资源 / 临界区"]
B --> C["互斥四原则<br/>空闲让进 / 忙则等待<br/>有限等待 / 让权等待"]
C --> D1["软件算法<br/>单标志→双标志→Peterson"]
C --> D2["硬件指令<br/>关中断 / TSL / Swap"]
C --> D3["信号量<br/>整型→记录型"]
C --> D4["管程"]
D1 -- "忙等、不满足让权等待" --> D2
D2 -- "仍忙等" --> D3
D3 -- "P/V 分散在代码中易错" --> D4
D3 --> E["扩展:同步问题<br/>前驱关系 / 经典模型"]
E --> F["PV 设计题"]
核心概念清单:
| 概念 | 定义要点 | 易考点 |
|---|---|---|
| 临界资源 | 一次只允许一个进程使用的资源(打印机、共享变量、缓冲区) | 局部变量不是临界资源(2016 年 30 题) |
| 临界区 | 访问临界资源的那段代码(不是资源本身) | 进入区/临界区/退出区/剩余区四段结构 |
| 互斥四原则 | 空闲让进、忙则等待、有限等待、让权等待 | “让权等待”是判别忙等方案的关键词(2016 年 27、2018 年 32、2020 年 32 题) |
| 信号量 S | 一个整数值 + 一个阻塞队列,只能经 P/V 访问 | 初值 = 可用资源数;当前值 1、初值 3 → 可用 1 个、等待 0 个(2010 年 25 题) |
| P / wait(S) | S--;若 S < 0 则阻塞自己并入队 |
P、V 本身必须是原子操作(2022 年 45 题第 1 问) |
| V / signal(S) | S++;若 S ≤ 0 则唤醒队首一个进程 |
唤醒≠立即运行,只是变就绪 |
| 管程 | 把共享数据结构和其上操作封装为一个模块,编译器保证任一时刻最多一个进程在内执行 | “管程只能实现互斥”是错误说法(2016 年 32 题,管程也能实现同步) |
来源:复习资料 P3 §四;王道 2027 操作系统 进程管理章。
实现机制
1. 软件算法的演进(每一步都在修补上一步违反的原则)
| 算法 | 思想 | 违反了哪条原则 |
|---|---|---|
| 单标志法(turn) | 轮流进,turn 是谁谁进 | 空闲让进:对方不想进,自己也进不了 |
| 双标志先检查 | 先看对方 flag 再设自己 flag | 忙则等待:检查和设置之间被插队 → 同时进入 |
| 双标志后检查 | 先设自己 flag 再看对方 | 可能死锁:都设了 flag,互相等待 |
| Peterson | flag + turn 结合:“我想进,但让你先” | 四原则中满足互斥与有限等待,但仍忙等(2010 年 27 题考的就是 Peterson 变体:能保证互斥、不会出现饥饿) |
2. 硬件方案:把“检查并设置”压成一条原子指令
- 关中断:进临界区前关中断、出后开中断。最简单,但只适用单处理机内核,用户程序不能用(2022 年 45 题第 3 问:开关中断是特权指令,且会屏蔽时钟中断影响调度)。
- TSL(Test and Set)/ Swap:一条指令内完成“读旧值 + 置新值”,硬件保证原子。2024 年 45 题考点:用普通函数模拟 swap 不具备原子性,函数执行中途被切换就会破坏互斥。
- 共同缺陷:等待的进程不停循环测试(忙等/自旋),不满足“让权等待”(2016 年 27 题 TSL 伪代码、2018 年 32 题)。
3. 信号量:用阻塞代替空转
- 整型信号量:
while (S <= 0);仍是忙等(2018 年 32 题辨析)。 - 记录型信号量:
S.value--后若< 0,把进程挂入阻塞队列(让出 CPU);S.value++后若<= 0,唤醒队首。由此实现让权等待——这是信号量相对前三种方案的本质进步。 - 三种典型用法(来源:复习资料 P3 §四):
| 用法 | 初值 | 含义 | P/V 配对位置 |
|---|---|---|---|
互斥 mutex |
1 | 临界区可用 | 同一进程内夹住临界区 |
同步 s |
0 | 事件是否已发生 | 前驱进程 V、后继进程 P(跨进程) |
资源计数 empty/full |
n / 0 | 空位数 / 产品数 | 生产者与消费者之间 |
4. 管程:把分散的 P/V 收拢
信号量的 P/V 由程序员手写、分散在各进程中,顺序写错就死锁(如生产者-消费者中 wait(mutex) 先于 wait(empty))。管程把共享数据 + 访问过程封装,由编译器保证互斥进入,程序员只描述条件(条件变量 wait/signal)。这是“机制从程序员责任变成语言/系统责任”的演化方向。
5. 用信号量解同步问题:PV 设计方法论
分析任何 PV 大题固定三步:
- 找关系:列出进程间所有约束,逐一归类——是“不能同时”(互斥)、“先做后做”(前驱同步),还是“容量有限”(计数资源)?
- 定信号量:每类约束一个(组)信号量;互斥初值 1,同步初值 0,计数初值 = 容量(注意初始已有资源时要相应调整,如 2015 年 45 题信箱 fullA 初值 = x)。
- 写代码守两条铁律:互斥 P/V 同进程成对;同步 P/V 跨进程配对;多个 P 连写时同步 P 在前、互斥 P 在后(否则死锁)。
五大经典模板(默写级):生产者-消费者、读者-写者、前驱图、哲学家(防死锁:限制同时拿筷人数或一次拿两支)、理发师/窗口服务类。完整代码骨架见复习资料 P3 §五。
方案比较
| 方案 | 能否互斥 | 让权等待 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|---|
| 软件算法(Peterson 等) | 能 | 否(忙等) | 纯软件、无硬件要求 | 复杂、忙等、仅限两进程为主 | 教学/理解演进 |
| 关中断 | 能 | 否 | 实现最简单 | 特权操作、单处理机、屏蔽时钟 | OS 内核短临界区 |
| TSL / Swap | 能 | 否(自旋) | 简单、多处理机可用 | 忙等、可能饥饿 | 自旋锁、短临界区 |
| 记录型信号量 | 能 | 能 | 通用、可计数、可同步 | P/V 分散易错、用错顺序会死锁 | 各类同步互斥问题 |
| 管程 | 能 | 能 | 编译器保证互斥、不易错 | 需语言支持 | 现代语言(Java synchronized 思想) |
考试最喜欢的混淆点:“能互斥”与“满足全部四原则”是两回事——Peterson、TSL、Swap 都能保证互斥,但都不满足让权等待;能同时满足的是记录型信号量与管程(2018 年 32 题答案:信号量方法)。
同步 vs 互斥(易混对):为什么易混——都用 P/V。本质区别——互斥是竞争关系(谁先用都行但不能一起用),同步是协作关系(顺序有语义)。一道题的判断线索:把对方进程删掉,若本进程逻辑仍成立只是会冲突 → 互斥;若本进程根本无法继续(缺输入/缺前提)→ 同步。
应用与考法
形态一:PV 设计大题(7~8 分,几乎每年一道)
| 年份题号 | 场景 | 模板归属 |
|---|---|---|
| 2009-45 | 三进程缓冲区,P2 取奇数、P3 取偶数 | 生产者-消费者(双消费者变体) |
| 2011-45 | 银行窗口 + 10 座位取号 | 理发师/服务类 |
| 2013-45 | 博物馆 500 人容量 + 单出入口 | 计数资源 + 互斥 |
| 2014-47 | 环形缓冲区,消费者连续取 10 件 | 生产者-消费者变体 |
| 2015-45 | A/B 信箱辩论,信箱有初始邮件 | 双向生产者-消费者 + 非零初值 |
| 2017-46 | 三线程复数运算互斥 | 纯互斥(找最小临界资源集) |
| 2019-43 | n 哲学家 + m 碗 + 筷子 | 哲学家变体(多资源防死锁) |
| 2020-45 | 前驱图 A、B→C,C、D→E | 前驱图 |
| 2022-45 | 整型信号量 + 开关中断实现 | P/V 原子性分析 |
| 2022-46 | 两线程 6 操作前驱约束 | 前驱图 |
| 2024-45 | swap 指令互斥代码改错 | 硬件原子性 |
| 2025-45 | 三人植树(挖坑/放苗/浇水 + 铁锹水桶 + 坑数 < 3) | 前驱链 + 计数 + 互斥综合 |
解题触发词:看到“最多容纳 N”“缓冲区容量 N”“资源各一个”→ 计数/互斥信号量;看到“A 完成后 B 才能”“坑数小于 3 才能挖”→ 同步/计数信号量;看到“说明信号量含义并赋初值”→ 初值必给分点。
形态二:概念辨析选择题
- 2010-25:信号量初值 3 当前值 1 → 可用 M=1、等待 N=0。
- 2010-27:Peterson 变体 → 能保证互斥、不会出现饥饿。
- 2016-27 / 2020-32:TSL 自旋不满足让权等待;临界区四原则辨析。
- 2016-30:判断哪些操作需互斥(共享变量才是临界资源)。
- 2016-32:管程错误叙述(“只能实现互斥”错误)。
- 2018-32:能实现让权等待的是信号量方法。
来源:真题markdown/2009-2024统考真题.md、真题markdown/2025年计算机408统考真题-副本.md、复习资料 P3 §四五。
前后联系
- 向前依赖:OS-1 进程/线程(并发主体、阻塞/就绪状态转换是信号量让权等待的基础);OS-0 中断(关中断方案、P/V 原子性依赖内核态);OS-2 调度(阻塞唤醒要进就绪队列)。
- 向后引出:
- P/V 顺序错误或互相持有等待 → 死锁(OS-4:哲学家问题的防死锁方案直接对应破坏死锁必要条件);
- 信号量等待外设、缓冲区 → I/O 缓冲管理(OS-8);
- 页表、文件系统等内核数据结构同样要互斥访问 → 内核同步(理解级);
- 跨学科:TSL/Swap 的原子性依赖计组“指令执行不可中断”(CO-4 指令周期),2022-45、2024-45 两题正是站在 OS × 计组交界处命题。
闭卷回忆链
- 为什么并发执行会出错?(指令交错 + 共享资源 → 竞争条件)
- 什么是临界资源和临界区?共享变量和局部变量哪个要保护?
- 实现互斥要满足哪四条原则?“让权等待”卡掉了哪些方案?
- 最早的纯软件方案怎么演进的?单标志 → 双标志 → Peterson 各自修好了什么、还留了什么病?
- 软件方案为什么都忙等?硬件怎么帮忙?(TSL/Swap 原子指令)
- TSL 既然原子,为什么还不满足四原则?(自旋 = 忙等)
- 信号量怎么做到让权等待?(记录型:阻塞队列 + 唤醒)
- P、V 操作各自的精确语义是什么?为什么 P/V 自身必须原子执行?
- 互斥、同步、计数三种信号量初值分别怎么设?P/V 配对位置有何不同?
- 为什么 P/V 用错了还要引入管程?
- 拿到一道 PV 大题,分析步骤是什么?(找关系 → 定信号量 → 同步 P 在互斥 P 前)
- 生产者-消费者中两个 wait 写反为什么会死锁?这和死锁四条件怎么呼应?