Appearance
信号量
考情分析
信号量和 PV 操作是进程同步章节的核心工具,几乎所有同步互斥的大题都基于信号量。这是 92 分同步章节中最核心的知识点。🔥🔥🔥 绝对核心。
锁只有"开/关"两种状态,只能解决互斥。但很多场景需要更细粒度的控制——比如缓冲区有 N 个空位,允许 N 个生产者同时放入,但第 N+1 个必须等。信号量就像停车场入口的计数牌——有空位才放行,没空位就排队等。
信号量的定义
**信号量(Semaphore)**是一个整型变量,只能通过两个原子操作来访问:
- P 操作(wait / proberen / 申请)
- V 操作(signal / verhogen / 释放)
记录型信号量
c
typedef struct {
int value; // 信号量的值
struct process *L; // 等待队列
} semaphore;P 操作
P(S) {
S.value--;
if (S.value < 0) {
将当前进程加入 S.L;
block(); // 阻塞当前进程
}
}V 操作
V(S) {
S.value++;
if (S.value <= 0) {
从 S.L 中唤醒一个进程;
wakeup(P);
}
}易错
S.value 的物理含义(选择题/简答题高频):
S.value > 0:有 S.value 个资源可用S.value = 0:没有资源可用,也没有进程等待S.value < 0:|S.value| 个进程正在等待该资源
常见错误:认为 S.value < 0 时绝对值表示"已分配的资源数"——错,表示的是等待进程数。
用信号量实现互斥
semaphore mutex = 1; // 初值为 1
// 进程 P1 // 进程 P2
P(mutex); P(mutex);
临界区; 临界区;
V(mutex); V(mutex);互斥信号量初值为 1(表示同一时刻最多 1 个进程进入临界区)。
用信号量实现同步
让 P1 的操作 A 在 P2 的操作 B 之前执行:
semaphore S = 0; // 初值为 0
// 进程 P1 // 进程 P2
操作 A; P(S); ← 等待 P1 完成 A
V(S); 操作 B; ← A 完成后才执行 B同步信号量初值为 0——「前 V 后 P」。
用信号量实现前驱关系
如果有更复杂的执行顺序要求(如 S1→S2, S1→S3, S2→S4, S3→S4):
为每条前驱边设置一个同步信号量(初值均为 0):
semaphore a=0, b=0, c=0, d=0;
P1: S1; V(a); V(b);
P2: P(a); S2; V(c);
P3: P(b); S3; V(d);
P4: P(c); P(d); S4;信号量的使用要点
| 要点 | 说明 |
|---|---|
| P/V 必须成对 | 漏写 P → 互斥失效;漏写 V → 死锁 |
| 互斥信号量初值为 1 | 表示一次只允许一个进程 |
| 同步信号量初值为 0 | 表示事件还没发生 |
| P/V 顺序不能错 | 有多个 P 操作时顺序至关重要(下篇详述) |
考研高频考点
- 🔥🔥🔥 用信号量实现互斥(mutex=1, P-临界区-V)
- 🔥🔥🔥 用信号量实现同步(S=0, 前V后P)
- 🔥🔥🔥 S.value 的物理含义(正=剩余资源数,负=等待进程数)
- 🔥🔥 P/V 操作的原子性(通过关中断实现)
- 🔥 用信号量实现前驱关系
信号量很强大但编程容易出错。下一篇看管程和条件变量如何提供更安全的同步抽象。