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 )//阻塞时间
  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS 信号量是一种同步机制,用于在任务之间共享资源。信号量是一个计数器,用于记录可用资源的数量。当一个任务需要使用共享资源时,它会尝试获取信号量。如果信号量计数器的值为正,则任务可以获取该信号量,并将计数器减一。如果信号量的计数器值为零,则任务将进入等待状态,直到有其他任务释放信号量FreeRTOS 中的信号量有两种类型:二进制信号量和计数信号量。 1. 二进制信号量:二进制信号量只有两种状态:可用和不可用。当计数器值为 0 时,信号量不可用;当计数器值为 1 时,信号量可用。 2. 计数信号量:计数信号量允许计数器的值大于 1。当计数器值为 0 时,信号量不可用;当计数器值大于 0 时,信号量可用。 使用 FreeRTOS 信号量需要调用以下函数: 1. `xSemaphoreCreateBinary()`: 创建二进制信号量。 2. `xSemaphoreCreateCounting()`: 创建计数信号量。 3. `xSemaphoreGive()`: 释放信号量。 4. `xSemaphoreTake()`: 获取信号量。 在使用信号量时,需要注意以下几点: 1. 释放信号量的任务必须先获取该信号量。 2. 在获取信号量时,可以设置超时时间,避免任务一直等待。 3. 在使用计数信号量时,需要注意计数器的上限,避免产生资源浪费或死锁的情况。 4. 在使用二进制信号量时,需要注意任务的优先级,避免优先级反转导致的问题。 总之,FreeRTOS 信号量是一种非常实用的同步机制,可以在多任务系统中解决资源共享的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值