408 知识网络
返回模块节点

OS-3 同步与互斥(信号量与 PV 设计)

操作系统

来源:30-OS3-同步与互斥.md · 完整笔记(7 节,未删减)

操作系统模块。对应全局地图高频因果链第 2 条(互斥同步链)。 依赖模块:OS-0(中断与内核态)、OS-1(进程/线程与状态转换);向后引出 OS-4 死锁。


核心问题

为什么需要这一套机制?

多道程序让多个进程并发执行、共享资源,但指令是交错执行的。一个看似原子的操作 x += 1 实际是“取 x → 加 1 → 写回”三条指令,两个线程交错执行会丢失更新(2016 年 30 题即考此:共享变量 xx+=1x+=2 必须互斥,而各自的局部变量 ab 不需要)。

如果没有任何机制:

  • 共享数据的最终结果依赖于不可控的交错顺序(竞争条件),程序失去确定性;
  • 打印机、缓冲区等独占资源会被同时使用,输出混乱、数据覆盖。

所以需要回答两个层次的问题:

  1. 互斥:同一时刻只允许一个进程进入访问临界资源的代码段(解决“不能同时”);
  2. 同步:多个进程之间存在执行先后顺序,必须能“等对方做完某件事我再做”(解决“有先有后”)。

互斥是特殊的同步(同步的等待条件是“临界区空闲”),二者共用同一套机制,但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. 找关系:列出进程间所有约束,逐一归类——是“不能同时”(互斥)、“先做后做”(前驱同步),还是“容量有限”(计数资源)?
  2. 定信号量:每类约束一个(组)信号量;互斥初值 1,同步初值 0,计数初值 = 容量(注意初始已有资源时要相应调整,如 2015 年 45 题信箱 fullA 初值 = x)。
  3. 写代码守两条铁律:互斥 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 × 计组交界处命题。

闭卷回忆链

  1. 为什么并发执行会出错?(指令交错 + 共享资源 → 竞争条件)
  2. 什么是临界资源和临界区?共享变量和局部变量哪个要保护?
  3. 实现互斥要满足哪四条原则?“让权等待”卡掉了哪些方案?
  4. 最早的纯软件方案怎么演进的?单标志 → 双标志 → Peterson 各自修好了什么、还留了什么病?
  5. 软件方案为什么都忙等?硬件怎么帮忙?(TSL/Swap 原子指令)
  6. TSL 既然原子,为什么还不满足四原则?(自旋 = 忙等)
  7. 信号量怎么做到让权等待?(记录型:阻塞队列 + 唤醒)
  8. P、V 操作各自的精确语义是什么?为什么 P/V 自身必须原子执行?
  9. 互斥、同步、计数三种信号量初值分别怎么设?P/V 配对位置有何不同?
  10. 为什么 P/V 用错了还要引入管程?
  11. 拿到一道 PV 大题,分析步骤是什么?(找关系 → 定信号量 → 同步 P 在互斥 P 前)
  12. 生产者-消费者中两个 wait 写反为什么会死锁?这和死锁四条件怎么呼应?