FreeRTOS互斥量解决优先级反转问题

发布时间:2023年12月25日

FreeRTOS互斥量

一、概念

FreeRTOS中的互斥量(Mutex)是一种特殊的二值信号量,它支持互斥量所有权、递归访问以及防止优先级翻转的特性。在FreeRTOS中,互斥量用于实现对临界资源的独占式处理。

互斥量的状态只有两种,开锁或闭锁。当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。当该任务释放这个互斥量时,该互斥量处于开锁状态,任务失去该互斥量的所有权。当一个任务持有互斥量时,其他任务将不能再对该互斥量进行开锁或持有。

此外,持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问。在信号量中,由于已经不存在可用的信号量,任务递归获取信号量时会发生主动挂起任务最终形成死锁。这是互斥量与一般的信号量的一个重要区别。

互斥量的典型应用场景:优先级反转

二、优先级反转

实验目的:实现优先级反转现象

优先级反转是指在某些操作系统中,由于任务调度和优先级分配的原因,高优先级的任务可能无法获得足够的CPU时间,而低优先级的任务反而占用大量CPU时间

具体实现:

创建3个Task(Task1、Task2、Task3)、1个二进制信号量、3个全局变量(cnt1、cnt2、cnt3)初始值为0

Task1:

  • 优先级最低
  • 初始化完毕后直接运行
  • 获取信号量
  • 累加cnt1到1000000后释放信号量(累加到1000000时长大于vTaskDelay(300))

Task2:

  • 优先级适中
  • 初始化完毕后vTaskDelay(150)再运行(目的是确保Task1获取了信号量)
  • 一直运行

Task3:

  • 优先级最高
  • 初始化完毕后vTaskDelay(300)再运行(目的是确保Task1获取了信号量,Task2也在运行了)
  • 获取信号量
  • 累加cnt3到1000000后释放信号量

实现代码:

#define mainDELAY_LOOP_COUNT 1000000

uint32_t cnt1 = 0;
uint32_t cnt2 = 0;
uint32_t cnt3 = 0;

uint8_t taskFlag = 0;
SemaphoreHandle_t SemaphoreHandle;

void vTask1( void *pvParameters )
{
	/* 打印任务1的信息 */
	printf( "Task1 start!\r\n");	

	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{	
		/* 表示Task1在运行 */
		taskFlag = 1;
		cnt1++;

		printf( "Task1 Take!\r\n");	
		/* 获取二进制信号量 */
		xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		for(cnt1 = 0; cnt1 < mainDELAY_LOOP_COUNT; cnt1++)
		{

		}
		printf( "Task1 Give!\r\n");
		/* 释放二进制信号量 */
		xSemaphoreGive(SemaphoreHandle);
			
	}
}

void vTask2( void *pvParameters )
{
	
	vTaskDelay(150);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task2在运行 */
		taskFlag = 2;

		
		printf( "Task2 Tack!\r\n");	

	}
}

void vTask3( void *pvParameters )
{
	
	vTaskDelay(300);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task3在运行 */
		taskFlag = 3;

		printf( "Task3 Tack!\r\n");	
		/* 获取二进制信号量 */
		xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		cnt2++;

		for(cnt3 = 0; cnt3 < mainDELAY_LOOP_COUNT; cnt3++)
		{

		}
		printf( "Task3 Give!\r\n");
		/* 释放二进制信号量 */
		xSemaphoreGive(SemaphoreHandle);
			
	}
}

int main( void )
{
	prvSetupHardware();
	
	xTaskCreate(vTask1, "Task 1", 100, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 100, NULL, 2, NULL);
	xTaskCreate(vTask3, "Task 2", 100, NULL, 3, NULL);
	
	/* 创建二进制信号量 */
	SemaphoreHandle = xSemaphoreCreateBinary();

	/* 启动调度器 */
	vTaskStartScheduler();

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

运行结果:

在这里插入图片描述

从测试结果来看:Task1运行后获取信号量,未来得及释放信号量时更高优先级的Task2开始运行,之后Task1没有机会释放信号量,Task3再运行时由于无法获取信号量一直处于阻塞状态,而优先级低于Task3的Task2一直处于运行状态,实现了优先级反转。

三、互斥量解决优先级反转

实验目的:使用互斥量解决优先级反转问题

原理分析:

1、Task1获取互斥量

2、Task1还未释放互斥量Task2开始运行,并一直运行,Task1没有机会释放互斥量

3、Task3运行,获取信号量时发现获取不到,使获取了互斥量的Task1继承自己的优先级(高于Task2)

4、Task1继续运行直到释放互斥量

5、Task3获取信号量,最高优先级的Task3得到运行

具体实现:

创建3个Task(Task1、Task2、Task3)、1个互斥量、3个全局变量(cnt1、cnt2、cnt3)初始值为0

Task1:

  • 优先级最低
  • 初始化完毕后直接运行
  • 获取互斥量
  • 累加cnt1到1000000后释放互斥量(累加到1000000时长大于vTaskDelay(300))

Task2:

  • 优先级适中
  • 初始化完毕后vTaskDelay(150)再运行(目的是确保Task1获取了互斥量)
  • 一直运行

Task3:

  • 优先级最高
  • 初始化完毕后vTaskDelay(300)再运行(目的是确保Task1获取了互斥量,Task2也在运行了)
  • 获取互斥量
  • 累加cnt3到1000000后释放互斥量

实现代码:

#define mainDELAY_LOOP_COUNT 1000000

uint32_t cnt1 = 0;
uint32_t cnt2 = 0;
uint32_t cnt3 = 0;

uint8_t taskFlag = 0;
SemaphoreHandle_t SemaphoreValue;
//SemaphoreHandle_t SemaphoreHandle;

void vTask1( void *pvParameters )
{
	/* 打印任务1的信息 */
	printf( "Task1 start!\r\n");	

	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{	
		/* 表示Task1在运行 */
		taskFlag = 1;
		cnt1++;

		printf( "Task1 Take!\r\n");	
		/* 获取互斥量 */
		xSemaphoreTake(SemaphoreValue, portMAX_DELAY);
		/* 获取二进制信号量 */
		//xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		for(cnt1 = 0; cnt1 < mainDELAY_LOOP_COUNT; cnt1++)
		{

		}
		printf( "Task1 Give!\r\n");
		/* 释放互斥量 */
		xSemaphoreGive(SemaphoreValue);
		/* 释放二进制信号量 */
		//xSemaphoreGive(SemaphoreHandle);
			
	}
}

void vTask2( void *pvParameters )
{
	
	vTaskDelay(150);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task2在运行 */
		taskFlag = 2;

		
		printf( "Task2 Tack!\r\n");	

	}
}

void vTask3( void *pvParameters )
{
	
	vTaskDelay(300);
	
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		/* 表示Task3在运行 */
		taskFlag = 3;

		printf( "Task3 Tack!\r\n");	
		/* 获取互斥量 */
		xSemaphoreTake(SemaphoreValue, portMAX_DELAY);
		/* 获取二进制信号量 */
		//xSemaphoreTake(SemaphoreHandle, portMAX_DELAY);
		
		cnt2++;

		for(cnt3 = 0; cnt3 < mainDELAY_LOOP_COUNT; cnt3++)
		{

		}
		printf( "Task3 Give!\r\n");
		/* 释放互斥量 */
		xSemaphoreGive(SemaphoreValue);
		/* 释放二进制信号量 */
		//xSemaphoreGive(SemaphoreHandle);
			
	}
}

int main( void )
{
	prvSetupHardware();
	
	xTaskCreate(vTask1, "Task 1", 100, NULL, 1, NULL);
	xTaskCreate(vTask2, "Task 2", 100, NULL, 2, NULL);
	xTaskCreate(vTask3, "Task 2", 100, NULL, 3, NULL);
	
	/* 创建互斥量 */
	SemaphoreValue = xSemaphoreCreateMutex();
	/* 创建二进制信号量 */
	//SemaphoreHandle = xSemaphoreCreateBinary();

	/* 启动调度器 */
	vTaskStartScheduler();

	/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
	return 0;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

从运行结果看,将信号量换成互斥量能够解决优先级反转问题。

文章来源:https://blog.csdn.net/studyingdda/article/details/135206722
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。