前情提要
FreeRTOS ~(四)同步互斥与通信 ~ (2/3)互斥的缺陷
FreeRTOS ~(五)队列的常规使用 ~ (2/5)队列解决互斥缺陷
FreeRTOS ~(六)信号量 ~ (2/3)信号量解决互斥缺陷
FreeRTOS ~(七)互斥量 ~ (1/3)互斥量解决互斥缺陷
互斥量解决优先级反转问题
上述的几篇文章说明了互斥的缺陷及一些解决方法,但是却没有解决一个问题:
互斥的两个任务的优先级反转问题.
为了说明说明是优先级反转,这里设置一个例子来实际操作一下;
举例说明:
创建三个任务,优先级不同,
如:
高优先级的优先级是3;
中优先级的优先级是2;
低优先级的优先级是1;
高优先级与低优先级都要使用到信号量,而中优先级不使用信号量,这里使用二值信号量来解决互斥问题;
高优先级执行后主动让自己Dealy 10ms,然后去获取信号量(take)
中优先级执行后主动让自己Delay 30ms,
低优先级任务则先获取信号量(take),然后执行一段较为耗时的打印工作,执行完成后,释放信号量(give)
通过设置相应的标志位(Flag),利用逻辑分析仪来观察Task执行情况.
代码如下:
static volatile uint8_t flagLPTaskRun = 0; /* 低优先级任务执行标志位 */
static volatile uint8_t flagMPTaskRun = 0; /* 中优先级任务执行标志位 */
static volatile uint8_t flagHPTaskRun = 0; /* 高优先级任务执行标志位 */
static void vLPTask( void *pvParameters ); /* 低优先级任务声明 */
static void vMPTask( void *pvParameters ); /* 中优先级任务声明 */
static void vHPTask( void *pvParameters ); /* 高优先级任务声明 */
/*-----------------------------------------------------------*/
/* 互斥量/二进制信号量句柄 */
SemaphoreHandle_t xLock;
/*-----------------------------------------------------------*/
int main( void )
{
prvSetupHardware();
/* 创建互斥量/二进制信号量 */
xLock = xSemaphoreCreateBinary();
xSemaphoreGive(xLock);
if( xLock != NULL )
{
/* 创建3个任务: LP,MP,HP(低/中/高优先级任务) */
xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );
xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );
xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );
/* 启动调度器 */
vTaskStartScheduler();
}
else
{
/* 无法创建互斥量/二进制信号量 */
}
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
/*-----------------------------------------------------------*/
static void vLPTask( void *pvParameters )
{
const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );
uint32_t i;
char c = 'A';
printf("LPTask start\r\n");
/* 无限循环 */
for( ;; )
{
/* 标志位表明正在执行的任务是低优先级任务 */
flagLPTaskRun = 1;
flagMPTaskRun = 0;
flagHPTaskRun = 0;
/* 获得互斥量/二进制信号量 */
xSemaphoreTake(xLock, portMAX_DELAY);
/* 耗时很久 */
printf("LPTask take the Lock for long time");
for (i = 0; i < 500; i++)
{
flagLPTaskRun = 1;
flagMPTaskRun = 0;
flagHPTaskRun = 0;
printf("%c", c + i);
}
printf("\r\n");
/* 释放互斥量/二进制信号量 */
xSemaphoreGive(xLock);
vTaskDelay(xTicksToWait);
}
}
/*-----------------------------------------------------------*/
static void vMPTask( void *pvParameters )
{
const TickType_t xTicksToWait = pdMS_TO_TICKS( 30UL );
flagLPTaskRun = 0;
flagMPTaskRun = 1;
flagHPTaskRun = 0;
printf("MPTask start\r\n");
/* 让LPTask、HPTask先运行 */
vTaskDelay(xTicksToWait);
/* 无限循环 */
for( ;; )
{
/* 标志位表明正在执行的任务是中优先级任务 */
flagLPTaskRun = 0;
flagMPTaskRun = 1;
flagHPTaskRun = 0;
}
}
/*-----------------------------------------------------------*/
static void vHPTask( void *pvParameters )
{
const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL );
/* 标志位表明正在执行的任务是高优先级任务 */
flagLPTaskRun = 0;
flagMPTaskRun = 0;
flagHPTaskRun = 1;
printf("HPTask start\r\n");
/* 主动进入阻塞态,10ms,让LPTask先运行 */
vTaskDelay(xTicksToWait);
/* 无限循环 */
for( ;; )
{
flagLPTaskRun = 0;
flagMPTaskRun = 0;
flagHPTaskRun = 1;
printf("HPTask wait for Lock\r\n");
/* 获得互斥量/二进制信号量 */
xSemaphoreTake(xLock, portMAX_DELAY);
flagLPTaskRun = 0;
flagMPTaskRun = 0;
flagHPTaskRun = 1;
/* 释放互斥量/二进制信号量 */
xSemaphoreGive(xLock);
}
}
/*-----------------------------------------------------------*/
如何解决优先级反转这个问题呢?
抛弃二值信号量,使用互斥量,下面简单修改一下代码即可,因为互斥量也是特殊的信号量,使用的一些API函数是一致的,
这里只需要将信号量的创建由二进制信号量,更换为互斥量即可.
int main( void )
{
prvSetupHardware();
/* 创建互斥量/二进制信号量 */
#if 0
xLock = xSemaphoreCreateBinary();
xSemaphoreGive(xLock);
#else
xLock = xSemaphoreCreateMutex();
#endif
if( xLock != NULL )
{
/* 创建3个任务: LP,MP,HP(低/中/高优先级任务) */
xTaskCreate( vLPTask, "LPTask", 1000, NULL, 1, NULL );
xTaskCreate( vMPTask, "MPTask", 1000, NULL, 2, NULL );
xTaskCreate( vHPTask, "HPTask", 1000, NULL, 3, NULL );
/* 启动调度器 */
vTaskStartScheduler();
}
else
{
/* 无法创建互斥量/二进制信号量 */
}
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
解决优先级反转的问题,互斥量使用的方法使优先级继承.