手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

8 篇文章 9 订阅
5 篇文章 11 订阅
本文详细解析了FreeRTOS操作系统中信号量和互斥量的源码,包括二进制信号量、计数型信号量的创建与释放,以及互斥量的创建、获取和释放过程。重点介绍了信号量和互斥量如何利用队列实现,并讨论了优先级继承在互斥量中的应用。
摘要由CSDN通过智能技术生成

FreeRTOS源码解析集合(全网最详细)
手把手教你FreeRTOS源码解析(一)——内存管理
手把手教你FreeRTOS源码详解(二)——任务管理
手把手教你FreeRTOS源码详解(三)——队列
手把手教你FreeRTOS源码详解(四)——信号量、互斥量、递归互斥量

信号量和互斥量是特殊的队列,关于队列的源码我们已经在前面讲解过了:
手把手教你FreeRTOS源码详解(三)——队列
本章我们来讲解一下信号量和互斥量的源码

1、信号量

信号量又分为二进制信号量和计数型信号量,二进制信号量相当于一个长度为一的队列,其消息数取值只能为0,1;计数型信号量创建时可以设置其队列长度与队列中的初始消息数,即其消息数的取值没有限制

1.1 创建二进制信号量xSemaphoreCreateBinary

二进制信号量的创建函数是对通用队列函数xQueueGenericCreate进行了简单的封装:

#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

利用xQueueGenericCreate函数创建的队列长度为1,队列项大小为semSEMAPHORE_QUEUE_ITEM_LENGTH,类型为二进制信号量queueQUEUE_TYPE_BINARY_SEMAPHORE

#define semSEMAPHORE_QUEUE_ITEM_LENGTH		( ( uint8_t ) 0U )

我们注意到如上宏定义,二进制信号量的队列项大小uxItemSize的实际大小为0,即二进制信号量实质上是一个队列长度为1,队列项大小为0的队列,程序实际使用的只有队列结构体,利用uxMessagesWaiting来判断二进制信号量这个“特殊的队列”中是否有信号量,当uxMessagesWaiting为0时,队列中就没有可获取的信号量,当uxMessagesWaiting为1时,队列中已经有信号量了并且无法再继续释放信号量

1.2 创建计数型信号量xSemaphoreCreateCounting

#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

创建计数型信号量的源码如上所示,入口参数有两个,uxMaxCount为最大计数值,uxInitialCount 为初始计数值,我们继续刨析其调用的函数
xQueueCreateCountingSemaphore:

xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

xQueueCreateCountingSemaphore实际调用的函数仍然是队列通用创建函数xQueueGenericCreate,最大计数值uxMaxCount作为所创建的队列长度,队列项大小仍为queueSEMAPHORE_QUEUE_ITEM_LENGTH(实际大小为0),类型为计数型信号量queueQUEUE_TYPE_COUNTING_SEMAPHORE ,最后用xHandle 来保存所创建队列的句柄

	if( xHandle != NULL )
	{
		( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;

		traceCREATE_COUNTING_SEMAPHORE();
	}

如果xHandle 不为空,即队列创建成功,则将队列中当前消息数uxMessagesWaiting 修改为初始计数值uxInitialCount

return xHandle;

最后将整个队列的起始地址作为句柄返回

1.3 释放信号量xSemaphoreGive

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

释放信号量函数xSemaphoreGive同样也是对写队列函数xQueueGenericSend进行了简单的封装,写入的内容为NULL,即实际上不写入内容,阻塞时间为semGIVE_BLOCK_TIME(为0),即释放信号量函数不会阻塞,采用的入队形式为后向入队queueSEND_TO_BACK
信号量队列项大小uxItemSize为0,我们来看一下拷贝入队函数prvCopyDataToQueue:

uxMessagesWaiting = pxQueue->uxMessagesWaiting;
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
	#if ( configUSE_MUTEXES == 1 )
	{
		if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
		{
			/* The mutex is no longer being held. */
			xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
			pxQueue->pxMutexHolder = NULL;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_MUTEXES */
}
else if( xPosition == queueSEND_TO_BACK )
{
	( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */
	pxQueue->pcWriteTo += pxQueue->uxItemSize;
	if( pxQueue->pcWriteTo >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */
	{
		pxQueue->pcWriteTo = pxQueue->pcHead;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}
else
{
	( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
	pxQueue->u.pcReadFrom -= pxQueue->uxItemSize;
	if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */
	{
		pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize );
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	if( xPosition == queueOVERWRITE )
	{
		if( uxMessagesWaiting > ( UBaseType_t ) 0 )
		{
			/* An item is not being added but overwritten, so subtract
			one from the recorded number of items in the queue so when
			one is added again below the number of recorded items remains
			correct. */
			--uxMessagesWaiting;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

当uxItemSize为0且不使用互斥锁时,该函数不会进行任何的拷贝操作

pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;

该函数此时主要的功能就是将队列中的消息数目增1,即信号“量”增1,从而实现了释放信号量的作用

1.4 获取信号量xSemaphoreTake

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

获取信号量函数xSemaphoreTake是对读队列函数xQueueGenericReceive进行了简单的封装,由于信号量不会实际上向队列中写入或者读取数据,因此读队列函数的数据存储区为NULL,获取信号量与释放信号量有些许不同,获取信号量是可以阻塞的,我们通过参数xBlockTime 就能看出
我们来看一下拷贝出队函数prvCopyDataFromQueue:

	static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer )
{
	if( pxQueue->uxItemSize != ( UBaseType_t ) 0 )
	{
		pxQueue->u.pcReadFrom += pxQueue->uxItemSize;
		if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */
		{
			pxQueue->u.pcReadFrom = pxQueue->pcHead;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports.  Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */
	}
}

该函数最开始就判断队列项大小是否不为0,但在信号量中队列项大小为0,因此获取信号量过程中也不会进行任何数据拷贝

pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;

该代码对队列中的当前消息数进行了减1操作,这段代码也是获取信号量xSemaphoreTake最为关键的,获取一次信号量,我们就对队列中的消息数uxMessagesWaiting减少1,从而实现了获取信号量的作用

2 互斥量

在信号量的使用过程中可能会出现优先级翻转,即高优先级的任务无法执行,这种情况下我们就可以采用互斥信号量,互斥信号量是一个拥有优先级继承的二进制信号量,可以降低优先级翻转带来的影响,但是不能消除优先级翻转

2.1 创建互斥信号量xSemaphoreCreateMutex

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

创建互斥信号量xSemaphoreCreateMutex实际调用的函数为xQueueCreateMutex,我们继续看xQueueCreateMutex源码:

const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );

互斥信号量队列长度uxMutexLength 为1,队列项大小uxMutexSize 为0,这点与普通的信号量相同,即互斥信号量实质上是一个队列长度为1,队列项大小为0,类型为ucQueueType 的队列

prvInitialiseMutex( pxNewQueue );

最后对互斥信号量的一些参数进行初始化,这里我们继续刨析prvInitialiseMutex源码:

		pxNewQueue->pxMutexHolder = NULL;
		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;

队列创建的时候会初始化部分结构体,但现在创建的是互斥信号量,部分与优先级继承相关的成员变量需要重新初始化,在结构体中,我们可能没有看到过pxMutexHolder 、uxQueueType ,其实它们都是在queue.c中定义的宏定义,如下:

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

pxMutexHolder 实际是队列尾pcTail,uxQueueType 实际是队列头pcHead,在初始化过程中将队列头pcHead(uxQueueType )指向NULL(queueQUEUE_IS_MUTEX)

pxNewQueue->u.uxRecursiveCallCount = 0;

uxRecursiveCallCount 是在递归互斥锁中使用的,我们在这里就不进行解释了

( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );

最后向互斥信号量这个队列中释放一次信号量,即互斥信号量初始化完成时,队列中就有信号量,不需用释放信号量就能直接获取一次信号量

2.2 获取互斥信号量xSemaphoreTake

无论是普通信号量还是互斥信号量,它们获取、释放信号量的函数都是一样的
获取信号量xSemaphoreTake实际调用的函数是xQueueGenericReceive,获取互斥信号量与普通信号量的区别如下:
1:

				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
						pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); 
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_MUTEXES */

用pxMutexHolder 来记录互斥锁的拥有者,这里主要是调用pvTaskIncrementMutexHeldCount函数来赋值的,该函数源码如下:

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

这个函数也十分简单,直接将当前任务的任务控制块成员变量uxMutexesHeld 增1,表明任务获取了一个互斥锁,最后返回当前任务的任务控制块,将其赋值给pxQueue->pxMutexHolder
2:

			#if ( configUSE_MUTEXES == 1 )
			{
				if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
				{
					taskENTER_CRITICAL();
					{
						vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
					}
					taskEXIT_CRITICAL();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			#endif

在前面记录了互斥锁的拥有者,在这里就调用函数vTaskPriorityInherit来判断互斥锁拥有者与当前任务的优先级大小,从而进一步判断是否需要优先级继承
vTaskPriorityInherit源码分析如下:

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 ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
			}
			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
			{
				/* Just inherit the priority. */
				pxTCB->uxPriority = pxCurrentTCB->uxPriority;
			}

若互斥锁拥有者不处于就绪状态,则只需要修改其优先级(继承当前任务的优先级)即可

2.2 释放互斥信号量xSemaphoreGive

释放信号量xSemaphoreGive实际调用的函数是xQueueGenericSend,释放互斥信号量与普通信号量的主要区别在拷贝入队函数prvCopyDataToQueue中,具体如下:

	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
	#if ( configUSE_MUTEXES == 1 )
	{
		if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
		{
			/* 恢复互斥锁拥有者优先级 */
			xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );
			/*清空互斥锁拥有者*/
			pxQueue->pxMutexHolder = NULL;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_MUTEXES */
}

若队列项大小uxItemSize 为0,且使用互斥锁时,会调用函数xTaskPriorityDisinherit来恢复互斥锁拥有者的优先级,然后清空互斥锁的拥有者
xTaskPriorityDisinherit:

( pxTCB->uxMutexesHeld )--;

首先将任务的互斥锁拥有数uxMutexesHeld 减去1

if( pxTCB->uxPriority != pxTCB->uxBasePriority )

判断互斥锁拥有者当前优先级是否与原始优先级相同(原始优先级uxBasePriority 在创建任务时就会被初始化),即判断优先级是否发生了改变

if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )

若互斥锁拥有者目前不再持有任何的互斥锁

				if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
				{
					taskRESET_READY_PRIORITY( pxTCB->uxPriority );
				}

将其从就绪列表中移除

pxTCB->uxPriority = pxTCB->uxBasePriority;

恢复互斥锁拥有者的优先级

listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );

恢复互斥锁拥有者事件列表成员变量xItemValue的值

prvAddTaskToReadyList( pxTCB );

最后将互斥锁拥有者添加至新的就绪列表中

3、递归互斥量

递归互斥量可以看作一个特殊的互斥量,普通的互斥量未能实现“谁上锁只能有谁解锁”,且普通互斥量获取一次互斥量后就不能再继续获取互斥量了,而递归互斥量实现了谁上锁只能谁来解锁,且一个任务获得多少次递归量就得由该任务释放相同次数的递归量才能解锁!

3.1 创建递归互斥量xSemaphoreCreateRecursiveMutex

#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

递归互斥量的创建也是对普通互斥量的创建函数进行了简单的封装,两者几乎一模一样,只不过队列类型变成了queueQUEUE_TYPE_RECURSIVE_MUTEX
注意:虽然递归互斥量虽然可以 多次获取互斥量,但是其队列长度只有1

3.2 获取递归互斥量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 );

	/* Comments regarding mutual exclusion as per those within
	xQueueGiveMutexRecursive(). */

	traceTAKE_MUTEX_RECURSIVE( pxMutex );
	
	/*若不是第一次获取互斥量,则判断此次获取互斥量的任务是否与之前获取互斥量的任务相同*/
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
	{
		/*若相同,则uxRecursiveCallCount增1,表明任务又一次获取递归互斥量*/
		( pxMutex->u.uxRecursiveCallCount )++;
		xReturn = pdPASS;
	}
	/*若为第一次获取互斥量*/
	else
	{
		/*调用xQueueGenericReceive获取队列中的信号量,注意:在该函数中会记录获取互斥量的任务*/
		xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );
		if( xReturn != pdFAIL )
		{
			/*获取成功,uxRecursiveCallCount加1,uxRecursiveCallCount记录递归互斥量的获取次数*/
			( pxMutex->u.uxRecursiveCallCount )++;
		}
		else
		{
			traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
		}
	}

	return xReturn;
}

由于队列长度只有1,所以只有第一次获取递归互斥量的时候才会调用xQueueGenericReceive来“获取队列消息”,参数uxRecursiveCallCount 用来记录任务获取递归互斥量的数目,当一个任务第二次及以上次数获得递归互斥量的时候,需要判断本次获取递归互斥量的任务是否与之前获取递归互斥量的任务相同,只有相同才能继续获取信号量,成功获取信号量后uxRecursiveCallCount 增一,表明又一次获取了信号量

3.3 释放递归互斥量xSemaphoreGiveRecursive

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

释放递归互斥量实际调用的函数为xSemaphoreGiveRecursive:

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

	configASSERT( pxMutex );

	/* 判断释放递归互斥量的任务是否与获取互斥量的任务相同	*/
	if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */
	{
		traceGIVE_MUTEX_RECURSIVE( pxMutex );

		/* 参数uxRecursiveCallCount减1,表明释放了一次递归互斥量 */
		( pxMutex->u.uxRecursiveCallCount )--;

		/* 判断参数uxRecursiveCallCount是否为0,只有其为0才调用xQueueGenericSend来“入队消息” */
		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
	{
		/* The mutex cannot be given because the calling task is not the
		holder. */
		xReturn = pdFAIL;

		traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
	}

	return xReturn;
}

递归互斥量实现了“谁上锁只能由谁来解锁”,如上面源码,释放递归互斥量时,需要判断获取互斥量的任务是否与释放递归互斥量的任务相同,只有相同时才能成功释放(pxMutexHolder 中记录了互斥锁拥有者,即记录了获取互斥量任务的句柄,然后用xTaskGetCurrentTaskHandle来获取当前任务句柄,两者相同才能成功释放),成功释放以后参数uxRecursiveCallCount 减1,表明释放了一次互斥量,当参数uxRecursiveCallCount 为0时,即此次释放互斥量为最后一次释放,调用xQueueGenericSend来“入队消息”,在函数xQueueGenericSend内部会清空参数pxMutexHolder (详见2.2 释放互斥信号量xSemaphoreGive),即清空互斥锁拥有者,实现真正的“解锁”

FreeRTOS 中,二值信号量互斥是两种常用的同步机制。 1. 二值信号量(Binary Semaphore): 二值信号量是一种同步机制,它只有两个状态:0 和 1。当一个任务获取到二值信号量时,将其状态设置为 1,其他任务试图获取该信号量时将被阻塞,直到之前的任务释放该信号量并将其状态重新设置为 0。 在 FreeRTOS 中,可以使用 `xSemaphoreCreateBinary()` 函数创建一个二值信号量,并使用 `xSemaphoreTake()` 和 `xSemaphoreGive()` 函数来获取和释放该信号量。头文件为 `semphr.h`。 2. 互斥(Mutex): 互斥是一种用于资源保护的同步机制。它可以确保在同一时间只有一个任务能够访问被保护资源。当一个任务获取到互斥时,其他试图获取该互斥的任务将被阻塞,直到持有互斥的任务释放它。 在 FreeRTOS 中,可以使用 `xSemaphoreCreateMutex()` 函数创建一个互斥,并使用 `xSemaphoreTake()` 和 `xSemaphoreGive()` 函数来获取和释放该互斥。头文件为 `semphr.h`。 以下是一个简单的示例代码,演示了如何使用二值信号量互斥: ```cpp #include <FreeRTOS.h> #include <task.h> #include <semphr.h> // 二值信号量 SemaphoreHandle_t binarySemaphore; // 互斥 SemaphoreHandle_t mutex; // 任务1 void task1(void *pvParameters) { while (1) { // 获取二值信号量 xSemaphoreTake(binarySemaphore, portMAX_DELAY); // 执行任务1操作 // 释放二值信号量 xSemaphoreGive(binarySemaphore); // 延时或进行其他操作 vTaskDelay(pdMS_TO_TICKS(1000)); } } // 任务2 void task2(void *pvParameters) { while (1) { // 获取互斥 xSemaphoreTake(mutex, portMAX_DELAY); // 执行任务2操作 // 释放互斥 xSemaphoreGive(mutex); // 延时或进行其他操作 vTaskDelay(pdMS_TO_TICKS(1000)); } } int main() { // 创建二值信号量 binarySemaphore = xSemaphoreCreateBinary(); // 创建互斥 mutex = xSemaphoreCreateMutex(); // 创建任务1 xTaskCreate(task1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务2 xTaskCreate(task2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 启动 FreeRTOS 调度器 vTaskStartScheduler(); while (1) { // 主循环中可以进行其他操作或者延时 // ... } return 0; } ``` 在上述示例中,示范了如何使用二值信号量互斥来实现任务间的同步和资源保护。你可以根据需求在任务中适当地获取和释放这些同步机制。 希望这个示例对你有所帮助!如果你有更多问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sense_long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值