1. 递归互斥量的作用
递归互斥量允许 同一个任务多次获取同一个互斥锁,每次获取都会增加锁的持有计数,必须释放相同次数后才能完全解锁。这种机制专为以下场景设计:
- 嵌套函数调用:同一任务在不同层级的函数中需要重复访问受保护的共享资源。
- 递归算法:任务在执行递归逻辑时需要确保资源独占。
示例场景:
void funcA() {
xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); // 第一次获取
// 访问共享资源
funcB(); // 嵌套调用 funcB
xSemaphoreGiveRecursive(xMutex); // 释放
}
void funcB() {
xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); // 第二次获取(允许)
// 访问共享资源
xSemaphoreGiveRecursive(xMutex); // 释放
}
2. 递归互斥量的实现
FreeRTOS 的递归互斥量通过 持有计数(Hold Count) 和 持有任务标识(Holder Task) 实现:
- 持有计数:记录锁被当前任务获取的次数。
- 持有任务:仅允许持有锁的任务重复获取。
关键函数
函数 | 功能 |
---|---|
xSemaphoreCreateRecursiveMutex() | 创建递归互斥量 |
xSemaphoreTakeRecursive() | 递归获取锁(需配合递归互斥量) |
xSemaphoreGiveRecursive() | 递归释放锁 |
3. 使用方法
步骤 1:创建递归互斥量
#include "FreeRTOS.h"
#include "semphr.h"
SemaphoreHandle_t xRecursiveMutex;
void main() {
xRecursiveMutex = xSemaphoreCreateRecursiveMutex();
configASSERT(xRecursiveMutex != NULL); // 确保创建成功
}
步骤 2:递归获取与释放
void TaskFunction(void *pvParameters) {
while (1) {
// 第一次获取
if (xSemaphoreTakeRecursive(xRecursiveMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// 第二次获取(允许)
if (xSemaphoreTakeRecursive(xRecursiveMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// 访问共享资源
xSemaphoreGiveRecursive(xRecursiveMutex); // 释放第二次
}
xSemaphoreGiveRecursive(xRecursiveMutex); // 释放第一次
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
4. 注意事项
-
必须成对使用递归函数:
使用xSemaphoreTakeRecursive()
和xSemaphoreGiveRecursive()
,禁止混用普通互斥量函数(如xSemaphoreTake()
)。 -
优先级继承机制:
FreeRTOS 的递归互斥量支持优先级继承(需配置configUSE_MUTEXES = 1
),防止优先级反转。 -
资源泄漏风险:
若获取和释放次数不匹配,会导致锁无法释放,其他任务永久阻塞。// 错误示例:获取两次但只释放一次 xSemaphoreTakeRecursive(xMutex, ...); // Hold count = 1 xSemaphoreTakeRecursive(xMutex, ...); // Hold count = 2 xSemaphoreGiveRecursive(xMutex); // Hold count = 1 → 锁未完全释放!
-
死锁场景:
- 跨任务递归:任务A持有锁时,任务B尝试获取 → 触发阻塞。
- 非递归互斥量:普通互斥量被同一任务重复获取会导致立即死锁。
5. 递归互斥量 vs 普通互斥量
特性 | 递归互斥量 | 普通互斥量 |
---|---|---|
同一任务重复获取 | 允许(计数递增) | 导致死锁 |
释放要求 | 必须释放相同次数 | 只需释放一次 |
适用场景 | 嵌套/递归调用 | 单次获取释放 |
函数接口 | TakeRecursive /GiveRecursive | Take /Give |
6. 配置要求
在 FreeRTOSConfig.h
中确保启用以下宏定义:
#define configUSE_RECURSIVE_MUTEXES 1 // 启用递归互斥量
#define configUSE_MUTEXES 1 // 启用互斥量(依赖项)
7. 性能与资源开销
- 内存占用:每个递归互斥量比普通互斥量多占用约 4 字节(存储持有计数)。
- 时间开销:递归操作的时间复杂度为 O(1),但嵌套次数过多可能影响实时性。
通过合理使用递归互斥量,可以安全地在嵌套或递归代码中管理共享资源,但需严格遵守获取/释放的对称性以避免死锁。