Appearance
题目
计算机系统中的进程之间往往需要相互协作以完成一个任务。在某网络系统中缓冲区 B 用于存放一个数据分组,对 B 的操作有 C1、C2、C3:
- C1:将一个数据分组写入 B
- C2:从 B 中读出一个数据分组
- C3:对 B 中的数据分组进行修改
要求 B 为空时才能执行 C1,B 非空时才能执行 C2 和 C3。
请回答下列问题。
(1) 假设进程 P1 和 P2 均需执行 C1,实现 C1 的代码是否为临界区?为什么?(2 分)
(2) 假设 B 初始为空,进程 P1 执行 C1 一次,进程 P2 执行 C2 一次。请定义尽可能少的信号量,并用 wait()/signal() 操作描述 P1、P2 之间的同步或互斥关系,说明所用信号量的作用及初值。(3 分)
(3) 假设 B 初始不为空,进程 P1 和 P2 各执行 C3 一次,请定义尽可能少的信号量,并用 wait()/signal() 操作描述 P1 和 P2 之间的同步或互斥关系,说明所用信号量的作用及初值。(3 分)
解析
(1)C1 是不是临界区?
答:是。
判定一段代码是否为"临界区",标准就一句:它是否访问了多个进程共享的、且不能并发修改的资源。C1 把数据分组写入 B——B 是 P1 和 P2 共享的缓冲区,且只能容纳"一个数据分组"。如果 P1、P2 同时执行 C1:
- P1 写到一半(比如分组前几个字节)就被 P2 抢先写
- 最后 B 里得到的是两份分组的"拼接残骸"——数据不一致
为防止这种结果,必须让 C1 互斥执行,所以它是临界区。
编者注(判定要点): "临界区"= 访问临界资源的那段代码,不是临界资源本身。本题里临界资源是 B,C1 是访问它的代码 → C1 才是临界区。这两个概念在选择题里也常混淆。
(2)P1 写一次 + P2 读一次:最少几个信号量?
关键观察:P1 和 P2 各执行一次,且行为不同(一写一读)——并不存在两个 P 同时写、或两个 P 同时读的场景。所以不需要互斥信号量,只需同步信号量让 P2 等 P1 写完。
只需 1 个信号量:
c
semaphore full = 0; // B 中是否有数据;0 表示空,1 表示有
P1() {
C1; // 写入 B
signal(full); // 通知 P2:B 已有数据
}
P2() {
wait(full); // 等待 B 中有数据
C2; // 读 B
}| 信号量 | 初值 | 作用 |
|---|---|---|
full | 0 | 表示"B 中有数据"事件,实现 P2 → P1 的单向同步(C2 等 C1 完成) |
编者注(易错点):
- 不需要
mutex:题目明确"P1 执行 C1 一次、P2 执行 C2 一次"——一写一读且各 1 次,不会撞车。强行加一个互斥信号量会被扣分("未做到尽可能少")。- 不需要
empty:题目说"B 初始为空",且只写 1 次,不会遇到"B 满了 P1 还要写"的情况。- 把这道题和"生产者-消费者"区别开:那个是循环执行,需要
empty+full+mutex;本题是一次性的,1 个full就够。
(3)P1 和 P2 各执行 C3 一次:最少几个信号量?
关键观察:两进程都执行 C3(修改 B 中的数据分组),属于"两个写者"。如果同时执行会互相覆盖、数据混乱——典型的需要互斥的场景。
但题目说 B 初始非空,C3 只读改不需要等 B 被填满,所以不需要同步信号量。
只需 1 个信号量:
c
semaphore mutex = 1; // 互斥访问 B 的临界区锁
P1() {
wait(mutex);
C3; // 修改 B
signal(mutex);
}
P2() {
wait(mutex);
C3; // 修改 B
signal(mutex);
}| 信号量 | 初值 | 作用 |
|---|---|---|
mutex | 1 | 互斥锁,保证同一时刻只有一个进程能执行 C3 |
编者注(对比 (2) 与 (3)):
对比维度 (2) C1 / C2 (3) C3 / C3 两进程动作 一写一读 都写 顺序约束 C2 必须等 C1 完成 谁先无所谓,但不能同时 需要的信号量 同步: full(init=0)互斥: mutex(init=1)关键技巧 用 signal/wait表达"事件完成"用 signal/wait包夹临界区同步信号量初值=0、互斥信号量初值=1,是这两类典型用法的"特征签名"。考场上看到初值能反推用途、看到用途能立刻给初值。
- 不要写"既同步又互斥":(3) 中两个进程顺序无所谓、且 B 已非空,硬塞一个
full信号量是多余的。考研标答严格按"尽可能少"扣分。