Appearance
408
操作系统
经典同步问题(生产者-消费者/读者-写者/哲学家进餐)
一、定位信息
- 所属圈层:核心层
- 前置知识:掌握信号量机制(OS-02-11),理解互斥与同步的概念(OS-02-09)
- 知识网络位置:经典同步问题是信号量应用的核心内容,是408综合题的高频出题来源
- 考点热度等级:H级(高频重点)——近5年综合题出现≥4次,是OS部分分值最高的考点之一
二、知识点讲解
2.1 生产者-消费者问题
问题描述:一组生产者进程向缓冲区生产数据,一组消费者进程从缓冲区消费数据。缓冲区大小为n。
约束条件:
- 缓冲区满时生产者必须等待
- 缓冲区空时消费者必须等待
- 任何时候只能有一个进程访问缓冲区(互斥)
信号量设置:
mutex = 1:互斥信号量,保护缓冲区empty = n:同步信号量,表示空缓冲区数量full = 0:同步信号量,表示满缓冲区数量
c
// 生产者
void producer() {
while (true) {
produce an item; // 生产数据
P(empty); // 申请空缓冲区
P(mutex); // 申请进入临界区
put item into buffer; // 放入缓冲区
V(mutex); // 释放临界区
V(full); // 通知有满缓冲区
}
}
// 消费者
void consumer() {
while (true) {
P(full); // 申请满缓冲区
P(mutex); // 申请进入临界区
remove item from buffer; // 取出数据
V(mutex); // 释放临界区
V(empty); // 通知有空缓冲区
consume the item; // 消费数据
}
}关键:P操作的顺序不能颠倒!必须先P(empty/full)再P(mutex),否则可能死锁。
2.2 读者-写者问题
问题描述:多个读者和写者访问同一个共享文件。
约束条件:
- 多个读者可以同时读(共享)
- 写者必须独占(互斥)
- 写者和读者不能同时访问
信号量设置:
rw_mutex = 1:读写互斥信号量mutex = 1:保护readcount变量readcount = 0:当前正在读的读者数
c
// 写者
void writer() {
P(rw_mutex); // 申请写权限
// 写操作
V(rw_mutex); // 释放写权限
}
// 读者
void reader() {
P(mutex); // 保护readcount
readcount++;
if (readcount == 1) // 第一个读者
P(rw_mutex); // 锁住写者
V(mutex);
// 读操作
P(mutex);
readcount--;
if (readcount == 0) // 最后一个读者
V(rw_mutex); // 解锁写者
V(mutex);
}特点:读者优先——只要有读者在读,后来的读者可以继续加入,写者可能饥饿。
2.3 哲学家进餐问题
问题描述:5个哲学家围坐圆桌,每两人之间有一根筷子(共5根),哲学家需要同时拿到左右两根筷子才能进餐。
核心问题:如果每个哲学家都先拿左边筷子再拿右边筷子,可能产生死锁(每人拿到一根筷子,都在等另一根)。
解决方案:
| 方案 | 思路 | 优缺点 |
|---|---|---|
| 限制同时就餐人数 | 最多允许4个哲学家同时拿筷子 | 简单,但限制了并发 |
| 奇偶策略 | 奇数号先拿左后拿右,偶数号先拿右后拿左 | 简单有效 |
| AND型信号量 | 同时申请两根筷子 | 一次申请所有资源 |
2.4 三个经典问题对比表
| 维度 | 生产者-消费者 | 读者-写者 | 哲学家进餐 |
|---|---|---|---|
| 核心问题 | 缓冲区同步 + 互斥 | 读写互斥 + 读者共享 | 资源分配 + 死锁预防 |
| 互斥需求 | 有(缓冲区访问) | 有(读写互斥) | 有(筷子使用) |
| 同步需求 | 有(缓冲区满/空) | 无(主要是互斥) | 有(同时拿两根筷子) |
| 信号量数量 | 3(mutex, empty, full) | 2–3 | 5–6 |
| 主要风险 | P操作顺序错误→死锁 | 写者饥饿 | 循环等待→死锁 |
| 考试频率 | 最高 | 高 | 中等 |
三、记忆与理解辅助
- 生产者-消费者P操作顺序口诀:"先同步后互斥"——先P(empty/full)再P(mutex),否则死锁
- 读者-写者核心:"第一个读者锁写者,最后一个读者解锁写者"
- 哲学家进餐核心:"不能所有人同时拿同一侧筷子"——打破循环等待
- 信号量设置口诀:"互斥信号量初值=1,同步信号量看资源数"
四、例题与精解
例题1(基础巩固)
题目:在生产者-消费者问题中,如果将生产者的P操作顺序改为 P(mutex); P(empty);,会导致什么问题?
命题意图:考查P操作顺序对死锁的影响。
精解:
- 审题分析:原顺序是P(empty)→P(mutex),改为P(mutex)→P(empty)
- 解题思路:分析当缓冲区满时的执行情况
- 完整步骤:
- 假设缓冲区已满(empty=0)
- 生产者执行P(mutex):成功,获得互斥锁
- 生产者执行P(empty):empty=0,阻塞
- 消费者想消费,执行P(full):成功
- 消费者执行P(mutex):被生产者持有,阻塞
- 结果:生产者等消费者释放empty,消费者等生产者释放mutex → 死锁
- 方法反思:P操作必须"先同步后互斥"——先申请资源信号量(empty/full),再申请互斥信号量(mutex)
例题2(中等提升)
题目:在读者-写者问题中,当前有3个读者正在读,此时一个写者和一个读者先后请求访问。以下说法正确的是( )
A. 写者立即开始写 B. 新来的读者必须等待写者完成 C. 新来的读者可以立即开始读,写者继续等待 D. 所有读者必须先退出,写者才能写
命题意图:考查读者-写者问题中的读者优先策略。
精解:
- 审题分析:当前3个读者在读,写者和新读者请求访问
- 解题思路:读者优先策略下,只要还有读者在读,新读者可以加入
- 完整步骤:
- 当前readcount=3,rw_mutex已被第一个读者锁住
- 写者请求P(rw_mutex):被阻塞(因为有读者在读)
- 新读者请求P(mutex)→readcount++(变为4)→P(mutex)释放
- 新读者可以立即开始读(因为readcount>0,不需要再P(rw_mutex))
- 写者必须等待所有读者(包括新来的)都完成后才能写
- 这就是"读者优先"的特点,可能导致写者饥饿
- 方法反思:读者优先策略简单但不公平,可能导致写者饥饿。写者优先策略可以解决这个问题
答案:C
五、考情分析
- 考查频次:近5年综合题出现≥4次
- 常见题型:综合题(10–15分),偶尔选择题(2分)
- 分值占比:综合题中10–15分
- 命题趋势:生产者-消费者是最高频的综合题,可能变式为多缓冲区或多类生产者/消费者。读者-写者和哲学家进餐也常出现
六、易错点提醒
错误表现:生产者-消费者中P操作顺序颠倒 错误原因:不理解"先同步后互斥"的原则 正确理解:必须先P(empty/full)再P(mutex),否则缓冲区满/空时会死锁
错误表现:读者-写者问题中忘记保护readcount变量 错误原因:readcount是共享变量,多个读者同时修改可能导致竞态条件 正确理解:readcount的修改必须用mutex信号量保护
错误表现:哲学家进餐问题中所有哲学家同时拿左边筷子 错误原因:没有打破循环等待条件 正确理解:必须通过限制人数、奇偶策略或AND信号量来打破循环等待
错误表现:V操作遗漏 错误原因:忘记在适当位置执行V操作 正确理解:有P必有V,遗漏V会导致信号量值永远为负,进程永远阻塞
七、来源标注
- 依据2026考研统考408大纲
- 依据《计算机操作系统》(汤小丹/汤子瀛版)第2章
- 依据《操作系统概念》(Silberschatz版)第6章
- 依据王道考研408操作系统辅导讲义