FreeRTOS semaphore (3) ------ priority inversion

1. Priority inversion

insert image description here

(1) Task H and task M are in a suspended state, waiting for a certain event to occur, and task L is running.

(2) At a certain moment, task L wants to access the shared resource. Before that, it must obtain the semaphore corresponding to the resource.

(3) Task L obtains the semaphore and starts using the shared resource.

(4) Since task H has a high priority, it deprives task L of the CPU usage right after the event it is waiting for occurs.

(5) Task H starts running.

(6) During the running of task H, the resource that task L is using is also used. Since the semaphore of this resource is still occupied by task L, task H can only enter the suspended state, waiting for task L to release the semaphore.

(7) Task L continues to run.

(8) Since the priority of task M is higher than that of task L, when the event that task M is waiting for occurs, task M deprives task L of the CPU usage right.

(9) Task M handles what needs to be processed.

(10) After task M finishes executing, return the CPU usage right to task L.

(11) Task L continues to run.

(12) The final task L completes all the work and releases the semaphore. So far, since the real-time kernel knows that there is a high-priority task waiting for the semaphore, the kernel performs task switching.

(13) Task H gets the semaphore and then runs.

In this case, the priority of task H actually drops to the priority level of task L. Because task H has to wait until task L releases the shared resource it occupies. Since task M deprives task L of CPU usage rights, the situation of task H is even worse, which means that task M has a higher priority than task H, resulting in a priority inversion.

2. Programming practice

1. Purpose of the experiment
There will be a problem of priority inversion when using binary semaphores. This experiment implements priority inversion through simulation to observe the impact of priority inversion on the preemptive kernel.

2. Experiment design
This experiment designs four tasks: start_task, high_task, middle_task, low_task, the tasks of these four tasks

The functions are as follows:
start_task : used to create other 3 tasks.
high_task : A high-priority task will obtain a binary semaphore, and will perform corresponding processing after the acquisition is successful, and will release the binary semaphore after the processing is completed.
middle_task : Middle priority task, a simple application task.
low_task : Low-priority tasks, like high-priority tasks, will obtain binary semaphores, and will perform corresponding processing after successful acquisition, but the difference is that low-priority tasks occupy binary semaphores for a longer time (Software analog occupancy).

In the experiment, a binary semaphore BinarySemaphore was created, and the two tasks of high priority and low priority will use this binary semaphore.

3. Experimental procedure and analysis
●Task setting

#define START_TASK_PRIO 1 //任务优先级
#define START_STK_SIZE 256 //任务堆栈大小
TaskHandle_t StartTask_Handler; //任务句柄
void start_task(void *pvParameters); //任务函数
#define LOW_TASK_PRIO 2 //任务优先级
#define LOW_STK_SIZE 256 //任务堆栈大小
TaskHandle_t LowTask_Handler; //任务句柄
void low_task(void *pvParameters); //任务函数
#define MIDDLE_TASK_PRIO 3 //任务优先级
#define MIDDLE_STK_SIZE 256 //任务堆栈大小
TaskHandle_t MiddleTask_Handler; //任务句柄
void middle_task(void *pvParameters); //任务函数
#define HIGH_TASK_PRIO 4 //任务优先级
#define HIGH_STK_SIZE 256 //任务堆栈大小
TaskHandle_t HighTask_Handler; //任务句柄
void high_task(void *pvParameters); //任务函数

//二值信号量句柄
SemaphoreHandle_t BinarySemaphore;//二值信号量
//LCD 刷屏时使用的颜色
int lcd_discolor[14]={
    
      WHITE, BLACK, BLUE, BRED,
						GRED, GBLUE, RED, MAGENTA, 
						GREEN, CYAN, YELLOW, BROWN, 
						BRRED, GRAY };

● main() function

int main(void)
{
    
    
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
delay_init(); //延时函数初始化
uart_init(115200); //初始化串口
LED_Init(); //初始化 LED
KEY_Init(); //初始化按键
BEEP_Init(); //初始化蜂鸣器
LCD_Init(); //初始化 LCD
my_mem_init(SRAMIN); //初始化内部内存池
 
 POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"ATK STM32F103/407");
LCD_ShowString(30,30,200,16,16,"FreeRTOS Examp 14-3");
LCD_ShowString(30,50,200,16,16,"Priority Overturn");
LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,90,200,16,16,"2016/11/25");
 //创建开始任务
 xTaskCreate((TaskFunction_t )start_task, //任务函数
 (const char* )"start_task", //任务名称
 (uint16_t )START_STK_SIZE, //任务堆栈大小
 (void* )NULL, //传递给任务函数的参数
 (UBaseType_t )START_TASK_PRIO, //任务优先级
 (TaskHandle_t* )&StartTask_Handler); //任务句柄 
 vTaskStartScheduler(); //开启任务调度
}

● task function

//开始任务任务函数
void start_task(void *pvParameters)
{
    
    
	taskENTER_CRITICAL(); //进入临界区
	//创建二值信号量
	BinarySemaphore=xSemaphoreCreateBinary(); (1)
	//二值信号量创建成功以后要先释放一下
	if(BinarySemaphore!=NULL)xSemaphoreGive(BinarySemaphore); (2)
	//创建高优先级任务
	 xTaskCreate((TaskFunction_t )high_task, 
	 (const char* )"high_task", 
	 (uint16_t )HIGH_STK_SIZE, 
	 (void* )NULL, 
	 (UBaseType_t )HIGH_TASK_PRIO, 
	 (TaskHandle_t* )&HighTask_Handler); 
	 //创建中等优先级任务
	 xTaskCreate((TaskFunction_t )middle_task, 
	 (const char* )"middle_task", 
	 (uint16_t )MIDDLE_STK_SIZE,
	 (void* )NULL,
	 (UBaseType_t )MIDDLE_TASK_PRIO,
	 (TaskHandle_t* )&MiddleTask_Handler); 
	//创建低优先级任务
	 xTaskCreate((TaskFunction_t )low_task, 
	 (const char* )"low_task", 
	 (uint16_t )LOW_STK_SIZE,
	 (void* )NULL,
	 (UBaseType_t )LOW_TASK_PRIO,
	 (TaskHandle_t* )&LowTask_Handler);
	 vTaskDelete(StartTask_Handler); //删除开始任务
	 taskEXIT_CRITICAL(); //退出临界区
}

//高优先级任务的任务函数
void high_task(void *pvParameters)
{
    
    
	u8 num;
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(5,110,115,314); //画一个矩形
	LCD_DrawLine(5,130,115,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,"High Task");
	while(1)
	{
    
    
		vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍
		num++;
		printf("high task Pend Sem\r\n");
		xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取二值信号量 (3)
		printf("high task Running!\r\n");
		LCD_Fill(6,131,114,313,lcd_discolor[num%14]); //填充区域
		LED1=!LED1;
		xSemaphoreGive(BinarySemaphore); //释放信号量 (4)
		vTaskDelay(500); //延时 500ms,也就是 500 个时钟节拍 
	}
}

//中等优先级任务的任务函数
void middle_task(void *pvParameters)
{
    
    
	u8 num;
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(125,110,234,314); //画一个矩形
	LCD_DrawLine(125,130,234,130); //画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Middle Task");
	while(1)
	{
    
    
		num++;
		printf("middle task Running!\r\n");
		LCD_Fill(126,131,233,313,lcd_discolor[13-num%14]); //填充区域
		LED0=!LED0;
		vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
	}
}

//低优先级任务的任务函数
void low_task(void *pvParameters)
{
    
    
	static u32 times;
	while(1)
	{
    
    
		xSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取二值信号量 (5)
		printf("low task Running!\r\n");
		for(times=0;times<20000000;times++) //模拟低优先级任务占用二值信号量 (6)
		{
    
    
			taskYIELD(); //发起任务调度
		}
	xSemaphoreGive(BinarySemaphore); //释放二值信号量 (7)
	vTaskDelay(1000); //延时 1s,也就是 1000 个时钟节拍
	}
}

(1) Call the function xSemaphoreCreateBinary() to create a binary semaphore.

(2) The binary semaphore created by default is invalid. Here, the function xSemaphoreGive() needs to be called to release the binary semaphore once. Otherwise, the tasks high_task() and low_task() will not get the semaphore.

(3) The high-priority task calls the function xSemaphoreTake() to obtain the binary semaphore.

(4) After use, you need to call the function xSemaphoreGive() to release the binary semaphore.

(5) The low-priority task obtains the binary semaphore BinarySemaphore.

(6) Low-priority tasks simulate long-term occupation of binary semaphores.

(7) The low-priority task releases the binary semaphore.

Analysis of program running results
(1), the low_task task gets the binary semaphore BinarySemaphore and starts running.

(2), high_task obtains the semaphore BinarySemaphore, but at this time the semaphore BinarySemaphore is occupied by the task low_task, so high_task has to wait until the low_task task releases the semaphore BinarySemaphore.

(3) Since high_task has not obtained the semaphore BinarySemaphore, it can only wait for a long time. In the red part of the code, high_task is not running, but middle_task is running all the time. It gives the feeling that the task priority of middle_task is higher than high_task. But in fact the task priority of high_task task is higher than that of middle_task, this is priority inversion!

(4). The high_task task runs because it has obtained the semaphore BinarySemaphore.

Guess you like

Origin blog.csdn.net/Dustinthewine/article/details/130396441