STM32FreeRTOS信号量(STM32cube高效开发)

news2025/1/25 9:17:57

一、信号量

(一)信号量概括

信号量是操作系统中重要的一部分,信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问
在这里插入图片描述

FreeRTOS 提供了多种信号量,按信号量的功能可分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量,不同类型的信号量有其不同的应用场景。

在这里插入图片描述
信号量(Semaphore)和互斥量(Mutex)都基于消息队列的基本数据结构,但是信号量和互斥量又有一些区别。
信号量没有优先级继承机制,使用二值信号量时容易出现优先级翻转问题,而二值信号量可以减缓优先级翻转问题。二值信号量适用于进程间同步,计数信号量适用于多个共享资源的访问控制,互斥量适用于对一个资源的互斥访问控制。

信号量与队列区别:
在这里插入图片描述

(二)二值信号量

1、二值信号量概述

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况。
二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步
在这里插入图片描述
在这里插入图片描述

2、二值信号量使用

二值信号量(Binary Semaphore)就是只有一个项的队列。
二值信号量就像是一个标志,适合用于进程间同步的通信
在这里插入图片描述

(1)上图有两个进程,一个负责释放二值信号量,另外一个负责获取。ADC中断ISR读取ADC转换结果后写入数据缓冲区,数据处理任务负责读取缓冲区数据并进行处理。

(2)数据缓冲区是两个进程间同步访问的对象。
假设数据缓冲区只存储一次的转换结果数据,ADC中断ISR读取ADC转换结果后写入到缓冲区中,并且释放二值信号量,此时二值信号量有效,表示缓冲区有了新的数据(理解为自定义的标志变量,查询标志变量被置1表明有数据了可以下一步处理

(3)数据处理任务总是获取(Take)二值信号量。
有效后(理解为裸机开发中标志变量置位),任务立刻进入就绪状态参与任务调度,就可以读取缓冲区的数据并进行处理。无效时(理解为裸机开发中,数据处理完毕,清空标志位),任务在阻塞状态等待;

使用二值信号量的过程:创建二值信号量 -> 释放二值信号量-> 获取二值信号量

(三)计数信号量

计数型信号量与二值信号量类似, 二值信号量相当于队列长度为 1 的队列,因此二值信号量只能容纳一个资源,这也是为什么命名为二值信号量,而计数型信号量相当于队列长度大于0 的队列,因此计数型信号量能够容纳多个资源,这是在计数型信号量被创建的时候确定的。
在这里插入图片描述

1、事件计数:

每次事件发生后,在事件处理函数中释放计数型信号量(计数型信号量的
资源数加 1),其他等待事件发生的任务获取计数型信号量(计数型信号量的资源数减 1),这么一来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作。

2、资源管理:

在这种场合下,计数型信号量的资源数代表着共享资源的可用数量,例如前面举例中停车场中的空车位。一个任务想要访问共享资源,就必须先获取这个共享资源的计数型信号量,之后在成功获取了计数型信号量之后,才可以对这个共享资源进行访问操作,当然,在使用完共享资源后也要释放这个共享资源的计数型信号量。在这种场合下,计数型信号量的资源数一般在创建时设置为受其管理的共享资源的最大可用数量。

在这里插入图片描述
(1)一个计数型信号量被创建时设置了初值4,这个值只是个计数值。
(2)有1个客人进店时就是获取(Take)信号量,计数信号量的值减1。当计数信号量的值变为0时,再有客人要进店时就得等待。
(3)如果有1个客人用餐结束离开了就是释放(Give)信号量,计数信号量的值加1,表示可用资源数量增加了1个。

(三)互斥量

1、互斥量概述

使用二值信号量时可能会出现优先级翻转的问题。互斥量引入了优先级继承机制,可以减缓优先级翻转问题。
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中
在这里插入图片描述
在互斥访问中互斥信号量相当于一把钥匙, 当任务想要访问共享资源的时候就必须先获得这把钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访问资源。
在这里插入图片描述
(1)两个任务互斥性地访问串口,即在任务A访问串口时,其他任务不能访问串口。
(2)互斥量相当于管理串口的一把钥匙。一个任务可以获取(Take)互斥量,获得互斥量后将独占对串口的访问,访问完后要释放(Give)互斥量。
(3)一个任务获得互斥量后,对资源进行访问时,其他想要获取互斥量的进程只能等待。

2、二值信号量出现的优先级翻转问题

在这里插入图片描述
二值信号量也可以用于互斥型资源访问控制,但是容易出现
优先级翻转(Priority Inversion)问题

例如:
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)
在这里插入图片描述
已知:
任务L、任务M、任务H任务优先级依次是低中高,任务L和任务H需要获取信号量才能运行,任务M不需要获取信号量。
任务运行过程:
(1)刚开始低优先级任务获取信号量后,任务L处于运行中,高优先级任务H随后处于就绪状态,出现了比运行任务L优先级更高的任务,应该抢断低优先级任务的cpu占有权进入运行态,但是高优先级任务只有获取到信号量才能进入运行态(任务L占有信号量并没有释放),由于没有有效的信号量,所以高优先任务H无奈只能进入阻塞态等待信号量的释放,此时处于运行中的还是低优先级任务。
(2)一段时间以后,中优先级任务M处于就绪态,中优先级任务显然优先级高于运行中的低优先级任务L,由于不需要获取信号量即可运行,所以此时中优先级任务M打断低优先级任务L的运行,中优先级任务得到了cpu的使用权限进入了运行态。
(3)当中优先级任务M运行完毕之后,由于任务H无法获取信号量,迫不得已运行权限还是给到了任务L,等任务L运行完毕释放信号量后此时信号量变为有效,高优先任务H获取到信号量才得以运行。

总之:
从上面优先级翻转的示例中,可以看出,任务 H 为最高优先级的任务,因此任务 H 执行的操作需要有较高的实时性,但是由于优先级翻转的问题,导致了任务 H 需要等到任务 L 释放信号量才能够运行,并且,任务 L 还会被其他介于任务 H 与任务 L 任务优先级之间的任务 M 抢占,因此任务 H 还需等待任务 M 运行完毕,这显然不符合任务 H 需要的高实时性要求。

黑色表示任务L运行的时间段,红色表示任务H运行,蓝色表示任务M运行时间
在这里插入图片描述

  • 低优先级任务TaskLP在t1时刻开始处于运行状态,并且获取了一个二值信号量semp。

  • 在时刻t2,高优先级任务TaskHP进入运行状态,它申请二值 信号量semp,但是二值信号量被任务TaskLP占用,所以TaskHP在时刻t3进入阻塞等待状态,TaskLP进入运行状态

  • 在时刻t4,中等优先级任务TaskMP抢占了TaskLP的CPU使用权,TaskMP不使用二值信号量,所以它一直运行到时刻t5才进入阻塞状态。

  • 从t5时刻开始TaskLP又进入运行状态,直到t6时刻释放二值信号量semp,TaskHP才能进入运行状态。

在这里插入图片描述
高优先级的任务TaskHP需要等待低优先级的任务TaskLP释放二值信号量之后才可以运行,这也是期望的运行效果。但是在t4时刻,虽然任务TaskMP的优先级比TaskHP低,
但是它先于TaskHP抢占了CPU的使用权,这破坏了基于优先级抢占式执行的原则,对系统的实时性是有不利影响的。

3、互斥信号量优先级继承改善优先级翻转问题

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

互斥信号量下任务的运行过程
在这里插入图片描述

  • t1时刻:TaskLP处于运行状态,并且获得了一个互斥量mutex
  • t2时刻:TaskHP进入运行状态
  • t3时刻: TaskHP申请互斥量mutex,但是互斥量被TaskLP占用,TaskHP进入阻塞等待状态,TaskLP进入运行状态。但是在t3时刻,RTOS将TaskLP的优先级临时提高到与TaskHP相同的级别,这就是优先级继承。
  • t4时刻,TaskMP进入就绪状态,但是因为TaskLP的临时优先级高于TaskMP,所以TaskMP无法获得CPU的使用权
  • t5时刻:TaskLP释放互斥量,任务TaskHP立刻抢占CPU的使用权,并恢复TaskLP原来的优先级。
  • t6时刻:TaskHP进入阻塞状态后,TaskMP才进入运行状态。

二、信号量操作函数

信号量和互斥量的主要操作是释放和获取

(一)二值信号量

在这里插入图片描述
1、xSemaphoreCreateBinary() 创建信号量

二值信号量被创建后是无效的,相当于值为0。释放二值信号量就是使其有效,相当于使其变为1。(在创建二值信号量完毕,使用之前需要手动释放一次信号量让信号量变为有效)

关于二值信号量在创建后是否需要先释放一次,取决于使用场景和期望的行为:

  • 不先释放的情况:
    如果希望在信号量创建后立即表示资源是不可用的(例如,你可能希望在初始化资源或进行某些设置之后再允许其他线程访问),则不需要在创建后先释放信号量。
  • 先释放的情况:
    如果希望在信号量创建后立即表示资源是可用的,那么需要在创建后先释放一次信号量。
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
//无参数、返回值是二值信号量句柄

官方例程:
(1)创建一个变量
(2)创建二值信号量将返回值赋值给创建的变量
(3)判断返回值是否为空,如果不为空表明创建成功
在这里插入图片描述
2、xSemaphoreGive()释放二值信号量
在释放信号量之前,需要先对要释放的信号量进行判断,判断信号量是否存在,存在的情况下才去执行相关的操作。

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

参数1:是要释放的信号量句柄
返回值:pdTRUE表明释放成功

3、xSemaphoreTake()获取二值信号量

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
参数1:信号量句柄
参数2:阻塞时间
返回值:pdTRUE表明获取信号量成功

(二)计数信号量

使用计数型信号量的过程:创建计数型信号量 ->释放信号量 -> 获取信号量

在这里插入图片描述

1、创建计数信号量

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif
参数1:计数信号量最大值
参数2:创建计数信号量时的初始值(计数用就设为0,有效资源个数就设为最大值)
返回值:计数信号量的句柄(NULL表示创建失败)

2、释放获取信号量同二值信号量

3、获取计数信号量当前计数值

#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

参数1:计数信号量句柄
返回值:当前信号量的计数值大小

(三)互斥量

使用互斥信号量:首先将宏configUSE_MUTEXES置一
使用流程:创建互斥信号量 ->(task)获取信号量 ->(give)释放信号量
在这里插入图片描述

1、xSemaphoreCreateMutex()创建互斥量

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif
返回值:互斥量的句柄

官方例子:

 Example usage:

 SemaphoreHandle_t xSemaphore;

 void vATask( void * pvParameters )
 {
    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
    // This is a macro so pass the variable in directly.
    xSemaphore = xSemaphoreCreateMutex();

    if( xSemaphore != NULL )
    {
        // The semaphore was created successfully.
        // The semaphore can now be used.
    }
 }

2、互斥信号量释放与获取同二值信号量

获取互斥量使用函数xSemaphoreTake(),释放信号量使用
函数xSemaphoreGive(),这两个函数的用法与获取和释放二值信号量一样。

注意1: 创建互斥信号量时,会主动释放一次信号量
注意2: 互斥信号量不支持中断中调用。
xSemaphoreGiveFromISR()和xSemaphoreTakeFromISR()不能应用于互斥量。
在这里插入图片描述

三、STM32Cube-FreeRTOS信号量实验后续更新

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

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

相关文章

python文本分析与贝叶斯算法

一、python文本分析概念 文本分析中第一个概念是停用词,这种词有三个特点:(1)语料中大量出现;(2)没啥用;(3)留着没用。比如符号(!#&a…

2024年最全洗地机选购攻略盘点丨希亦、小米、云鲸、海尔洗地机哪款值得入手?

在现代家居清洁中,洗地机是不可或缺的得力助手,它融合了吸尘、拖地等多种功能。面对市场上琳琅满目的洗地机品牌和型号,选择一个可靠的品牌至关重要。优质的品牌能够提供高品质的产品,使您的清洁工作更加轻松高效。本文将向您推荐…

Android开发基础面试题,PDF超过6000页

前言 从毕业到现在面试也就那么几家公司,单前几次都比较顺利,在面到第三家时都给到了我offer!前面两次找工作,没考虑到以后需要什么,自己的对未来的规划是什么,只要有份工作,工资符合自己的要求…

阿里云域名优惠口令2024年最新,com、cn和域名注册续费使用

2024年阿里云域名优惠口令,com域名续费优惠口令“com批量注册更享优惠”,cn域名续费优惠口令“cn注册多个价格更优”,cn域名注册优惠口令“互联网上的中国标识”,阿里云优惠口令是域名专属的优惠码,可用于域名注册、续…

领到了腾讯云服务器红包,可以用于购买服务器,开心!

在2024年腾讯云新春采购节优惠活动上,可以领取新年惊喜红包,打开活动链接 https://curl.qcloud.com/oRMoSucP 会自动弹出红包领取窗口,如下图: 腾讯云2024新春采购节红包领取 如上图所示,点击“领”红包,每…

[C语言]——C语言常见概念(1)

目录 一.C语言是什么、 二.C语言的历史和辉煌 三.编译器的选择(VS2022为例) 1.编译和链接 2.编译器的对比 3.VS2022 的优缺点 四.VS项目和源文件、头文件介绍 五.第⼀个C语言程序 ​​​​​​​ 一.C语言是什么、 ⼈和⼈交流使⽤的是⾃然语⾔&…

20 easy 70. 爬楼梯

//假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 // // 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? // // // // 示例 1: // // //输入:n 2 //输出:2 //解释:有两种方法可以爬到楼顶。 /…

cuda WSL2 无需单独安装

https://docs.nvidia.com/cuda/wsl-user-guide/index.html 这个写的很详细

Day27-Linux系统服务管理

Day27-Linux系统服务管理 1. ltrace:跟踪进程调用库函数2. 进程管理总结3. 什么是平均负载4. 可运行状态和不可中断状态是什么5. 平均负载其实就是【单位时间内的活跃进程数】。6. 如何查看Linux系统的cpu核数和颗数?7. 平均负载案例分析实战7.1 场景一&…

Pandas 之 merge

merge的作用: merge函数在Python的pandas库中的作用是用来合并两个或多个DataFrame数据表,依据指定的一个或多个键(通常是列名)进行连接操作[1]。 merge函数可以有多种连接类型(如内连接inner、左连接left、右连接ri…

SmartX 携手 openGauss 社区发布联合方案评测与性能最佳实践 | 附优化方法与测试数据

近日,北京志凌海纳科技有限公司(以下简称 “SmartX”)携手 openGauss 社区完成了 openGauss 数据库基于 SmartX 超融合平台(SMTX OS)和 SmartX 分布式存储平台(SMTX ZBS)的性能测试和调优。 结…

13. C++类使用方式

【类】 C语言使用函数将程序代码模块化,C通过类将代码进一步模块化,类用于将实现一种功能的全局数据、以及操作这些数据的函数集中存储在一起,同时可以设置类成员的访问权限,禁止外部代码使用和篡改本类成员,类成员访…

Vue开发实例(六)实现左侧菜单导航

左侧菜单导航 一、一级菜单二、二级菜单三、三级菜单1、加入相关事件 四、菜单点击跳转1. 创建新页面2. 配置路由3. 菜单中加入路由配置4、处理默认的Main窗口为空的情况 五、动态左侧菜单导航1、动态实现一级菜单2、动态实现二级菜单 一、一级菜单 在之前的Aside.vue中去实现…

Dynamo初学尝试梳理(五)-代码块上

“学而时习之,不亦说乎”,今天接着来,稍微提高点难度(高手直接忽略就行)。 代码块(Code Block),是 dynamo 中可以直接输入 DesignScript 的节点。可以通过双击鼠标左键,快…

JAVA WEB案例-登录校验-日志记录

一 前言 在现代社会中,随着互联网的快速发展,WEB应用的安全性问题变得越来越突出。作为一名程序员,我们不仅要注重WEB应用的功能实现,还需要重视安全性问题。在实际开发中,登录校验是非常重要的安全措施,能…

使用mapbox navigation搭建一个安卓导航 示例

一.代码示例地址: https://github.com/mapbox/mapbox-navigation-android-examples/tree/main 二. 具体步骤: git clone gitgithub.com:mapbox/mapbox-navigation-android-examples.git Go to app/src/main/res/values Look for mapbox_access_token.…

[R] Underline your idea with ggplot2

Preview: # 介绍:之前的教程中,我们学习了如何使条形图或直方图看起来更好 比如: 1. How to select a graph calibrate the geom part 2. How to select variables calibrate the aes part 3. How to add a title calibrate the labs …

Golang各版本的GC详解

go v1.3的标记清除法 清除的第一步:stw将可达对象标记删除未被标记对象 go v1.5三色标记法 从根节点出发,将下一个节点遍历为灰色,放入灰色集合中遍历灰色节点集合,把灰色能到达的节点标记为灰色,把自身标记为黑色&a…

安全增强型 Linux

书接上篇 一查看selinux状态 SELinux的状态: enforcing:强制,每个受限的进程都必然受限 permissive:允许,每个受限的进程违规操作不会被禁止,但会被记录于审计日志 disabled:禁用 相关命令…

SystemVerilog Support

介绍 AMD Vivado™合成支持可以合成的SystemVerilog RTL的子集。这个以下部分介绍了这些数据类型。 针对特定文件的SystemVerilog 默认情况下,Vivado合成工具使用Verilog 2005语法编译*.v文件和*.sv文件使用SystemVerilog语法。要在Vivado IDE中将SystemVerilog作…