PV 操作是操作系统中用于实现进程同步与互斥的重要机制,基于信号量(Semaphore)的原子操作,由荷兰计算机科学家 Edsger Dijkstra 提出。其核心包括两个原子操作:P 操作(Proberen,测试)和 V 操作(Verhogen,增加)。
-
PV 操作的定义
- P 操作(wait 操作):
对信号量 S 执行S = S - 1;
若S < 0,说明资源不可用或有进程等待,则将当前进程阻塞并插入该信号量的等待队列中;
否则,进程继续执行。 - V 操作(signal 操作):
对信号量 S 执行S = S + 1;
若S ≤ 0,说明有进程正在等待该资源,则唤醒一个等待进程并将其移入就绪队列;
否则,进程继续执行。
- P 操作(wait 操作):
-
PV 操作实现进程互斥
使用一个二进制信号量mutex(初始值为 1)来保护临界区资源:P(mutex) 临界区(访问共享资源) V(mutex)由于
mutex初始为 1,仅允许一个进程进入临界区。其他试图进入的进程将在 P 操作时被阻塞,直到前一个进程执行 V 操作释放锁。 -
示例:交通流量统计程序的互斥实现
共享变量:COUNT(记录车流量)
进程 P₁:统计车流量 → 修改 COUNT
进程 P₂:打印 COUNT 并重置为 0为避免数据竞争,两者都必须通过 PV 操作访问 COUNT:
// P₁: P(mutex) COUNT = COUNT + 新增车辆数 V(mutex) // P₂: P(mutex) print(COUNT) COUNT = 0 V(mutex)这样确保任一时刻只有一个进程在操作 COUNT,实现了互斥访问。
-
PV 操作实现进程同步
同步关注的是进程间的执行顺序。例如,要求进程 P₂ 必须在 P₁ 完成某项操作后才能执行。
可定义一个信号量synch,初值为 0:- P₁ 执行完特定操作后执行
V(synch)(表示“消息已产生”) - P₂ 在开始前先执行
P(synch)(等待“消息到达”)
代码结构如下:
// 初始化:synch = 0 // 进程 P₁: 执行操作 ... V(synch) // 通知 P₂ 可以开始了 // 进程 P₂: P(synch) // 等待 P₁ 完成 开始执行 ...此机制保证了 P₂ 不会早于 P₁ 完成前运行,从而实现同步。
- P₁ 执行完特定操作后执行
PV 操作是并发控制的基础工具,既能解决临界资源的竞争问题(互斥),也能协调进程之间的执行次序(同步),广泛应用于操作系统内核、多线程编程及分布式系统设计中。
信号量(Semaphore)是一种用于控制多个进程或线程对共享资源访问的同步机制,由荷兰计算机科学家 Edsger Dijkstra 提出。它是一个整型变量,仅能通过两个原子操作 P 操作(wait)和 V 操作(signal)进行修改:
- P 操作:申请资源,将信号量减 1;若结果小于 0,则进程被阻塞。
- V 操作:释放资源,将信号量加 1;若结果小于等于 0,则唤醒一个等待进程。
根据初始值不同,信号量可分为:
- 二进制信号量(Binary Semaphore):初值为 1 或 0,只能取 0 或 1,用于实现互斥。
- 计数信号量(Counting Semaphore):初值可大于 1,用于管理多个相同类型的资源(如缓冲区槽位数量)。
信号量与互斥锁的区别如下:
| 对比项 | 信号量(Semaphore) | 互斥锁(Mutex) |
|---|---|---|
| 用途 | 可用于实现互斥和同步(如顺序控制、资源计数) | 仅用于实现线程/进程间的互斥访问 |
| 资源数量 | 支持多个资源的管理(计数信号量) | 仅保护单一资源(唯一所有权) |
| 所有权概念 | 无严格所有权,任何线程都可以执行 V 操作唤醒其他线程 | 有严格的所有权:必须由加锁的线程解锁 |
| 使用灵活性 | 更灵活,可用于复杂同步场景(如生产者-消费者) | 使用受限,专用于临界区保护 |
| 实现基础 | 基于计数器 + 等待队列 | 通常基于信号量实现的一种特殊情形(二进制信号量 + 所有权机制) |
⚠️ 注意:虽然二进制信号量在功能上类似互斥锁,但由于缺乏“拥有权”检查,可能导致错误释放(例如非持有者释放锁),从而引发安全问题。而互斥锁强制要求锁的获取和释放必须由同一个线程完成,更安全。
✅ 总结:
- 互斥锁是信号量的一个特例(二进制信号量 + 所有权机制),专用于互斥;
- 信号量更通用,既能实现互斥,也能实现进程间同步和资源管理。


1012

被折叠的 条评论
为什么被折叠?



