FreeRTOS 中的递归互斥信号量(Recursive Mutex)是一种允许同一任务多次获取并持有的互斥量,专为解决任务需要重复访问同一共享资源的场景设计(例如递归函数或嵌套调用)。其核心特性是支持优先级继承,且任务需等量释放(获取多少次,就必须释放多少次)。以下是递归互斥信号量相关的 API 函数详解,包括动态和静态创建两种方式。
1. 递归互斥量的核心特性
- 递归获取:同一任务可多次获取递归互斥量,不会导致死锁。
- 等量释放:必须调用
xSemaphoreGiveRecursive()
释放相同次数才能恢复信号量为可用状态。 - 优先级继承:与普通互斥量相同,支持优先级继承(需配置
configUSE_MUTEXES = 1
)。 - 不可跨任务释放:必须由持有递归互斥量的任务释放。
2. 动态创建递归互斥量
函数原型
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
- 功能:动态分配内存并初始化一个递归互斥量。
- 参数:无。
- 返回值:
- 成功:返回递归互斥量句柄(
SemaphoreHandle_t
)。 - 失败:返回
NULL
(内存不足时)。
- 成功:返回递归互斥量句柄(
- 特点:
- 初始状态为 可用(计数值为 1)。
- 需确保 FreeRTOS 堆内存足够(通过
configTOTAL_HEAP_SIZE
配置)。 - 必须使用
xSemaphoreTakeRecursive()
和xSemaphoreGiveRecursive()
操作。
示例代码
SemaphoreHandle_t xRecursiveMutex = NULL;
void vTaskExample(void) {
// 动态创建递归互斥量
xRecursiveMutex = xSemaphoreCreateRecursiveMutex();
if (xRecursiveMutex != NULL) {
// 创建成功
}
}
3. 静态创建递归互斥量
函数原型
SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic(StaticSemaphore_t *pxMutexBuffer);
- 功能:使用用户预先分配的静态内存初始化递归互斥量。
- 参数:
pxMutexBuffer
:指向StaticSemaphore_t
类型变量的指针,用于存储互斥量内部数据。
- 返回值:
- 成功:返回递归互斥量句柄。
- 失败:返回
NULL
(参数无效时)。
- 特点:
- 初始状态为 可用。
- 需预先分配
StaticSemaphore_t
结构体内存(全局或静态变量)。
示例代码
StaticSemaphore_t xRecursiveMutexBuffer;
SemaphoreHandle_t xRecursiveMutex = NULL;
void vTaskExample(void) {
// 静态创建递归互斥量
xRecursiveMutex = xSemaphoreCreateRecursiveMutexStatic(&xRecursiveMutexBuffer);
if (xRecursiveMutex != NULL) {
// 创建成功
}
}
4. 递归获取(Take)互斥量
函数原型
BaseType_t xSemaphoreTakeRecursive(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait // 阻塞时间
);
- 功能:递归获取互斥量。若已被其他任务占用,任务将阻塞等待;若已被当前任务占用,直接增加持有计数。
- 参数:
xSemaphore
:递归互斥量句柄。xTicksToWait
:阻塞时间(单位:系统节拍)。portMAX_DELAY
表示无限等待(需配置INCLUDE_vTaskSuspend=1
)。
- 返回值:
pdTRUE
:成功获取互斥量。pdFALSE
:超时或参数无效。
使用示例
void vNestedFunction(SemaphoreHandle_t xMutex) {
// 递归获取互斥量(嵌套调用)
if (xSemaphoreTakeRecursive(xMutex, portMAX_DELAY) == pdTRUE) {
// 安全访问共享资源
// ...
xSemaphoreGiveRecursive(xMutex); // 递归释放
}
}
void vTaskEntry(void *pvParameters) {
if (xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
// 调用嵌套函数(内部再次获取同一互斥量)
vNestedFunction(xRecursiveMutex);
xSemaphoreGiveRecursive(xRecursiveMutex); // 释放最外层获取
}
}
5. 递归释放(Give)互斥量
函数原型
BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xSemaphore);
- 功能:递归释放互斥量,减少持有计数。当计数归零时,其他任务可获取。
- 参数:
xSemaphore
:递归互斥量句柄。
- 返回值:
pdPASS
:释放成功。pdFAIL
:释放失败(当前任务未持有该互斥量,或计数已归零)。
6. 删除递归互斥量
函数原型
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);
- 功能:删除递归互斥量并释放内存(仅对动态创建的互斥量有效)。
- 参数:
xSemaphore
:递归互斥量句柄。
- 注意:
- 删除前需确保当前任务已完全释放互斥量(计数归零)。
使用场景示例
嵌套函数访问共享资源
SemaphoreHandle_t xRecursiveMutex = xSemaphoreCreateRecursiveMutex();
void vInnerFunction(void) {
if (xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
// 嵌套层级的资源访问
// ...
xSemaphoreGiveRecursive(xRecursiveMutex);
}
}
void vOuterFunction(void) {
if (xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY) == pdTRUE) {
vInnerFunction(); // 内部再次获取同一互斥量
// ...
xSemaphoreGiveRecursive(xRecursiveMutex);
}
}
注意事项
-
严格匹配获取与释放次数
若任务获取递归互斥量 N 次,必须释放 N 次 才能真正释放资源。否则:- 未完全释放:其他任务无法获取。
- 过度释放:
xSemaphoreGiveRecursive()
返回pdFAIL
。
-
禁止跨任务操作
必须由持有递归互斥量的任务释放,不可跨任务调用xSemaphoreGiveRecursive()
。 -
优先级继承机制
递归互斥量与普通互斥量共享优先级继承逻辑,当高优先级任务等待时,持有互斥量的任务(即使处于递归获取状态)会临时提升优先级。 -
中断中不可使用
递归互斥量不能在中断服务程序(ISR)中操作(无FromISR
版本)。 -
死锁风险
若任务在递归获取后因逻辑错误未完全释放,会导致资源永久锁死。建议使用代码审查或静态分析工具检查释放次数。
递归互斥量 vs 普通互斥量
特性 | 递归互斥量 | 普通互斥量 |
---|---|---|
重复获取 | 允许同一任务多次获取 | 同一任务重复获取会导致死锁 |
释放次数 | 需等量释放 | 只需释放一次 |
适用场景 | 嵌套函数、递归调用 | 单次资源访问 |
API 操作 | TakeRecursive /GiveRecursive | Take /Give |
通过合理使用递归互斥量,可以简化嵌套资源访问的代码逻辑,避免因重复获取同一资源导致的死锁问题。