欢迎光临
我们一直在努力

FreeRTOS 优先级继承:一文搞懂优先级翻转与互斥量

一句话总结

优先级继承就是:高优先级任务等锁时,把“持锁的低优先级任务”临时提升到高优先级,让它快点跑完、快点放锁。


1. 先看问题:优先级翻转

假设有三个任务,优先级:

H:高优先级
M:中优先级
L:低优先级

H 和 L 都要用同一个锁,M 不用锁,但很吃 CPU。

正常情况:

  • L 先拿到锁,进入临界区。
  • H 就绪,抢占 CPU,尝试拿锁,失败,只能等。
  • M 就绪,因为 M 优先级比 L 高,所以 M 抢占 L。
  • L 被 M 挤走,无法继续执行,也就无法释放锁。
  • H 只能等 M 跑完,再等 L 跑完,最后才能拿锁。
  • 结果:高优先级的 H 被中优先级的 M 间接拖住了。

    这就是经典的 优先级翻转。


    2. 优先级继承怎么解决?

    还是上面的场景,但这次 H 和 L 用的是 互斥量 Mutex。

    当 H 尝试拿锁失败、进入阻塞时,FreeRTOS 发现:

    锁在 L 手里,而 H 优先级比 L 高。

    于是系统做一件事:

    把 L 的优先级临时提升到 H 的优先级。

    这样 M 就绪后,优先级比“临时提升后的 L”低,无法抢占 L。

    L 就能尽快执行完临界区,释放锁。

    释放锁后:

    • L 恢复原来的低优先级;
    • H 拿到锁继续运行。

    一句话:谁持锁,谁继承;锁一放,优先级还回去。


    3. FreeRTOS 里怎么用?

    只有 互斥量 Mutex 支持优先级继承。

    二值信号量不支持,因为它不记录“谁持有锁”。

    创建互斥量:

    SemaphoreHandle_t xMutex;

    xMutex = xSemaphoreCreateMutex();

    使用:

    xSemaphoreTake(xMutex, portMAX_DELAY);

    // 访问共享资源,临界区尽量短

    xSemaphoreGive(xMutex);

    注意:需要确保 FreeRTOSConfig.h 中:

    #define configUSE_MUTEXES 1


    4. 代码示例

    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"

    SemaphoreHandle_t xMutex;

    void LowTask(void *pv)
    {
    while (1) {
    xSemaphoreTake(xMutex, portMAX_DELAY);

    // 模拟访问共享资源
    // 注意:持锁时不要 vTaskDelay,不要长时间阻塞
    volatile int i;
    for (i = 0; i < 1000000; i++) {
    // busy wait
    }

    xSemaphoreGive(xMutex);
    vTaskDelay(pdMS_TO_TICKS(100));
    }
    }

    void HighTask(void *pv)
    {
    while (1) {
    xSemaphoreTake(xMutex, portMAX_DELAY);

    // 访问共享资源

    xSemaphoreGive(xMutex);
    vTaskDelay(pdMS_TO_TICKS(500));
    }
    }

    void MidTask(void *pv)
    {
    while (1) {
    // 不碰锁,但很占 CPU
    volatile int i;
    for (i = 0; i < 500000; i++) {
    // busy wait
    }
    vTaskDelay(pdMS_TO_TICKS(10));
    }
    }

    void app_main(void)
    {
    xMutex = xSemaphoreCreateMutex();

    xTaskCreate(LowTask, "Low", 128, NULL, 1, NULL);
    xTaskCreate(MidTask, "Mid", 128, NULL, 2, NULL);
    xTaskCreate(HighTask, "High", 128, NULL, 3, NULL);
    }

    如果你把 xSemaphoreCreateMutex() 换成 xSemaphoreCreateBinary(),优先级继承就没了,H 可能被 M 拖很久。


    5. 常见坑

  • 用二值信号量做互斥
    二值信号量适合同步,比如中断通知任务;做互斥要用 Mutex。

  • 持锁时调用 vTaskDelay 或阻塞
    优先级继承也救不了。你持锁却主动不运行,别人还是得等。

  • 在中断里用 Mutex
    Mutex 不能在中断中使用。中断和任务共享资源,优先用临界区、关中断,或者中断只发通知,任务里处理。

  • 以为优先级继承能防死锁
    不能。两个任务互相等对方的锁,照样死锁。

  • 忘了开 configUSE_MUTEXES
    没开的话,互斥量相关功能用不了。


  • 6. 总结

    点说明
    解决问题 优先级翻转
    核心动作 高优先级任务等锁时,临时提升持锁低优先级任务的优先级
    谁支持 FreeRTOS 的 Mutex
    谁不支持 二值信号量、计数信号量
    什么时候恢复 释放锁后恢复原优先级
    注意 不能防死锁,不能在中断用,临界区要短

    记住一句话:

    高任务等锁,低任务临时升;低任务放锁,优先级还原。

    理解了这个,FreeRTOS 里的优先级继承基本就通了。

    赞(0)
    未经允许不得转载:171主机测评 » FreeRTOS 优先级继承:一文搞懂优先级翻转与互斥量
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址