FreeRTOS--递归锁

发布时间 2023-12-17 16:52:43作者: 流翎

示例源码基于FreeRTOS V9.0.0

递归锁

1. 概述

递归锁是特殊的互斥量,允许同一任务多次获取和释放锁,而不会造成死锁;

获取和释放的次数必须相同;

递归锁的实现依赖于内部的uxRecursiveCallCount变量,它标记递归的次数,每次上锁加1,每次解锁减1,减为0才真正释放锁;

递归锁也不能在中断内使用;

使用递归锁需开启configUSE_RECURSIVE_MUTEXES宏。

2. 接口API

2.1 创建递归锁

// 动态创建
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
	#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif

// 静态创建
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
	#define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore ) xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, pxStaticSemaphore )
#endif /* configSUPPORT_STATIC_ALLOCATION */

参考FreeRTOS--互斥量 xQueueCreateMutex和xQueueCreateMutexStatic接口分析,递归锁和互斥量在创建时的差异仅为队列类型。

2.2 删除递归锁

#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )

需注意,不能在任务持有信号量的时候使用vSemaphoreDelete接口删除信号量!!!

2.3 获取递归锁

#if( configUSE_RECURSIVE_MUTEXES == 1 )
	#define xSemaphoreTakeRecursive( xMutex, xBlockTime )	xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
#endif

使用宏xSemaphoreTakeRecursive获取递归锁,实际调用的是xQueueTakeMutexRecursive:

#if ( configUSE_RECURSIVE_MUTEXES == 1 )

    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 = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );

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

#endif /* configUSE_RECURSIVE_MUTEXES */
  • 如果是当前任务已经持有了递归锁,则累加递归次数(uxRecursiveCallCount),并返回pdPASS;

  • 如果是其他任务持有,或者没人持有,调用xQueueGenericReceive尝试获取,如果获取成功,累加递归次数(uxRecursiveCallCount),并返回pdPASS,否则返回xQueueGenericReceive返回值;

xTaskGetCurrentTaskHandle返回了当前任务块TCB指针,定义在task.c,实现如下:

#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) )

	TaskHandle_t xTaskGetCurrentTaskHandle( void )
	{
	TaskHandle_t xReturn;

		/* A critical section is not required as this is not called from
		an interrupt and the current TCB will always be the same for any
		individual execution thread. */
		xReturn = pxCurrentTCB;

		return xReturn;
	}

#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */

2.4 释放递归锁

#if( configUSE_RECURSIVE_MUTEXES == 1 )
	#define xSemaphoreGiveRecursive( xMutex )	xQueueGiveMutexRecursive( ( xMutex ) )
#endif
#if ( configUSE_RECURSIVE_MUTEXES == 1 )

    BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
    {
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;

        configASSERT( pxMutex );

        /* If this is the task that holds the mutex then pxMutexHolder will not
        change outside of this task.  If this task does not hold the mutex then
        pxMutexHolder can never coincidentally equal the tasks handle, and as
        this is the only condition we are interested in it does not matter if
        pxMutexHolder is accessed simultaneously by another task.  Therefore no
        mutual exclusion is required to test the pxMutexHolder variable. */
        if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */
        {
            traceGIVE_MUTEX_RECURSIVE( pxMutex );

            /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to
            the task handle, therefore no underflow check is required.  Also,
            uxRecursiveCallCount is only modified by the mutex holder, and as
            there can only be one, no mutual exclusion is required to modify the
            uxRecursiveCallCount member. */
            ( pxMutex->u.uxRecursiveCallCount )--;

            /* Has the recursive call count unwound to 0? */
            if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
            {
                /* Return the mutex.  This will automatically unblock any other
                task that might be waiting to access the mutex. */
                ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            xReturn = pdPASS;
        }
        else
        {
            /* The mutex cannot be given because the calling task is not the
            holder. */
            xReturn = pdFAIL;

            traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
        }

        return xReturn;
    }

#endif /* configUSE_RECURSIVE_MUTEXES */
  • 如果是当前任务已经持有了递归锁,则递减递归次数(uxRecursiveCallCount),然后检查递归次数是否归0,是的话,调用xQueueGenericSend释放锁。不管计数是否归0,都返回pdPASS;

  • 如果是其他任务持有,或者没人持有,则返回pdFAIL;