FreeRTOS学习 -- 7、信号量

本文介绍了FreeRTOS中的信号量类型(二值、计数型、互斥和递归互斥),包括它们的原理、API函数如xSemaphoreCreateBinary、xSemaphoreGive、xSemaphoreTake等的使用,以及优先级反转问题的讨论。通过实例演示了如何在任务中应用这些信号量进行资源管理和同步操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考博客:【学习FreeRTOS】第14章——FreeRTOS信号量_uxsemaphoregetcount函数用不了-优快云博客

1、什么是信号量?

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。

  • 信号量:用于传递状态(区别于队列传递消息)

  • 信号量的计数值都有限制:限定最大值。

  • 如果最大值被限定为1,那么它就是二值信号量;如果最大值不是1,它就是计数型信号量

 

计数值大于0表示有信号量资源,释放信号量是信号量计数值加一,获取信号量是计数值减一。

2、二值信号量

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

 

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

  • xSemaphoreCreateBinary():使用动态方式创建二值信号量
  • xSemaphoreCreateBinaryStatic():使用静态方式创建二值信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreGiveFromlSR():在中断中释放信号量
  • xSemaphoreTake():获取信号量
  • xSemaphoreTakeFromISR():在中断中获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值

 2.1 创建二值信号量xSemaphoreCreateBinary()

SemaphoreHandle_t   xSemaphoreCreateBinary(void) 
#define   			xSemaphoreCreateBinary(void)
					xQueueGenericCreate(1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

#define semSEMAPHORE_QUEUE_ITEM_LENGTH      ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_BASE                ( ( uint8_t ) 0U )	/* 队列 */
#define queueQUEUE_TYPE_SET                 ( ( uint8_t ) 0U )	/* 队列集 */
#define queueQUEUE_TYPE_MUTEX               ( ( uint8_t ) 1U )	/* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE  ( ( uint8_t ) 2U )	/* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE    ( ( uint8_t ) 3U )	/* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX     ( ( uint8_t ) 4U )	/* 递归互斥信号量 */

 2.2 释放信号量xSemaphoreGive()

BaseType_t	xSemaphoreGive(xSemaphore) 
#define   	xSemaphoreGive(xSemaphore)	
			xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK)
#define   	semGIVE_BLOCK_TIME	( ( TickType_t ) 0U )

  • 形参xSemaphore:要释放的信号量句柄
  • 返回值:pdPASS,释放信号量成功;errQUEUE_FULL,释放信号量失败
  • 注意:释放信号量相当于写队列,但是当计数最大时无法阻塞,返回错误

 2.3 获取信号量xSemaphoreTake()

同释放信号的API函数一样,不管是二值信号量、计数型信号量、互斥信号量都是用该函数获取信号量。

BaseType_t	xSemaphoreTake( xSemaphore, xBlockTime ) 
#define 	xSemaphoreTake( xSemaphore, xBlockTime )    
			xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
BaseType_t 	xQueueSemaphoreTake( 	QueueHandle_t xQueue,
                                	TickType_t xTicksToWait )
  • 形参xSemaphore:要获取的信号量句柄
  • 形参xBlockTime:阻塞时间
  • 返回值:pdTRUE,获取信号量成功;pdFALSE,超时,获取信号量失败
  • 注意:获取信号量相当于读队列,同样可以阻塞

 2.4 获取信号量计数值uxSemaphoreGetCount()

UBaseType_t 	uxSemaphoreGetCount(xSemaphore)
#define 		uxSemaphoreGetCount(xSemaphore) 
				uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
  • 形参xSemaphore:信号量句柄
  • 返回值:整数,当前信号量计数值的大小

 2.5 二值信号量实验

实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用
实验设计:将设计三个任务:start_task、task1、task2
start_task-用来创建task1和task2任务
task1-用于按键扫描,当检测到按键KEY0被按下时,释放二值信号量
task2-获取二值信号量,当成功获取后打印提示信息

 

3.计数型信号量

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

事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务
会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0。

资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目。

API函数:

  • xSemaphoreCreateCounting() :使用动态方式创建计数信号量
  • xSemaphoreCreateCountingStatic() :使用静态方式创建计数信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreGiveFromlSR():在中断中释放信号量
  • xSemaphoreTake():获取信号量
  • xSemaphoreTakeFromISR():在中断中获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值

3.1 创建计数信号量xSemaphoreCreateCounting()

#define	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )
		xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 
  • 形参uxMaxCount:计数值的最大值限定
  • 形参uxInitialCount:计数值的初始值
  • 返回值:NULL,创建失败;其他值,创建成功返回计数型信号量的句柄

释放和获取计数型信号量与二值信号量操作相同。

3.2 计数型信号量实验

实验目的:学习 FreeRTOS 的计数型信号量相关API函数的使用
实验设计:将设计三个任务:start_task、task1、task2
start_task-用来创建task1和task2任务
task1-用于按键扫描,当检测到按键KEY0被按下时,释放计数型信号量
task2-每过一秒获取一次计数型信号量,当成功获取后打印信号量计数值

 4、优先级反转操作

优先级反转即为高优先级的任务后执行,低优先级的任务先执行。

       优先级反转在抢占式内核中是十分常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。在使用二值信号量的时候,经常会遇到遇到优先级翻转的问题。

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

5、互斥信号量

       互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

 此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低(注意没有消除)
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响 注意,互斥信号量不能用于中断服务函数中,原因如下:

互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

互斥信号量相关的API函数:

使用互斥信号量首先将宏configUSE_MUTEXES设置为1.

  • xSemaphoreCreateMutex() :使用动态方式创建互斥信号量
  • xSemaphoreCreateMutexStatic() :使用静态方式创建互斥信号量
  • xSemaphoreGive():释放信号量
  • xSemaphoreTake():获取信号量
  • uxSemaphoreGetCount():获取信号量的计数值
#define   xSemaphoreCreateMutex(void)      
          xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
  • 返回值:NULL,创建失败;其他值,创建成功返回计数型信号量的句柄
  • 注意:创建互斥信号量时,会主动释放一次信号量

6、递归互斥信号量

       归互斥信号量可以看作是特殊的互斥信号量,与互斥信号量不同的是,递归互斥信号量在被获取后,可以被其持有者重复获取,当然递归互斥信号量的持有者需要释放递归互斥信号量与之获取递归互斥信号量相同的次数,递归互斥信号量才算被释放。
递归互斥信号量与互斥信号量一样,也具备优先级继承机制,因此也不能在中断服务函数中使用递归互斥信号量。

7、队列、二值信号量、数值信号量、互斥信号量对比总结

  1. 二值信号量、计数信号量、互斥信号量的创建函数不同,但是释放和获取均相同,此外注意,互斥信号量在中断中无法使用,所有没有中断中释放/获取信号量
  2. 二值信号量、计数信号量、互斥信号量的创建函数不同,但是底层调用的都是同一个API(队列创建函数),只是内部机制不同、某些参数不同,创建的信号量储存结构只有结构体,没有队列项,依靠变量uxMessagesWaiting储存信号量信息
  3. 二值信号量、计数信号量、互斥信号量的释放信号量通用,底层调用与队列写入函数相同,只是参数不同
  4. 二值信号量、计数信号量、互斥信号量的获取信号量通用,底层调用与队列写入函数不相同(类似),无buf参数。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值