FreeRTOS semaphore (5) ------ recursive mutex semaphore


1. Introduction to recursive mutex semaphore

The recursive mutex semaphore can be regarded as a special mutex semaphore. The task that has acquired the mutex semaphore cannot acquire the mutex semaphore again, but the recursive mutex semaphore is different, and the recursive mutex has been acquired The semaphore task can acquire this recursive mutex semaphore again, and the number of times is unlimited! The number of recursive mutex semaphores a task has successfully acquired using the function xSemaphoreTakeRecursive() has to be released using the function xSemaphoreGiveRecursive()! For example, if a task has successfully acquired 5 recursive semaphores, then this task must also release 5 recursive semaphores.

The recursive mutex semaphore also has a priority inheritance mechanism, so when the task finishes using the recursive mutex semaphore, you must remember to release it. Like the mutex semaphore, the recursive mutex semaphore cannot be used in the interrupt service function.

● Due to the existence of priority inheritance, the recursive mutex semaphore can only be used in tasks, not in interrupt service functions!
● The interrupt service function cannot set the blocking time.

To use the recursive mutex semaphore, the macro configUSE_RECURSIVE_MUTEXES must be 1!

Second, create a mutex semaphore

FreeRTOS provides two mutex semaphore creation functions, as shown in the following table:
insert image description here

1. xSemaphoreCreateRecursiveMutex()

This function is used to create a recursive mutex semaphore, and the required memory is allocated through dynamic memory management. This function is essentially a macro, and the function xQueueCreateMutex () that actually completes the creation of the semaphore is as follows:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

Parameters:
None.

Return value:
NULL: Mutex semaphore creation failed.
Other values: The handle to the successfully created mutex semaphore.

2. xSemaphoreCreateRecursiveMutexStatic()

This function also creates a recursive mutex semaphore, but the RAM required by the semaphore needs to be allocated by the user if this function is used to create a recursive mutex semaphore. This function is a macro, and the specific creation process is through the function xQueueCreateMutexStatic (). Completed, the function prototype is as follows:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )

Parameters:
pxMutexBuffer: This parameter points to a variable of type StaticSemaphore_t, which is used to save the semaphore structure.

Return value:
NULL: Mutex semaphore creation failed.
Other values: The handle to the successfully created mutex semaphore.

3. Analysis of recursive semaphore creation process

Here we only analyze the dynamic creation of mutex semaphore function xSemaphoreCreateRecursiveMutex (), this function is a macro, defined as follows:

#define xSemaphoreCreateRecursiveMutex() 
xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

It can be seen that the real function is the function xQueueCreateMutex(), which is also used to create the mutex semaphore, but the type is selected as queueQUEUE_TYPE_RECURSIVE_MUTEX when creating the recursive mutex semaphore.

Fourth, release the recursive mutex semaphore

The recursive mutex semaphore has a dedicated release function: xSemaphoreGiveRecursive(), which is a macro, as follows:

#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )

The parameter of the function is the recursive mutex semaphore to be released. The real release is
done by the function xQueueGiveMutexRecursive(). The code of this function is as follows:

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
    
    
	BaseType_t xReturn;
	Queue_t * const pxMutex = ( Queue_t * ) xMutex;
	configASSERT( pxMutex );
	//检查递归互斥信号量是不是被当前任务获取的,要释放递归互斥信号量的任务肯定是当
	//前正在运行的任务。 因为同互斥信号量一样,递归互斥信号量的获取和释放要在同一个
	//任务中完成!如果当前正在运行的任务不是递归互斥信号量的拥有者就不能释放!
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) (1)
	{
    
    
		traceGIVE_MUTEX_RECURSIVE( pxMutex );
		( pxMutex->u.uxRecursiveCallCount )--; (2)
		if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ) (3)
		{
    
    
			( void ) xQueueGenericSend( pxMutex, NULL, \ (4)
			queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
		}
		else
		{
    
    
			mtCOVERAGE_TEST_MARKER();
		}
		xReturn = pdPASS; (5)
	}
	else
	{
    
    
		xReturn = pdFAIL; (6)
		traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}
	return xReturn;
}

(1), which task acquires the recursive mutex semaphore, which task will release it! The task to release the recursive mutex must be the currently running task. Check whether this task is the owner of the recursive mutex semaphore, if not, the release cannot be completed.

(2), uxRecursiveCallCount minus one, uxRecursiveCallCount is used to record the number of times the recursive semaphore is acquired. Since the recursive mutex semaphore can be acquired multiple times by a task, it must be released multiple times when it is released, but only when it is released for the last time will the function xQueueGenericSend() be called to complete the release process, and other times it is just simple Just subtract one from uxRecursiveCallCount.

(3). When uxRecursiveCallCount is 0, it means that it is released for the last time.

(4) If it is the last release, call the function xQueueGenericSend() to complete the real release process. The blocking time is queueMUTEX_GIVE_BLOCK_TIME, and the macro queueMUTEX_GIVE_BLOCK_TIME is 0.

(5) The recursive mutex semaphore is successfully released, and pdPASS is returned.

(6). The release of the recursive mutex semaphore fails, and pdFAIL is returned.

Since the recursive mutex semaphore can be repeatedly acquired by a task, it must be released multiple times when it is released, but the function xQueueGenericSend() will be called to complete the real release only when it is released for the last time. For other releases, simply decrease uxRecursiveCallCount by one.

Five, get the recursive mutex semaphore

The acquisition of the recursive mutex semaphore uses the function xSemaphoreTakeRecursive(), which is a macro and is defined as follows:

#define xSemaphoreTakeRecursive( xMutex, xBlockTime )
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

The first parameter of the function is the recursive mutex semaphore handle to be obtained, and the second parameter is the blocking time. The real acquisition process is completed by the function xQueueTakeMutexRecursive(), which is as follows:

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, //要获取的信号量
TickType_t xTicksToWait )//阻塞时间
{
    
    
	BaseType_t xReturn;
	Queue_t * const pxMutex = ( Queue_t * ) xMutex;
	configASSERT( pxMutex );
	traceTAKE_MUTEX_RECURSIVE( pxMutex );
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) (1)
	{
    
    
		( pxMutex->u.uxRecursiveCallCount )++; (2)
		xReturn = pdPASS;
	}
	else
	{
    
    
		xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); (3)
		if( xReturn != pdFAIL )
		{
    
    
			( pxMutex->u.uxRecursiveCallCount )++; (4)
		}
		else
		{
    
    
			raceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
		}
	}
	return xReturn;
}

(1), judging whether the current task to obtain the recursive mutex semaphore is already the owner of the recursive mutex semaphore. Through this step, it can be judged whether the current task acquires the recursive mutex semaphore for the first time or repeatedly acquires it.

(2) If the current task is already the owner of the recursive mutex semaphore, it means that the task has acquired the recursive mutex semaphore. This time, the recursive mutex semaphore is obtained repeatedly, so simply add 1 to uxRecursiveCallCount , and then return pdPASS to indicate successful acquisition.

(3) If the task is to obtain the recursive mutex semaphore for the first time, the function xQueueGenericReceive() needs to be called to complete the real acquisition process.

(4) After the first successful acquisition of the recursive mutex semaphore, add one to uxRecursiveCallCount.

6. Example of using recursive mutex semaphore

FreeRTOS officially provides a simple example, you can refer to it, the example is as follows:

SemaphoreHandle_t RecursiveMutex; //递归互斥信号量句柄
//某个任务中创建一个递归互斥信号量
void vATask( void * pvParameters )
{
    
    
	//没有创建创建递归互斥信号量之前不要使用!
	RecursiveMutex = xSemaphoreCreateRecursiveMutex(); //创建递归互斥信号量
	for( ;; )
	{
    
     
		/************任务代码**************/
	}
}

//任务调用的使用递归互斥信号量的功能函数。
void vAFunction( void )
{
    
    
	/**********其他处理代码*****************/
	if( xMutex != NULL )
	{
    
    
	//获取递归互斥信号量,阻塞时间为 10 个节拍
		if( xSemaphoreTakeRecursive( RecursiveMutex, 10 ) == pdTRUE )
		{
    
    
			/***********其他处理过程*************/
			//这里为了演示,所以是顺序的获取递归互斥信号量,但是在实际的代码中肯定
			//不是这么顺序的获取的,真正的代码中是混合着其他程序调用的。
			xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
			xSemaphoreTakeRecursive( RecursiveMutex, ( TickType_t ) 10 );
			//任务获取了三次递归互斥信号量,所以就得释放三次!
			xSemaphoreGiveRecursive( RecursiveMutex);
			xSemaphoreGiveRecursive( RecursiveMutex);
			xSemaphoreGiveRecursive( RecursiveMutex);
			//递归互斥信号量释放完成,可以被其他任务获取了
		}
		else
		{
    
     
			/**********递归互斥信号量获取失败***********/
		}
	}
}

Guess you like

Origin blog.csdn.net/Dustinthewine/article/details/130416793