Skip to content

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–35–6
主要风险P操作顺序错误→死锁写者饥饿循环等待→死锁
考试频率最高中等

三、记忆与理解辅助

  1. 生产者-消费者P操作顺序口诀:"先同步后互斥"——先P(empty/full)再P(mutex),否则死锁
  2. 读者-写者核心:"第一个读者锁写者,最后一个读者解锁写者"
  3. 哲学家进餐核心:"不能所有人同时拿同一侧筷子"——打破循环等待
  4. 信号量设置口诀:"互斥信号量初值=1,同步信号量看资源数"

四、例题与精解

例题1(基础巩固)

题目:在生产者-消费者问题中,如果将生产者的P操作顺序改为 P(mutex); P(empty);,会导致什么问题?

命题意图:考查P操作顺序对死锁的影响。

精解

  1. 审题分析:原顺序是P(empty)→P(mutex),改为P(mutex)→P(empty)
  2. 解题思路:分析当缓冲区满时的执行情况
  3. 完整步骤
    • 假设缓冲区已满(empty=0)
    • 生产者执行P(mutex):成功,获得互斥锁
    • 生产者执行P(empty):empty=0,阻塞
    • 消费者想消费,执行P(full):成功
    • 消费者执行P(mutex):被生产者持有,阻塞
    • 结果:生产者等消费者释放empty,消费者等生产者释放mutex → 死锁
  4. 方法反思:P操作必须"先同步后互斥"——先申请资源信号量(empty/full),再申请互斥信号量(mutex)

例题2(中等提升)

题目:在读者-写者问题中,当前有3个读者正在读,此时一个写者和一个读者先后请求访问。以下说法正确的是( )

A. 写者立即开始写 B. 新来的读者必须等待写者完成 C. 新来的读者可以立即开始读,写者继续等待 D. 所有读者必须先退出,写者才能写

命题意图:考查读者-写者问题中的读者优先策略。

精解

  1. 审题分析:当前3个读者在读,写者和新读者请求访问
  2. 解题思路:读者优先策略下,只要还有读者在读,新读者可以加入
  3. 完整步骤
    • 当前readcount=3,rw_mutex已被第一个读者锁住
    • 写者请求P(rw_mutex):被阻塞(因为有读者在读)
    • 新读者请求P(mutex)→readcount++(变为4)→P(mutex)释放
    • 新读者可以立即开始读(因为readcount>0,不需要再P(rw_mutex))
    • 写者必须等待所有读者(包括新来的)都完成后才能写
    • 这就是"读者优先"的特点,可能导致写者饥饿
  4. 方法反思:读者优先策略简单但不公平,可能导致写者饥饿。写者优先策略可以解决这个问题

答案:C


五、考情分析

  • 考查频次:近5年综合题出现≥4次
  • 常见题型:综合题(10–15分),偶尔选择题(2分)
  • 分值占比:综合题中10–15分
  • 命题趋势:生产者-消费者是最高频的综合题,可能变式为多缓冲区或多类生产者/消费者。读者-写者和哲学家进餐也常出现

六、易错点提醒

  1. 错误表现:生产者-消费者中P操作顺序颠倒 错误原因:不理解"先同步后互斥"的原则 正确理解:必须先P(empty/full)再P(mutex),否则缓冲区满/空时会死锁

  2. 错误表现:读者-写者问题中忘记保护readcount变量 错误原因:readcount是共享变量,多个读者同时修改可能导致竞态条件 正确理解:readcount的修改必须用mutex信号量保护

  3. 错误表现:哲学家进餐问题中所有哲学家同时拿左边筷子 错误原因:没有打破循环等待条件 正确理解:必须通过限制人数、奇偶策略或AND信号量来打破循环等待

  4. 错误表现:V操作遗漏 错误原因:忘记在适当位置执行V操作 正确理解:有P必有V,遗漏V会导致信号量值永远为负,进程永远阻塞


七、来源标注

  • 依据2026考研统考408大纲
  • 依据《计算机操作系统》(汤小丹/汤子瀛版)第2章
  • 依据《操作系统概念》(Silberschatz版)第6章
  • 依据王道考研408操作系统辅导讲义

考研全科复习资料 - 基于2026考研统考大纲