FreeRTOS互斥量
目录
- 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;
}
运行结果:
从运行结果看,将信号量换成互斥量能够解决优先级反转问题。