FreeRTOS 原理 --- 互斥锁

发布时间 2023-10-19 22:41:23作者: 流水灯

互斥锁相比于二值信号量,有以下特点:

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;
    }