FreeRTOS学习(十七)——优先级翻转与优先级继承 📅 2026/8/5 9:17:57 上一节学习了互斥量Mutex它可以保护共享资源保证同一时刻只有一个任务能够访问串口、LCD、Flash等资源。但是如果多个不同优先级的任务同时竞争一个互斥量就可能产生一个比较隐蔽的问题——优先级翻转Priority Inversion。这一节学习什么是优先级翻转以及FreeRTOS是如何利用**优先级继承Priority Inheritance**解决这一问题的。这也是嵌入式软件工程师面试中经常出现的知识点。一、什么是优先级翻转假设系统中有三个任务Task_H 高优先级3Task_M 中优先级2Task_L 低优先级1其中Task_L首先获得了互斥量Mutex开始访问串口。此时Task_H也需要访问串口。但是Mutex已经被Task_L占用因此Task_H ↓ 等待Mutex按理来说只需要等待Task_L执行完释放互斥量即可。但是Task_M此时变为就绪态。由于Task_M 优先级 Task_L于是CPU开始执行Task_M而Task_L一直得不到CPU。结果就是Task_H ↓ 等待Task_L ↓ Task_L又一直被Task_M抢占最终高优先级任务 反而等待低优先级任务。这就是优先级翻转二、优先级翻转过程整个过程可以表示为Task_L 获得Mutex ↓ Task_H需要Mutex ↓ Task_H阻塞 ↓ Task_M开始运行 ↓ Task_L没有机会运行 ↓ Mutex一直不能释放 ↓ Task_H持续等待可以看到真正阻塞高优先级任务的不是Task_L而是Task_M这也是优先级翻转最容易让人困惑的地方。三、为什么优先级翻转危险假设Task_H负责电机控制而Task_M只是LED闪烁如果发生优先级翻转LED任务 一直抢占CPU ↓ 电机控制一直等待显然是不合理的。在实时系统中高优先级任务 必须尽快得到CPU。因此必须解决优先级翻转问题。四、什么是优先级继承FreeRTOS互斥量支持Priority Inheritance即优先级继承。它的思想很简单当Task_H等待Mutex而Mutex被Task_L占有时。FreeRTOS会暂时把Task_L的优先级提升为Task_H的优先级。例如原来Task_L Priority 1现在自动变成Priority 3这样Task_M Priority 2就不能抢占Task_L了。五、优先级继承过程加入优先级继承以后Task_L获得Mutex ↓ Task_H等待Mutex ↓ Task_L优先级提升 ↓ Task_L继续运行 ↓ 释放Mutex ↓ Task_L恢复原优先级 ↓ Task_H立即运行整个过程中Task_M不会再影响Task_H等待互斥量。因此优先级翻转被解决。六、代码示例创建互斥量SemaphoreHandle_t Mutex; Mutex xSemaphoreCreateMutex();任务1void LowTask(void *pvParameters) { while(1) { xSemaphoreTake( Mutex, portMAX_DELAY ); printf(Low Task\r\n); vTaskDelay(pdMS_TO_TICKS(2000)); xSemaphoreGive(Mutex); vTaskDelay(pdMS_TO_TICKS(1000)); } }任务2void HighTask(void *pvParameters) { while(1) { xSemaphoreTake( Mutex, portMAX_DELAY ); printf(High Task\r\n); xSemaphoreGive(Mutex); vTaskDelay(pdMS_TO_TICKS(500)); } }程序运行过程中如果LowTask已经获得互斥量。随后HighTask请求同一个互斥量。FreeRTOS会自动进行优先级继承。整个过程不需要用户手动修改优先级。七、为什么二值信号量不能解决很多初学者容易认为二值信号量 ↓ 也能锁资源。事实上二值信号量 没有优先级继承。因此如果使用xSemaphoreCreateBinary();保护共享资源。发生优先级翻转以后。FreeRTOS不会自动提高低优先级任务的优先级。所以保护共享资源 推荐使用Mutex 不要使用Binary Semaphore。八、什么时候会发生优先级翻转通常需要满足三个条件第一多个任务优先级不同。第二多个任务共同访问同一个共享资源。第三共享资源采用互斥机制保护。例如USART SPI I2C Flash SD卡这些都是实际工程中经常发生优先级翻转的地方。九、实际工程建议互斥量虽然能够解决优先级翻转但仍然要尽量缩短Take ↓ Give之间的代码。例如xSemaphoreTake(); printf(); xSemaphoreGive();是合理的。但是xSemaphoreTake(); vTaskDelay(5000); xSemaphoreGive();则不建议这样写。因为任务长时间占有互斥量会增加其他任务等待时间降低系统实时性。因此共享资源保护范围越小越好互斥量占用时间越短越好。十、总结这一节学习了FreeRTOS中的优先级翻转与优先级继承。当高优先级任务等待低优先级任务占有的互斥量而低优先级任务又被中优先级任务抢占时就会发生优先级翻转。FreeRTOS互斥量支持优先级继承机制能够临时提高持有互斥量任务的优先级使其尽快释放共享资源从而减少高优先级任务的等待时间。因此需要牢记二值信号量用于任务同步。互斥量用于共享资源保护。互斥量具有优先级继承而二值信号量没有。