You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FreeRTOS如何查询当前持有二进制信号量的任务

结论

FreeRTOS 原生没有直接提供查询二进制信号量持有任务的公共API,这和二进制信号量的设计定位直接相关:二进制信号量本身没有要求「take和give操作必须在同一个任务中执行」,因此内核默认不会记录它的持有者信息。


可行方案

方案1:改用互斥量(符合原生设计的最优方案)

如果你的场景不需要在中断中操作该同步对象,完全可以把myBinarySemaphore替换为互斥量(通过xSemaphoreCreateMutex()创建)。互斥量为了实现优先级继承机制,内部默认会记录持有者任务的TCB指针,你可以通过访问内核内部结构体拿到持有者信息。

注意:该方法用到的结构体成员属于内核内部实现,不同版本FreeRTOS的结构体定义可能有差异,需要和你使用的内核版本匹配。

代码示例:

// 必须引入内核内部头文件,才能访问信号量、任务的内部结构体
#include "task.h"
#include "semphr.h"

// 自定义获取互斥量持有者的函数
TaskHandle_t xGetMutexOwner(SemaphoreHandle_t xMutex)
{
    if(xMutex == NULL) return NULL;
    // 互斥量内部的pxMutexHolder成员就是持有者任务的TCB指针
    return ((Semaphore_t *)xMutex)->pxMutexHolder;
}

void taskC(void *pvParameters)
{
    // 注意:创建taskA、taskB时需要提前保存它们的任务句柄,不能直接用函数名比较
    extern TaskHandle_t xTaskAHandle, xTaskBHandle;
    for(;;)
    {
        TaskHandle_t xOwner = xGetMutexOwner(myMutex);
        if(xOwner == xTaskAHandle)
            printf("taskA has the mutex\n");
        else if (xOwner == xTaskBHandle)
            printf("taskB has the mutex\n");
        else
            printf("No task holds the mutex\n");
        // 必须加延时,否则taskC会占满CPU,其他任务无法拿到执行权
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

方案2:必须用二进制信号量的场景,自行封装接口

如果你确实需要使用二进制信号量(比如需要在中断中执行give操作),只需要对信号量的take、give接口做一层简单封装,用全局变量记录持有者即可,实现成本很低:

// 全局变量记录信号量持有者
TaskHandle_t xBinarySemHolder = NULL;

// 封装take接口
BaseType_t xMyBinarySemTake(SemaphoreHandle_t xSem, TickType_t xTicksToWait)
{
    BaseType_t xRet = xSemaphoreTake(xSem, xTicksToWait);
    if(xRet == pdTRUE)
    {
        xBinarySemHolder = xTaskGetCurrentTaskHandle();
    }
    return xRet;
}

// 封装任务上下文的give接口
BaseType_t xMyBinarySemGive(SemaphoreHandle_t xSem)
{
    BaseType_t xRet = xSemaphoreGive(xSem);
    if(xRet == pdTRUE)
    {
        xBinarySemHolder = NULL;
    }
    return xRet;
}

// 封装中断上下文的give接口(如果需要)
BaseType_t xMyBinarySemGiveFromISR(SemaphoreHandle_t xSem, BaseType_t *pxHigherPriorityTaskWoken)
{
    BaseType_t xRet = xSemaphoreGiveFromISR(xSem, pxHigherPriorityTaskWoken);
    if(xRet == pdTRUE)
    {
        xBinarySemHolder = NULL;
    }
    return xRet;
}

之后把taskA、taskB中原来的xSemaphoreTake、xSemaphoreGive替换为上面的封装接口,taskC直接读取xBinarySemHolder和任务句柄比较即可。


注意事项

查询信号量持有者的逻辑本身存在天然竞态:你查询到的结果仅代表查询瞬间的状态,下一刻持有者可能就已经释放了信号量,不要在生产代码的核心逻辑中强依赖该查询结果。

内容的提问来源于stack exchange,提问作者PLB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:09:00