FreeRTOS 原理 --- 互斥锁
互斥锁相比于二值信号量,有以下特点:
1、通过优先级继承,防止优先级反转
2、只有互斥锁持有的线程可以释放互斥锁
3、FreeRTOS 提供支持递归版本的互斥锁
创建互斥锁
互斥锁使用的描述符是队列的描述符,不单独定义互斥锁描述符。
初始化时,指定队列的长度 pxNewQueue->uxLength 为1,指定队列消息占用大小 pxNewQueue->uxItemSize 为0,用队列所含消息的条数 pxQueue->uxMessagesWaiting 代表锁是否被占用,为1表示锁空闲,为0表示锁被占用,初始化为1
#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) { Queue_t *pxNewQueue; const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); prvInitialiseMutex( pxNewQueue ); return pxNewQueue; }
成功获得锁
获取锁成功后,记录占用锁的任务描述符
#if ( configUSE_MUTEXES == 1 ) { if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { /* Record the information required to implement priority 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(); } }
void *pvTaskIncrementMutexHeldCount( void )
{
/* If xSemaphoreCreateMutex() is called before any tasks have been created
then pxCurrentTCB will be NULL. */
if( pxCurrentTCB != NULL )
{
( pxCurrentTCB->uxMutexesHeld )++;
}
return pxCurrentTCB;
}
获取锁失败
获取锁失败,判断当前线程的优先级A是否大于拥有锁的线程的优先级B,如果是则把拥有锁的线程优先级提升到A
并把当前线程记录到互斥锁的链表里,用于锁被释放后可以唤醒当前线程;并把当前线程从就绪链表移到阻塞链表
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) { taskENTER_CRITICAL(); { xInheritanceOccurred = xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); // 优先级继承 } taskEXIT_CRITICAL(); } else { mtCOVERAGE_TEST_MARKER(); } }
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
释放锁
如果当前释放锁的线程不是拥有锁的线程,则断言
/* A task can only have an inherited priority if it holds the mutex.
If the mutex is held by a task then it cannot be given from an
interrupt, and if a mutex is given by the holding task then it must
be the running state task. */
configASSERT( pxTCB == pxCurrentTCB );
如果释放锁的线程的优先级是继承高优先级线程的,恢复原本的优先级;从当前优先级的链表中删除此线程,添加到原本优先级的链表中
if( pxTCB->uxPriority != pxTCB->uxBasePriority ) { /* Only disinherit if no other mutexes are held. */ if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) { /* A task can only have an inherited priority if it holds the mutex. If the mutex is held by a task then it cannot be given from an interrupt, and if a mutex is given by the holding task then it must be the running state task. Remove the holding task from the ready list. */ if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) // 从当前优先级的链表中删除此线程 { taskRESET_READY_PRIORITY( pxTCB->uxPriority ); } else { mtCOVERAGE_TEST_MARKER(); } /* Disinherit the priority before adding the task into the new ready list. */ traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); pxTCB->uxPriority = pxTCB->uxBasePriority; // 恢复原本的优先级 /* Reset the event list item value. It cannot be in use for any other purpose if this task is running, and it must be running to give back the mutex. */ 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 ); // 添加到原本优先级的链表中 /* Return true to indicate that a context switch is required. This is only actually required in the corner case whereby multiple mutexes were held and the mutexes were given back in an order different to that in which they were taken. If a context switch did not occur when the first mutex was returned, even if a task was waiting on it, then a context switch should occur when the last mutex is returned whether a task is waiting on it or not. */ xReturn = pdTRUE; }
支持递归的互斥锁
如果尝试获取锁的线程是持有锁的线程,递归计数加1,返回成功
#if( configUSE_RECURSIVE_MUTEXES == 1 ) #define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) #endif BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) { BaseType_t xReturn; Queue_t * const pxMutex = ( Queue_t * ) xMutex; configASSERT( pxMutex ); /* Comments regarding mutual exclusion as per those within xQueueGiveMutexRecursive(). */ traceTAKE_MUTEX_RECURSIVE( pxMutex ); if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ { ( pxMutex->u.uxRecursiveCallCount )++; xReturn = pdPASS; } else { xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); /* pdPASS will only be returned if the mutex was successfully obtained. The calling task may have entered the Blocked state before reaching here. */ if( xReturn != pdFAIL ) { ( pxMutex->u.uxRecursiveCallCount )++; } else { traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); } } return xReturn; }