FreeRTOS个人笔记-互斥量

根据个人的学习方向,学习FreeRTOS。由于野火小哥把FreeRTOS讲得比较含蓄,打算在本专栏尽量细化一点。作为个人笔记,仅供参考或查阅。

配套资料:FreeRTOS内核实现与应用开发实战指南、野火FreeRTOS配套视频源码、b站野火FreeRTOS视频。搭配来看更佳哟!!!

互斥量

互斥量,是一种特殊的二值信号量,它和信号量不同的是,
它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。任意时刻互斥量的状态只有两种,开锁或闭锁。
当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。
当该任务释放这个互斥量时,该互斥量处于开锁状态, 任务失去该互斥量的所有权。
当一个任务持有互斥量时,其他任务将不能再对该互斥量进行开锁或持有。持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性,
这个特性与一般的信号量有很大的不同,在信号量中,由于已经不存在可用的信号量, 任务递归获取信号量时会发生主动挂起任务最终形成死锁。

二值信号量和互斥信号量非常相似,都可以用于临界资源访问也可以用于同步功能。但是有一些细微差别:
互斥信号量有优先级继承机制, 二值信号量则没有这个机制。
这使得二值信号量更偏向应用于同步功能(任务与任务间的同步或任务和中断间同步), 而互斥信号量更偏向应用于临界资源的访问。

用于互锁的互斥量可以充当保护资源的令牌, 当一个任务希望访问某个资源时,它必须先获取令牌。当任务使用完资源后,必须还回令牌,以便其它任务可以访问该资源。
当任务获取到信号量的时候才能开始使用被保护的资源,使用完就释放信号量,下一个任务才能获取到信号量从而可用使用被保护的资源。
但是信号量会导致的另一个潜在问题,那就是任务优先级翻转 。 而 FreeRTOS 提供的互斥量可以通过优先级继承算法, 可以降低优先级翻转问题产生的影响,
所以,用于临界资源的保护一般建议使用互斥量。
优先级继承算法是指,假设某个低优先级的任务占有某种资源,可暂时提高该任务优先级,使该任务与在所有等待该资源的任务中优先级最高那个任务的优先级相等,
而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。因此,继承优先级的任务避免了系统资源被任何中间优先级的任务抢占。
这个优先级继承机制确保高优先级任务进入阻塞状态的时间尽可能短,以及将已经出现的“优先级翻转”危害降低到最小。

在很多场合中,某些资源只有一个,当低优先级任务正在占用该资源的时候,即便高优先级任务也只能乖乖的等待低优先级任务使用完该资源后释放资源。
这里高优先级任务无法运行而低优先级任务可以运行的现象称为“优先级翻转”。

对于第一点,L 任务正在使用某临界资源, H 任务被唤醒,执行 H 任务。但 L 任务并未执行完毕,此时临界资源还未释放。 

对于第二点,这个时刻 H 任务也要对该临界资源进行访问,但 L 任务还未释放资源,由于保护机制,H 任务进入阻塞态,L 任务得以继续运行,此时已经发生了优先级翻转现象。

对于第三点,某个时刻 M 任务被唤醒,由于 M 任务的优先级高于 L 任务, M 任务抢占了 CPU 的使用权,M任务开始运行,此时 L 任务尚未执行完,临界资源还没被释放。

对于第四点,M 任务运行结束,归还 CPU 使用权,L 任务继续运行。

对于第五点,L任务运行结束,释放临界资源,H 任务得以对资源进行访问,H 任务开始运行。

对于第一点,L 任务正在使用某临界资源,L 任务正在使用某临界资源, H 任务被唤醒,执行 H 任务。但 L 任务并未执行完毕,此时临界资源还未释放。

对于第二点,某一时刻 H 任务也要对该资源进行访问,由于保护机制,H 任务进入阻塞态。此时发生优先级继承,系统将 L 任务的优先级暂时提升到与 H 任务优先级相同,L任务继续执行。

对于第三点,在某一时刻 M 任务被唤醒,由于此时 M 任务的优先级暂时低于 L 任务,所以 M 任务仅在就绪态,而无法获得 CPU 使用权。

对于第四点,L任务运行完毕,H 任务获得对资源的访问权,H 任务从阻塞态变成运行态,此时 L 任务的优先级会变回原来的优先级。

对于第五点,当 H 任务运行完毕,M任务得到 CPU 使用权,开始执行。

对于第六点,系统正常运行,按照设定好的优先级运行。

互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。在初始化的时候,互斥量处于开锁的状态,而被任务持有的时候则立刻转为闭锁的状态。
互斥量更适合于:可能会引起优先级翻转的情况。
递归互斥量更适用于:任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。

互斥量不能在中断服务函数中使用,因为其特有的优先级继承机制只在任务起作用,在中断的上下文环境毫无意义。

互斥量结构体

如果使用信号量或者互斥量,需要包含 semphr.h 头文件。 
FreeRTOS 的互斥量控制块结构体与消息队列结构体是一模一样的, 只不过结构体中某些成员变量代表的含义不一样。

typedef struct QueueDefinition
{
	int8_t *pcHead;					//队列头指针
	int8_t *pcTail;					//队列尾指针
	int8_t *pcWriteTo;				//指向队列消息存储区下一个可用消息空间

	//pcReadFrom 与 uxRecursiveCallCount 是一对互斥变量, 使用联合体用来确保两个互斥的结构体成员不会同时出现。 
	//当结构体用于队列时,pcReadFrom 指向出队消息空间的最后一个,即读取消息时候是从 pcReadFrom 指向的空间读取消息内容
	//当结构体用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用” 的次数。
	union							
	{
		int8_t *pcReadFrom;			
		UBaseType_t uxRecursiveCallCount;
	}u;

	//发送消息阻塞列表,用于保存阻塞在此队列的任务,任务按照优先级进行排序,由于队列已满,想要发送消息的任务无法发送消息。
	//获取消息阻塞列表,用于保存阻塞在此队列的任务,任务按照优先级进行排序,由于队列是空的,想要获取消息的任务无法获取到消息。
	List_t xTasksWaitingToSend;		
	List_t xTasksWaitingToReceive;	

	//uxMessagesWaiting用于消息队列,记录当前消息队列的消息个数
	//uxMessagesWaiting用于互斥量,表示有效信号量的个数。1:有效,0:无效
	//uxLength用于消息队列,表示队列长度,即能存放多少消息
	//uxLength用于互斥量,表示最大的信号量可用个数,最大为1。因为信号量要么是有效的,要么是无效的。
	//uxItemSize用于消息队列,表示单个消息的大小
	//uxItemSize用于互斥量,则无需存储空间,为 0 即可。
	volatile UBaseType_t uxMessagesWaiting;
	UBaseType_t uxLength;			
	UBaseType_t uxItemSize;			

	//这两个成员变量为 queueUNLOCKED 时,表示队列未上锁;当这两个成员变量为 queueLOCKED_UNMODIFIED 时,表示队列上锁。
	//队列上锁后,储存从队列收到的列表项数目,也就是出队的数量,如果队列没有上锁,设置为 queueUNLOCKED
	//队列上锁后,储存发送到队列的列表项数目,也就是入队的数量,如果队列没有上锁,设置为 queueUNLOCKED。
	volatile int8_t cRxLock;		
	volatile int8_t cTxLock;		

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;	
	#endif

	#if ( configUSE_QUEUE_SETS == 1 )
		struct QueueDefinition *pxQueueSetContainer;
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t uxQueueNumber;
		uint8_t ucQueueType;
	#endif

} xQUEUE;
typedef xQUEUE Queue_t;

创建互斥量

xSemaphoreCreateMutex()用于创建一个互斥量,并返回一个互斥量句柄。该句柄的原型是一个 void 型的指针,在使用之前必须先由用户定义一个互斥量句柄。

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif

不是递归的互斥量由函数 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic()创建,且只能被同一个任务获取一次, 如果同一个任务想再次获取则会失败。


xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量。
它可以被同一个任务获取很多次,获取多少次就需要释放多少次。递归信号量与互斥量一样,都实现了优先级继承机制,可以降低优先级反转的危害。
如果创建成功则返回一个递归互斥量句柄,用于访问创建的递归互斥量。 如果创建不成功则返回 NULL。

#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
	#define xSemaphoreCreateRecursiveMutex() 	xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

不管是 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic(),还是xSemaphoreCreateRecursiveMutex(),都是在xQueueCreateMutex()函数下做宏扩展。

xQueueCreateMutex()函数如下

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

#endif /* configUSE_MUTEXES */

回顾一下xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );
xQueueGenericCreate()为消息队列创建函数,uxMutexLength为队列长度,uxMutexSize为单个消息大小,ucQueueType为队列类型。

xQueueGenericCreate()用到了prvInitialiseMutex( ),prvInitialiseMutex( )如下

#define pxMutexHolder					pcTail
#define uxQueueType						pcHead
#define queueQUEUE_IS_MUTEX				NULL

#if( configUSE_MUTEXES == 1 )

	static void prvInitialiseMutex( Queue_t *pxNewQueue )
	{
		if( pxNewQueue != NULL )
		{			
			pxNewQueue->pxMutexHolder = NULL;
			pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
			
			pxNewQueue->u.uxRecursiveCallCount = 0;

			traceCREATE_MUTEX( pxNewQueue );
		
			( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
		}
		else
		{
			traceCREATE_MUTEX_FAILED();
		}
	}

#endif /* configUSE_MUTEXES */

xSemaphoreCreateMutex()函数实例

SemaphoreHandle_t MuxSem_Handle;

void vATask( void * pvParameters )
{
	/* 创建一个互斥量 */
	MuxSem_Handle= xSemaphoreCreateMutex();
	
	if (MuxSem_Handle!= NULL ) 
	{
		/* 互斥量创建成功 */
	}
}

xSemaphoreCreateRecursiveMutex()函数实例

SemaphoreHandle_t xMutex;

void vATask( void * pvParameters )
{
	/* 创建一个递归互斥量 */
	xMutex = xSemaphoreCreateRecursiveMutex();

	if ( xMutex != NULL ) 
	{
		/* 递归互斥量创建成功 */
	}
 }


删除互斥量

vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递归互斥量。 如果有任务阻塞在该信号量上,那么不要删除该信号量。 
删除信号量过程其实就是删除消息队列过程, 因为信号量其实就是消息队列, 只不过是无法存储消息的队列而已。

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

获取互斥量 

当互斥量处于开锁的状态,任务才能获取互斥量成功,当任务持有了某个互斥量的时候,其它任务就无法获取这个互斥量,需要等到持有互斥量的任务进行释放后,其他任务才能获取成功,任务通过互斥量获取函数来获取互斥量的所有权。 
任务对互斥量的所有权是独占的,任意时刻互斥量只能被一个任务持有,如果互斥量处于开锁状态,那么获取该互斥量的任务将成功获得该互斥量,并拥有互斥量的使用权;
如果互斥量处于闭锁状态,获取该互斥量的任务将无法获得互斥量,任务将被挂起,在任务被挂起之前,会进行优先级继承,如果当前任务优先级比持有互斥量的任务优先级高,那么将会临时提升持有互斥量任务的优先级。

#define xSemaphoreTake( xSemaphore, xBlockTime )		xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL , (xBlockTime ) , pdFALSE ) 

如果获取的对象是互斥量,那么这个函数就拥有优先级继承算法,如果获取对象不是互斥量,就没有优先级继承机制。

BaseType_t xQueueGenericReceive( QueueHandle_t 		xQueue,
								 void * const 		pvBuffer,
								 TickType_t 		xTicksToWait,
								 const BaseType_t 	xJustPeeking )
{
	BaseType_t 	xEntryTimeSet = pdFALSE;
	TimeOut_t 	xTimeOut;
	int8_t 		*pcOriginalReadPosition;
	Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	/* 已删除一些断言 */

	for ( ;; ) 
	{
		taskENTER_CRITICAL();
		{			
			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; 
			/* 看看队列中有没有消息 */
			if ( uxMessagesWaiting > ( UBaseType_t ) 0 ) 
			{
				/*防止仅仅是读取消息,而不进行消息出队操作*/
				pcOriginalReadPosition = pxQueue->u.pcReadFrom;
			
				/* 拷贝消息到用户指定存放区域 pvBuffer */
				prvCopyDataFromQueue( pxQueue, pvBuffer );
			
				if ( xJustPeeking == pdFALSE ) 
				{
					/* 读取消息并且消息出队 */
					traceQUEUE_RECEIVE( pxQueue );
			
					/* 获取了消息,当前消息队列的消息个数需要减一 */
					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
			
					/* 如果系统支持使用互斥量 */
					#if ( configUSE_MUTEXES == 1 )
					{
						/* 如果队列类型是互斥量 */
						if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX) 
						{
							/* 获取当前任务控制块 */ 
							pxQueue->pxMutexHolder =( int8_t * )pvTaskIncrementMutexHeldCount();
						} else 
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif
			
					/* 判断一下消息队列中是否有等待发送消息的任务 */
					if ( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE) 
					{
						/* 将任务从阻塞中恢复 */
						if ( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend))!= pdFALSE )
						{
							/* 如果被恢复的任务优先级比当前任务高,会进行一次任务切换 */
							queueYIELD_IF_USING_PREEMPTION();
						} else 
						{
							mtCOVERAGE_TEST_MARKER();
						}
					} else 
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
						
				taskEXIT_CRITICAL();
				return pdPASS;
			}	/* 消息队列中没有消息可读 */		
			else 
			{
				if ( xTicksToWait == ( TickType_t ) 0 ) 
				{
					/* 不等待,直接返回 */
					taskEXIT_CRITICAL();
					traceQUEUE_RECEIVE_FAILED( pxQueue );
					return errQUEUE_EMPTY;
				} else if ( xEntryTimeSet == pdFALSE ) 
				{
					/* 初始化阻塞超时结构体变量,初始化进入阻塞的时间 xTickCount 和溢出次数 xNumOfOverflows */
					vTaskSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				} else 
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* 检查超时时间是否已经过去了*/
		if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE ) 
		{
			/* 如果队列还是空的 */
			if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) 
			{
				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );

				/* 如果系统支持使用互斥量 */
				#if ( configUSE_MUTEXES == 1 )
				{
					/* 如果队列类型是互斥量 */
					if ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) 
					{
						taskENTER_CRITICAL();
						{
							/* 进行优先级继承 */
							vTaskPriorityInherit((void*)pxQueue->pxMutexHolder); 
						}
						taskEXIT_CRITICAL();
					} else 
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif

				/* 将当前任务添加到队列的等待接收列表中以及阻塞延时列表,阻塞时间为用户指定的超时时间 xTicksToWait */

				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
				prvUnlockQueue( pxQueue );
				if ( xTaskResumeAll() == pdFALSE ) 
				{
					/* 如果有任务优先级比当前任务高,会进行一次任务切换 */
					portYIELD_WITHIN_API();
				} else 
				{
					mtCOVERAGE_TEST_MARKER();
				}
			} else 
			{
				/* 如果队列有消息了,就再试一次获取消息 */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		} else 
		{
			/* 超时时间已过,退出 */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			if ( prvIsQueueEmpty( pxQueue ) != pdFALSE ) 
			{
				/* 如果队列还是空的,返回错误代码 errQUEUE_EMPTY */
				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			} else 
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
 }

vTaskPriorityInherit()函数进行优先级继承,vTaskPriorityInherit()如下

#if ( configUSE_MUTEXES == 1 )

void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
	TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; 


	if ( pxMutexHolder != NULL ) 
	{
		/* 判断当前任务与持有互斥量任务的优先级 */
		if ( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) 	//进行优先级继承
		{
			//持有互斥量的任务在等待事件列表中,就调整互斥锁持有者等待的事件列表项的优先级,因为待会会暂时修改持有互斥量任务的优先级。
			if ( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) 
			{
				/* 调整互斥锁持有者等待的事件列表项的优先级 */
				listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); 
			} else 
			{
				mtCOVERAGE_TEST_MARKER();
			}
			
			/* 如果被提升优先级的任务处于就绪列表中,如果修改了任务的优先级,那么在就绪列表中的任务也要重新排序*/
			if (listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) 
			{
				/* 先将任务从就绪列表中移除 */
				if ( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 
				{
					taskRESET_READY_PRIORITY( pxTCB->uxPriority ); 
				} else 
				{
					mtCOVERAGE_TEST_MARKER();
				
					/* 暂时提升持有互斥量任务的优先级,提升到与当前任务优先级一致*/
					pxTCB->uxPriority = pxCurrentTCB->uxPriority; 
				
					/* 再插入就绪列表中 */
					prvAddTaskToReadyList( pxTCB ); 
				} else 
				{
					/* 如果任务不是在就绪列表中,就仅仅是提升任务优先级即可 */
					pxTCB->uxPriority = pxCurrentTCB->uxPriority; 
				}

				traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );
			} else 
			{
				mtCOVERAGE_TEST_MARKER();
			}
		} else 
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}
#endif /* configUSE_MUTEXES */	

xSemaphoreTake()函数实例 

static void HighPriority_Task(void* parameter)
{
	BaseType_t xReturn = pdTRUE;	/* 定义一个创建信息返回值,默认为 pdTRUE */
	
	while (1) 
	{
		printf("HighPriority_Task 获取信号量\n"); 
		
		//获取互斥量 MuxSem,没获取到则一直等待
		xReturn = xSemaphoreTake(MuxSem_Handle,portMAX_DELAY); 
		if (pdTRUE == xReturn)
			printf("HighPriority_Task Runing\n");
		LED1_TOGGLE;
		
		//处理临界资源
		printf("HighPriority_Task 释放信号量!\r\n");
		xSemaphoreGive( MuxSem_Handle );//释放互斥量

		vTaskDelay(1000);
	}
 }

xSemaphoreTakeRecursive()用于获取递归互斥量。互斥量之前必须由xSemaphoreCreateRecursiveMutex()这个函数创建。

要注意的是该函数不能用于获取由函数xSemaphoreCreateMutex()创建的互斥量。

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

xBlockTime,如果不是持有互斥量的任务去获取无效的互斥量,那么任务将进行等待用户指定超时时间,单位为 tick(即系统节拍周期)。 
获取成功则返回 pdTRUE, 在超时之前没有获取成功则返回 errQUEUE_EMPTY。

#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 );
	
	traceTAKE_MUTEX_RECURSIVE( pxMutex );
	
	
	if ( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle())		/* 如果持有互斥量的任务就是当前任务 */
	{
		/* u.uxRecursiveCallCount 自加,表示调用了多少次递归互斥量获取 */
		( pxMutex->u.uxRecursiveCallCount )++;
		xReturn = pdPASS;	
	} else 	/* 如果持有递归互斥量的任务不是当前任务,就只能等待递归互斥量被释放 */
	{	
		xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); 
		
		if ( xReturn != pdFAIL ) 
		{
			/* 获取递归互斥量成功,记录递归互斥量的获取次数 */
			( pxMutex->u.uxRecursiveCallCount )++; 
		} else 
		{
			traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
		}
	}
	
	return xReturn;
}
#endif 

释放互斥量

任务想要访问某个资源的时候,需要先获取互斥量,然后进行资源访问,在任务使用完该资源的时候,必须要及时归还互斥量,这样别的任务才能对资源进行访问。
xSemaphoreGive()用于释放互斥量。互斥量的释放只能在任务中, 不允许在中断中释放互斥量。

#define xSemaphoreGive( xSemaphore )		xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

xQueueGenericSend()使用prvCopyDataToQueue()恢复任务的初始优先级
prvCopyDataToQueue()使用xTaskPriorityDisinherit()恢复任务的初始优先级

xSemaphoreGiveRecursive()用于释放递归互斥量。已经获取递归互斥量的任务可以重复获取该递归互斥量。

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

使用 xSemaphoreTakeRecursive() 函数成功获取几次递归互斥量,就要使用 xSemaphoreGiveRecursive()函数返还几次,在此之前递归互斥量都处于无效状态,别的任务就无法获取该递归互斥量。
只有已持有互斥量所有权的任务才能释放递归互斥量,每释放一次该递归互斥量,它的计数值就减 1。当该互斥量的计数值为 0 时(即持有任务已经释放所有的持有操作),互斥量则变为开锁状态,等待在该互斥量上的任务将被唤醒。 
如果任务的优先级被互斥量的优先级翻转机制临时提升,那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级。

#if ( configUSE_RECURSIVE_MUTEXES == 1 )

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

	configASSERT( pxMutex ); 
	
	/* 判断任务是否持有这个递归互斥量,只有拥有这个递归互斥量所有权的任务才能对其进行释放操作 */
	if ( pxMutex->pxMutexHolder == (void *)xTaskGetCurrentTaskHandle() )
	{ 
		traceGIVE_MUTEX_RECURSIVE( pxMutex );
	
		/* 调用次数的计数值减一 */
		( pxMutex->u.uxRecursiveCallCount )--; 
		
		/* 如果计数值减到 0 ,表明这个递归互斥量已经可以变得有效了*/
		if ( pxMutex->u.uxRecursiveCallCount==(UBaseType_t) 0 )
		{ 
			/* 释放成功,递归互斥量无效变有效 */
			( void ) xQueueGenericSend( pxMutex , NULL , queueMUTEX_GIVE_BLOCK_TIME , queueSEND_TO_BACK ); 
		} else 
		{
			mtCOVERAGE_TEST_MARKER();
		}
	
		xReturn = pdPASS;
	} else 
	{
		/* 这个任务不具备释放这个互斥量的权利 */
		xReturn = pdFAIL; 
		
		traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}
	
	return xReturn;
}
	
#endif /* configUSE_RECURSIVE_MUTEXES */

互斥量和递归互斥量的最大区别在于一个递归互斥量可以被已经获取这个递归互斥量的任务重复获取,而不会形成死锁。 
这个递归调用功能是通过队列结构体成员 u.uxRecursiveCallCount 实现的,这个变量用于存储递归调用的次数,每次获取递归互斥量后,这个变量加 1,在释放递归互斥量后,这个变量减 1。
只有这个变量减到 0,即释放和获取的次数相等时,互斥量才能变成有效状态,然后才允许使用 xQueueGenericSend()函数释放一个递归互斥量。

xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()函数实例 

SemaphoreHandle_t xMutex = NULL;

void vATask( void * pvParameters )
{
	/* 创建一个递归互斥量用于保护共享资源 */
	xMutex = xSemaphoreCreateRecursiveMutex();
}

void vAnotherTask( void * pvParameters )
 {
	/* 其他功能代码 */

	if ( xMutex != NULL ) 
	{
		/* 尝试获取递归互斥量,如果不可用则等待 10 个 ticks */
		if(xSemaphoreTakeRecursive(xMutex,( TickType_t ) 10 )== pdTRUE) 
		{
			/* 获取到递归信号量,可以访问共享资源 */
			/* ... 其他功能代码 */
			
			/* 重复获取递归互斥量 */
			xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
			xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
			
			/* 释放递归互斥量,获取了多少次就要释放多少次 */
			xSemaphoreGiveRecursive( xMutex );
			xSemaphoreGiveRecursive( xMutex );
			xSemaphoreGiveRecursive( xMutex );
			
			/* 现在递归互斥量可以被其他任务获取 */
		} else 
		{
			/* 没能成功获取互斥量,所以不能安全的访问共享资源 */
		}
	}
 }

互斥量实验

模拟优先级翻转实验

在 FreeRTOS 中创建了三个任务与一个二值信号量, 任务分别是高优先级任务,中优先级任务,低优先级任务, 用于模拟产生优先级翻转。 
低优先级任务在获取信号量的时候,被中优先级打断,中优先级的任务执行时间较长,因为低优先级还未释放信号量,那么高优先级任务就无法取得信号量继续运行,此时就发生了优先级翻转,任务在运行中,使用串口打印出相关信息。

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"

static TaskHandle_t AppTaskCreate_Handle 		= NULL;	/* 创建任务句柄 */
static TaskHandle_t LowPriority_Task_Handle 	= NULL;	/*LowPriority_Task 任务句柄 */
static TaskHandle_t MidPriority_Task_Handle 	= NULL;	/* MidPriority_Task 任务句柄 */
static TaskHandle_t HighPriority_Task_Handle 	= NULL;	/* HighPriority_Task 任务句柄 */

SemaphoreHandle_t BinarySem_Handle = NULL;

int main(void)
{
	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

	BSP_Init();
	/* 创建 AppTaskCreate 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )AppTaskCreate,				/* 任务入口函数 */
							(const char* )"AppTaskCreate",				/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )1,							/* 任务的优先级 */
							(TaskHandle_t* )&AppTaskCreate_Handle);		/* 任务控制块指针 */
 
	/* 启动任务调度 */
	if (pdPASS == xReturn)
		vTaskStartScheduler(); /* 启动任务,开启调度 */
	else
		return -1;

	while (1); /* 正常不会执行到这里 */
 }

static void AppTaskCreate(void)
{
	BaseType_t xReturn = pdPASS;	/* 定义一个创建信息返回值,默认为 pdPASS */

	taskENTER_CRITICAL(); 			//进入临界区

	/* 创建 Test_Queue */
	BinarySem_Handle = xSemaphoreCreateBinary();
	if (NULL != BinarySem_Handle)
		printf("BinarySem_Handle 二值信号量创建成功!\r\n");

	xReturn = xSemaphoreGive( BinarySem_Handle );			//给出二值信号量

	/* 创建 LowPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )LowPriority_Task, 			/* 任务入口函数 */
							(const char* )"LowPriority_Task",			/*任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL, 								/* 任务入口函数参数 */
							(UBaseType_t )2, 							/* 任务的优先级 */
							(TaskHandle_t* )&LowPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 LowPriority_Task 任务成功!\r\n");

	/* 创建 MidPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )MidPriority_Task, 			/* 任务入口函数 */
							(const char* )"MidPriority_Task",			/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )3,							/* 任务的优先级 */
							(TaskHandle_t*)&MidPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 MidPriority_Task 任务成功!\n");

	/* 创建 HighPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )HighPriority_Task, 		/* 任务入口函数 */
							(const char* )"HighPriority_Task",			/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )4, 							/* 任务的优先级 */
							(TaskHandle_t* )&HighPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 HighPriority_Task 任务成功!\n\n");

	vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务

	taskEXIT_CRITICAL(); //退出临界区
}

static void LowPriority_Task(void* parameter)
{
	static uint32_t i;
	BaseType_t xReturn = pdPASS;	/* 定义一个创建信息返回值,默认为 pdPASS */
	
	while (1) 
	{
		printf("LowPriority_Task 获取信号量\n");
		
		//获取二值信号量 xSemaphore,没获取到则一直等待
		xReturn = xSemaphoreTake( BinarySem_Handle , portMAX_DELAY ); 
		if ( xReturn == pdTRUE )
			printf("LowPriority_Task Runing\n\n");
		
		for (i=0; i<2000000; i++) 	//模拟低优先级任务占用信号量
		{ 
			taskYIELD();			//发起任务调度
		}
		
		printf("LowPriority_Task 释放信号量!\r\n");
		
		xReturn = xSemaphoreGive( BinarySem_Handle );	//给出二值信号量
		
		LED1_TOGGLE;

		vTaskDelay(500);
	}
}

static void MidPriority_Task(void* parameter)
{
	while (1) 
	{
		printf("MidPriority_Task Runing\n");
		vTaskDelay(500);
	}
}

static void HighPriority_Task(void* parameter)
{
	BaseType_t xReturn = pdTRUE;	/* 定义一个创建信息返回值,默认为 pdPASS */
	
	while (1)
	{
		printf("HighPriority_Task 获取信号量\n");
		
		//获取二值信号量 xSemaphore,没获取到则一直等待
		xReturn = xSemaphoreTake( BinarySem_Handle , portMAX_DELAY ); 
		if (pdTRUE == xReturn)
			printf("HighPriority_Task Runing\n");
		
		LED1_TOGGLE;
		
		xReturn = xSemaphoreGive( BinarySem_Handle );	//给出二值信号量
		
		vTaskDelay(500);
	}
}

互斥量实验

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"

static TaskHandle_t AppTaskCreate_Handle 		= NULL;	/* 创建任务句柄 */
static TaskHandle_t LowPriority_Task_Handle 	= NULL;	/*LowPriority_Task 任务句柄 */
static TaskHandle_t MidPriority_Task_Handle 	= NULL;	/* MidPriority_Task 任务句柄 */
static TaskHandle_t HighPriority_Task_Handle 	= NULL;	/* HighPriority_Task 任务句柄 */

SemaphoreHandle_t BinarySem_Handle = NULL;

int main(void)
{
	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */

	BSP_Init();
	/* 创建 AppTaskCreate 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )AppTaskCreate,				/* 任务入口函数 */
							(const char* )"AppTaskCreate",				/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )1,							/* 任务的优先级 */
							(TaskHandle_t* )&AppTaskCreate_Handle);		/* 任务控制块指针 */
 
	/* 启动任务调度 */
	if (pdPASS == xReturn)
		vTaskStartScheduler(); /* 启动任务,开启调度 */
	else
		return -1;

	while (1); /* 正常不会执行到这里 */
}

static void AppTaskCreate(void)
{
	BaseType_t xReturn = pdPASS;	/* 定义一个创建信息返回值,默认为 pdPASS */

	taskENTER_CRITICAL(); 			//进入临界区

	/* 创建 MuxSem */
	MuxSem_Handle = xSemaphoreCreateMutex();
	if (NULL != MuxSem_Handle)
		printf("MuxSem_Handle 互斥量创建成功!\r\n");

	xReturn = xSemaphoreGive( MuxSem_Handle );			//给出互斥量

	/* 创建 LowPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )LowPriority_Task, 			/* 任务入口函数 */
							(const char* )"LowPriority_Task",			/*任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL, 								/* 任务入口函数参数 */
							(UBaseType_t )2, 							/* 任务的优先级 */
							(TaskHandle_t* )&LowPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 LowPriority_Task 任务成功!\r\n");

	/* 创建 MidPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )MidPriority_Task, 			/* 任务入口函数 */
							(const char* )"MidPriority_Task",			/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )3,							/* 任务的优先级 */
							(TaskHandle_t*)&MidPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 MidPriority_Task 任务成功!\n");

	/* 创建 HighPriority_Task 任务 */
	xReturn = xTaskCreate(	(TaskFunction_t )HighPriority_Task, 		/* 任务入口函数 */
							(const char* )"HighPriority_Task",			/* 任务名字 */
							(uint16_t )512, 							/* 任务栈大小 */
							(void* )NULL,								/* 任务入口函数参数 */
							(UBaseType_t )4, 							/* 任务的优先级 */
							(TaskHandle_t* )&HighPriority_Task_Handle);	/*任务控制块指针 */
	if (pdPASS == xReturn)
		printf("创建 HighPriority_Task 任务成功!\n\n");

	vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务

	taskEXIT_CRITICAL(); //退出临界区
}

static void LowPriority_Task(void* parameter)
{
	static uint32_t i;
	BaseType_t xReturn = pdPASS;	/* 定义一个创建信息返回值,默认为 pdPASS */
	
	while (1) 
	{
		printf("LowPriority_Task 获取信号量\n");
		
		//获取互斥量 MuxSem ,没获取到则一直等待
		xReturn = xSemaphoreTake( MuxSem_Handle , portMAX_DELAY ); 
		if ( xReturn == pdTRUE )
			printf("LowPriority_Task Runing\n\n");
		
		for (i=0; i<2000000; i++) 	//模拟低优先级任务占用信号量
		{ 
			taskYIELD();			//发起任务调度
		}
		
		printf("LowPriority_Task 释放信号量!\r\n");	
		xReturn = xSemaphoreGive( MuxSem_Handle );	//给出互斥量
		
		LED1_TOGGLE;

		vTaskDelay(1000);
	}
}

static void MidPriority_Task(void* parameter)
{
	while (1) 
	{
		printf("MidPriority_Task Runing\n");
		vTaskDelay(1000);
	}
}

static void HighPriority_Task(void* parameter)
{
	BaseType_t xReturn = pdTRUE;	/* 定义一个创建信息返回值,默认为 pdPASS */
	
	while (1)
	{
		printf("HighPriority_Task 获取信号量\n");
		
		//获取二值信号量 xSemaphore,没获取到则一直等待
		xReturn = xSemaphoreTake( MuxSem_Handle , portMAX_DELAY ); 
		if (pdTRUE == xReturn)
			printf("HighPriority_Task Runing\n");
		
		LED1_TOGGLE;
		
		printf("HighPriority_Task 释放信号量!\r\n");
		xReturn = xSemaphoreGive( MuxSem_Handle );	//给出互斥量
		
		vTaskDelay(1000);
	}
}

至此,互斥量内容就已经结束,多数函数都已经被官方封装好,我们直接用就行。
xSemaphoreCreateMutex()                                              创建互斥量
xSemaphoreCreateRecursiveMutex()                              创建递归互斥量
        
vSemaphoreDelete( xSemaphore )                                  删除互斥量

xSemaphoreTake( xSemaphore, xBlockTime )                获取互斥量
xSemaphoreTakeRecursive( xMutex, xBlockTime )         获取递归互斥量

xSemaphoreGive( xSemaphore )                                    释放互斥量
xSemaphoreGiveRecursive( xMutex )                             释放递归互斥量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值