一、优先级翻转简介
优先级翻转顾名思义就是:高优先级的任务变成最后执行,低优先级的任务反而优先执行。优先级翻转在抢占式内核中是很常见的问题,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破环任务的预期顺序,可能会导致未知的严重后果。在使用二值信号量的时候,经常会遇到优先级翻转问题。
二、实验
2.1.实验设计
本实验设计四个任务:
- start_task:创建其他任务
- high_task:高优先级任务,会获取二值信号量,获取成功以后打印提示信息,处理完后释放信号量
- middle_task:中等优先级任务,简单的应用任务
- low_task:低优先级任务,和高优先级任务操作一致,不同的是低优先级任务占用信号量的时间久一点
2.2.软件设计
在入口函数里面创建二值信号量,并释放一个资源给下面两个任务:
void freertos_demo(void)
{
semphr_handle = xSemaphoreCreateBinary();
if(semphr_handle != NULL)
{
printf("二值信号量创建成功\r\n");
}
xSemaphoreGive(semphr_handle);
xTaskCreate((TaskFunction_t) start_task,
(char*) "start_task",
(uint16_t) START_TASK_STACK_SIZE,
(void*) NULL,
(UBaseType_t) START_TASK_PRIO,
(TaskHandle_t*) &start_task_handler);
vTaskStartScheduler();
}
下面是三个不同优先级的任务,high_task 函数优先级最高,依次递减:
void low_task(void *pvParameters)
{
while(1)
{
xSemaphoreTake(semphr_handle, portMAX_DELAY);
printf("低优先级任务获取信号量成功\r\n");
printf("低优先级任务正在运行\r\n");
delay_ms(5000);
printf("低优先级任务释放信号量\r\n");
xSemaphoreGive(semphr_handle);
vTaskDelay(1000);
}
}
void middle_task(void *pvParameters)
{
uint8_t i = 0;
while(1)
{
printf("中等任务运行:%d\r\n",++i);
vTaskDelay(1000);
}
}
void high_task(void *pvParameters)
{
while(1)
{
xSemaphoreTake(semphr_handle, portMAX_DELAY);
printf("高优先级任务获取信号量成功\r\n");
printf("高优先级任务正在运行\r\n");
delay_ms(1000);
printf("高优先级任务释放信号量\r\n");
xSemaphoreGive(semphr_handle);
vTaskDelay(1000);
}
}
下图是运行的结果图:
根据上图分析,二值信号量创建成功并释放了一个资源之后,自然高优先级任务就优先获取该信号量,运行结束并释放信号量之后,就到中等优先级任务运行了,中等优先级任务运行不受信号量控制,因此中等优先级任务运行按 1 秒的节奏运行,最后到低优先级任务获取信号量,但是该任务需要运行 5 秒,才能释放信号量给高优先级运行,这 5 秒之内,中等优先级任务也就运行了 5 次,因此就出现了优先级翻转。
为什么低优先级任务里面用了delay_ms(5000)
函数,CPU会跑去运行中等优先级任务呢?
- 这是因为被 SysTick 中断打断,导致调度器仍能切换任务,该函数里面操作的也是 SysTick 的寄存器