【FreeRTOS】详细讲解FreeRTOS中信号量并通过具体示例讲述其用法

314 阅读6分钟

#金石计划征文活动

   在裸机开发中,大家是不是经常使用标志位锁机制计数值等来判断某段程序是否应该执行呢?其实此处的信号量与之功能上类似。

信号量

  信号量(Semaphore),是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,信号量可以为各个需要同步或互斥的任务之间实现临界资源的保护。   信号量的分类:

  • 二值信号量   二值信号量,可认为其是长度为1的队列,其既可以用于临界资源访问,也可以用于同步功能。   当其用于临界资源访问时,与互斥信号量类似。   当其作为用于同步时,常用于中断与任务任务与任务间的同步,其处理类似裸机开发时,改变中断中某二值变量(True/False)导致在while(1)循环中轮询不同的任务。

二值信号量访问示意图
  • 计数信号量   如果说二值信号量的队列长度为1,那么计数信号量的队列长度就是一个大于0的值。   计数信号量,用于计数,常用于事件计数资源管理。在创建使用时,先定义计数信号量最大值,然后每次事件或中断发生时,就释放一个信号量(也就是可用信号量减1),等到事件或中断处理完毕后归还一个信号量(也就是可用信号量加1)。当可用信号量的值等于最大信号量时,事件或中断就无法访问信号量,只有等可用信号量值大于0时才能访问。   这就类似,某停车场中一共有100个停车位,每次进入一辆车停放在该地,那么停车场内的可用车位就会以1为被减数持续减少;每当有一辆车驶出该停车场时,停车场内可用车位就会以1位被加数持续增加。倘若使出车辆未增加可用车位数量,那么可用车位数就会持续减少,一旦可用车位的数量小于0,即使停车场让小车进入,那么该车也没处可放,因此,每使出一辆车,可用车位数值就要加1(归还可用信号量)。
以停车类比计数信号量示意图
  • 互斥信号量   互斥信号量其实是特殊的二值信号量,其具有优先级继承机制(其实质就是提高任务优先级),从而使它更适用于简单互锁,也就是保护临界资源,常见的互斥信号量有互斥锁。   当互斥信号量值域为01时,互斥信号量在功能上就类似于二值信号量,因此互斥信号量可以说是特殊的二值信号量。
  • 递归信号量   递归信号量,从字面意思上来看,就是可以重复获取调用的。按照信号量的特性,每使用一个可用信号量数量就会减少一个;但递归就是一个特殊情况,对于已经获取递归互斥量的任务可以重复获取该递归互斥量,该任务拥有递归信号量的所有权。   需要注意的是:任务获取递归信号量的次数要与返还次数相同在本任务返回递归信号量前信号量都处于无效状态,即其他任务无法访问,此时只有递归信号量所持有的任务才能获取与释放。

相关函数解析

下面是对相关函数参数的详细解释:

1. 创建信号量

xSemaphoreCreateBinary()
  • 功能:创建一个二值信号量。
  • 参数:无参数。
  • 返回值:返回一个SemaphoreHandle_t类型的句柄,用于后续的信号量操作。
xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
  • 功能:创建一个计数信号量。
  • 参数
    • uxMaxCount:信号量的最大计数,表示信号量可以计数的最大值。
    • uxInitialCount:信号量的初始计数,表示信号量创建时的可用数量。
  • 返回值:返回一个SemaphoreHandle_t类型的句柄,用于后续的信号量操作。

2. 等待(获取)信号量

xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait )
  • 功能:等待(获取)信号量。
  • 参数
    • xSemaphore:信号量的句柄,由xSemaphoreCreateBinary()xSemaphoreCreateCounting()返回。
    • xTicksToWait:等待信号量的时间,单位为系统时钟节拍(tick)。如果设置为portMAX_DELAY,则表示无限等待。
  • 返回值:如果成功获取信号量,返回pdTRUE;如果超时或失败,返回pdFALSE

3. 释放信号量

xSemaphoreGive( SemaphoreHandle_t xSemaphore )
  • 功能:释放信号量。
  • 参数
    • xSemaphore:信号量的句柄,由xSemaphoreCreateBinary()xSemaphoreCreateCounting()返回。
  • 返回值:如果成功释放信号量,返回pdTRUE;如果失败,返回pdFALSE

4. 递归信号量的获取和释放

xSemaphoreTakeRecursive( SemaphoreHandle_t xMutex, TickType_t xTicksToWait )
  • 功能:递归获取信号量。
  • 参数
    • xMutex:递归信号量的句柄,必须先通过xSemaphoreCreateRecursiveMutex()创建。
    • xTicksToWait:等待信号量的时间,单位为系统时钟节拍(tick)。如果设置为portMAX_DELAY,则表示无限等待。
  • 返回值:如果成功获取信号量,返回pdTRUE;如果超时或失败,返回pdFALSE
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
  • 功能:递归释放信号量。
  • 参数
    • xMutex:递归信号量的句柄,必须先通过xSemaphoreCreateRecursiveMutex()创建。
  • 返回值:如果成功释放信号量,返回pdTRUE;如果失败,返回pdFALSE

5. 信号量的状态查询

xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex )
  • 功能:查询互斥信号量的持有者。
  • 参数
    • xMutex:互斥信号量的句柄。
  • 返回值:返回持有互斥信号量的任务的句柄,如果没有任务持有或信号量未被创建为互斥信号量,返回NULL

6. 信号量的删除

vSemaphoreDelete( SemaphoreHandle_t xSemaphore )
  • 功能:删除信号量,释放相关资源。
  • 参数
    • xSemaphore:要删除的信号量的句柄。
  • 返回值:无返回值,这是一个空操作函数。

这些函数是FreeRTOS中信号量操作的基本接口,通过这些接口可以实现任务间的同步和互斥。在实际使用中,需要根据具体的应用场景选择合适的信号量类型和操作。

注意事项

  • 信号量超时:在等待信号量时,可以指定超时时间,如果在超时时间内信号量未变为可用,函数将返回失败。
  • 优先级反转问题:在使用信号量时,可能会出现优先级反转的问题,需要通过优先级继承等机制来解决。
  • 信号量滥用:过度使用信号量可能会导致系统性能下降,应合理设计同步机制。