当前位置: 首页 > news >正文

认识信号量机制、以及用信号量来实现进程互斥于进程同步

一、信号量机制

信号量机制是操作系统提供的一种高效同步工具,用户进程可以通过使用操作系统提供一对原语来对信号量进行操作,从而很方便的视线了进程互斥、进程同步。

1. 信号量

信号量本质是一个表示系统资源数量的变量,可以是整型或结构体(记录型)。它的核心作用是:

  • 当表示 “资源” 时:值为当前可用资源的数量;
  • 当用于 “同步” 时:值可表示进程间的执行顺序关系(如 “等待 - 唤醒” 信号)。

2. 一对原语---P、V 原语

对信号量的操作只有三种:初始化P 操作(申请资源)V 操作(释放资源),且 P、V 操作是原子操作(执行过程不可中断,避免并发漏洞)。

  • P 操作:对应wait(S)原语,意为 “申请一个单位资源”;
  • V 操作:对应signal(S)原语,意为 “释放一个单位资源”;
  • 其中S是信号量变量,是操作的核心参数。

二、整型信号量

用一个整数型的变量作为信号量,用来表示系统中 某种资源的量

1. 定义与操作逻辑

以 “计算机系统有 1 台打印机” 为例,整型信号量的实现如下:

// 初始化:S=1,表示1台可用打印机
int S = 1;// P操作(申请资源):进入区
void wait(int S) {while (S <= 0);  // 资源不够时,循环等待(忙等)S = S - 1;       // 资源足够,占用1个
}// V操作(释放资源):退出区
void signal(int S) {S = S + 1;       // 释放1个资源,归还系统
}

2. 如何实现进程互斥?

以上述打印机为例,两个进程使用打印机的逻辑:

// 进程P0
wait(S);          // 申请打印机(P操作)
使用打印机...     // 临界区:唯一进程可进入
signal(S);        // 释放打印机(V操作)// 进程P1
wait(S);          // 若P0未释放,P1会卡在while循环
使用打印机...
signal(S);

核心逻辑:通过 P 操作 “检查并占用资源”,V 操作 “释放资源”,确保同一时间只有一个进程进入临界区,实现互斥。

3. 致命缺陷:不满足 “让权等待”

整型信号量的最大问题是忙等(Busy Waiting):当资源不足时,进程会在while (S <= 0)循环中持续检查,占用 CPU 却不做有用功,违背 “让权等待” 原则(进程无法获取资源时,应主动让出 CPU,避免资源浪费)。

⚠️ 易错点:整型信号量的 “忙等” 与自旋锁类似,但自旋锁是 “短时间等待” 优化,而整型信号量的忙等无时间限制,效率极低,实际系统中很少使用。


三、记录型信号量:解决整型信号量的 “忙等”问题

为了满足 “让权等待” 原则,操作系统引入记录型信号量—— 在整型基础上增加 “等待队列”,资源不足时让进程阻塞而非忙等,是实际系统中常用的信号量类型。

1. 定义:结构体存储资源与等待队列

记录型信号量是一个结构体,包含两个核心字段:

typedef struct {int value;          // 剩余资源数量(核心)struct process *L;  // 等待该资源的进程队列(解决忙等的关键)
} semaphore;

2. P、V 操作逻辑

以 “系统有 2 台打印机” 为例(初始化S.value=2,S.L=NULL),操作逻辑如下:

(1)P 操作(申请资源):先减后判断
void wait(semaphore S) {S.value--;  // 先申请:资源数减1if (S.value < 0) {  // 资源不足(负数绝对值=等待进程数)block(S.L);     // 进程自我阻塞:从运行态→阻塞态,插入等待队列S.L}// 资源足够:直接进入临界区
}
  • S.value--是 “先占坑”,避免多个进程同时申请导致资源计数错误;
  • 当S.value < 0:说明当前资源已分配完,当前进程需阻塞,主动让出 CPU(满足 “让权等待”);
  • 例如:2 台打印机被 P0、P1 占用(S.value=0),P2 申请时S.value=-1,P2 阻塞,等待队列S.L=[P2]。

(2)V 操作(释放资源):先加后判断
void signal(semaphore S) {S.value++;  // 先释放:资源数加1if (S.value <= 0) {  // 仍有进程在等待(负数→有等待,0→刚唤醒最后一个)wakeup(S.L);     // 唤醒等待队列首进程:从阻塞态→就绪态}// 无进程等待:直接结束
}
  • S.value++是 “先归还”,更新资源计数;
  • 当S.value <= 0:说明释放资源后仍有进程在等待(如S.value=-1→释放后S.value=0,需唤醒 1 个进程);
  • 例如:P0 释放打印机(S.value=-1+1=0),此时S.value<=0,唤醒等待队列的 P2,P2 进入就绪态。

3. 记录型信号量的优势

  • 解决 “忙等”:资源不足时进程阻塞,让出 CPU,提高资源利用率;
  • 支持多资源管理:可通过多个信号量分别管理不同类型资源(如打印机、内存、磁盘);
  • 灵活支持同步与互斥:既能实现 “同一资源互斥访问”,也能实现 “多进程按顺序执行”。

四、信号量的两大核心应用

信号量机制的核心价值,在于能灵活实现进程互斥进程同步。

1. 应用 1:实现进程互斥

设置一个 “互斥信号量”mutex,初始化mutex=1(表示 “临界区可用”),在所有进程的 “临界区前后” 分别执行 P、V 操作。

示例:两个进程共享一个缓冲区

// 初始化互斥信号量:mutex=1
semaphore mutex = {1, NULL};// 进程A
P(mutex);          // 申请进入临界区(互斥)
写入数据到缓冲区... // 临界区
V(mutex);          // 释放临界区// 进程B
P(mutex);          // 若A未释放,B会阻塞
从缓冲区读取数据... // 临界区
V(mutex);

关键点

  • 互斥信号量mutex的初值必须为 1;
  • 每个进程的临界区必须被 “P (mutex)” 和 “V (mutex)” 包围,且不能遗漏(否则会导致互斥失效)。

2. 应用 2:实现进程同步

设置 “同步信号量”S,初始化S=0(表示 “初始时需等待”),在 “需要等待的进程” 后执行 P 操作,在 “触发等待进程的进程” 后执行 V 操作,强制进程按顺序执行。

示例:进程 A 必须在进程 B 之后执行(B 先输出 “Hello”,A 再输出 “World”)

// 初始化同步信号量:S=0(A需等待B的信号)
semaphore S = {0, NULL};// 进程B
printf("Hello ");  // B的任务
V(S);              // 释放信号,通知A“可以执行”// 进程A
P(S);              // 等待B的信号(S初始为0,A会阻塞,直到B执行V(S))
printf("World");   // A的任务,确保在B之后执行

关键点

  • 同步信号量S的初值根据执行顺序设定(通常为 0,表示 “先执行的进程需触发后执行的进程”);
  • P 操作放在 “需要等待的进程” 的关键节点前,V 操作放在 “触发等待的进程” 的关键节点后。

五、核心总结

知识点

关键内容

信号量核心操作

P(S):申请资源(S--,<0 则阻塞);V(S):释放资源(S++,<=0 则唤醒)

整型信号量缺陷

不满足 “让权等待”,存在忙等

记录型信号量优势

有等待队列,资源不足时进程阻塞,满足让权等待

原子操作的意义

避免 P/V 操作被中断,防止信号量值混乱(如多个进程同时执行 S-- 导致计数错误)

六、与互斥锁的对比:何时选信号量?

上一篇的互斥锁和本篇的信号量都能实现进程互斥,但适用场景不同:

维度

互斥锁

信号量

核心功能

仅实现临界区互斥

可实现互斥 + 同步 + 多资源管理

资源类型

单一种类资源(如一把锁)

多种资源(多个信号量)

等待机制

阻塞等待(传统互斥锁)

阻塞等待(记录型)

适用场景

简单共享资源竞争(如单缓冲区)

复杂进程协作(如生产者 - 消费者、读者 - 写者)

http://www.dtcms.com/a/336338.html

相关文章:

  • 计算机网络 TCP time_wait 状态 详解
  • VirtualBox-4.3.10-93012-Win.exe 安装教程附详细步骤(附安装包下载)
  • 为何她总在关键时“失联”?—— 解密 TCP 连接异常中断
  • TensorRT-LLM.V1.1.0rc1:Dockerfile.multi文件解读
  • LeetCode 刷题【44. 通配符匹配】
  • 多墨智能-AI一键生成工作文档/流程图/思维导图
  • 《WINDOWS 环境下32位汇编语言程序设计》第3章 使用MASM
  • Redis面试精讲 Day 23:Redis与数据库数据一致性保障
  • 什么是回表?
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘scikit-image’问题
  • Hooks useState的使用
  • leetcode热题100——day33
  • 视频内容提取与AI总结:提升学习效率的实用方法
  • 【深度学习新浪潮】近三年图像处理领域无监督学习的研究进展一览
  • 科目二的四个电路
  • 《Vuejs设计与实现》第 14 章(内建组件和模块)
  • 概率dp|math
  • Android中切换语言的方法
  • 基于Netty的高并发WebSocket连接管理与性能优化实践指南
  • ReactNode 类型
  • 第12章《学以致用》—PowerShell 自学闭环与实战笔记
  • “让机器人更智慧 让具身体更智能”北京世界机器人大会行业洞察
  • Python 调试工具的高级用法
  • OJ目录饿
  • Python 基础语法(二)
  • Kubernetes存储迁移实战:从NFS到阿里云NAS完整指南
  • 【踩坑笔记】50系显卡适配的 PyTorch 安装
  • XF 306-2025 阻燃耐火电线电缆检测
  • JavaScript 性能优化实战:从评估到落地的全链路指南
  • Docker Compose 安装 Neo4j 的详细步骤