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

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

信号量

信号量(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 时(即持有任务已经释放所有的持有操作) , 互斥量则变为开锁状态, 等待在该互斥量上的任务将被唤醒。 如果任务的优先级被互斥量的优先级翻转机制临时提升, 那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级。
 


 

 

 


 


 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【hot100-java】【搜索二维矩阵 II】
  • 装备综合保障研究进展整理
  • 15_分布式数据结构
  • Lua 与 C#交互
  • 通过域名无法访问不到网站,IP可正常访问(DNS污染)
  • 01 Docker概念和部署
  • 计算机网络基础概念 交换机、路由器、网关、TBOX
  • 大数据之Flink(二)
  • 基于SpringBoot+Vue+MySQL的滑雪场管理系统
  • vscode配置django环境并创建django项目
  • 基于单片机的电子药箱控制系统设计
  • 百度视频排名代发(百度视频秒收录代发)
  • 基于Spring Boot开发一个自习室预定系统
  • 故障恢复(残次版)
  • 如何分析建筑资质加盟呢?
  • 【剑指offer】让抽象问题具体化
  • Effective Java 笔记(一)
  • es的写入过程
  • Facebook AccountKit 接入的坑点
  • JavaScript 基本功--面试宝典
  • Linux链接文件
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Sublime text 3 3103 注册码
  • Vue官网教程学习过程中值得记录的一些事情
  • yii2中session跨域名的问题
  • 编写符合Python风格的对象
  • 编写高质量JavaScript代码之并发
  • 汉诺塔算法
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 机器学习学习笔记一
  • 那些被忽略的 JavaScript 数组方法细节
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 入门到放弃node系列之Hello Word篇
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (k8s)kubernetes集群基于Containerd部署
  • (k8s中)docker netty OOM问题记录
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (四)js前端开发中设计模式之工厂方法模式
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • .net程序集学习心得
  • .net后端程序发布到nignx上,通过nginx访问
  • @ConditionalOnProperty注解使用说明
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • @RequestMapping-占位符映射
  • @selector(..)警告提示
  • @synthesize和@dynamic分别有什么作用?
  • @TableLogic注解说明,以及对增删改查的影响
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [1]从概念到实践:电商智能助手在AI Agent技术驱动下的落地实战案例深度剖析(AI Agent技术打造个性化、智能化的用户助手)