信号量(semaphore)

一、信号量简介

       前面介绍的消息队列主要用于传输数据:任务与任务之间、任务与中断之间

       在有些情况下,不需要传输数据,只需要传递状态即可

                • 车开出停车位,你的车可以停进来了

                • 课已经录制完成,你可以进行观看了

1.1 信号量的含义

        信号量是一种实现任务间通信的机制可以实现任务之间的同步临界资源的互斥访问,可以实现对共享资源的有序访问(用于传递状态

共享资源的访问:

※ 案列一:汽车驶入或离开停车位,停车位的个数(计数型信号量)

※案列二:公共电话的使用(二值信号量)

        

任务之间的同步(任务与任务、任务与中断):

        在执行中断服务函数的时候,可以通过释放信号量(不做具体的处理,以提高系统的实时性)来通知某个任务所期待的事件发生了。当退出中断后,通过调度器,同步的任务(做出相应的处理)就会执行

        信号量用于控制共享资源的访问的场景相当于一个上锁机制,只有获得这把锁的钥匙才可以进行下一步操作

Q:既然队列也可以实现同步与互斥那为什么还要信号量?
答:信号量相比队列更节省空间,因为实现同步与互斥不需要传递数据,所以信号量没有队列后面的环形存储区,信号量主要就是依靠计数值uxMessagesWaiting(在队列中表示队列现有消息个数,在信号量中表示有效信号量个数)。

           当计数值 > 0,代表信号量有资源

           当释放信号量,信号量计数值(资源数)加一

           当获取信号量,信号量计数值(资源数)减一 

        

 信号:通知某个对象

  量:资源的数量:计数值都有限制

         • 限定最大值是1,则是二进制信号量

         • 限定最大值不是1,则是计数值信号量

FreeRTOS中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量

总结:信号量是用于传递状态

1.2 队列与信号量的对比

队列

信号量

可以容纳多个数据

创建队列有两部分内存:队列结构体+队列项存储空间

仅存放计数值,无法存放其他数据;

创建信号量,只需分配信号量结构体

写入队列:当队列满时,可阻塞

释放信号量:不可阻塞,计数值++

当计数值为最大值时,返回失败

读取队列:当队列为空时,可阻塞

获取信号量:计数值--

当没有资源时,可阻塞

1.3 二值信号量与计数型信号量的区别

二值信号量计数型信号量
队列长度1大于1
资源初始化0可以根据情况设定

 二、二值信号量

二值信号量的本质:一个队列长度为 1 ,队列项大小为0队列 ,该队列就只有空和满两种情况

        二值信号量通常用于互斥访问或任务同步与互斥信号量(拥有优先级继承)比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步(任务与任务、任务与中断)

         同步:所有任务排着队一件件的往后进行,上件事情没有完成,就继续做上件事情,等上件事情完成后才会去做下一件事情

 使用二值信号量的过程创建二值信号量 ——> 释放二值信号量(队满) ——> 获取二值信号量(队空)     

:创建二值信号量时,初始值为0,所以先要释放信号量

2.1 二值信号量相关API函数

头文件:semphr. h

函数

描述

xSemaphoreCreateBinary()

使用动态方式创建二值信号量

xSemaphoreCreateBinaryStatic()

使用静态方式创建二值信号量

xSemaphoreGive()

释放信号量

xSemaphoreGiveFromISR()

在中断中释放信号量

xSemaphoreTake()

获取信号量

xSemaphoreTakeFromISR()

在中断中获取信号量

 注:二值、计数型、互斥信号量释放与获取均用这两个函数 

 2.1.1 动态创建二值信号量:

实质:创建一个长度为1、队列项大小为0的队列

 2.1.2 释放信号量函数:

※ 对于信号量而言,释放不允许设置阻塞时间,如果队列已满,则直接返回

入队方式:向后入队

 2.1.3 获取信号量函数: 

※ 对于信号量而言,获取允许设置阻塞时间,如果队列为空,则可以选择是否进行等待

可以看出:二值信号量的API函数与队列操作的函数相同,区别只是入口参数不同 

 三、计数型信号量

        计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定

计数型信号量常用于:事件计数、资源管理

注:不同适用场合,信号量创建时的计数值不同

3.1 计数值信号量相关API函数

函数

描述

xSemaphoreCreateCounting()

使用动态方法创建计数型信号量。

xSemaphoreCreateCountingStatic()

使用静态方法创建计数型信号量

uxSemaphoreGetCount()

获取信号量的计数值

             计数型信号量的释放和获取与二值信号量相同 !

3.1.1 动态创建计数值信号量

3.1.2  获取当前计数值

四、互斥信号量(mutex)

4.1 优先级反转:

前面说到,二值信号量用于互斥访问时,会出现优先级反转的情况 

优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行

        高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转) 

4.2 互斥信号量

        互斥信号量:互斥信号量其实就是一个拥有优先级继承二值信号量,在同步的应用中二值信号量最适合互斥信号量适合用于那些需要互斥访问的应用

4.2.1 优先级继承

        优先级继承:暂时提高某个占有某种资源的低优先级任务的优先级,使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值

         当任务L获取信号量后,还没来得及释放,就被高优先级的任务抢占,但没有信号量,高优先级任务H就进入阻塞,此时任务L运行,并将任务L的优先级设置为与任务H同等优先级,即使任务M就绪,也不会得到执行,直到L释放信号量,H获取信号量,任务H开始运行

        此时任务H的阻塞时间仅仅是任务L 的执行时间将优先级翻转的危害降到了最低。优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响

总结:

所谓优先级继承,其实就是低优先级任务继承高优先级任务的优先级
发生优先级继承:在高优先级任务获取信号量失败(低优先级任务已经持有信号量)进入阻塞态之前,将持有信号量的低优先级任务的优先级提升

解除优先级继承:在持有信号量的低优先级任务释放信号量的时候,将自己的优先级恢复到初始值(因为已经释放了信号量,完成了任务)

4.3 互斥信号量相关API函数

使用流程:创建互斥信号量 ——> (task获取信号量 ——>(give)释放信号量 

注意:创建互斥信号量时,会主动释放一次信号量

使用互斥信号量:首先将宏configUSE_MUTEXES置1

函数

描述

xSemaphoreCreateMutex()

使用动态方法创建互斥信号量。

xSemaphoreCreateMutexStatic()

使用静态方法创建互斥信号量。

 4.3.1 动态创建互斥信号量

互斥信号量的释放和获取函数与二值信号量相同

        只不过互斥信号量由于涉及到任务优先级继承的性质,而中断不属于任务,没法处理中断由新阿基继承,因此不支持中断中调用

五、递归互斥信号量

5.1 互斥信号的缺点

(1)不能实现由A获取就由A释放

比如:任务A、B互斥访问串口打印资源,假如任务A已经获取,还没释放。此时,任务C偷偷释放,使得资源失去保护,任何任务都可以访问该资源,会发生混乱

(2)死锁

比如:任务A进行两次获取,第二次获取必定失败,导致任务被阻塞,其他任务想要获取,该任务还未释放,导致死锁

5.2 递归互斥信号量

        递归互斥信号量是一种特殊的互斥信号量。已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量(即可以嵌套使用),且次数不限。一个任务获取了多少次递归互斥信号量就必须释放多少次释放之前递归互斥量都处于无效状态其他任务无法获取只有持有递归信号量的任务才能获取和释放

     

  递归互斥信号量不能在中断函数中使用 

5.3 递归互斥信号量相关API函数

5.3.1 动态创建

使用条件:configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_mutexes 都必须在 FreeRTOSConfig.h 中定义为 1

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

返回值:

成功:返回创建的互斥锁的句柄

失败:返回 NULL

 SemaphoreHandle_t xMutex;

 void vATask( void * pvParameters )
 {
    Create a recursive mutex.
    xMutex = xSemaphoreCreateRecursiveMutex();

    if( xMutex != NULL )
    {
        /* The recursive mutex was created successfully and
        can now be used. */
    }
 }

注:

使用流程:创建递归互斥信号量 ——> (task获取信号量 ——>(give)释放信号量 

注意:创建递归互斥信号量时,会主动释放一次信号量

 

5.3.2 获取递归互斥信号量

使用条件:configUSE_RECURSIVE_MUTEXES 设置为 1

xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex,
                         TickType_t xTicksToWait );

 SemaphoreHandle_t xMutex = NULL;

 // A task that creates a mutex.
 void vATask( void * pvParameters )
 {
    // Create the mutex to guard a shared resource.
    xMutex = xSemaphoreCreateRecursiveMutex();
 }

 // A task that uses the mutex.
 void vAnotherTask( void * pvParameters )
 {
    // ... Do other things.

    if( xMutex != NULL )
    {
        // See if we can obtain the mutex.  If the mutex is not available
        // wait 10 ticks to see if it becomes free.    
        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
        {
     
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );

          
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );
            xSemaphoreGiveRecursive( xMutex );

            // Now the mutex can be taken by other tasks.
        }
        else
        {
            // We could not obtain the mutex and can therefore not access
            // the shared resource safely.
        }
    }
 }

 5.3.3 释放递归互斥信号量

使用条件:将 configUSE_RECURSIVE_MUTEXES 设置为 1

xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习测4-缺陷管理略

缺陷 缺陷管理工具 jira 禅道 qc cq Bugfree缺陷的类型: 遗漏 missing 该做的没做 错误 error 该做的做错了 额外的实现 extra 不该做的做了软件缺陷的表现形式: 一.软件未实现需求规格说明书要求的功能 二.软件出现了需求规…

windows电脑如何运行python的定时任务

这里需要使用:windows系统设置-控制面板里的计划任务 1.打开计划任务之后,选择:创建基本任务 2.填写名称,这里根据自己具体的项目需求填写,然后点击下一步。 3.选择每日,再点击下一步 4.设置时间&…

【腾讯内推】腾讯2025校招/青云计划/社招——长期有效

及时跟进进度,保证不让简历石沉大海! 涵盖NLP/CV/CG/ML/多模态/数据科学/多媒体等各方向! 定向匹配优质团队/竞争力薪酬/覆盖全球工作地点! 招聘对象: 本硕博:2024年1月-2025年12月毕业的同学 目前最热岗位: 技术研究-自然语言处理 技术研究-计算机视觉 …

【CV炼丹师勇闯力扣训练营 Day24:§7 回溯3】

CV炼丹师勇闯力扣训练营 代码随想录算法训练营第24天 93 复原IP地址 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。 例如:“0.1.2.201” 和 “192.168.…

VBA提取word表格内容到excel

这是一段提取word表格中部分内容的vb代码。 Sub 提取word表格() mypath ThisWorkbook.Path & "\"myname Dir(mypath & "*.doc*")n 4 index of rowsRange("A1:F1") Array("课程代码", "课程名称", "专业&…

【Spring Boot】统一数据返回

目录 统一数据返回一. 概念二.实现统一数据返回2.1 重写responseAdvice方法2.2 重写beforeBodyWriter方法 三. 特殊类型-String的处理四. 全部代码 统一数据返回 一. 概念 其实统一数据返回是运用了AOP(对某一类事情的集中处理)的思维,简单…

【qt】如何获取网卡的信息?

网卡不只一种,有有线的,有无线的等等 我们用QNetworkInterface类的静态函数allInterfaces() 来获取所有的网卡 返回的是一个网卡的容器. 然后我们对每个网卡来获取其设备名称和硬件地址 可以通过静态函数humanReadableName() 来获取设备名称 可以通过静态函数**hardwareAddre…

10元 DIY 一个柔性灯丝氛围灯

之前TikTok上特别火的线性氛围灯Augelight刚出来的时候一度卖到80多美金,国内1688也能到400多人民币。 随着各路国内厂商和DIY创客的跟进,功能变多的同时价格一路下滑,虽然有的质感的确感人,但是便宜啊。 甚至关注的up有把成本搞到…

C语言 -- 操作符详解​

C语言 -- 操作符详解​ 1. 操作符的分类2. 二进制和进制转换​2.1 2进制转10进制​2.1.1 10进制转2进制数字​ 2.2 2进制转8进制和16进制​2.2.1 2进制转8进制​2.2.2 2进制转16进制​ 3. 原码、反码、补码​4. 移位操作符​4.1 左移操作符​ 4.2 右移操作符​5. 位操作符&…

野指针的概念 如果规避野指针

目录 野指针的概念 有关野指针的代码 如何规避野指针 野指针的概念 野指针就是指针指向的位置是不可知的&#xff08;随机的&#xff0c;不正确的&#xff0c;没有明确限制的&#xff09; 有关野指针的代码 指针未初始化&#xff1a; #include<stdio.h> int main…

通过RpmBuild构建redis-5.0.9版本的RPM类型包

系列文章目录 rpmbuild基础知识 文章目录 系列文章目录前言一、rpmbuild相关操作1、安装rpmbuild命令2、安装spec文件检查工具3、查看rpmbuild版本4、编译工具安装5、修改rpm制作包的默认路径 二、资源准备1、创建rpmbuild工作目录2、目录作用解释3、下载redis源码包4、上传re…

nginx.conf配置文件

1、全局模块 worker_processes 1; 工作进程数&#xff0c;一般设置成服务器内核数的2倍&#xff08;一般不超过8个&#xff0c;超过8个反而会降低性能&#xff0c;一般是4个&#xff0c;1-2个也可以&#xff09; 处理进程的过程必然涉及配置文件和展示页面&#xff0c;也就是…

AI Agent技术的最新进展与改变世界的典型项目巡礼

AI Agent 探索 1. AI Agent 技术发展以及典型项目 1.0 前 AI Agent 时代 在学术探索的浩瀚星空中&#xff0c;机器人技术领域的璀璨明珠莫过于Agent技术的深入研究&#xff0c;这一领域历来是创新与突破的温床。回溯至大模型浪潮兴起之前&#xff0c;Agent技术的辉煌篇章便已…

G9 - ACGAN理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 环境步骤环境设置数据准备工具方法模型设计模型训练模型效果展示 总结与心得体会 上周已经简单的了解了ACGAN的原理&#xff0c;并且不经实践的编写了部分…

Spring Boot集成jacoco实现单元测试覆盖统计

1.什么是jacoco&#xff1f; JaCoCo&#xff0c;即 Java Code Coverage&#xff0c;是一款开源的 Java 代码覆盖率统计工具。支持 Ant 、Maven、Gradle 等构建工具&#xff0c;支持 Jenkins、Sonar 等持续集成工具&#xff0c;支持 Java Agent 技术远程监控 Java 程序运行情况…

便携式气象站:预测天气的得力助手

在户外探险、农业种植、环境监测等领域&#xff0c;气象信息的准确性对于决策至关重要。 一、便携式气象站的工作原理 便携式气象站是一种集成了多种气象传感器的设备&#xff0c;能够实时监测和记录环境中的温度、湿度、气压、风速、风向、降雨量等气象参数。 二、便携式气象站…

模板初阶和string容器

目录 1.模板 函数模板 函数模板的调用规则&#xff1a; 类模板 容器与迭代器 string的简单介绍 iterator&#xff08;迭代器&#xff09; begin()与end() rbegin&#xff08;&#xff09;和rend&#xff08;&#xff09; Capacity&#xff08;容量&#xff09; shrink…

Alibaba Cloud Toolkit前端使用proxy代理配置

1、vscode 先安装插件 Alibaba Cloud Toolkit 2、前端代码: /personnel: {// target: http://xxx.xx.xxx.xx:9100, // 测试环境// target: http://xxx.xx.xxx.xx:9200, // 线上环境target: http://127.0.0.1:18002, // toolkit 代理changeOrigin: true

如何取消闪迪Micro SD卡的写保护?这个技巧很有效!

由于受写保护影响&#xff0c;无法格式化闪迪Micro SD卡&#xff1f;别担心&#xff01;通过本文你可以学习如何解除闪迪Micro SD卡的写保护。 我的闪迪SD卡有写保护怎么办&#xff1f; “我打算格式化我的闪迪SD卡。但当我进行格式化时&#xff0c;提示我磁盘被写保护。我想用…

机器人具身智能Embodied AI

强调智能体&#xff08;如机器人&#xff09;通过物理身体在物理世界中的实时感知、交互和学习来执行任务。 通过物理交互来完成任务的智能系统。它由“本体”&#xff08;即物理身体&#xff09;和“智能体”&#xff08;即智能核心&#xff09;耦合而成&#xff0c;能够在复…