FreeRTOS源码理解(七)——队列

该博客围绕FreeRTOS源码展开,涵盖思路梳理、中断、任务等多方面内容。重点介绍了队列,包括队列结构体、创建、上锁、解锁等操作,还提及信号量均用队列实现,最后进行了文档汇总。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FreeRTOS源码理解(一)——FreeRTOS思路梳理

FreeRTOS源码理解(二)——中断和列表项

FreeRTOS源码理解(三)——任务

FreeRTOS源码理解(四)——任务调度器

FreeRTOS源码理解(五)——任务切换

FreeRTOS源码理解(六)——时间管理

FreeRTOS源码理解(七)——队列

FreeRTOS源码理解(八)——信号量

FreeRTOS源码理解(九)——事件组和任务通知

FreeRTOS源码理解(十)——软件定时器和空闲任务

FreeRTOS源码理解(十一)——内存管理

FreeRTOS源码理解(十二)——汇总

队列

信号量均是用队列实现的,包括二值信号量计数信号量互斥信号量递归互斥信号量

队列结构体

typedef struct QueueDefinition
{
	int8_t *pcHead;					/* 指向队列存储区开始地址 */
	int8_t *pcTail;					/* 指向队列存储区末尾 */
	int8_t *pcWriteTo;				/* 指向存储区下一个空闲区域 */

	union							/* 用一个union,节省空间 */
	{
		int8_t *pcReadFrom;			/* 用作队列时,指向第一个出队的队列项首地址 */
		UBaseType_t uxRecursiveCallCount;/* 用作递归互斥量时,用来记录递归互斥量被调用的次数 */
	} u;

	List_t xTasksWaitingToSend;		/* 等待发送列表,因为队列满了而无法入队的任务挂在此列表上,按优先级顺序挂载 */
	List_t xTasksWaitingToReceive;	/* 等待接收队列,因为队列为而接收失败的任务挂在此列表上,按优先级顺序挂载 */

	volatile UBaseType_t uxMessagesWaiting;/* 目前队列中的消息数量 */
	UBaseType_t uxLength;			/* 队列存储区长度 */
	UBaseType_t uxItemSize;			/* 队列项最大长度,以字节为单位 */

	volatile int8_t cRxLock;		/* 队列上锁后,出队的队列项数量;当队列没有上锁,此字段为queueUNLOCKED */
	volatile int8_t cTxLock;		/* 队列上锁后,入队的队列项数量;当队列没有上锁,此字段为queueUNLOCKED */

	#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
		uint8_t ucStaticallyAllocated;	/* 静态存储的话,设置为pdTURE */
	#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;

队列创建

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
    Queue_t *pxNewQueue;
    size_t xQueueSizeInBytes;
    uint8_t *pucQueueStorage;

    configASSERT( uxQueueLength > ( UBaseType_t ) 0 );

    if( uxItemSize == ( UBaseType_t ) 0 )
    {
        /* 队列项大小为0,不需要存储区 */
        xQueueSizeInBytes = ( size_t ) 0;
    }
    else
    {
        /* 分配相应大小的存储区 */
        xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); 
    }

    pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );

    /* 申请内存成功 */
    if( pxNewQueue != NULL )
    {
        pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );

        #if( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
            /* 动态方法创建,这个字段赋为pdFALSE */
            pxNewQueue->ucStaticallyAllocated = pdFALSE;
        }
        #endif /* configSUPPORT_STATIC_ALLOCATION */

        /* 初始化一个新的队列,为队列中的成员变量赋值,处理发送和接收列表 */
        prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
    }

    return pxNewQueue;
}

队列上锁

#define prvLockQueue( pxQueue )								\
	taskENTER_CRITICAL();									\
	{														\
		if( ( pxQueue )->cRxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED;	\
		}													\
		if( ( pxQueue )->cTxLock == queueUNLOCKED )			\
		{													\
			( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED;	\
		}													\
	}														\
	taskEXIT_CRITICAL()

将cRxLock和cTxLock设置为queueLOCKED_UNMODIFIED

队列解锁

static void prvUnlockQueue( Queue_t * const pxQueue )
{
	/* 此函数必须在任务调度器挂起后调用 */

    /* 上锁计数器会记录上锁期间入队或出队的队列项数量 */
	/* 上锁后队列项可以加入或移出队列,但是相应的事件列表不会更新 */
    /* 解锁时要更新相应的事件列表 */
	taskENTER_CRITICAL();
	{
		int8_t cTxLock = pxQueue->cTxLock;

		/* 判断数据在上锁期间是否被加入到队列中 */
		while( cTxLock > queueLOCKED_UNMODIFIED )
		{
			/**********************************************************/
            /**********************省略队列集代码************************/
            /**********************************************************/
            
			#else /* configUSE_QUEUE_SETS */
			{
				/* 将任务从事件列表中移除 */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
					{
						/* 从列表中移除任务优先级更高,进行一次任务切换 */
						vTaskMissedYield();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					break;
				}
			}
			#endif /* configUSE_QUEUE_SETS */

			--cTxLock;
		}

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

	/* 处理cRxLock */
	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();
}

向队列发送消息

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;

	configASSERT( pxQueue );
	configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
	configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	for( ;; )
	{
        /* 关中断,进入临界区 */
		taskENTER_CRITICAL();
		{
			/* 查看队列中是否还有剩余空间,如果采用的覆写方式入队则不用在乎队列是否是满的 */
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
				traceQUEUE_SEND( pxQueue );
                /* 将数据拷贝到队列中 */
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
				
                /**********************************************************/
                /**********************省略队列集代码************************/
                /**********************************************************/
                
                #else /* configUSE_QUEUE_SETS */
				{
					/* 判断是否有任务在等待队列消息而阻塞 */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
                        /* 有的话移除任务,取消阻塞:将TCB中的事件等待列表项和状态等待列表项从当前所在列表中移除,添加任务到就绪列表 */
						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();
					}
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
            /* 如果队列满了 */
			else
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					taskEXIT_CRITICAL();
					/* 阻塞时间为0则直接返回 */
					traceQUEUE_SEND_FAILED( pxQueue );
					return errQUEUE_FULL;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/* 阻塞时间不为0则初始化超时时间结构体 */
					vTaskSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* 时间结构体已经初始化过了 */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

        /* 挂起任务调度器 */
		vTaskSuspendAll();
        /* 队列上锁 */
		prvLockQueue( pxQueue );

		/* 更新时间并检查是否超时 */
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
		{
            /* 未超时 */
			if( prvIsQueueFull( pxQueue ) != pdFALSE )
			{
				traceBLOCKING_ON_QUEUE_SEND( pxQueue );
                /* 将当前任务的事件列表项加入到队列的等待发送列表中 */
                /* 将当前任务的状态列表项从就绪列表中移除,将其加入到延时列表中 */
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

				/* 解锁队列 */
				prvUnlockQueue( pxQueue );

				/* 唤醒所有,开启OS时钟计数 */
				if( xTaskResumeAll() == pdFALSE )
				{
                    /* 保证执行一次上下文切换 */
					portYIELD_WITHIN_API();
				}
			}
			else
			{
				/* 如果队列未满,解锁队列,再试一次 */
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			/* 超时 */
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();

			traceQUEUE_SEND_FAILED( pxQueue );
			return errQUEUE_FULL;
		}
	}
}

读取队列数据

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;

	configASSERT( pxQueue );
	configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif

	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;

			/* 判断队列中是否有数据 */
			if( uxMessagesWaiting > ( UBaseType_t ) 0 )		//如果队列中有数据
			{
				/* 读取数据到pvBuffer中,并更新u.pcReadFrom的值 */
				pcOriginalReadPosition = pxQueue->u.pcReadFrom;

				prvCopyDataFromQueue( pxQueue, pvBuffer );

                /* 判断是否删除读取的队列项 */
				if( xJustPeeking == pdFALSE ) //如果要删除读取的队列项
				{
					traceQUEUE_RECEIVE( pxQueue );

					/* 更新队列中队列项数量,完成数据的删除 */
					pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;

					#if ( configUSE_MUTEXES == 1 )		//用户定义了互斥信号量
					{
						if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )	//如果此队列是互斥信号量
						{
							/* 将pxMutexHolder标记为获取到互斥信号量的任务TCB */
                            /* 将任务TCB中互斥信号量的数量加一   ( pxCurrentTCB->uxMutexesHeld )++; */
							pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); 
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configUSE_MUTEXES */

                    /* 如果等待发送列表不为空,有任务因队列满而阻塞,则唤醒一个发送任务 */
					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 //如果不删除队列项
				{
					traceQUEUE_PEEK( pxQueue );

					/* 不删除队列项,恢复u.pcReadFrom */
					pxQueue->u.pcReadFrom = pcOriginalReadPosition;

					/* 队列中还有数据,查看是否有其他任务阻塞在获取队列数据中,有的话唤醒 */
					if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
					{
						if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
						{
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else	//队列中无数据
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* 队列为空且等待时间为0,直接返回 */
					taskEXIT_CRITICAL();
					traceQUEUE_RECEIVE_FAILED( pxQueue );
					return errQUEUE_EMPTY;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/* 确保时间结构体的初始化 */
					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

                /* 将任务的时间列表项插入到队列的等待列表 */
                /* 将任务的状态列表项插入到延时列表 */
				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 )
			{
				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
}

取消事件列表任务阻塞 xTaskRemoveFromEventList

BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
{
    TCB_t *pxUnblockedTCB;
    BaseType_t xReturn;

    //获取列表中第一个列表项的所属任务TCB
	pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
	configASSERT( pxUnblockedTCB );
    /* 将要解除阻塞的任务事件列表项移出 */
	( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) );

	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )	//任务调度器未被挂起
	{
        /* 将任务TCB的状态列表项从延时列表中移出,将其添加到就绪列表中 */
		( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );
		prvAddTaskToReadyList( pxUnblockedTCB );
	}
	else
	{
		/* 将任务事件列表项添加到等待唤醒列表中 */
		vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
	}

	if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
	{
		/* 如果解除阻塞的优先级较现在运行的任务优先级高 */
		xReturn = pdTRUE;

		/* 标志一下,之后调用一次任务切换 */
		xYieldPending = pdTRUE;
	}
	else
	{
		xReturn = pdFALSE;
	}

	#if( configUSE_TICKLESS_IDLE != 0 )
	{
		/* If a task is blocked on a kernel object then xNextTaskUnblockTime
		might be set to the blocked task's time out time.  If the task is
		unblocked for a reason other than a timeout xNextTaskUnblockTime is
		normally left unchanged, because it is automatically reset to a new
		value when the tick count equals xNextTaskUnblockTime.  However if
		tickless idling is used it might be more important to enter sleep mode
		at the earliest possible time - so reset xNextTaskUnblockTime here to
		ensure it is updated at the earliest possible time. */
		prvResetNextTaskUnblockTime();
	}
	#endif

	return xReturn;
}

文档汇总

文档汇总

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值