FreeRTOS 信号量(三) ------ 优先级翻转

一、优先级翻转

在这里插入图片描述

(1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。

(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。

(3) 任务 L 获得信号量并开始使用该共享资源。

(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。

(5) 任务 H 开始运行。

(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。

(7) 任务 L 继续运行。

(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生 后,任务 M 剥夺了任务L 的 CPU 使用权。

(9) 任务 M 处理该处理的事。

(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。

(11) 任务 L 继续运行。

(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优先级的任务在等待这个信号量,故内核做任务切换。

(13) 任务 H 得到该信号量并接着运行。

在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级翻转。

二、编程实战

1、实验目的
在使用二值信号量的时候会存在优先级翻转的问题,本实验通过模拟的方式实现优先级翻转,观察优先级翻转对抢占式内核的影响。

2、实验设计
本实验设计四个任务:start_task、high_task 、middle_task ,low_task,这四个任务的任务

功能如下:
start_task:用来创建其他 3 个任务。
high_task :高优先级任务,会获取二值信号量,获取成功以后会进行相应的处理,处理完成以后就会释放二值信号量。
middle_task :中等优先级的任务,一个简单的应用任务。
low_task:低优先级任务,和高优先级任务一样,会获取二值信号量,获取成功以后会进行相应的处理,不过不同之处在于低优先级的任务占用二值信号量的时间要久一点(软件模拟占用)。

实验中创建了一个二值信号量 BinarySemaphore,高优先级和低优先级这两个任务会使用这个二值信号量。

3、实验程序与分析
●任务设置

#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()函数

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(); //开启任务调度
}

● 任务函数

//开始任务任务函数
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)、调用函数 xSemaphoreCreateBinary()创建二值信号量。

(2)、默认创建的二值信号量是无效的,这里需要先调用函数 xSemaphoreGive()释放一次二值信号量。否则任务 high_task()和 low_task()都会获取不到信号量。

(3)、高优先级任务调用函数 xSemaphoreTake()获取二值信号量。

(4)、使用完以后需要调用函数 xSemaphoreGive()释放二值信号量。

(5)、低优先级任务获取二值信号量 BinarySemaphore。

(6)、低优先级任务模拟长时间占用二值信号量。

(7)、低优先级任务释放二值信号量。

程序运行结果分析
(1)、low_task 任务获取到二值信号量 BinarySemaphore 开始运行。

(2)、high_task 获取信号量 BinarySemaphore,但是此时信号量 BinarySemaphore 被任务low_task 占用着,因此 high_task 就要一直等待,直到 low_task 任务释放信号量 BinarySemaphore。

(3)、由于 high_task 没有获取到信号量 BinarySemaphore,只能一直等待,红色部分代码中high_task 没有运行,而 middle_task 一直在运行,给人的感觉就是 middle_task 的任务优先级高于 high_task。但是事实上 high_task 任务的任务优先级是高于 middle_task 的,这个就是优先级反转!

(4)、high_task 任务因为获取到了信号量 BinarySemaphore 而运行.

猜你喜欢

转载自blog.csdn.net/Dustinthewine/article/details/130396441