4. 案例4
任务定义
在FreeRTOS多任务系统中,当高优先级任务等待低优先级任务持有的共享资源时,若中优先级任务抢占CPU,将导致高优先级任务被无限期阻塞——即优先级反转。本案例构建一个最小可复现场景:三个任务(高、中、低优先级)竞争一个共享资源,先演示无保护时高优先级任务被中优先级任务长时间阻塞的问题,再引入互斥量(含优先级继承机制)验证问题得到解决。
场景设计与代码实现
系统包含三个任务:HighTask(优先级3)、MidTask(优先级2)、LowTask(优先级1)。共享资源为一个全局计数器,由临界区模拟。LowTask先获取资源并长时间占用;MidTask在LowTask持有资源期间持续运行;HighTask等待资源。通过串口日志输出各任务进入/退出时间戳,对比使用互斥量前后的阻塞时长。
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
/* 共享资源 */
static volatile uint32_t shared_counter = 0;
static SemaphoreHandle_t xMutex;
/* 任务句柄 */
static TaskHandle_t hHigh, hMid, hLow;
/* 模拟占用资源的耗时操作 */
static void busy_wait(uint32_t us) {
volatile uint32_t count = us * (configCPU_CLOCK_HZ / 1000000) / 10;
while (count--) { __NOP(); }
}
/* 低优先级任务:先获取资源,长时间占用 */
static void vLowTask(void *arg) {
(void)arg;
for (;;) {
/* 尝试获取互斥量(或直接进入临界区) */
#ifdef USE_MUTEX
xSemaphoreTake(xMutex, portMAX_DELAY);
#endif
/* 占用资源 200ms */
shared_counter++;
vTaskDelay(pdMS_TO_TICKS(200));
#ifdef USE_MUTEX
xSemaphoreGive(xMutex);
#endif
vTaskDelay(pdMS_TO_TICKS(50)); /* 释放CPU让其他任务运行 */
}
}
/* 中优先级任务:无资源需求,但持续占用CPU */
static void vMidTask(void *arg) {
(void)arg;
TickType_t start;
for (;;) {
start = xTaskGetTickCount();
/* 持续运行 100ms,模拟CPU密集型工作 */
while ((xTaskGetTickCount() - start) < pdMS_TO_TICKS(100)) {
busy_wait(100);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/* 高优先级任务:等待共享资源 */
static void vHighTask(void *arg) {
(void)arg;
TickType_t start, wait_time;
for (;;) {
vTaskDelay(pdMS_TO_TICKS(500)); /* 等待低任务先占用资源 */
start = xTaskGetTickCount();
#ifdef USE_MUTEX
xSemaphoreTake(xMutex, portMAX_DELAY);
#else
/* 无保护:直接访问共享资源(模拟进入临界区) */
taskENTER_CRITICAL();
#endif
/* 访问资源 */
shared_counter++;
wait_time = xTaskGetTickCount() - start;
#ifdef USE_MUTEX
xSemaphoreGive(xMutex);
#else
taskEXIT_CRITICAL();
#endif
/* 打印等待时间 */
printf("[High] waited %d ms\n", (int)(wait_time * portTICK_PERIOD_MS));
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vPriorityInversionDemo(void) {
/* 创建互斥量 */
xMutex = xSemaphoreCreateMutex();
/* 创建任务:低(1)、中(2)、高(3) */
xTaskCreate(vLowTask, "Low", 128, NULL, 1, &hLow);
xTaskCreate(vMidTask, "Mid", 128, NULL, 2, &hMid);
xTaskCreate(vHighTask, "High", 128, NULL, 3, &hHigh);
/* 启动调度器 */
vTaskStartScheduler();
}
编译与运行
本代码基于FreeRTOS内核,需在嵌入式平台(如STM32F4 + Keil/IAR)或FreeRTOS模拟器(如QEMU + Eclipse)中编译。关键配置:configUSE_MUTEXES必须为1,configUSE_PREEMPTION为1,configPRIO_BITS至少3位。串口重定向printf到调试终端。
编译时通过宏USE_MUTEX控制是否启用互斥量:
/* 不使用互斥量(演示问题) */
// #define USE_MUTEX
/* 使用互斥量(验证解决) */
#define USE_MUTEX
验证方法与判定标准
步骤1(问题复现):注释掉USE_MUTEX,编译烧录。观察串口输出,High任务每次等待时间应接近300ms(200ms低任务占用 + 100ms中任务抢占)。若等待时间持续大于200ms且不稳定,说明优先级反转发生。
步骤2(解决验证):启用USE_MUTEX,重新编译烧录。观察输出,High任务等待时间应显著缩短至约200ms(仅低任务占用时间),且不会因中任务抢占而延长。若等待时间稳定在200ms±10ms,判定互斥量优先级继承机制生效。
判定标准:对比两组日志中High任务的最大等待时间。无互斥量时最大等待时间 > 250ms;有互斥量时最大等待时间 ≤ 210ms,且中任务运行期间High任务不被阻塞,即问题解决。
结果判定
通过上述对比实验,可明确验证:未使用互斥量时,High任务最大等待时间约300ms(200ms低任务占用 + 100ms中任务抢占),出现优先级反转;使用互斥量后,由于优先级继承机制,Low任务在持有资源期间临时提升至High任务优先级,中任务无法抢占,High任务等待时间缩短至约200ms,问题得到解决。若实测数据符合上述趋势,即可判定案例通过。