一句话总结
优先级继承就是:高优先级任务等锁时,把“持锁的低优先级任务”临时提升到高优先级,让它快点跑完、快点放锁。
1. 先看问题:优先级翻转
假设有三个任务,优先级:
H:高优先级
M:中优先级
L:低优先级
H 和 L 都要用同一个锁,M 不用锁,但很吃 CPU。
正常情况:
结果:高优先级的 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 里的优先级继承基本就通了。
