递归互斥信号量相关的 API 函数详解

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);
    }
}

注意事项

  1. 严格匹配获取与释放次数
    若任务获取递归互斥量 N 次,必须释放 N 次 才能真正释放资源。否则:

    • 未完全释放:其他任务无法获取。
    • 过度释放:xSemaphoreGiveRecursive() 返回 pdFAIL
  2. 禁止跨任务操作
    必须由持有递归互斥量的任务释放,不可跨任务调用 xSemaphoreGiveRecursive()

  3. 优先级继承机制
    递归互斥量与普通互斥量共享优先级继承逻辑,当高优先级任务等待时,持有互斥量的任务(即使处于递归获取状态)会临时提升优先级。

  4. 中断中不可使用
    递归互斥量不能在中断服务程序(ISR)中操作(无 FromISR 版本)。

  5. 死锁风险
    若任务在递归获取后因逻辑错误未完全释放,会导致资源永久锁死。建议使用代码审查或静态分析工具检查释放次数。


递归互斥量 vs 普通互斥量

特性递归互斥量普通互斥量
重复获取允许同一任务多次获取同一任务重复获取会导致死锁
释放次数需等量释放只需释放一次
适用场景嵌套函数、递归调用单次资源访问
API 操作TakeRecursive/GiveRecursiveTake/Give

通过合理使用递归互斥量,可以简化嵌套资源访问的代码逻辑,避免因重复获取同一资源导致的死锁问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九层指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值