12、FreeRTOS信号量(semaphore)

文章目录

  • 一、信号量的特性
    • 1.1 使用场景
    • 1.2 什么是信号量
    • 1.3 信号量和队列的区别
    • 1.4 两种信号量的对比
  • 二、二值信号量/计数信号量
    • 2.1 什么是二值信号量
    • 2.2 什么是计数信号量
    • 2.2 (二值信号量/计数信号量) 相关API
  • 三、互斥量(mutex)
    • 3.2 什么是优先级翻转
    • 3.3 互斥量的使用场合
    • 3.4 互斥量API
  • 四、递归锁
    • 4.1 死锁的概念
    • 4.2 自我死锁
    • 4.3 API函数
  • 五、注意事项

一、信号量的特性

信号量分为以下几种

  • 二值信号量
  • 计数信号量
  • 互斥信号量
  • 递归互斥信号量

1.1 使用场景

前面介绍的队列(queue)可以用于传输数据:在任务之间、任务和中断之间。 有时候我们只需要传递状态,并不需要传递具体的信息,比如:

  • 我的事做完了,通知一下你
  • 卖包子了、卖包子了,做好了1个包子!做好了2个包子!做好了3个包子!
  • 这个停车位我占了,你们只能等着

在这种情况下我们可以使用 信号量(semaphore),它更节省内存。

1.2 什么是信号量

信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并发调用。

信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以用来表示资源的数量,当我们的量只有0和1的时候,它就可以被称作二值信号量,只有两个状态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。

信号量也是队列的一种

1.3 信号量和队列的区别

队列信号量
可以容纳多个数据, 创建队列时有2部分内存: 队列结构体、存储数 据的空间只有计数值,无法容纳其他数据。 创建信号量时,只需要分配信号量结构体
生产者:没有空间存入数据时可以阻塞生产者:用于不阻塞,计数值已经达到最大时 返回失败
消费者:没有数据时可以阻塞消费者:没有资源时可以阻塞

1.4 两种信号量的对比

信号量的计数值都有限制:限定了最大值。如果最大值被限定为1,那么它就是二进制信号量;如果最 大值不是1,它就是计数型信号量。 差别列表如下:

二值信号量计数信号量
被创建时初始值为0被创建时初始值可以设定
其他操作是一样的其他操作是一样的

在这里插入图片描述

二、二值信号量/计数信号量

2.1 什么是二值信号量

二值信号量其实就是一个长度为1,大小为零的队列,只有0和1两种状态,通常情况下,我们用它来进行互斥访问或任务同步

互斥访问:比如门钥匙,只有获取到钥匙才可以开门。

任务同步:比如我录完视频你才可以看视频。

在这里插入图片描述

2.2 什么是计数信号量

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

在这里插入图片描述

2.2 (二值信号量/计数信号量) 相关API

  • 创建

    使用信号量之前,要先创建,得到一个句柄;使用信号量时,要使用句柄来表明使用哪个信号量。 对于二进制信号量、计数型信号量,它们的创建函数不一样:

    创建方式二值信号量计数信号量
    动态创建xSemaphoreCreateBinaryxSemaphoreCreateCounting
    静态创建xSemaphoreCreateBinaryStaticxSemaphoreCreateBinaryStatic

    创建二进制信号量的函数原型如下:

    /* 创建一个二进制信号量,返回它的句柄。
    * 此函数内部会分配信号量结构体
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateBinary( void );
    /* 创建一个二进制信号量,返回它的句柄。
    * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t
    *pxSemaphoreBuffer );
    
    

    创建计数型信号量的函数原型如下:

    /* 创建一个计数型信号量,返回它的句柄。
    * 此函数内部会分配信号量结构体
    * uxMaxCount: 最大计数值
    * uxInitialCount: 初始计数值
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t
    uxInitialCount);
    /* 创建一个计数型信号量,返回它的句柄。
    * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
    * uxMaxCount: 最大计数值
    * uxInitialCount: 初始计数值
    * pxSemaphoreBuffer: StaticSemaphore_t结构体指针
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,
    UBaseType_t uxInitialCount,
    StaticSemaphore_t
    *pxSemaphoreBuffer );
    
  • 删除

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

    /*
    * xSemaphore: 信号量句柄,你要删除哪个信号量
    */
    void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
    
  • 获取/释放**( give/take)**

    操作方式任务中使用在任务中使用中断中使用在中断中使用
    释放信号量(give)xSemaphoreGivexSemaphoreGiveFromISR
    获取信号量(take)xSemaphoreTakexSemaphoreTakeFromISR

    xSemaphoreGive 的函数原型如下:

    BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    

    xSemaphoreGive 函数的参数与返回值列表如下:

    参数说明
    xSemaphore信号量句柄,释放哪个信号量
    返回值pdTRUE表示成功,
    如果二进制信号量的计数值已经是1,再次调用此函数则返回失败;
    如果计数型信号量的计数值已经是最大值,再次调用此函数则返回失败

    pxHigherPriorityTaskWoken的函数原型如下:

    BaseType_t xSemaphoreGiveFromISR(  SemaphoreHandle_t xSemaphore,
      								  BaseType_t *pxHigherPriorityTaskWoken);
    

    xSemaphoreGiveFromISR 函数的参数与返回值列表如下:

    参数说明
    xSemaphore信号量句柄,释放哪个信号量
    pxHigherPriorityTaskWoken如果释放信号量导致更高优先级的任务变为了就绪态, 则*pxHigherPriorityTaskWoken = pdTRUE
    返回值pdTRUE表示成功,
    如果二进制信号量的计数值已经是1,再次调用此函数则返回失 败;
    如果计数型信号量的计数值已经是最大值,再次调用此函数则返 回失败

    xSemaphoreTake 的函数原型如下:

    BaseType_t xSemaphoreTake(
    SemaphoreHandle_t xSemaphore,
    TickType_t xTicksToWait
    );
    
    

    xSemaphoreTake函数的参数与返回值列表如下:

    参数说明
    xSemaphore信号量句柄,获取哪个信号量
    xTicksToWait如果无法马上获得信号量,阻塞一会:
    0:不阻塞,马上返回
    portMAX_DELAY: 一直阻塞直到成功
    其他值: 阻塞的Tick个数,
    可以使用 pdMS_TO_TICKS() 来指定阻塞时间为若干 ms
    返回值pdTRUE表示成功

    xSemaphoreTakeFromISR的函数原型如下:

    BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,
      								 BaseType_t *pxHigherPriorityTaskWoken  );
    

    xSemaphoreTakeFromISR函数的参数与返回值列表如下:

    参数说明
    xSemaphore信号量句柄,获取哪个信号量
    pxHigherPriorityTaskWoken如果获取信号量导致更高优先级的任务变为了就绪态, 则*pxHigherPriorityTaskWoken = pdTRUE
    返回值pdTRUE表示成功

三、互斥量(mutex)

## 3.1 什么是互斥量?

在多数情况下,互斥型信号量和二值型信号量非常相似,但是从功能上 二值型信号量用于同步,而 互斥型信号量用于资源保护

互斥型信号量和二值型信号量还有一个 最大的区别,互斥型信号量可以有效解决优先级反转现象。

3.2 什么是优先级翻转

实质:低优先级任务将二值信号量获取,导致高优先级无法获取二值信号量阻塞,而不用获取信号量的中优先级任务能够打断低优先级任务,等到中优先级任务执行完后,得到信号量的低优先级任务继续执行,等到执行完后归还二值信号量,高优先级任务获取信号量后才能执行,这种现象称之为优先级翻转。

在这里插入图片描述
​ 以上图为例,系统中有3个不同优先级的任务H/M/L,最高优先级任务H和最低优先级任务L通过信号量机 制,共享资源。目前任务L占有资源,锁定了信号量,Task H运行后将被阻塞,直到Task L释放信号量后, Task H才能够退出阻塞状态继续运行。但是Task H在等待Task L释放信号量的过程中,中等优先级任务M抢 占了任务L,从而延迟了信号量的释放时间,导致Task H阻塞了更长时间,这种现象称为优先级倒置或反转。

优先级继承: 当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

3.3 互斥量的使用场合

在多任务系统中,任务A正在使用某个资源,还没用完的情况下任务B也来使用的话,就可能导致问题。 比如对于串口,任务A正使用它来打印,在打印过程中任务B也来打印,客户看到的结果就是A、B的信 息混杂在一起。

3.4 互斥量API

要想使用互斥量,需要在配置文件 FreeRTOSConfig.h 中定义:

#define configUSE_MUTEXES 1
  • 创建

    互斥量是一种特殊的二进制信号量。 使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。 创建互斥量的函数有2种:动态分配内存,静态分配内存,函数原型如下:

    /* 创建一个互斥量,返回它的句柄。
    * 此函数内部会分配互斥量结构体
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateMutex( void );
    /* 创建一个互斥量,返回它的句柄。
    * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针
    * 返回值: 返回句柄,非NULL表示成功
    */
    SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer
    );
    
    
  • 其他函数

    要注意的是,互斥量不能在ISR中使用。 各类操作函数,比如删除、give/take,跟一般是信号量是一样的。

    /*
    * xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
    */
    void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
    /* 释放 */
    BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    /* 释放(ISR版本) */
    BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,
    								BaseType_t *pxHigherPriorityTaskWoken);
    /* 获得 */
    BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
    							TickType_t xTicksToWait);
    /* 获得(ISR版本) */
    xSemaphoreGiveFromISR(	SemaphoreHandle_t xSemaphore,
    						BaseType_t *pxHigherPriorityTaskWoken);
    
    

四、递归锁

4.1 死锁的概念

日常生活的死锁:我们只招有工作经验的人!我没有工作经验怎么办?那你就去找工作啊!

假设有2个互斥量M1M2,2个任务AB

  • A获得了互斥量M1
  • B获得了互斥量M2
  • A还要获得互斥量M2才能运行,结果A阻塞
  • B还要获得互斥量M1才能运行,结果B阻塞
  • A、B都阻塞,再无法释放它们持有的互斥量
  • 死锁发生!

4.2 自我死锁

假设这样的场景:

  • 任务A获得了互斥锁M
  • 它调用一个库函数
  • 库函数要去获取同一个互斥锁M,于是它阻塞:任务A休眠,等待任务A来释放互斥锁!
  • 死锁发生!

4.3 API函数

怎么解决这类问题?可以使用递归锁(Recursive Mutexes),它的特性如下:

  • 任务A获得递归锁M后,它还可以多次去获得这个锁
  • "take"了N次,要"give"N次,这个锁才会被释放

递归锁的函数根一般互斥量的函数名不一样,参数类型一样,列表如下:

动作递归锁一般互斥量
创建xSemaphoreCreateRecursiveMutexxSemaphoreCreateMutex
获得xSemaphoreTakeRecursivexSemaphoreTake
释放xSemaphoreGiveRecursivexSemaphoreGive
  • 函数原型如下:
/* 创建一个递归锁,返回它的句柄。
* 此函数内部会分配互斥量结构体
* 返回值: 返回句柄,非NULL表示成功
*/
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
/* 释放 */
BaseType_t xSemaphoreGiveRecursive( SemaphoreHandle_t xSemaphore );
/* 获得 */
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xSemaphore,
									TickType_t xTicksToWait);

五、注意事项

使用互斥量的两个任务是相同优先级时的注意事项


文章是自己总结而记录,有些知识点没说明白的,请各位看官多多提意见,多多交流,欢迎大家留言
如果技术交流可以加以下群,方便沟通
QQ群:370278903
点击链接加入群聊【蜡笔小芯的嵌入式交流群】
![])

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

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

相关文章

Kotlin基本特性

目录 函数 if when 循环 面向对象 继承 主构造函数 接口 修饰符 ​编辑数据类 单例类 Lambda编程 集合 lambda用法 常见函数式API 空指针 判空辅助工具 字符串内嵌表达式 函数 fun add1(a:Int,b:Int):Int{return ab }fun add2(a:Int,b:Int):Int ab // 只…

【JVM】ASM开发

认识ASM ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。 ASM可以直接产生二进制class文件,也可以在类被加载入虚拟机之前动态改变类行为,ASM从类文件中读入信息后能够改变类行为,分析类信息&#xff…

KBU1010-ASEMI新能源专用KBU1010

编辑:ll KBU1010-ASEMI新能源专用KBU1010 型号:KBU1010 品牌:ASEMI 封装:KBU-4 最大重复峰值反向电压:1000V 最大正向平均整流电流(Vdss):10A 功率(Pd):中小功率 芯片个数:4…

yum、rpm相关命令-工具手册

1.rpm文件: 1.1安装rpm文件: rpm -ivh kde-select.rpm23 #--nodeps强制安装,无视环境缺少依赖的检查 rpm -ivh --nodeps kde-select.rpm #--force --replacefiles可以无视rpm的冲突去强制替换(如两个rpm的安装路径相同了会有冲突) rpm -ivh --nodeps --force --replacef…

解析Linux键盘组合键产生信号的完整过程:从硬件中断到信号发送

前言 每一个了解Linux的都知道这样一个知识,CtrlC组合键能够终止一个进程。 个人了解进程相关知识之后知道,一个进程被终止只会有有三种情况: 代码运行完毕,结果正确代码运行完毕,结果不正确代码运行异常&#xff…

FreeRTOS【2】配置文件

1.开发背景 基于上一篇指引,移植了 FreeRTOS 到系统核心移植到了工程中,但是有很多功能没有配置,下面介绍常用的配置。 2.开发需求 配置 FreeRTOS 常用功能 3.开发环境 window10 MDK STM32F429 FreeRTOS10.3.1 4.实现步骤 4.1 配置文…

【QT】初始QT

目录 一.背景1.GUI开发的各种技术方案2.什么是框架3.QT支持的系统4.QT的版本5.QT的优点6.QT的应用常见 二.环境搭建1.认识QTSDK中的重要工具2.使用QT Creator创建项目3.项目解释(1)main.cpp(2)widget.h(3)widget.cpp(4)widget.ui(5)Empty.pro(6)临时文件 三.初始QT1.Hello Worl…

YOLOv8预测流程-原理解析[目标检测理论篇]

接下来是我最想要分享的内容,梳理了YOLOv8预测的整个流程,以及训练的整个流程。 关于YOLOv8的主干网络在YOLOv8网络结构介绍-CSDN博客介绍了,为了更好地介绍本章内容,还是把YOLOv8网络结构图放在这里,方便查看。 1.前言…

以太网技术介绍

随着通信和计算机技术的不断发展,无论是骨干网还是接入网,以太网都已成为应用场景最多,应用范围最广泛的技术之一。对于初次应用以太网的读者,本文主要给出以太网技术的基础知识,并对以太网涉及的部分协议进行简要说明…

找不到msvcr120.dll无法执行代码?几种方法一键修复msvcr120.dll难题

电脑出现“找不到msvcr120.dll无法执行代码”是什么情况?msvcr120.dll文件是Microsoft Visual C Redistributable的一部分,它是应用程序在Windows操作系统上正常运行所必需的动态链接库文件之一。因此,缺少了msvcr120.dll文件,相应…

Sora惊艳亮相:AI技术掀起创作革命,影视产业迎来新风貌!

Sora平台近期发布了名为"Sora首次印象"的更新,为用户带来了令人瞩目的变化。该更新不仅展示了Sora平台的发展方向,还介绍了其在电影制作、广告宣传等领域的潜在应用。 同时,Sora的首席执行官Sam Altman与好莱坞影视工作室进行了会…

电火灶是燃气灶吗?节能、环保效果怎么样?

随着科技的进步,厨房中的传统设备也逐步被新型、高效且环保的设备所替代。电火灶,作为一种新型的电火烹饪设备,逐渐进入人们的视野。那么,电火灶是否与传统的燃气灶有所区别?其节能与环保效果又如何呢?下面…

精益生产咨询公司:深入探讨其独特魅力与核心竞争力

精益生产咨询公司,作为专注于帮助企业实现精益转型和效率提升的专业机构,在现代工业生产中扮演着不可或缺的角色。这些公司不仅具备深厚的行业经验和专业知识,还能够根据企业的实际情况和需求,提供个性化的解决方案和持续的支持服…

stm32f103c8t6之4x4矩阵按键

基于普中精灵开发板 1、矩阵按键原理 当我们需要使用较多的按键时,单片机的IO口可能不够用,这是就需要使用矩阵按键。 对应IO口如下: 步骤解析: 1、全部按键都没有按下时,全行IO为低电平(全列对应的IO设置为下拉低…

maven mirrorOf的作用

在工作中遇到了一个问题导致依赖下载不了,最后发现是mirror的问题,决定好好去看一下mirror的配置,以及mirrorOf的作用,以前都是直接复制过来使用,看了之后才明白什么意思。 过程 如果你设置了镜像,镜像会匹…

基于Springboot+Vue的Java项目-车辆管理系统开发实战(附演示视频+源码+LW)

大家好!我是程序员一帆,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &am…

网易云新玩法:教你赚取第一桶金!

在现今的音乐应用市场中,有几款软件备受广大用户的青睐。 其中,QQ音乐、酷狗音乐以及网易云音乐都是大家耳熟能详的名字。 这些平台不仅提供了丰富的音乐资源,还具备了许多便捷的功能,使得用户们能够享受到更为优质的音乐体验。…

Covalent Network(CQT)通过 “新曙光” 计划实现重要里程碑,增强以太坊时光机,提供 30% 的年化质押收益率

Covalent Network(CQT)作为集成超过 280 条区块链,并服务于超过 2.8 亿个钱包的领先结构化数据基础设施层,宣布了其战略计划 “新曙光” 中的一个重要进展。随着网络升级并完成了准备工作的 75%,这将为即将部署的以太坊…

Jsp+Servlet实现图片上传和点击放大预览功能(提供Gitee源码)

前言:在最近老项目的开发中,需要做一个图片上传和点击放大的功能,在Vue和SpringBoot框架都有现成封装好的组件和工具类,对于一些上世纪的项目就没这么方便了,所以需要自己用原生的代码去编写,这里分享一下我…

Nextcloud私有云盘-重新定义云存储体验

Nextcloud私有云盘-重新定义云存储体验 1. 什么是Nextcloud ​ Nextcloud是一个开源的云存储和协作平台,旨在为个人用户、企业和团队提供安全、隐私保护的数据存储和共享解决方案。它允许您在不同设备之间同步、共享文件,提供了强大的协作工具和应用生…