FreeRTOS 信号量(五) ------ 递归互斥信号量


一、递归互斥信号量简介

递归互斥信号量可以看作是一个特殊的互斥信号量,已经获取了互斥信号量的任务就不能再次获取这个互斥信号量,但是递归互斥信号量不同,已经获取了递归互斥信号量的任务可以再次获取这个递归互斥信号量,而且次数不限!一个任务使用函数 xSemaphoreTakeRecursive()成功的获取了多少次递归互斥信号量就得使用函数 xSemaphoreGiveRecursive()释放多少次!比如某个任务成功的获取了 5 次递归信号量,那么这个任务也得同样的释放 5 次递归信号量。

递归互斥信号量也有优先级继承的机制,所以当任务使用完递归互斥信号量以后一定要记得释放。同互斥信号量一样,递归互斥信号量不能用在中断服务函数中。

● 由于优先级继承的存在,就限定了递归互斥信号量只能用在任务中,不能用在中断服务函数中!
● 中断服务函数不能设置阻塞时间。

要使用递归互斥信号量的话宏 configUSE_RECURSIVE_MUTEXES 必须为 1!

二、创建互斥信号量

FreeRTOS 提供了两个互斥信号量创建函数,如下表所示:
在这里插入图片描述

1. xSemaphoreCreateRecursiveMutex()

此函数用于创建一个递归互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex (),此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )

参数:
无。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

2. xSemaphoreCreateRecursiveMutexStatic()

此函数也是创建递归互斥信号量的,只不过使用此函数创建递归互斥信号量的话信号量所需 要 的 RAM 需 要 由 用 户 来 分 配 , 此 函 数 是 个 宏 , 具 体 创 建 过 程 是 通 过 函 数xQueueCreateMutexStatic ()来完成的,函数原型如下:

SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer )

参数:
pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

三、递归信号量创建过程分析

这里只分析动态创建互斥信号量函数 xSemaphoreCreateRecursiveMutex (),此函数是个宏,定义如下:

#define xSemaphoreCreateRecursiveMutex() 
xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

可以看出,真正干事的是函数 xQueueCreateMutex(),互斥信号量的创建也是用的这个函数,只是在创建递归互斥信号量的时候类型选择为queueQUEUE_TYPE_RECURSIVE_MUTEX。

四、释放递归互斥信号量

递归互斥信号量有专用的释放函数:xSemaphoreGiveRecursive(),此函数为宏,如下:

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

函 数 的 参 数 就 是 就 是 要 释 放 的 递 归 互 斥 信 号 量 , 真 正 的 释 放 是 由 函 数
xQueueGiveMutexRecursive()来完成的,此函数代码如下:

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)、哪个任务获取到的递归互斥信号量,哪个任务就释放!要释放递归互斥信号量的任务肯定是当前正在运行的任务。检查这个任务是不是递归互斥信号量的拥有者,如果不是的话就不能完成释放。

(2)、uxRecursiveCallCount 减一,uxRecursiveCallCount 用来记录递归信号量被获取的次数。由于递归互斥信号量可以被一个任务多次获取,因此在释放的时候也要多次释放,但是只有在最后一次释放的时候才会调用函数 xQueueGenericSend()完成释放过程,其他的时候只是简单的将 uxRecursiveCallCount 减一即可。

(3)、当 uxRecursiveCallCount 为 0 的时候说明是最后一次释放了。

(4)、如果是最后一次释放的话就调用函数 xQueueGenericSend()完成真正的释放过程。阻塞时间是 queueMUTEX_GIVE_BLOCK_TIME,宏queueMUTEX_GIVE_BLOCK_TIME 为 0。

(5)、递归互斥信号量释放成功,返回 pdPASS。

(6)、递归互斥信号量释放未成功,返回 pdFAIL。

由于递归互斥信号量可以被一个任务重复的获取,因此在释放的时候也要释放多次,但是只有在最后一次释放的时候才会调用函数 xQueueGenericSend()完成真正的释放。其他释放的话只是简单的将 uxRecursiveCallCount 减一。

五、获取递归互斥信号量

递归互斥信号量的获取使用函数 xSemaphoreTakeRecursive(),此函数是个宏,定义如下:

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

函数第一个参数是要获取的递归互斥信号量句柄,第二个参数是阻塞时间。真正的获取过程是由函数 xQueueTakeMutexRecursive()来完成的,此函数如下:

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)、判断当前要获取递归互斥信号量的任务是不是已经是递归互斥信号量的拥有者。通过这一步就可以判断出当前任务是第一次获取递归互斥信号量还是重复获取。

(2)、如果当前任务已经是递归互斥信号量的拥有者,那就说明任务已经获取了递归互斥信号量,本次是重复获取递归互斥信号量,那么就简单的将 uxRecursiveCallCount 加一,然后返回pdPASS 表示获取成功。

(3)、如果任务是第一次获取递归互斥信号量的话就需要调用函数xQueueGenericReceive()完成真正的获取过程。

(4)、第一次获取递归互斥信号量成功以后将 uxRecursiveCallCount 加一。

六、递归互斥信号量使用示例

FreeRTOS 官方提供了一个简单的示例,大家可以参考一下,示例如下:

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
		{
    
     
			/**********递归互斥信号量获取失败***********/
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Dustinthewine/article/details/130416793