PV操作是操作系统中的进程同步机制,由荷兰计算机科学家Dijkstra提出,用于解决多进程/线程的互斥与同步问题。
- P操作(
wait):申请资源,若资源不足则阻塞进程。 - V操作(
signal):释放资源,唤醒等待进程。
1. PV操作核心原理
(1) 信号量(Semaphore)
- 整型变量
S,表示可用资源数量。 - 两个原子操作(不可中断):
- P(S):
S--,如果S < 0,则进程阻塞。 - V(S):
S++,如果S ≤ 0,则唤醒一个阻塞进程。
(2) 信号量类型
类型 | 初始值 | 用途 |
互斥信号量 |
| 实现临界区互斥访问 |
同步信号量 |
| 控制进程执行顺序(如生产者-消费者) |
2. PV操作经典问题
(1) 互斥访问临界资源
semaphore mutex = 1; // 互斥信号量
Process P_i() {
P(mutex); // 进入临界区
// 访问临界资源
V(mutex); // 离开临界区
}特点:
- 同一时间仅允许一个进程进入临界区。
- P和V必须成对出现,否则会导致死锁或资源泄漏。
(2) 生产者-消费者问题
semaphore empty = N; // 缓冲区空槽数
semaphore full = 0; // 缓冲区数据数
semaphore mutex = 1; // 互斥锁
Producer() {
while (true) {
生产数据;
P(empty); // 申请空槽
P(mutex); // 申请缓冲区访问权
放入缓冲区;
V(mutex); // 释放缓冲区
V(full); // 增加数据计数
}
}
Consumer() {
while (true) {
P(full); // 申请数据
P(mutex); // 申请缓冲区访问权
取出数据;
V(mutex); // 释放缓冲区
V(empty); // 增加空槽计数
消费数据;
}
}关键点:
- 缓冲区访问必须互斥(
mutex)。 - 必须先检查资源再互斥(
P(empty)在P(mutex)之前),否则可能死锁。
(3) 读者-写者问题
semaphore rw_mutex = 1; // 读写互斥
semaphore count_mutex = 1; // 读者计数互斥
int reader_count = 0; // 当前读者数
Writer() {
P(rw_mutex); // 申请写权限
// 写数据...
V(rw_mutex); // 释放写权限
}
Reader() {
P(count_mutex); // 保护reader_count
reader_count++;
if (reader_count == 1) {
P(rw_mutex); // 第一个读者阻塞写者
}
V(count_mutex);
// 读数据...
P(count_mutex);
reader_count--;
if (reader_count == 0) {
V(rw_mutex); // 最后一个读者释放写权限
}
V(count_mutex);
}特点:
- 读者优先:只要有一个读者在读,写者就必须等待。
- 可扩展为写者优先或公平竞争版本。
3. PV操作常见考题
(1) 问:PV操作能否解决死锁?
✅ 可以预防死锁,但需满足:
- 资源有序申请(避免循环等待)。
- 超时机制(避免无限等待)。
(2) 问:如果P操作顺序错误会发生什么?
❌ 死锁!例如:
// 错误写法(可能导致死锁)
Producer() {
P(mutex); // 先申请互斥锁
P(empty); // 再申请空槽(如果empty=0,阻塞且不释放mutex)
// ...
}正确顺序:先申请资源信号量(empty/full),再申请互斥锁(mutex)。
4. 记忆口诀
- P操作:“申请资源,不够就等。”
- V操作:“释放资源,唤醒别人。”
- 互斥锁:“初值1,P进V出。”
- 同步信号量:“初值0,P等V放。”
5. 典型例子
题目:
某系统有3个进程P1、P2、P3,共享一个缓冲区,P1生产数据,P2加工数据,P3消费数据。用PV操作实现同步。
答案:
semaphore empty = 1; // 缓冲区是否空
semaphore processed = 0; // 数据是否加工
semaphore consumed = 0; // 数据是否消费
P1() { // 生产者
while (true) {
生产数据;
P(empty);
放入缓冲区;
V(processed); // 通知P2
}
}
P2() { // 加工者
while (true) {
P(processed);
加工数据;
V(consumed); // 通知P3
}
}
P3() { // 消费者
while (true) {
P(consumed);
取出数据;
V(empty); // 通知P1
消费数据;
}
}
















