消息队列实现原理

消息队列创建QueueCreate.c

 

//队列实现,实际是xQueueGenericCreate函数,为什么呢

//队列的创建类型
#define queueQUEUE_TYPE_BASE				( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_SET					( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_MUTEX 				( ( uint8_t ) 1U )
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE	( ( uint8_t ) 2U )
#define queueQUEUE_TYPE_BINARY_SEMAPHORE	( ( uint8_t ) 3U )
#define queueQUEUE_TYPE_RECURSIVE_MUTEX		( ( uint8_t ) 4U )


#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xQueueCreate( uxQueueLength, uxItemSize ) 
	xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif

	/*
		参数:
			uxQueueLength
			uxItemSize
			ucQueueType
		返回值
			QueueHandle_t  队列的句柄 其实是队列控制块地址
			
	*/
	QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
	{
	Queue_t *pxNewQueue;
	size_t xQueueSizeInBytes;
	uint8_t *pucQueueStorage;


		if( uxItemSize == ( UBaseType_t ) 0 )//队列内存空间为空
		{
			/* 队列字节大小 赋值为0 */
			xQueueSizeInBytes = ( size_t ) 0;
		}
		else
		{
			/* 队列字节大小 赋值为  长度*每个队列项大小*/
			xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
		}
		// 申请内存空间  消息队列控制块大小+消息队列空间大小
		pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

		if( pxNewQueue != NULL )
		{
			/* 找到消息队列操作空间的首地址 */
			pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );
			//初始化一个新的消息队列
			prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
		}

		return pxNewQueue;
	}

/*

	参数:
		pucQueueStorage :队列操作空间的首地址
		pxNewQueue : 队列的句柄
	返回值:
	
*/
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
	/* Remove compiler warnings about unused parameters should
	configUSE_TRACE_FACILITY not be set to 1. */
	( void ) ucQueueType;

	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/* 把队列控制块首地址赋值到队列头指针上  ????? 这是互斥信号使用,后面再分析 */
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/* 把队列空间首地址赋值给队列头指针上 */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}

	/* 长度 单元大小 */
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	//队列重置函数
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
}

/*
	参数:
		xQueue 队列句柄
		xNewQueue 操作队列的状态是什么,新建pdTRUE还是已经创建好了
	返回值:
*/
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	//进入临界段,这是时候操作队列控制块,不允许打断
	taskENTER_CRITICAL();
	{
	/*
		1、头地址赋值
		2、等待处理的消息个数为0
		3、写入指针赋值为队列头指针
		4、读出指针写入最后一个可用消息
		5、赋值队列锁为解锁状态
	*/
		pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
		pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
		pxQueue->pcWriteTo = pxQueue->pcHead;
		pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );
		pxQueue->cRxLock = queueUNLOCKED;
		pxQueue->cTxLock = queueUNLOCKED;

		//判断是否为新建队列
		if( xNewQueue == pdFALSE )
		{
			/* 判断发送等待列表里面是否有任务 */
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
				//移除事件列表中的任务
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
					//进行上下文切换
					queueYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else//新建队列,直接初始化 发送和接受 列表项
		{
			/* Ensure the event queues start in the correct state. */
			vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	taskEXIT_CRITICAL();

	/* A value is returned for calling semantic consistency with previous
	versions. */
	return pdPASS;
}

消息队列删除QueueDelete.c

void vQueueDelete( QueueHandle_t xQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

	configASSERT( pxQueue );
	traceQUEUE_DELETE( pxQueue );

	#if ( configQUEUE_REGISTRY_SIZE > 0 )
	{
		vQueueUnregisterQueue( pxQueue );
	}
	#endif

	#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
	{
		/*其实就是释放消息队列的内存空间*/
		vPortFree( pxQueue );
	}
	#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
	{
		/* The queue could have been allocated statically or dynamically, so
		check before attempting to free the memory. */
		if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
		{
			/*其实就是释放消息队列的内存空间*/
			vPortFree( pxQueue );
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#else
	{
		/* The queue must have been statically allocated, so is not going to be
		deleted.  Avoid compiler warnings about the unused parameter. */
		( void ) pxQueue;
	}
	#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}

消息队列接收queuereceive.c

/*
	最终调用发送消息接口是xQueueGenericReceive???为什么
	队列出队,有两种模式
		一种是:出队后,删除已经读取到队列项或者消息空间
		另一种是:出队后,不删除,然后恢复出队记录地址,让其他任务或者中断,继续读取使用
	类型:
		pdFALSE		删除
		pdTRUE		不删除

*/
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) 
xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )

BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )

				//重点分析这一个,其他流程和发送流程差不多
				//判断是否删除已经接收到的消息空间
 				if( xJustPeeking == pdFALSE )
 				{
 					traceQUEUE_RECEIVE( pxQueue );
						
					//更新消息等待的记录值,让它减一
 					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
 				}
 				else
 				{
					//不删除 就重新赋值未读取消息之前的地址,到出队指针
 					pxQueue->u.pcReadFrom = pcOriginalReadPosition;
 
 				}

消息队列发送queuesend.c

/*
	最终调用发送消息接口是xQueueGenericSend???为什么
	由于信号量都是有消息队列实现的,这个时候,操作系统,定义了消息队列的类型
	类型:
	#define	queueSEND_TO_BACK		( ( BaseType_t ) 0 ) 1、从队尾加入
	#define	queueSEND_TO_FRONT		( ( BaseType_t ) 1 ) 2、从队头加入
	#define queueOVERWRITE			( ( BaseType_t ) 2 ) 3、覆盖入队
*/
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) 
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
/*
	参数:
		xQueue
		pvItemToQueue
		xTicksToWait
		queueSEND_TO_BACK
	返回值:BaseType_t 

*/
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;


	/* 
		使用for死循环的目的,为了快速的处理消息拷贝,消息处理功能
	*/
	for( ;; )
	{	
		//进入了临界段
		taskENTER_CRITICAL();
		{
			/* 
				1、判断消息队列是否满了
				2、是否允许覆盖入队
				这两个条件任何一个成立,都执行入队操作
			*/
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
				//拷贝数据到队列操作空间内
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

				#if ( configUSE_QUEUE_SETS == 1 )
				
				#else /* configUSE_QUEUE_SETS */
				{
					/* 判断等待接收的列表是否为空. */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						//溢出列表,改变等待任务状态为就绪态
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* 进行上下文切换*/
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else if( xYieldRequired != pdFALSE )
					{
						/* 再次进行上下文切换 */
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_QUEUE_SETS */
				//退出临界段,返回成功
				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
				//不允许入队,先判断是否需要阻塞
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 不许阻塞,退出临界段,之后返回队满 */
					taskEXIT_CRITICAL();
					traceQUEUE_SEND_FAILED( pxQueue );
					return errQUEUE_FULL;
				}
				//超时结构体是否操作过
				else if( xEntryTimeSet == pdFALSE )
				{
					/* 超时结构体初始化*/
					vTaskSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		退出临界段
		taskEXIT_CRITICAL();
		
		//后面的代码都是允许阻塞处理

		/* 
			1、挂起了调度器 ----不让其他任务打断
			2、队列上锁------不让中断打断 (因为之前已经退出临界段了)

		*/

		vTaskSuspendAll();
		prvLockQueue( pxQueue );

		/* 判断阻塞时间是否超时了 */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
			//判断队列是否满
			if( prvIsQueueFull( pxQueue ) != pdFALSE )
			{
				//队满,把当前任务,添加到等待发送的事件列表中,内部还把任务添加到延时列表中去
				traceBLOCKING_ON_QUEUE_SEND( pxQueue );
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

				/* 解锁*/
				prvUnlockQueue( pxQueue );

				/* 恢复调度器 */
				if( xTaskResumeAll() == pdFALSE )
				{
					//进行上下文切换
					portYIELD_WITHIN_API();
				}
			}
			else
			{
				//队列未满 解锁,恢复调度器,重新进行入队操作
				/* Try again. */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}//
		//已经超时了,解锁,开始调度器 返回队满
		else
		{
			/* The timeout has expired. */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			traceQUEUE_SEND_FAILED( pxQueue );
			return errQUEUE_FULL;
		}
	}
}

在中断中发送queuesendformIsr.c

/*
	最终调用发送消息接口是xQueueGenericSendFromISR???为什么
	由于信号量都是有消息队列实现的,这个时候,操作系统,定义了消息队列的类型
	类型:
	#define	queueSEND_TO_BACK		( ( BaseType_t ) 0 ) 1、从队尾加入
	#define	queueSEND_TO_FRONT		( ( BaseType_t ) 1 ) 2、从队头加入
	#define queueOVERWRITE			( ( BaseType_t ) 2 ) 3、覆盖入队
*/
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) 
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
/*
	参数:
		xQueue
		pvItemToQueue
		NULL
		queueSEND_TO_BACK
	返回值:BaseType_t 

*/
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;


	/*带返回值的关闭中断,需要保存上次关闭中断的状态值,恢复时候,写入 */
	uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
	{
		//队满?覆盖入队?
		if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
		{
			//获取了队列发送锁的状态值
			const int8_t cTxLock = pxQueue->cTxLock;
			//拷贝数据到队列操作空间内
			( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

			/* 判断队列是否上锁 */
			/*
				#define queueUNLOCKED					( ( int8_t ) -1 ) 解锁状态
				#define queueLOCKED_UNMODIFIED			( ( int8_t ) 0 )  上锁状态初值
			*/
			if( cTxLock == queueUNLOCKED )
			{
				#if ( configUSE_QUEUE_SETS == 1 )
				
				#else /* configUSE_QUEUE_SETS */
				{
					//恢复等待消息任务,中断内没有进行上下文切换
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							/* The task waiting has a higher priority so record that a
							context	switch is required. */
							if( pxHigherPriorityTaskWoken != NULL )
							{
								*pxHigherPriorityTaskWoken = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_QUEUE_SETS */
			}
			else //队列已经上锁
			{
				/* 发送锁加一 */
				pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
			}
			//返回成功
			xReturn = pdPASS;
		}
		else
		{	
			//返回队满
			traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
			xReturn = errQUEUE_FULL;
		}
	}
	//开启中断,保存上次状态值
	portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

	return xReturn;
}
//队列上锁
把发送和接受锁都赋值为上锁的初始值
#define prvLockQueue( pxQueue )								\
	taskENTER_CRITICAL();									\
	{														\
		if( ( pxQueue )->cRxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;	\
		}													\
		if( ( pxQueue )->cTxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;	\
		}													\
	}														\
	taskEXIT_CRITICAL()
	
/*
	队列解锁
	参数:消息队列句柄
*/
static void prvUnlockQueue( Queue_t * const pxQueue )
{
	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */

	/* 进入临界段 */
	taskENTER_CRITICAL();
	{
		//获取发送锁的状态值
		int8_t cTxLock = pxQueue->cTxLock;

		/* 遍历解锁  直到解锁为止 */
		while( cTxLock > queueLOCKED_UNMODIFIED )
		{
			/* Data was posted while the queue was locked.  Are any tasks
			blocked waiting for data to become available? */
			#if ( configUSE_QUEUE_SETS == 1 )
		
			#else /* configUSE_QUEUE_SETS */
			{
				/* 解除等待消息任务,进行上下文切换 */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
					{
						/* The task waiting has a higher priority so record that
						a context switch is required. */
						vTaskMissedYield();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					break;
				}
			}
			#endif /* configUSE_QUEUE_SETS */
			//队列发送锁减一
			--cTxLock;
		}
		//最后,解除发送锁
		pxQueue->cTxLock = queueUNLOCKED;
	}
	//退出临界段
	taskEXIT_CRITICAL();

	/* Do the same for the Rx lock. */
	taskENTER_CRITICAL();
	{
		int8_t cRxLock = pxQueue->cRxLock;

		while( cRxLock > queueLOCKED_UNMODIFIED )
		{
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
			{
				if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
					vTaskMissedYield();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				--cRxLock;
			}
			else
			{
				break;
			}
		}

		pxQueue->cRxLock = queueUNLOCKED;
	}
	taskEXIT_CRITICAL();
}

消息队列在中断中接收 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值