在裸机开发中,大家是不是经常使用标志位、锁机制、计数值等来判断某段程序是否应该执行呢?其实此处的信号量与之功能上类似。
信号量
信号量(Semaphore),是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,信号量可以为各个需要同步或互斥的任务之间实现临界资源的保护。 信号量的分类:
- 二值信号量
二值信号量,可认为其是长度为1的队列,其既可以用于临界资源访问,也可以用于同步功能。
当其用于临界资源访问时,与互斥信号量类似。
当其作为用于同步时,常用于中断与任务、任务与任务间的同步,其处理类似裸机开发时,改变中断中某二值变量(
True/False
)导致在while(1)
循环中轮询不同的任务。
二值信号量访问示意图
- 计数信号量 如果说二值信号量的队列长度为1,那么计数信号量的队列长度就是一个大于0的值。 计数信号量,用于计数,常用于事件计数与资源管理。在创建使用时,先定义计数信号量最大值,然后每次事件或中断发生时,就释放一个信号量(也就是可用信号量减1),等到事件或中断处理完毕后归还一个信号量(也就是可用信号量加1)。当可用信号量的值等于最大信号量时,事件或中断就无法访问信号量,只有等可用信号量值大于0时才能访问。 这就类似,某停车场中一共有100个停车位,每次进入一辆车停放在该地,那么停车场内的可用车位就会以1为被减数持续减少;每当有一辆车驶出该停车场时,停车场内可用车位就会以1位被加数持续增加。倘若使出车辆未增加可用车位数量,那么可用车位数就会持续减少,一旦可用车位的数量小于0,即使停车场让小车进入,那么该车也没处可放,因此,每使出一辆车,可用车位数值就要加1(归还可用信号量)。
- 互斥信号量
互斥信号量其实是特殊的二值信号量,其具有优先级继承机制(其实质就是提高任务优先级),从而使它更适用于简单互锁,也就是保护临界资源,常见的互斥信号量有互斥锁。
当互斥信号量值域为
0
与1
时,互斥信号量在功能上就类似于二值信号量,因此互斥信号量可以说是特殊的二值信号量。 - 递归信号量 递归信号量,从字面意思上来看,就是可以重复获取调用的。按照信号量的特性,每使用一个可用信号量数量就会减少一个;但递归就是一个特殊情况,对于已经获取递归互斥量的任务可以重复获取该递归互斥量,该任务拥有递归信号量的所有权。 需要注意的是:任务获取递归信号量的次数要与返还次数相同,在本任务返回递归信号量前信号量都处于无效状态,即其他任务无法访问,此时只有递归信号量所持有的任务才能获取与释放。
相关函数解析
下面是对相关函数参数的详细解释:
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中信号量操作的基本接口,通过这些接口可以实现任务间的同步和互斥。在实际使用中,需要根据具体的应用场景选择合适的信号量类型和操作。
注意事项
- 信号量超时:在等待信号量时,可以指定超时时间,如果在超时时间内信号量未变为可用,函数将返回失败。
- 优先级反转问题:在使用信号量时,可能会出现优先级反转的问题,需要通过优先级继承等机制来解决。
- 信号量滥用:过度使用信号量可能会导致系统性能下降,应合理设计同步机制。