FreeRTOS-任务通知源码分析

任务通知可用来代替信号量、消息队列、事件标志位,而且使用任务通知的形式效率会更高,它不需要像信号量那样创建队列和操作队列,任务通知的存储变量来自任务控制块中,当宏 configUSE_TASK_NOTIFICATIONS 被定义为1时就可以使用任务通知相关的函数了。任务通知的接口函数同样存在发送和接收,发送的接口函数如下:

/* 发送通知,有通知值但不保留接收任务原通知值 */
xTaskNotify( xTaskToNotify, ulValue, eAction )
xTaskNotifyFromISR(xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken)

/* 发送通知(可代替计数型信号量),没有通知值并且不保留接收任务的通知值,会将接收任务的通知值加一 */
xTaskNotifyGive(xTaskToNotify)
vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken )

/* 发送通知,有通知值并且保留接收任务的原通知值 */
xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue)
xTaskNotifyAndQueryFromISR(xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken)

这几个发送通知的接口实际上都是宏,定义如下:

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )

#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )
#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )

有分任务之间的接口和中断的接口,核心主要就是这两个函数:

	BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
	BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken )

任务通知的写入方式有以下几种,使用一个枚举来定义:

typedef enum
{
	eNoAction = 0,				/* 不升级通知值 */
	eSetBits,					/* 更新指定的bit */
	eIncrement,					/* 通知值加一 */
	eSetValueWithOverwrite,		/* 覆写的方式更新通知值 */
	eSetValueWithoutOverwrite	/* 不覆盖通知值 */
} eNotifyAction;

xTaskGenericNotify 通用通知函数的源码分析如下:

#if( configUSE_TASK_NOTIFICATIONS == 1 )

	/* 形参:
	 * 	xTaskToNotify:任务句柄(要通知的任务)
	 * 	ulValue:任务通知值
	 * 	eAction:任务通知更新的方法
	 * 	pulPreviousNotificationValue:用于保存任务更新前的任务通知值
	 */
	BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
	{
	TCB_t * pxTCB;
	BaseType_t xReturn = pdPASS;
	uint8_t ucOriginalNotifyState;

		configASSERT( xTaskToNotify );
		pxTCB = xTaskToNotify;

		/* 进入临界区 */
		taskENTER_CRITICAL();
		{
			if( pulPreviousNotificationValue != NULL )
			{
				/* 形参作为返回值,返回值空间有效,返回任务的原通知值 */
				*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
			}

			/* 获取被通知任务原来的状态 */
			ucOriginalNotifyState = pxTCB->ucNotifyState;

			/* 更新被通知任务的状态为接收 */
			pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

			/* 判断任务通知的更新方法 */
			switch( eAction )
			{
				case eSetBits	:
					/* 用做二值信号量 */
					pxTCB->ulNotifiedValue |= ulValue;
					break;

				case eIncrement	:
					/* 用做计数型信号量 */
					( pxTCB->ulNotifiedValue )++;
					break;

				case eSetValueWithOverwrite	:
					/* 覆写的方式写入数据 */
					pxTCB->ulNotifiedValue = ulValue;
					break;

				case eSetValueWithoutOverwrite :
					/* 不覆盖方式写入数据 */
				
					if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
					{
						/* 被通知任务的状态不处于接收状态的话直接赋值 */
						pxTCB->ulNotifiedValue = ulValue;
					}
					else
					{
						/* 如果任务处于接收状态的话就不能被写入,因为使用的不覆写的方式,返回错误 */
						xReturn = pdFAIL;
					}
					break;

				case eNoAction:
					break;

				default:
					configASSERT( pxTCB->ulNotifiedValue == ~0UL );

					break;
			}

			traceTASK_NOTIFY();

			/* 如果被通知任务原来的状态为等待通知而进入阻塞,这里解锁阻塞 */
			if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
			{
				/* 将任务从原来的状态列表中移除 */
				( void ) uxListRemove( &( pxTCB->xStateListItem ) );
				/* 将任务添加到就绪列表中 */
				prvAddTaskToReadyList( pxTCB );

				configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

				#if( configUSE_TICKLESS_IDLE != 0 )
				{
					prvResetNextTaskUnblockTime();
				}
				#endif

				/* 如果任务的优先级高于当前优先级则进行一次任务切换 */
				if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
				{
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif /* configUSE_TASK_NOTIFICATIONS */

在中断中使用的发送函数 xTaskGenericNotifyFromISR 分析如下:

#if( configUSE_TASK_NOTIFICATIONS == 1 )

	/* 形参:
	 * 	xTaskToNotify:任务句柄
	 * 	ulValue:任务通知值
	 * 	eAction:任务通知更新的方法
	 * 	pulPreviousNotificationValue:用于保存任务更新前的任务通知值
	 *	pxHigherPriorityTaskWoken:使用变量记退出此函数以后是否进行任务切换,用于需要提供一个变量来保存这个值
	 */
	BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken )
	{
	TCB_t * pxTCB;
	uint8_t ucOriginalNotifyState;
	BaseType_t xReturn = pdPASS;
	UBaseType_t uxSavedInterruptStatus;

		configASSERT( xTaskToNotify );

		portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
		pxTCB = xTaskToNotify;

		/* 进入中断临界区 */
		uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
		{
			if( pulPreviousNotificationValue != NULL )
			{
				/* 形参作为返回值,返回值空间有效,返回任务的原通知值 */
				*pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
			}

			/* 获取被通知任务原来的状态 */
			ucOriginalNotifyState = pxTCB->ucNotifyState;
			/* 更新被通知任务的状态为接收 */
			pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;

			/* 判断任务通知的更新方法 */
			switch( eAction )
			{
				case eSetBits	:
					/* 用做二值信号量 */
					pxTCB->ulNotifiedValue |= ulValue;
					break;

				case eIncrement	:
					/* 用做计数型信号量 */
					( pxTCB->ulNotifiedValue )++;
					break;

				case eSetValueWithOverwrite	:
					/* 覆写的方式写入数据 */
					pxTCB->ulNotifiedValue = ulValue;
					break;

				case eSetValueWithoutOverwrite :
					/* 不覆盖方式写入数据 */
				
					if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
					{
						/* 被通知任务的状态不处于接收状态的话直接赋值 */
						pxTCB->ulNotifiedValue = ulValue;
					}
					else
					{
						/* 如果任务处于接收状态的话就不能被写入,返回错误 */
						xReturn = pdFAIL;
					}
					break;

				case eNoAction :
					break;

				default:
					configASSERT( pxTCB->ulNotifiedValue == ~0UL );
					break;
			}

			traceTASK_NOTIFY_FROM_ISR();

			/* 如果被通知任务原来的状态为等待通知而进入阻塞,这里解锁阻塞 */
			if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
			{
				configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

				/* 如果调度器休眠没休眠,执行这个分支 */
				if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
				{
					/* 将任务从原来的状态列表中移除 */
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
					/* 将任务添加到就绪列表中 */
					prvAddTaskToReadyList( pxTCB );
				}
				/* 如果调度器休眠了,执行这个分支 */
				else
				{
					/* 将任务插入到挂机就绪列表,调度器恢复后再放入就绪列表 */
					vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
				}

				/* 如果任务的优先级高于当前优先级则进行一次任务切换 */
				if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
				{
					if( pxHigherPriorityTaskWoken != NULL )
					{
						/* 返回告知需要进行一次任务切换 */
						*pxHigherPriorityTaskWoken = pdTRUE;
					}
					xYieldPending = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		/* 退出中断临界区 */
		portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

		return xReturn;
	}

#endif

下面获取任务通知的接口:

/* 获取任务通知,可以设置在退出此函数的时候将任务通知值清零或减一,用于代替二值信号和计数型信号量 */
ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait)

/* 获取任务通知,等待任务通知,比ulTaskNotifyTake强大,全功能版任务通知获取函数 */
xTaskNotifyWait(xTaskToNotify, ulValue, eAction)

ulTaskNotifyTake 的源码分析如下:

#if( configUSE_TASK_NOTIFICATIONS == 1 )

	/* 形参:
	 * 	xClearCountOnExit:为 pdFALSE 的话在退出时通知值减一,类似计数型信号量,
	 *					   为 pdTURE 的话在退出时通知值清零,类似二值信号量
	 * 	xTicksToWait:阻塞时间
	 */
	uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
	{
	uint32_t ulReturn;

		/* 进入临界区 */
		taskENTER_CRITICAL();
		{
			/* 如果当前任务通知值为0 */
			if( pxCurrentTCB->ulNotifiedValue == 0UL )
			{
				/* 任务的通知状态标记为等待通知 */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

				/* 如果任务的等待时间大于0 */
				if( xTicksToWait > ( TickType_t ) 0 )
				{
					/* 将任务添加到延时任务列表 */
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_TAKE_BLOCK();

					/* 进行一次任务调度 */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		/* 进入临界区,执行到这里说明接收到了来自其它任务的通知或超时 */
		taskENTER_CRITICAL();
		{
			traceTASK_NOTIFY_TAKE();
			/* 获取当前任务的通知值 */
			ulReturn = pxCurrentTCB->ulNotifiedValue;

			/* 通知值不为0 */
			if( ulReturn != 0UL )
			{
				if( xClearCountOnExit != pdFALSE )
				{
					/* 类似二值信号量,获取到后清零 */
					pxCurrentTCB->ulNotifiedValue = 0UL;
				}
				else
				{
					/* 类似计数信号量,获取到后减一 */
					pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1;
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
			/* 当前任务的通知状态改为等待通知 */
			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		return ulReturn;
	}

#endif

xTaskNotifyWait 源码分析如下:

#if( configUSE_TASK_NOTIFICATIONS == 1 )

	/* 形参:
	 *	ulBitsToClearOnEntry:进入函数时是否清除任务bit,没接收到任务通知的时候将任务
	 *						  通知值与此参数的取反值进行按位与运算,当此参数
	 *						  为0xffffffff时会将任务的通知值清零
	 * 	xClearCountOnExit:为 pdFALSE 的话在退出时通知值减一,类似计数型信号量,
	 *					   为 pdTURE 的话在退出时通知值清零,类似二值信号量
	 *	pulNotificationValue:用于保存任务通知值
	 * 	xTicksToWait:阻塞时间
	 */
	BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
	{
	BaseType_t xReturn;

		/* 进入临界区 */
		taskENTER_CRITICAL();
		{
			/* 当前任务的通知状态不是接收状态,即还没接收到任务通知 */
			if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
			{
				/* 清除指定的bit */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;

				/* 任务的通知状态标记为等待通知 */
				pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;

				/* 用户设置的超时时间大于0 */
				if( xTicksToWait > ( TickType_t ) 0 )
				{
					/* 将任务添加到延时列表中 */
					prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
					traceTASK_NOTIFY_WAIT_BLOCK();

					/* 进行一次任务调度 */
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();
		
		/* 进入临界区,执行到这里说明接收到了来自其它任务的通知或超时 */
		taskENTER_CRITICAL();
		{
			traceTASK_NOTIFY_WAIT();

			/* 用户传入的用于保存通知值的变量不为空 */
			if( pulNotificationValue != NULL )
			{
				/* 返回任务当前通知值 */
				*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
			}

			/* 未接收到任务通知返回 pdFALSE */
			if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )			{

				xReturn = pdFALSE;
			}
			/* 接收到任务通知返回 pdTRUE */
			else
			{
				/* 清除指定的bit */
				pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
				xReturn = pdTRUE;
			}
			/* 任务的通知状态标记为等待通知 */
			pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
		}
		/* 退出临界区 */
		taskEXIT_CRITICAL();

		return xReturn;
	}

#endif 

关于任务通知的代码相对比较简单,都是通过直接赋值的形式,不存在像信号量那样的入队和出队过程。

你可能感兴趣的:(FreeRTOS)