FreeRTOS 优先级反转与优先级继承

1. 优先级反转

举个栗子:假设任务 A、B 都想使用串口,A 优先级比较低

  • 任务 A 获得了串口的互斥量
  • 任务 B 也想使用串口,它将会阻塞、等待 A 释放互斥量
  • 高优先级的任务,被低优先级的任务延迟,这被称为"优先级反转"(priority inversion)

如果涉及 3 个任务,可以让"优先级反转"的后果更加恶劣。

使用互斥量可以通过"优先级继承",可以很大程度解决"优先级反转"的问题这也是
FreeRTOS 中互斥量和二级制信号量的差别。

1.1 代码实例分析:

main 函数创建了 3 个任务:LPTask/MPTask/HPTask(低/中/高优先级任务)

/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;

int main(void)
{
    prvSetupHardware();
    /* 创建互斥量/二进制信号量 */
    xLock = xSemaphoreCreateBinary();

    if (xLock != NULL)
    {
        /* 创建 3 个任务: LP,MP,HP(低/中/高优先级任务)
         */
        xTaskCreate(vLPTask, "LPTask", 1000, NULL, 1, NULL);
        xTaskCreate(vMPTask, "MPTask", 1000, NULL, 2, NULL);
        xTaskCreate(vHPTask, "HPTask", 1000, NULL, 3, NULL);
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建互斥量/二进制信号量 */
    }
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

LPTask/MPTask/HPTask 三个任务的代码和运行过程如下图所示:

 过程:

  • A:HPTask 优先级最高,它最先运行。在这里故意打印,这样才可以观察到 flagHPTaskRun 的脉冲。
  • HP Delay:HPTask 阻塞
  • B:MPTask 开始运行。在这里故意打印,这样才可以观察到 flagMPTaskRun 的脉冲。
  • MP Delay:MPTask 阻塞
  • C:LPTask 开始运行,获得二进制信号量,然后故意打印很多字符
  • D:HP Delay 时间到,HPTask 恢复运行,它无法获得二进制信号量,一直阻塞等待
  • E:MP Delay 时间到,MPTask 恢复运行,它比 LPTask 优先级高,一直运行。导致 LPTask 无法运行,自然无法释放二进制信号量,于是 HPTask 用于无法运行。

总结:

  1. LPTask 先持有二进制信号量,但是 MPTask 抢占 LPTask,使得 LPTask 一直无法运行也就无法释放信号量
  2. 由于LPTask 无法释放信号量导致 HPTask 任务无法运行,优先级最高的 HPTask 竟然一直无法运行

程序运行的时序图如下:

2. 优先级继承

        优先级反转的问题在于,LPTask 低优先级任务获得了锁,但是它优先级太低而无法运行。如果能提升 LPTask 任务的优先级,让它能尽快运行、释放锁,"优先级反转"的问题不就解决了吗?

2.1 优先级继承案例
  • 假设持有互斥锁的是任务 A,如果更高优先级的任务 B 也尝试获得这个锁
  • 任务 B 说:你既然持有宝剑,又不给我,那就继承我的愿望吧,于是任务 A 就继承了任务 B 的优先级
  • 这就叫:优先级继承
  • 等任务 A 释放互斥锁时,它就恢复为原来的优先级
  • 互斥锁内部就实现了优先级的提升、恢复
2.2 代码实例分析:
/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;

int main(void)
{
    prvSetupHardware();
    /* 创建互斥量/二进制信号量 */
    //xLock = xSemaphoreCreateBinary( );
    xLock = xSemaphoreCreateMutex( );

    if (xLock != NULL)
    {
        /* 创建 3 个任务: LP,MP,HP(低/中/高优先级任务)
         */
        xTaskCreate(vLPTask, "LPTask", 1000, NULL, 1, NULL);
        xTaskCreate(vMPTask, "MPTask", 1000, NULL, 2, NULL);
        xTaskCreate(vHPTask, "HPTask", 1000, NULL, 3, NULL);
        /* 启动调度器 */
        vTaskStartScheduler();
    }
    else
    {
        /* 无法创建互斥量/二进制信号量 */
    }
    /* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
    return 0;
}

把二进制信号量换为互斥锁

运行时序图如下图所示:

  • A:HPTask 执行 xSemaphoreTake(xLock, portMAX_DELAY);,它的优先级被 LPTask 继承
  • B:LPTask 抢占 MPTask,运行
  • C:LPTask 执行 xSemaphoreGive(xLock);,它的优先级恢复为原来值
  • D:HPTask 得到互斥锁,开始运行
  • 互斥锁的"优先级继承",可以减小"优先级反转"的影响

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值