FreeRTOS 第十六章 信号量

消息队列是信号量的底层实现逻辑。

信号量是通信机制。信号量可以代表资源,不为0则有资源,就可以提出申请,申请了资源后资源量(消息量)就会减1,释放了信号量,资源量(消息量)就会加1。当没有资源(消息)的时候,就阻塞,当资源为满的时候,当然可以申请,因为资源量一开始就是定义好的,而且一个任务获取了资源后还必须要释放资源操作,所以一个资源量定义为5个,当它当地可用资源为5个时,就不可能有任务还会释放资源,毕竟它没有获取资源。

二值信号量可以看作只有一个消息空间的消息队列,要么空要么满。

二值信号可用于同步功能或者临界资源访问,创建二值信号量设置为空。同步是指,假设需要任务2在任务1之后马上执行,那么就可以在任务1执行完了特定目标之后释放信号量(往消息队列发送一个消息,二值信号量(消息)变为1,可以获取信号量(消息),则可以解除等待读任务),而只需要在任务2函数中加个获取二值信号量函数而阻塞,当任务1释放信号量,其就会马上得到信号量被加入就序列表,如果优先级高,则直接运行。也可以实现中断后同步任务,中断只需要释放信号量,任务就获取这个信号量。

计数信号量看作长度等于总的信号量个数的消息队列。互斥信号量是特殊的二值信号量,创建是满的,也就是有1个信号。递归信号量是可以重复调用的信号量。

二值信号量创建。

因为信号量是依赖消息队列的,所以它们的控制块是一样的,只是个别成员意义不同。

//在消息队列中表示消息的个数,在信号量表示有效信号量的个数
volatile UBaseType_t uxMessagesWaiting;   
//用作消息时表示队列的长度,信号量表示最大的信号量的可用个数
	UBaseType_t uxLength;			
//用作消息队列表示消息空间的大小,信号量时为0,无须分配空间
	UBaseType_t uxItemSize;			//消息空间大小

创建二值信号量的动态创建函数 xSemaphoreCreateBinary(); 其返回一个BinarySem_Handle 句柄,它是通过一个宏展开的,

#define xSemaphoreCreateBinary() 
xQueueGenericCreate( ( UBaseType_t ) 1,
                semSEMAPHORE_QUEUE_ITEM_LENGTH, 
                 queueQUEUE_TYPE_BINARY_SEMAPHORE )

其实就是调用消息队列的创建函数,传入的第一个参数表示是队列长度1,第二个是消息空间大小0,第三个表示是创建二值信号量。以下是一些宏定义以在队列通用创建函数第三个参数使用。

#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 ) //递归

在二值信号量创建后,消息空间的大小是传入的0,就相当于消息队列只有一个消息队列TCB,所以消息是通过 uxMessagesWaiting这个来实现的。因为其默认为0,因此在别的任务获取信号量之前应该释放信号量,把uxMessagesWaiting加1。

计数信号的创建 

xSemaphoreCreateCounting( uxMaxCount,uxInitaialCount)

在创建时指定最大的信号量个数以及初始化信号量个数,传入的比如是5,5,表示最多容纳五个信号量,初始值信号也是5个。

查看源码可知,调用这个这个函数的时候,是把uxMaxCount作为通用队列创建的第一个参数也就是长度,第二个是0,第三个是表示创建计数信号量。该函数执行完后uxMessagesWaiting为0,因为其在消息队列创建是代表消息的个数,刚刚创建当然为0,然后把uxInitaialCount初始计数值赋给这个uxMessagesWaiting。可见,二值和计数信号量的底层实现都是依靠uxMessagesWaiting来计数的。注意,二值信号量和计数信号量是使用同一个句柄SemaphoreHandle_t,不能搞混了,句柄感觉都是空指针。

信号量删除函数

vSemaphoreDelete(),可用来删除互斥,二值,递归,计数信号量,通用的,假如有任务阻塞在列表中就不要删除了。其本质就是队列删除函数 vQueueDelete()

信号量释放函数

xSemaphoreGive(),其本质就是调用的消息队列的发送函数

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

在函数中只需要传入二值量的句柄就可以,而且阻塞时间是semGIVE_BLOCK_TIME,为0. 所以当释放信号时,如果uxMessagesWaiting为0,则其加一,并返回pdPASS,如果uxMessagesWaiting为1,表示队列已经满了,并且阻塞时间为0,就直接返回err_QUEUE_FULL。

当然也有在中断中使用的中断版本xSemaphoreGiveFromISR()

在中断中可释放二值信号量,计数信号量,不能释放互斥量。

该函数的本质是调用xQueueGiveFromISR(),消息队列的发送函数的中断版本。当然调用时其参数不止一个信号量句柄,当然还有老朋友pxHigherPriorityTaskWoken,当释信号量时,一些阻塞的任务就有可能被加入就绪,而且很有可能是一个高优先级的,所以在这个函数执行完之后把pxHigherPriorityTaskWoken设置为pdtrue,则表示要切换任务。

信号量的获取函数 xSemaphoreTake(),可获取二值量,计数信号量,互斥量

就是调用通用队列接收函数,传入参数,信号量的句柄和阻塞时间。获取到了则返回pdtrue,不能获取则加入阻塞列表,在阻塞时间内有信号量入队,则获得信号量,解除阻塞,如果这段时间过了还没有得到数据,也直接进入就绪列表,返回errQUEUE_EMPTY,如果阻塞时间设置为port_MAX_DELAY,就会一直阻塞,直到该信号量有效。

信号量的获取函数的中断版本 xSemaphoreTakeFromISR(),不怎么常用,毕竟中断常作为生产者。假如使用,它也需要传入信号量句柄和老朋友pxHigherPriorityTaskWoken。

注意:在信号量这块,可看到只有获取信号量函数xSemaphoreTake加了第二个参数,即阻塞时间,而信号量释放函数是默认阻塞时间为0,也就是如果信号量满了且没有其它任务获取信号量,是不能一直释放的,会马上返回err_QUEUE_FULL.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值