信号量(二值信号量和计数信号量)和互斥量

news2024/9/21 18:00:42

信号量

信号量(Semaphore) 是一种实现任务间通信的机制, 可以实现任务之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的任务来访问临界资源。 在多任务系统中, 各任务之间需要同步或互斥实现临界资源的保护, 信号量功能可以为用户提供这方面的支持。

抽象的来讲, 信号量是一个非负整数, 所有获取它的任务都会将该整数减一(获取它当然是为了使用资源) , 当该整数值为零时, 所有试图获取它的任务都将处于阻塞状态。 通常一个信号量的计数值用于对应有效的资源数, 表示剩下的可被占用的互斥资源数。 其值的含义分两种情况:
●0: 表示没有积累下来的释放信号量操作, 且有可能有在此信号量上阻塞的任务。
●正值, 表示有一个或多个释放信号量操作。

 信号:起通知作用
 量:还可以用来表示资源的数量
 当"量"没有限制时,它就是"计数型信号量"(Counting Semaphores)
 当"量"只有 0、 1 两个取值时,它就是"二进制信号量"(Binary Semaphores)
 支持的动作: "give"给出资源,计数值加 1; "take"获得资源,计数值减 1

计数:事件产生时"give"信号量,让计数值加 1;处理事件时要先"take"信号量,就是获得信号量,让计数值减 1。
资源管理:要想访问资源需要先"take"信号量,让计数值减 1;用完资源后"give"信号量,让计数值加 1。

二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。
二值信号量和互斥信号量(以下使用互斥量表示互斥信号量) 非常相似, 但是有一些细微差别: 互斥量有优先级继承机制, 二值信号量则没有这个机制。 这使得二值信号量更偏向应用于同步功能(任务与任务间的同步或任务和中断间同步) , 而互斥量更偏向应用于临界资源的访问。用作同步时, 信号量在创建后应被置为空, 任务 1 获取信号量而进入阻塞,任务 2 在某种条件发生后, 释放信号量, 于是任务 1 获得信号量得以进入就绪态, 如果任务 1 的优先级是最高的, 那么就会立即切换任务, 从而达到了两个任务间的同步。 同样的, 在中断服务函数中释放信号量, 任务 1 也会得到信号量, 从而达到任务与中断间的同步。

可以将二值信号量看作只有一个消息的队列, 因此这个队列只能为空或满(因此称为二值)我们在运用的时候只需要知道队列中是否有消息即可, 而无需关注消息是什么。

计数信号量

二进制信号量可以被认为是长度为 1 的队列, 而计数信号量则可以被认为长度大于 1 的队列, 信号量使用者依然不必关心存储在队列中的消息, 只需关心队列是否有消息即可。

二进制信号量跟计数型的唯一差别,就是计数值的最大值被限定为1。


 

信号量跟队列的对比


 

 常用信号量 API 函数

创建

使用信号量之前,要先创建,得到一个句柄;使用信号量时,要使用句柄来表明使用哪个信号量。


 

删除 

对于动态创建的信号量,不再需要它们时,可以删除它们以回收内存。
vSemaphoreDelete可以用来删除二进制信号量、计数型信号量

信号量释放函数 (give)

二进制信号量、计数型信号量的 give、 take 操作函数是一样的

 

 

 

 

 信号量获取函数 (take)

 

 

 

互斥量 

互斥量又称互斥信号量(本质是信号量) , 是一种特殊的二值信号量, 它和信号量不同的是, 它支持互斥量所有权、 递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。

互斥量简介

互斥信号量其实就是一个拥有优先级继承的二值信号量, 在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。 互斥信号量适合用于那些需要互斥访问的应用中。 在互斥访问中互斥信号量相当于一个钥匙, 当任务想要使用资源的时候就必须先获得这个钥匙, 当使用完资源以后就必须归还这个钥匙, 这样其他的任务就可以拿着这个钥匙去使用资源。

互斥信号量使用和二值信号量相同的 API 操作函数, 所以互斥信号量也可以设置阻塞时间, 不同于二值信号量的是互斥信号量具有优先级继承的特性。 当一个互斥信号量正在被一个低优先级的任务使用, 而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。 不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级, 这个过程就是优先级继承。 优先级继承尽可能的降低了高优先级任务处于阻塞态的时间, 并且将已经出现的“优先级翻转” 的影响降到最低。

互斥量的优先级继承机制

在 FreeRTOS 操作系统中为了降低优先级翻转问题利用了优先级继承算法
优先级继承算法是指, 暂时提高某个占有某种资源的低优先级任务的优先级, 使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等, 而当这个低优先级任务执行完毕释放该资源时, 优先级重新回到初始设定值。 因此, 继承优先级的任务避免了系统资源被任何中间优先级的任务抢占。

互斥量与二值信号量最大的不同是: 互斥量具有优先级继承机制, 而信号量没有。 也就是说, 某个临界资源受到一个互斥量保护, 如果这个资源正在被一个低优先级任务使用, 那么此时的互斥量是闭锁状态, 也代表了没有任务能申请到这个互斥量, 如果此时一个高优先级任务想要对这个资源进行访问, 去申请这个互斥量, 那么高优先级任务会因为申请不到互斥量而进入阻塞态, 那么系统会将现在持有该互斥量的任务的优先级临时提升到与高优先级任务的优先级相同, 这个优先级提升的过程叫做优先级继承。 这个优先级继承机制确保高优先级任务进入阻塞状态的时间尽可能短, 以及将已经出现的“优先级翻转” 危害降低到最小。

互斥量应用场景

互斥量的使用比较单一, 因为它是信号量的一种, 并且它是以锁的形式存在。
在初始化的时候, 互斥量处于开锁的状态, 而被任务持有的时候则立刻转为闭锁的状态。

互斥量更适合于
 ●可能会引起优先级翻转的情况。
递归互斥量更适用于:
●任务可能会多次获取互斥量的情况下。 这样可以避免同一任务多次递归持有而造成死锁的问题。 

多任务环境下往往存在多个任务竞争同一临界资源的应用场景, 互斥量可被用于对临界资源的保护从而实现独占式访问。 另外, 互斥量可以降低信号量存在的优先级翻转问题带来的影响。 

比如有两个任务需要对串口进行发送数据, 其硬件资源只有一个, 那么两个任务肯定不能同时发送啦, 不然导致数据错误, 那么, 就可以用互斥量对串口资源进行保护, 当一个任务正在使用串口的时候, 另一个任务则无法使用串口, 等到任务使用串口完毕之后, 另外一个任务才能获得串口的使用权。

另外需要注意的是互斥量不能在中断服务函数中使用, 因为其特有的优先级继承机制只在任务起作用, 在中断的上下文环境毫无意义。

常用API函数

互斥量创建函数

xSemaphoreCreateMutex()用于创建一个互斥量, 并返回一个互斥量句柄。

该句柄的原型是一个 void 型的指针, 在使用之前必须先由用户定义一个互斥量句柄。 要想使用该函数必须在 FreeRTOSConfig.h 中configSUPPORT_DYNAMIC_ALLOCATION 定义为 1, 即开启动态内存分配, 其实该宏在 FreeRTOS.h 中默认定义为 1, 即所有 FreeRTOS 的对象在创建的时候都默认使用动态内存分配方案, 同时还需在 FreeRTOSConfig.h 中把configUSE_MUTEXES 宏定义打开, 表示使用互斥量。

 

递归互斥量创建函数 

xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量, 不是递归的互斥量由函数 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic()创建(我们只讲解动态创建) , 且只能被同一个任务获取一次, 如果同一个任务想再次获取则会失败。 递归信号量则相反, 它可以被同一个任务获取很多次, 获取多少次就需要释放多少次。 递归信号量与互斥量一样, 都实现了优先级继承机制, 可以降低优先级反转的危害。

要想使用该函数必须在 FreeRTOSConfig.h 中把宏configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_MUTEXES 均定义为 1。configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 即表示开启动态内存分配, 其实该宏在 FreeRTOS.h 中默认定义为 1, 即所有 FreeRTOS 的对象在创建的时候都默认使用动态内存分配方案


 

互斥量删除函数 

互斥量的本质是信号量, 直接调用 vSemaphoreDelete()函数进行删除即可。

互斥量获取函数 

当互斥量处于开锁的状态, 任务才能获取互斥量成功, 当任务持有了某个互斥量的时候, 其它任务就无法获取这个互斥量, 需要等到持有互斥量的任务进行释放后, 其他任务才能获取成功, 任务通过互斥量获取函数来获取互斥量的所有权。 任务对互斥量的所有权是独占的, 任意时刻互斥量只能被一个任务持有, 如果互斥量处于开锁状态, 那么获取该互斥量的任务将成功获得该互斥
量, 并拥有互斥量的使用权; 如果互斥量处于闭锁状态, 获取该互斥量的任务将无法获得互斥量, 任务将被挂起, 在任务被挂起之前, 会进行优先级继承, 如果当前任务优先级比持有互斥量的任务优先级高, 那么将会临时提升持有互斥量任务的优先级。 互斥量的获取函数是一个宏定义, 实际调用的函数就是xQueueGenericReceive()。

递归互斥量获取函数

xSemaphoreTakeRecursive()是一个用于获取递归互斥量的宏, 与互斥量的获取函数一样, xSemaphoreTakeRecursive()也是一个宏定义, 它最终使用现有的队列机制, 实际执行的函数是 xQueueTakeMutexRecursive()。 互斥量之前必须由 xSemaphoreCreateRecursiveMutex()这个函数创建。 要注意的是该函数不能用于获取由函数 xSemaphoreCreateMutex()创建的互斥量。 要想使用该函数必须在头文件 FreeRTOSConfig.h 中把宏 configUSE_RECURSIVE_MUTEXES 定义为1。


 

互斥量释放函数 xSemaphoreGive()

任务想要访问某个资源的时候, 需要先获取互斥量, 然后进行资源访问, 在任务使用完该资源的时候, 必须要及时归还互斥量, 这样别的任务才能对资源进行访问

FreeRTOS 给我们提供了互斥量释放函数 xSemaphoreGive(), 任务可以调用 xSemaphoreGive()函数进行释放互斥量, 表示我已经用完了, 别人可以申请使用, 互斥量的释放函数与信号量的释
放函数一致, 都是调用 xSemaphoreGive()函数, 但是要注意的是, 互斥量的释放只能在任务中, 不允许在中断中释放互斥量。

使用该函数接口时, 只有已持有互斥量所有权的任务才能释放它, 当任务调用 xSemaphoreGive()函数时会将互斥量变为开锁状态, 等待获取该互斥量的任务将被唤醒。 如果任务的优先级被互斥量的优先级翻转机制临时提升, 那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级


 

递归互斥量释放函数 xSemaphoreGiveRecursive() 

xSemaphoreGiveRecursive()是一个用于释放递归互斥量的宏。 要想使用该函数必须在头文件 FreeRTOSConfig.h 把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。


 

xSemaphoreGiveRecursive()函数用于释放一个递归互斥量。 已经获取递归互斥量的任务可以重复获取该递归互斥量。 使用 xSemaphoreTakeRecursive()函数成功获取几次递归互斥量, 就要使用 xSemaphoreGiveRecursive()函数返还几次, 在此之前递归互斥量都处于无效状态, 别的任务就无法获取该递归互斥量。

使用该函数接口时, 只有已持有互斥量所有权的任务才能释放它, 每释放一次该递归互斥量, 它的计数值就减 1。 当该互斥量的计数值为 0 时(即持有任务已经释放所有的持有操作) , 互斥量则变为开锁状态, 等待在该互斥量上的任务将被唤醒。 如果任务的优先级被互斥量的优先级翻转机制临时提升, 那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级。
 


 

 

 


 


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2127282.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图神经网络介绍3

1. 图同构网络:Weisfeiler-Lehman 测试与图神经网络的表达力 本节介绍一个关于图神经网络表达力的经典工作,以及随之产生的另一个重要的模型——图同构网络。图同构问题指的是验证两个图在拓扑结构上是否相同。Weisfeiler-Lehman 测试是一种有效的检验两…

第二期: 第二节 , 逻辑编程 , gpio

1 首先就是 看原理图: 这里有两个 LED 核心板的原理图。 可以看到 是这个脚。 2 然后就是 查看数据手册。 从 数据手册可以看出 ,一共有这么多的 gpio 组, 但是这些 组 是有复用的&#xf…

多文件编程实现链表创建,插入,输出(上)

linklist.c #include "linklist.h" //创建空的链表,为头结点在堆区分配空间 linklist_t *creat_empty_linklist() {linklist_t *head NULL;head (linklist_t *) malloc(sizeof(linknode_t));if(NULL head){printf("malloc is fail!\n");ret…

网格参数的应用和数学基础

引言 对于任意两个拓扑结构相似的表面,可以计算它们之间的一一对应映射。如果其中一个表面由三角形网格表示,那么计算这种映射的问题被称为网格参数化。映射到的表面通常被称为参数域。表面网格与各种域之间的参数化在计算机图形学和几何处理中有广泛的应…

移动WEB开发(第二天)_flex布局

移动WEB开发(第二天)_flex布局 移动web开发——flex布局1.0传统布局和flex布局对比1.1传统布局1.2 flex布局1.3 建议 2.0 flex布局原理3.0 父项常见属性3.1 flex-direction设置主轴的方向3.2 justify-content 设置主轴上的子元素排列方式3.3 flex-wrap设…

9月美联储决策前哨战——美国CPI数据来袭

随着本周关键CPI数据的即将发布,市场正翘首以待,这将是美联储在9月17日至18日议息会议前获取的最后一块重要经济拼图。鉴于美联储官员已进入传统的政策静默期,8月份的CPI报告无疑将成为交易员们评估未来货币政策走向的重要标尺。 欧洲央行降…

python列表判断是否为空的三种方式

#列表是否为空判断 a[]一: if a:print(not null) else:print(null)二: b len(a) if b 0:print(null) else:print(not null)三: if not a:print(null) else:print(not null)运行结果:

Day9 | Java框架 | SpringBoot

Day9 | Java框架 | SpringBoot SpringBoot简介入门程序概述起步依赖 基础配置配置文件格式:3种yaml语法规则yaml数据读取三种格式 多环境启动配置文件参数命令行参数多环境开发控制:Maven & SpringBoot 多环境兼容 配置文件分类:4种 整合…

Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装

音频解析 音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。 FFmpeg音频解码的基本步骤如下: 初始化FFmpeg解码器(4.0版本后可省略): 调用av_register_all()初始化编解码器。 调用avcodec_register_all()注册所有编解码器。 打开输入的音频流:…

k8s以及prometheus

#生成控制器文件并建立控制器 [rootk8s-master ~]# kubectl create deployment bwmis --image timinglee/myapp:v1 --replicas 2 --dry-runclient -o yaml > bwmis.yaml [rootk8s-master ~]# kubectl expose deployment bwmis --port 80 --target-port 80 --dry-runclient…

【深海王国】初中生也能画的电路板?目录合集

Hi٩(๑ ^ o ^ ๑)۶, 各位深海王国的同志们,早上下午晚上凌晨好呀~辛勤工作的你今天也辛苦啦 (o゜▽゜)o☆ 今天大都督为大家带来系列文章《初中生也能画的电路板》,帮你一周内快速入门PCB设计,手把手教你从元器件库添加、电路原理图绘制、…

如何解决在idea中的hadoop日志错误

在idea中操作hadoop的时候,每次运行代码都会发现有个日志错误,虽然不影响程序运行,但是无法打印日志。这是缺少依赖,和windows上缺少log4j的文件 解决方案: 1、导入slf4j依赖 2、导入hadoop中的log4j文件 1、从hado…

校园安全无小事,EasyCVR视频综合管理平台助力智慧校园视频监控系统全面升级

随着信息技术的飞速发展,智慧校园作为教育信息化的重要载体,正逐步成为提升校园安全管理、优化教育资源配置、增强师生互动体验的关键手段。其中,高效、智能的视频监控系统作为智慧校园不可或缺的一部分,扮演着至关重要的角色。TS…

Benvista PhotoZoom Pro / Classic 9.0.2 Win/mac + Plug-in中文破解版

对数码照片放大的质量不满意? 使用 BenVista PhotoZoom Classic9 调整图像大小,并通过我们屡获殊荣的独特 S-Spline 技术获得出色的效果! 更高质量:PhotoZoom Classic9 专门用于在保持质量的同时放大照片。 该软件配备了 BenVista…

C++核心编程和桌面应用开发 第一天

目录 1.C的编程方式 2.双冒号::运算符 3.命名空间 3.1作用 3.2命名空间内的东西 3.3注意事项 4.using的用法 4.1using的声明 4.2using编译指令 5.C相较于C的增强 5.1全局变量检测增强 5.2函数检测增强 5.3类型转换检测增强 5.4结构体增强 5.5三目运算符增强 5.…

如何高效阅读论文呢???

论文题目《多模态数据融合研究综述》,介绍了多模态数据融合技术以及对齐方法,最后是展望以及未来的发展。 泛读这篇论文不在状态,求助前辈们该如何高效的阅读论文呢?(我是刚入学的研一学生,对于读论文这块…

【H2O2|全栈】关于CSS(2)CSS基础(二)

目录 CSS基础知识 前言 准备工作 选择器的组合 盒模型 示例网页代码 后代选择器 亲代选择器 相邻兄弟选择器 后续兄弟选择器 多个元素选择器 通配符选择器 优先级 其他应用 伪类 锚链接的属性 列表的属性 list-style-type list-style-position list-style…

1.任务的创建与状态

1.什么叫现场? 就是程序暂停瞬间所有寄存器的值 2.如何"保存现场"? 就是把"所有寄存器"保存进哪里? 保存进"内存",这块内存被称为栈 3.栈来自哪里? a.在FreeRTOS里定义了一个大数组 b.FreeRTOS里的malloc函数,从这个大数组里分配内存 c.创…

MySQL一:在Ubuntu下安装MySQL数据库

目录 前言 1.查看操作系统版本 2.添加MySQLAPT源 2.1下载发布包 ​编辑 2.2安装发布包 3.安装MySQL 4.查看MySQL状态 5.开启自启动 ​编辑 6.登录MySQL 前言 操作系统版本为Ubuntu 22.04.6LTS 1.查看操作系统版本 lsb_release -a 2.添加MySQLAPT源 2.1下载发布包 M…

【深度学习】神经网络-怎么分清DNN、CNN、RNN?

怎么分清DNN、CNN、RNN? 最“大”的概念是人工神经网络(Artificial Neural Network, ANN),它是较为广泛的术语,通常指的是一类模拟生物神经网络的数学模型,其中包括神经元、权重和连接。在这个术语下&#…