IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> FreeRTOS的信号量 -> 正文阅读

[嵌入式]FreeRTOS的信号量

一、信号量简介

信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量。不同的信号量其应用场景不同,但有些应用场景是可以互换着使用的。

二、二值信号量

  1. 简介

二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的。二值信号量通常用于互斥访问或任务同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问。(什么是互斥访问)

信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一个信号量上的话那么优先级最高的哪个任务优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。

二值信号量使用案例

在以前,使用DTU模块的时候都是创建一个任务,在while中通过一直循环查询4G模块是否有发送消息过来,当有数据过来的时候再进行处理,这样的方式浪费CPU的资源。最理想的方法就是当没有网络数据的时候网络任务就进入阻塞态,把 CPU 让给其他的任务,当有数据的时候网络任务才去执行。使用了二值信号量,在中断服务函数只需要释放信号量,而在网络处理函数中就一直请求二值信号量,当请求到了,就可以去处理。假如一个中断只通知一个任务的话,可以使用任务通知功能来替代二值信号量,而且使用任务通知的话速度更快,代码量更少。

运行示例图:
在这里插入图片描述

  1. 相关API函数
  • 二值信号量的创建
    信号量创建
    新版动态创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary( void ) //创建成功的二值信号量的句柄。否则为NULL
  • 二值信号量的释放
    释放信号量的函数

函数 xSemaphoreGive()
此函数用于释放二值信号量、计数型信号量或互斥信号量,此函数是一个宏,真正释放信
号量的过程是由函数 xQueueGenericSend()来完成的

BaseType_t xSemaphoreGive( xSemaphore )//返回pdPASS释放信号量。
										//返回errQUEUE_FULL: 释放信号量失败。

中断级别

BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,	//要获取的信号量句柄
 								BaseType_t * pxHigherPriorityTaskWoken)
 								/**标记退出此函数以后是否进行任务切换,这个变量的值由这
								三个函数来设置的,用户不用进行设置,用户只需要提供一
								个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退
								出中断服务函数之前一定要进行一次任务切换。**/

//根据pxHigherPriorityTaskWoken进行切换任务的函数
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
  • 获取信号量
    获取信号量
    任务函数中
//pdTRUE: 获取信号量成功。
//pdFALSE: 超时,获取信号量失败。
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,//要获取的信号量句柄
						 TickType_t xBlockTime)		//阻塞时间

中断函数中

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, //要获取的信号量句柄
 								BaseType_t * pxHigherPriorityTaskWoken)
 								/**标记退出此函数以后是否进行任务切换,这个变量的值由这
								三个函数来设置的,用户不用进行设置,用户只需要提供一
								个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退
								出中断服务函数之前一定要进行一次任务切换。**/

//根据pxHigherPriorityTaskWoken进行切换任务的函数
portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);

三、计数型信号量

计数型信号量用于事件计数和资源管理。

  1. 事件计数

在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量(信号量计数值减一,信号量值就是队列结构体成员变量uxMessagesWaiting)来处理事件。在这种场合中创建的计数型信号量初始计数值为 0。

  1. 资源管理

在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。

创建计数型信号量的函数
动态方法创建

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount,//计数信号量最大计数值
										   UBaseType_t uxInitialCount )//计数信号量初始值

静态方法创建

SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, //计数信号量最大计数值
												  UBaseType_t uxInitialCount,//初始值 
												  StaticSemaphore_t * pxSemaphoreBuffer )
												  //指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

发送信号量和获取信号量和二值信号量的函数相同

四、互斥信号量

1、互斥信号量的应用场景------------>>优先级翻转

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

运行过程如下图所示:
运行示意图
(1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。
(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
(3) 任务 L 获得信号量并开始使用该共享资源。
(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
(5) 任务 H 开始运行。
(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务
L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。
(7) 任务 L 继续运行。
(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务
L 的 CPU 使用权。
(9) 任务 M 处理该处理的事。
(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。
(11) 任务 L 继续运行。
(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高
优先级的任务在等待这个信号量,故内核做任务切换。
(13) 任务 H 得到该信号量并接着运行。

FreeRTOS是一个实时操作系统,优先级顺序是 H>M>L,可是实际运行效果却是 M>L>H,这就会导致一个高优先级任务在等待一个低优先级任务,而低优先级任务却无法执行类似死锁的情形发生。

2、优先级继承

优先级继承就是为了解决优先级反转问题而提出的一种优化机制。其大致原理是让低优先级任务在获得互斥信号量的时候(如果有高优先级的线程也需要使用该互斥信号量时),临时提升其优先级。以前其能更快的执行并释放同步资源。释放同步资源后再恢复其原来的优先级。

3、互斥信号量的使用

互斥信号量具有优先级继承,优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。
互斥信号了不能用于中断中,原因如下:

  • 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态
  • 中断中调用互斥量,会打破优先级继承。

创建互斥信号量
创建互斥信号量

/** 返回值
**	NULL: 互斥信号量创建失败。
**	其他值: 创建成功的互斥信号量的句柄。
**/
SemaphoreHandle_t xSemaphoreCreateMutex( void )

SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )
//pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体

五、递归信号量

在互斥信号量中,已经获取了互斥信号量的任务就不能再次获取这个互斥信号量。递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,而且次数不限!
一个任务使用函数 xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。

1、创建递归信号量

创建递归信号量

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )//动态方法创建

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )//静态方法创建
//pxMutexBuffer:此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

2、获取和获取递归信号量

xSemaphoreGiveRecursive( xMutex )//递归互斥信号量句柄

xSemaphoreTakeRecursive( xMutex, //递归互斥信号量句柄
						xBlockTime )//阻塞时间
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:44:32  更:2021-09-24 10:45:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:53:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码