PV操作详解:进程同步核心机制
PV操作是一种用于实现进程间同步与互斥的核心机制由荷兰计算机科学家E.W.Dijkstra提出。它包含两个不可中断的原子操作通常与信号量Semaphore结合使用。1. PV操作与信号量信号量是一个用于控制多个进程对共享资源访问的整型变量。其值通常代表可用资源的数量。根据信号量的用途其初始化值有所不同。PV操作的定义如下P操作(Proberen, 意为“尝试”或“等待”): 当一个进程需要申请一个资源时执行P操作。它会检查信号量的值如果值大于0则将其减1表示成功获取一个资源如果值等于0则进程被阻塞进入等待队列直到有资源被释放。V操作(Verhogen, 意为“增加”或“释放”): 当一个进程释放一个资源时执行V操作。它会将信号量的值加1。如果此时有进程正在等待该资源即等待队列非空则会唤醒其中一个等待进程。信号量可分为互斥信号量 (Mutex): 用于实现互斥访问其初始值通常为1保证同一时刻只有一个进程能进入临界区。资源信号量: 用于实现同步其初始值代表某类资源的初始可用数量例如生产者-消费者问题中的缓冲区空位数量empty或已存放产品数量full。2. PV操作的应用模型PV操作是解决一系列经典并发问题的基石。2.1 生产者-消费者问题这是最经典的同步问题。生产者进程生产数据放入缓冲区消费者进程从缓冲区取出数据消费。需要解决三个同步互斥点1缓冲区满时生产者等待2缓冲区空时消费者等待3缓冲区的访问必须是互斥的。假设缓冲区大小为N使用三个信号量mutex: 互斥信号量初始为1用于保证对缓冲区的互斥访问。empty: 资源信号量初始为N代表空缓冲区数量。full: 资源信号量初始为0代表已填充的缓冲区数量。其伪代码实现如下// 生产者进程 void producer() { while (true) { 生产一个产品 item; P(empty); // 申请一个空缓冲区若无则等待 P(mutex); // 申请进入临界区访问缓冲区 将 item 放入缓冲区; V(mutex); // 离开临界区释放缓冲区访问权 V(full); // 释放一个“已填充”资源通知消费者 } } // 消费者进程 void consumer() { while (true) { P(full); // 申请一个已填充的产品若无则等待 P(mutex); // 申请进入临界区访问缓冲区 从缓冲区取出一个产品 item; V(mutex); // 离开临界区释放缓冲区访问权 V(empty); // 释放一个“空位”资源通知生产者 消费 item; } }注意两个P操作的顺序至关重要。生产者必须先P(empty)再P(mutex)消费者必须先P(full)再P(mutex)。如果顺序颠倒可能导致死锁。例如若生产者先P(mutex)获得了缓冲区锁但发现empty0缓冲区满而阻塞此时消费者因无法获得mutex而无法消费释放空位双方将永远等待下去。2.2 读者-写者问题该问题中多个读者进程可以同时读取共享数据但写者进程必须独占访问。它更侧重于实现复杂的同步策略例如“写者优先”或“读者优先”。一个简单的“读者优先”模型可能导致写者饥饿可以使用一个互斥信号量rw_mutex控制写互斥初始为1和一个计数器read_count记录当前读者数量及其保护锁mutex初始为1来实现。// 写者进程 void writer() { while (true) { P(rw_mutex); // 申请独占写权限 执行写操作; V(rw_mutex); // 释放写权限 } } // 读者进程 void reader() { while (true) { P(mutex); // 保护 read_count read_count; if (read_count 1) { P(rw_mutex); // 第一个读者需要“锁住”写者 } V(mutex); 执行读操作; P(mutex); read_count--; if (read_count 0) { V(rw_mutex); // 最后一个读者“释放”写者 } V(mutex); } }3. 使用PV操作实现进程互斥对于只需要互斥访问的临界区使用一个初始值为1的互斥信号量即可。semaphore mutex 1; // 互斥信号量 void process_i() { while (true) { // ... 剩余区 P(mutex); // 进入区尝试获取锁 // ... 临界区访问共享资源 V(mutex); // 退出区释放锁 // ... 剩余区 } }4. 信号量的底层实现与注意事项PV操作必须是原子操作即在执行期间不可被中断。在单处理器系统中可以通过屏蔽中断来实现在多处理器系统中则需要借助硬件提供的特殊原子指令如Test-and-Set, Compare-and-Swap。信号量的实现通常包含一个整型值value和一个进程等待队列queue。P操作和V操作的具体逻辑如下表所示操作伪代码逻辑描述P(semaphore S)S.value--;if (S.value 0) {将当前进程状态置为“等待”将当前进程PCB插入S.queue调度其他进程运行}V(semaphore S)S.value;if (S.value 0) {从S.queue中移出一个等待进程P将进程P状态置为“就绪”将P插入就绪队列}核心要点当S.value为负数时其绝对值表示正在等待该信号量的进程数量。在使用PV操作时必须警惕死锁。死锁发生的四个必要条件是互斥、持有并等待、非抢占、循环等待。错误的P操作顺序如前文生产者-消费者问题所述或资源分配不当都可能引发死锁。银行家算法是一种经典的死锁避免算法它通过预判分配后系统是否处于安全状态来决定是否分配资源。参考来源pv原语模拟实现_手把手教你如何玩转操作系统信号量和PV操作PV操作【操作系统学习笔记二】之 进程进程调度进程同步与互斥计算机四级嵌入式之操作系统原理四并发与同步信号量Semaphore(信号量)