前情提要
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 0xLock = xSemaphoreCreateBinary();xSemaphoreGive(xLock);#elsexLock = xSemaphoreCreateMutex();#endifif( 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;
}
解决优先级反转的问题,互斥量使用的方法使优先级继承.