优先级翻转
优先级翻转简介:
就是高优先级的任务运行起来的效果好像成了低优先级,而低优先级比高优先级先运行。
优先级翻转如下所示:
优先级翻转过程:
为什么会发生优先级翻转?
因为两个任务(L和H)使用了同一个二值信号量,而在这两个任务之间,又有一个中等优先级的任务M,在这种情况下就容易发生优先级翻转。主要就是因为二值信号量产生的,低优先级任务L占用了信号量没有释放,导致高优先级任务请求信号量时无效,此时高优先级任务无法运行。
实验设计
总体思路是,任务L和任务H都请求信号量,当接收到信号量时,任务H立马释放信号量,而任务L做软件延时占用一段时间后再释放信号量,看程序的执行流程。测试代码如下所示:
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_usart.h"
#include "bsp_beep.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include <string.h>
void start_task(void *pvParameters);
void low_task(void *pvParameters);
void middle_task(void *pvParameters);
void high_task(void *pvParameters);
/* 任务相关参数 */
#define START_TASK_SIZE 256
#define START_TASK_PRIO 1
TaskHandle_t start_Task_Handle;
#define LOW_TASK_SIZE 256
#define LOW_TASK_PRIO 2
TaskHandle_t low_task_Handle;
#define MIDDLE_TASK_SIZE 256
#define MIDDLE_TASK_PRIO 3
TaskHandle_t middle_task_Handle;
#define HIGH_TASK_SIZE 256
#define HIGH_TASK_PRIO 4
TaskHandle_t high_task_Handle;
SemaphoreHandle_t binary_semphr = NULL; // 二值信号量句柄
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
MX_I2C2_Init();
// 创建开始任务
xTaskCreate((TaskFunction_t )start_task,
(char * )"start_task",
(uint16_t )START_TASK_SIZE,
(void * )NULL,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t * )&start_Task_Handle);
vTaskStartScheduler(); // 开启调度器
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
binary_semphr = xSemaphoreCreateBinary(); // 创建信号量
if (binary_semphr != NULL)
{
printf("\n二值信号量创建成功");
xSemaphoreGive(binary_semphr); // 释放信号量
}
else
{
printf("\n二值信号量创建失败");
}
// 创建低优先级任务
xTaskCreate((TaskFunction_t )low_task,
(char * )"low_task",
(uint16_t )LOW_TASK_SIZE,
(void * )NULL,
(UBaseType_t )LOW_TASK_PRIO,
(TaskHandle_t * )&low_task_Handle);
// 创建中优先级任务
xTaskCreate((TaskFunction_t )middle_task,
(char * )"middle_task",
(uint16_t )MIDDLE_TASK_SIZE,
(void * )NULL,
(UBaseType_t )MIDDLE_TASK_PRIO,
(TaskHandle_t * )&middle_task_Handle);
// 创建高优先级任务
xTaskCreate((TaskFunction_t )high_task,
(char * )"high_task",
(uint16_t )HIGH_TASK_SIZE,
(void * )NULL,
(UBaseType_t )HIGH_TASK_PRIO,
(TaskHandle_t * )&high_task_Handle);
taskEXIT_CRITICAL();
// 删除开始任务
vTaskDelete(start_Task_Handle);
}
void low_task(void *pvParameters)
{
BaseType_t error_state;
uint32_t limit;
for (;;)
{
if (binary_semphr != NULL)
{
// 一直等待二值信号量
error_state = xSemaphoreTake(binary_semphr, portMAX_DELAY);
if (error_state == pdTRUE)
{
printf("\n低优先级任务获取到信号量,正在占用...");
}
else
{
printf("\n低优先级任务获取信号量失败");
}
#if 1
// 模拟低优先级占用信号量
for (limit = 0; limit < 5000000; limit++)
{
taskYIELD(); // 一直调用任务切换
}
#else
HAL_Delay(8000);
#endif
// 释放二值信号量
printf("\n低优先级任务占用信号量结束,即将释放");
xSemaphoreGive(binary_semphr);
}
vTaskDelay(500);
}
}
void middle_task(void *pvParameters)
{
for (;;)
{
printf("\n中优先级任务正在运行");
vTaskDelay(1000);
}
}
void high_task(void *pvParameters)
{
for (;;)
{
if (binary_semphr != NULL)
{
printf("\n高优先级任务正在等待信号量...");
// 一直等待二值信号量
xSemaphoreTake(binary_semphr, portMAX_DELAY);
printf("\n高优先级任务获取到信号量,即将释放...");
xSemaphoreGive(binary_semphr); // 释放信号量
}
vTaskDelay(1000);
}
}
程序运行结果如下所示:
互斥信号量
互斥信号量简介:
创建互斥信号量:
测试实验:修改上一个优先级翻转的实验,看测试结果。
修改过程:将使用二值信号量的地方修改成使用互斥信号量。
修改如下:
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
mutex_semphr = xSemaphoreCreateMutex(); // 创建互斥信号量
if (mutex_semphr != NULL)
{
printf("\n互斥信号量创建成功");
xSemaphoreGive(mutex_semphr); // 释放信号量
}
else
{
printf("\n互斥信号量创建失败");
}
// 创建低优先级任务
xTaskCreate((TaskFunction_t )low_task,
(char * )"low_task",
(uint16_t )LOW_TASK_SIZE,
(void * )NULL,
(UBaseType_t )LOW_TASK_PRIO,
(TaskHandle_t * )&low_task_Handle);
// 创建中优先级任务
xTaskCreate((TaskFunction_t )middle_task,
(char * )"middle_task",
(uint16_t )MIDDLE_TASK_SIZE,
(void * )NULL,
(UBaseType_t )MIDDLE_TASK_PRIO,
(TaskHandle_t * )&middle_task_Handle);
// 创建高优先级任务
xTaskCreate((TaskFunction_t )high_task,
(char * )"high_task",
(uint16_t )HIGH_TASK_SIZE,
(void * )NULL,
(UBaseType_t )HIGH_TASK_PRIO,
(TaskHandle_t * )&high_task_Handle);
taskEXIT_CRITICAL();
// 删除开始任务
vTaskDelete(start_Task_Handle);
}
void low_task(void *pvParameters)
{
BaseType_t error_state;
uint32_t limit;
for (;;)
{
if (mutex_semphr != NULL)
{
// 一直等待互斥信号量
error_state = xSemaphoreTake(mutex_semphr, portMAX_DELAY);
if (error_state == pdTRUE)
{
printf("\n低优先级任务获取到信号量,正在占用...");
}
else
{
printf("\n低优先级任务获取信号量失败");
}
#if 0
// 模拟低优先级占用信号量
for (limit = 0; limit < 5000000; limit++)
{
taskYIELD(); // 一直调用任务切换
}
#else
HAL_Delay(10000);
#endif
// 释放互斥信号量
printf("\n低优先级任务占用信号量结束,即将释放");
xSemaphoreGive(mutex_semphr);
}
vTaskDelay(500);
}
}
void middle_task(void *pvParameters)
{
for (;;)
{
printf("\n中优先级任务正在运行");
vTaskDelay(1000);
}
}
void high_task(void *pvParameters)
{
for (;;)
{
if (mutex_semphr != NULL)
{
printf("\n高优先级任务正在等待信号量...");
// 一直等待互斥信号量
xSemaphoreTake(mutex_semphr, portMAX_DELAY);
printf("\n高优先级任务获取到信号量,即将释放...");
xSemaphoreGive(mutex_semphr); // 释放信号量
}
vTaskDelay(1000);
}
}
测试效果如下:
递归互斥信号量: