目录
1 优先级翻转问题
2 互斥信号量概念及其应用
2.2FreeRTOS互斥信号量介绍
2.3FreeRTOS互斥信号量工作原理
3 互斥信号量函数应用
3.1功能分析
3.2API详解
3.3功能实现
4 递归互斥信号量函数应用
4.1死锁现象
编辑
4.2API详解
4.3解决死锁
5 互斥信号量实现原理
5.1互斥信号量创建
5.2互斥信号量获取&释放
5.3优先级继承原理
5.4递归互斥信号量获取&释放
1 优先级翻转问题
二值信号量中一个bug
低优先级任务可以被高优先级任务抢占,但是如果这个时候低优先级任务占用信号量,那么高优先级任务会阻塞。这时候中优先级抢占了低优先级的任务,再等低优先级任务运行完释放信号量,这时候高优先级任务才运行。
上述情况中优先级任务抢占了高优先级任务,系统会出现问题,保证不了了任务的实时性。
功能需求
- 新建三个任务,优先级分别为中高低
- 新建二值信号量,用于模拟优先级翻转
- 低优先级任务获取信号量后,被中优先级打断,中优先级任务执行时间较长,因为低优先级任务还未释放信号量,高优先级任务就无法获取信号量继续运行
使用接口在低优先级任务中模拟调度 ,内部其实就是PendSV
//创建二值信号量
osSemaphoreDef(PrBinarySem);
PrBinarySemHandle = osSemaphoreCreate(osSemaphore(PrBinarySem), 1);//创建low、nomal、high任务osThreadDef(DelayTask, Delay_Task, osPriorityLow, 0, 128);DelayTaskHandle = osThreadCreate(osThread(DelayTask), NULL);osThreadDef(LedTask, Led_Task, osPriorityNormal, 0, 128);LedTaskHandle = osThreadCreate(osThread(LedTask), NULL);osThreadDef(HighTask, High_Task, osPriorityHigh, 0, 128);HighTaskHandle = osThreadCreate(osThread(HighTask), NULL);uint32_tvoid Delay_Task(void const * argument)
{for(;;){printf("Low Task Take Sem\r\n"); if(xSemaphoreTake(PrBinarySemHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}//为了保证中优先级任务有足够时间抢占抢占for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give Sem\r\n"); xSemaphoreGive(PrBinarySemHandle);osDelay(500);}
}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take Sem\r\n"); if(xSemaphoreTake(PrBinarySemHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give Sem\r\n"); xSemaphoreGive(PrBinarySemHandle);osDelay(500);}
}
2 互斥信号量概念及其应用
2.1互斥信号量概念
用于解决优先级反转问题
当高优先级任务运行时,如果信号量被低优先级任务获取,那么临时提高低优先级任务。
但是二值信号量是共有的,所有任务都可以获取。
互斥信号量特性:
- 优先级继承
- 任务独享公共资源
2.2FreeRTOS互斥信号量介绍
Mutex 互斥信号量
RecursiveMuxtex 互斥信号量(解决普通信号量的死锁问题)
2.3FreeRTOS互斥信号量工作原理
互斥信号量工作原理即特性
- 优先级继承
- 任务独享公共资源
上述伪代码,再调用bar申请了互斥信号,当再去调用foo的时候又去申请了互斥信号量,但是bar的互斥信号量还未解锁,造成了死锁。即如果调用2次即死锁。
递归信号量即每次lock一次+1,每次解锁-1,解决死锁问题。当lock次数为0,即恢复原有状态。
3 互斥信号量函数应用
3.1功能分析
- 1、修改优先级翻转实验
- 2、使用互斥信号量,解决优先级翻转问题
3.2API详解
GetmutexHolder是独有的api,查看当前是谁独占了资源。如果没有被占用,我们即可获取信号量,使用take(take和give都是标准的api)
3.3功能实现
使能互斥锁
生产工程代码
void MX_FREERTOS_Init(void) {osMutexDef(PrMutex);PrMutexHandle = osMutexCreate(osMutex(PrMutex));//略 ...}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);}
}void Delay_Task(void const * argument)
{for(;;){printf("Low Task Take Mutex\r\n"); if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give Mutex\r\n"); xSemaphoreGive(PrMutexHandle);osDelay(500);}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take Mutex\r\n"); //高优先级任务获取互斥锁if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give Mutex\r\n"); xSemaphoreGive(PrMutexHandle);osDelay(500);}
}
效果
4 递归互斥信号量函数应用
4.1死锁现象
普通信号量只能获取一次,修改一下高优先级任务。
void High_Task(void const * argument)
{for(;;){printf("High Task Take RecursiveMutex 1\r\n"); if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}//再次获取信号量if(xSemaphoreTake(PrMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give RecursiveMutex 2\r\n"); xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}
4.2API详解
获取接口(不能再临界区内调用,即模拟的中断中)
释放接口(FAIL,代表持有者不是任务本身)
4.3解决死锁
- 1、模拟死锁现象
- 2、使用递归互斥信号量解决死锁问题
osMutexId myRecursiveMutexHandle;void MX_FREERTOS_Init(void)
{osMutexDef(myRecursiveMutex);myRecursiveMutexHandle = osRecursiveMutexCreate(osMutex(myRecursiveMutex));//略 ...
}void Led_Task(void const * argument)
{for(;;){printf("Normal Task is Runing\r\n");osDelay(500);//1msʱ»ù}
}void High_Task(void const * argument)
{for(;;){printf("High Task Take RecursiveMutex 1\r\n"); if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Take RecursiveMutex 2\r\n"); if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("High Task is Runing\r\n");}printf("High Task Give RecursiveMutex 1\r\n"); xSemaphoreGiveRecursive(myRecursiveMutexHandle);printf("High Task Give RecursiveMutex 2\r\n"); xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}void Delay_Task(void const * argument)
{for(;;){printf("Low Task Take RecursiveMutex\r\n"); if(xSemaphoreTakeRecursive(myRecursiveMutexHandle,portMAX_DELAY) == pdPASS){printf("Low Task is Runing\r\n");}for(i=0;i<2000000;i++){taskYIELD();}printf("Low Task Give RecursiveMutex\r\n"); xSemaphoreGiveRecursive(myRecursiveMutexHandle);osDelay(500);}
}
效果
5 互斥信号量实现原理
5.1互斥信号量创建
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )||||#endifQueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ){Queue_t *pxNewQueue;const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;//创建消息队列/*队列长度:1队列大小:0队列类型:queueQUEUE_TYPE_MUTEX*/pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );//初始化互斥信号量->其实就是初始化消息队列的控制块prvInitialiseMutex( pxNewQueue );return pxNewQueue;}static void prvInitialiseMutex( Queue_t *pxNewQueue ){if( pxNewQueue != NULL ){/*1、信号的持有者为空2、消息队列的类型为互斥信号量3、递归记录初始为04、往消息队列发送一个消息->其实赋值互斥信号量为1*/pxNewQueue->pxMutexHolder = NULL;pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;/* In case this is a recursive mutex. */pxNewQueue->u.uxRecursiveCallCount = 0;traceCREATE_MUTEX( pxNewQueue );/* Start with the semaphore in the expected state. */( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );}else{traceCREATE_MUTEX_FAILED();}}
5.2互斥信号量获取&释放
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )/*队列不为空处理>01、判断是否为互斥信号量2、记录当前任务为信号持有者*/#if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* Record the information required to implementpriority inheritance should it become necessary. */pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */}else{mtCOVERAGE_TEST_MARKER();}}/*队列为空处理==01、判断是否为互斥信号量2、进入临界段3、优先级继承*/ #if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){taskENTER_CRITICAL();{vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endif
#define xSemaphoreGive( xSemaphore )
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )//互斥信号量处理在数据拷贝接口中xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/*1、判断是否为互斥信号量2、恢复任务优先级3、信号持有者赋值为空,也就是说其他任务可以获取了*/#if ( configUSE_MUTEXES == 1 ){if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* The mutex is no longer being held. */xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );pxQueue->pxMutexHolder = NULL;}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES */
5.3优先级继承原理
//持有者记录增加
#if ( configUSE_MUTEXES == 1 )void *pvTaskIncrementMutexHeldCount( void ){/* If xSemaphoreCreateMutex() is called before any tasks have been createdthen pxCurrentTCB will be NULL. */if( pxCurrentTCB != NULL ){//持有者任务控制块里 持有记录加一( pxCurrentTCB->uxMutexesHeld )++;}//返回当前任务控制块return pxCurrentTCB;}#endif /* configUSE_MUTEXES *///优先级继承
//参数:持有互斥信号量的任务控制块
#if ( configUSE_MUTEXES == 1 )void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;//1、任务控制块不为空if( pxMutexHolder != NULL ){//2、优先级小于当前获取信号量的有限,才会去处理继承if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ){/* */if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ){//3、修改持有者事件列表中,列表项的属性值 为当前任务优先级listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */}else{mtCOVERAGE_TEST_MARKER();}/* 4、判断持有者任务是否在就绪列表中 4.1、移除4.2、修改任务优先级,这个修改是任务控制块里的信息4.3、添加到新的就绪列表中*/if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ){if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}/* Inherit the priority before being moved into the new list. */pxTCB->uxPriority = pxCurrentTCB->uxPriority;prvAddTaskToReadyList( pxTCB );}else{/* Just inherit the priority. */pxTCB->uxPriority = pxCurrentTCB->uxPriority;}traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES *///优先级恢复
/*1、
*/
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;BaseType_t xReturn = pdFALSE;if( pxMutexHolder != NULL ){//1、持有者任务持有记录减一( pxTCB->uxMutexesHeld )--;/* Has the holder of the mutex inherited the priority of anothertask? *///2、优先级是否修改过if( pxTCB->uxPriority != pxTCB->uxBasePriority ){//3、递归记录为0的时候/* Only disinherit if no other mutexes are held. */if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ){/* 4、从当前就绪列表中移除*/if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}/* 5、恢复任务优先级 */traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );pxTCB->uxPriority = pxTCB->uxBasePriority;/*6、已经不是持有者,把任务添加到新的就绪列表中去*/listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */prvAddTaskToReadyList( pxTCB );/* 触发上下文切换,释放CPU使用权 */xReturn = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}return xReturn;}
5.4递归互斥信号量获取&释放
#define xSemaphoreGiveRecursive( xMutex )
xQueueGiveMutexRecursive( ( xMutex ) )/*参数:信号量句柄步骤:1、判断当前任务是否为持有者1.1、递归记录减一1.2、判断记录是否为01.3、发送一个消息2、不为持有者返回错误*/
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ){BaseType_t xReturn;Queue_t * const pxMutex = ( Queue_t * ) xMutex;if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */{( pxMutex->u.uxRecursiveCallCount )--;if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ){( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );}else{mtCOVERAGE_TEST_MARKER();}xReturn = pdPASS;}else{xReturn = pdFAIL;}return xReturn;}
#if( configUSE_RECURSIVE_MUTEXES == 1 )#define xSemaphoreTakeRecursive( xMutex, xBlockTime )xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
#endif/*参数:互斥信号句柄,超时等待时间步骤:1、判断是否为持有者1.1、递归记录加一1.2、返回成功2、不为持有者2.1、接收消息---获取信号量2.2、获取成功----递归记录加一2.3、获取失败---- 返回失败*/
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ){BaseType_t xReturn;Queue_t * const pxMutex = ( Queue_t * ) xMutex;if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */{( pxMutex->u.uxRecursiveCallCount )++;xReturn = pdPASS;}else{xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );/* pdPASS will only be returned if the mutex was successfullyobtained. The calling task may have entered the Blocked statebefore reaching here. */if( xReturn != pdFAIL ){( pxMutex->u.uxRecursiveCallCount )++;}else{traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );}}return xReturn;}