freertos之queue浅析

本文深入剖析FreeRTOS中的队列机制,包括队列结构、信号量与互斥信号量的实现原理,以及队列创建、初始化、数据接收与发送的具体流程。探讨了基于xQUEUE结构的信号量操作,揭示了队列在实时操作系统中扮演的关键角色。

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

前言

queue队列是freertos实现计数型信号量、互斥型信号量等的基础,在freertos中最重要的结构除了TCB 、List,就是Queue了。

队列数据结构分析

freertos中信号量、互斥信号量等操作都是基于这个xQUEUE结构的,十分重要

typedef struct QueueDefinition
{
	int8_t *pcHead;					
	/*指向队列数据存储的起始位置*/
	int8_t *pcTail;					
	/*指向队列存储区域末尾的字节。一旦分配了多余的字节来存储队列项,这是用来做记号的。 */
	int8_t *pcWriteTo;				
	/*< 指向存储区域中空闲的下一个位置。 */

	union/* 使用union是编码标准的一个例外,以确保两个互斥结构成员不会同时出现(浪费RAM)。*/
	{
		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;		
	/*存储在锁定队列时从队列接收(从队列中删除)的项数。当队列未锁定时,设置为queue解锁。*/
	volatile int8_t cTxLock;		
	/*< 存储传输到队列的项数(添加到队列中)当队列被锁定时。当队列未锁定时,设置为queue解锁。*/
} xQUEUE;

代码分析

队列创建

跟task创建一样,也分为静态和动态两个方法,xQueueGenericCreateStaticxQueueGenericCreate下面具体看相关代码

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 ){
		/*没有队列存储区域。*/
		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 ){
			/*可以静态或动态创建队列,因此注意,此任务是动态创建的,以备稍后使用删除。*/
			pxNewQueue->ucStaticallyAllocated = pdFALSE;
		}
		#endif 
		prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
	}
	return pxNewQueue;
}


static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, 
				const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, 
				const uint8_t ucQueueType, Queue_t *pxNewQueue ){
	if( uxItemSize == ( UBaseType_t ) 0 )
	{
		/* 没有为队列存储区域分配RAM,但是PC头不能设置为NULL,
		因为NULL用作表示队列被用作的键一个互斥锁。
		因此,只需将pcHead设置为指向队列为良性已知位于内存映射中的值。*/
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}else{
		/* Set the head to the start of the queue storage area. */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}

	/* 按照队列类型所在的位置初始化队列成员定义的。*/
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
}



BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
	taskENTER_CRITICAL();
	{
		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{
			/*确保事件队列以正确的状态启动。*/
			vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	taskEXIT_CRITICAL();
	return pdPASS;
}

mutex创建

其本质与queue创建是一样的,但是有几个细节需要注意。
顺序是先创建一个queue,然后针对于mutex特定的数据结构(使用的是union)进一步做初始化

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


static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
	if( pxNewQueue != NULL ){
		/* 队列创建函数将设置所有队列结构成员对于一般队列是正确的,
		但是这个函数正在创建一个互斥锁。覆盖那些需要不同设置的成员-
		特别是优先级继承所需的信息。*/
		pxNewQueue->pxMutexHolder = NULL;
		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
		/* 如果是地柜互斥锁的话 */
		pxNewQueue->u.uxRecursiveCallCount = 0;
		/* Start with the semaphore in the expected state. */
		( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
	}
}

计数信号量创建

其本质与queue创建是一样的,但是有几个细节需要注意。
顺序是先创建一个queue,然后针对于CountingSemaphore特定的数据结构(信号量的初始值)进一步做初始化。

QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle;
	xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );
	if( xHandle != NULL )
	{//针对于计数信号量初始值初始化
		( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
	}
	return xHandle;
}

队列数据的接收和发送

可以想象一下流程,队列的发送一般是检测等待接收队列的列表,如果等待接收列表不为空,那么就直接唤醒等待列表中的等待任务,否则就将数据写入到队列存储区域。

<think>首先,用户的问题是关于FreeRTOSqueue.c文件在第1026行出现的错误信息或问题原因。用户说:"我想查找FreeRTOSqueue.c文件在第1026行出现的错误信息或问题原因",并问:"FreeRTOS queue.c error at line 1026"。 在系统级指令中,有引用内容: - 引用[1]:关于移植FreeRTOS时报错在queue.c的2762行,错误是#268: declaration may not appear after executable statement in block。 - 引用[2]:关于FreeRTOS队列的源码分析,提到dummy结构用于数据隐藏。 - 引用[3]:关于queue.c文件详解,包括发送消息的位置。 用户的问题具体是第1026行,但引用中没有直接提到1026行。引用[1]提到2762行,引用[3]提到发送消息的位置,但没指定行号。所以,我需要基于这些引用和一般知识来回答。 用户的需求是查找queue.c文件在1026行的错误信息或问题原因。既然引用没有直接对应,我应该解释可能的原因,并建议用户检查代码。 在FreeRTOS中,queue.c文件处理队列操作。常见错误可能包括: - 语法错误,如声明在可执行语句后(如引用[1]的错误)。 - 配置问题,如FreeRTOSConfig.h设置不当。 - 使用不当,如队列操作无效。 引用[1]的错误是#268: declaration may not appear after executable statement in block,这通常发生在C语言中,当变量声明放在可执行语句之后。C89标准要求在块的开头声明变量,而C99及以后允许混合声明和语句。但一些编译器可能严格遵循C89。 在FreeRTOSqueue.c中,1026行可能涉及类似问题。我需要回忆或推断queue.c的源码。 由于我无法访问实际文件,我应该基于一般知识: - FreeRTOSqueue.c文件是开源的,可以查看源码。 - 1026行可能是一个函数的一部分,如xQueueGenericSend或类似。 在引用[3]中,提到了发送消息的位置,这可能在xQueueGenericSend函数中。 可能的错误: 1. 编译器错误:如声明位置问题。 2. 配置错误:如队列大小或类型不匹配。 3. 使用错误:如任务优先级问题。 在回答中,我需要: - 使用中文。 - 结构清晰,逐步解决问题。 - 添加引用标识。 - 生成相关问题。 - 确保LaTeX语法正确:行内数学用$...$,独立公式用$$...$$。 回答结构: 1. 介绍问题:解释用户查询。 2. 分析可能原因:基于引用和一般知识。 3. 建议解决方案:如何调试或修复。 4. 添加引用。 5. 生成相关问题。 生成相关问题: - 如如何查看FreeRTOS源码。 - 常见队列错误。 - 编译器设置。 最终,引用标识:在相关段落末尾添加[^1], [^2], [^3]。 用户说:"上述引用仅供参考,并不是用户真正的需求",所以我不能完全依赖引用,但可以引用它们。 用户上一次问题是关于queue.c的1026行,所以我需要针对这个。 可能的回答: - 在FreeRTOS中,queue.c文件在1026行可能是一个函数调用或声明。 - 常见错误如引用[1]的#268错误,但行号不同。 - 建议用户检查代码上下文。 由于引用[1]是2762行,1026行可能不同。或许在1026行是另一个部分。 在FreeRTOS源码中,我可以回忆: - 例如,xQueueGenericSend函数可能在某个行号范围。 - 但1026行具体是什么,我不确定。 最好建议用户查看源码或提供更多信息。 在回答中,我可以说: - 基于引用,类似错误可能由于编译器兼容性问题。 - 在1026行,可能涉及变量声明或队列操作。 最终,生成相关问题。</think>### FreeRTOS queue.c 文件第 1026 行错误分析 您的问题是关于 FreeRTOS 中 `queue.c` 文件在第 1026 行出现的错误信息或原因。我将基于 FreeRTOS 源码的一般结构、常见错误模式和相关引用,逐步分析可能的原因。需要注意的是,FreeRTOS 的源码版本和编译器环境会影响具体错误,以下分析基于常见场景(如 FreeRTOS V10.4.6 和 ARM Cortex-M 编译器)。 #### 步骤 1: 理解错误背景 在 FreeRTOS 中,`queue.c` 文件负责队列操作(如发送、接收消息)。第 1026 行通常位于核心函数如 `xQueueGenericSend()` 或 `prvCopyDataToQueue()` 中。常见错误包括: - **编译器语法错误**:如变量声明位置不当(C89 标准要求声明必须在块开头)。 - **配置冲突**:FreeRTOSConfig.h 中的设置(如 `configUSE_QUEUE_SETS`)与代码不兼容。 - **内存或指针问题**:如无效的队列句柄或数据溢出。 引用[1]中提到类似错误(#268: declaration may not appear after executable statement in block),这通常由编译器严格模式触发,但行号不同(2762 行),表明此类问题可能在文件多处出现[^1]。 #### 步骤 2: 可能错误原因分析(针对第 1026 行) 基于 FreeRTOS 源码结构和引用内容,以下是第 1026 行错误的常见原因: 1. **变量声明位置错误(最常见)**: - 在 C89 标准下,变量声明必须位于代码块开头。如果第 1026 行是声明语句(如 `BaseType_t xStatus;`)出现在可执行语句(如函数调用)之后,编译器会报错 `#268`。 - **示例代码上下文**:在 `queue.c` 中,`xQueueGenericSend()` 函数可能包含类似逻辑: ```c /* 可执行语句(如第 1025 行) */ taskENTER_CRITICAL(); /* 第 1026 行:变量声明 */ BaseType_t xYieldRequired; // 如果此行报错,即声明位置不当 ``` - **修复建议**:将声明移至代码块顶部,或切换到支持 C99 的编译器(允许混合声明和语句)[^1]。 2. **队列操作参数无效**: - 引用[3]指出,队列发送函数(如 `xQueueGenericSend()`)的参数可能无效,例如: - 无效的队列句柄(如未初始化或已删除)。 - 参数 `xPosition` 值非法(必须为 `queueSEND_TO_BACK`, `queueSEND_TO_FRONT`, 或 `queueOVERWRITE`)。 - 在第 1026 行,可能涉及参数检查或数据复制,错误表现为运行时错误(如 HardFault)而非编译错误[^3]。 3. **编译器或配置问题**: - **编译器兼容性**:某些编译器(如 ARMCC 或 GCC 的旧版本)对源码要求严格。引用[1]的错误源于此。 - **FreeRTOSConfig.h 设置**:如 `configSUPPORT_DYNAMIC_ALLOCATION` 未启用,但代码尝试动态创建队列。 - **内存对齐问题**:在队列数据复制时,如果数据指针未对齐,可能触发总线错误(常见于第 1026 行的 `memcpy` 操作)。 4. **源码版本差异**: - FreeRTOS 不同版本的 `queue.c` 行号可能偏移。例如: - V10.4.6 中,第 1026 行可能在 `prvCopyDataToQueue()` 函数内。 - 错误信息类似 `error: #20: identifier "xxx" is undefined`,表明变量未定义(可能因宏配置遗漏)。 #### 步骤 3: 调试与解决方案 1. **检查源码上下文**: - 打开 `queue.c` 文件,查看第 1026 行及附近代码(如 1020–1030 行)。例如: ```c /* 假设第 1026 行内容 */ BaseType_t xYieldRequired = pdFALSE; // 常见声明行 ``` - 如果错误是编译时错误(如 `#268`),移动声明到块开头。 2. **验证编译器设置**: - 在 IDE(如 Keil 或 IAR)中启用 C99 模式:添加编译器标志如 `--c99`。 - 确保 FreeRTOS 头文件路径正确,避免配置宏冲突。 3. **测试简化用例**: - 创建一个最小测试队列: ```c QueueHandle_t xQueue = xQueueCreate(10, sizeof(int)); // 创建队列 int data = 42; xQueueSend(xQueue, &data, portMAX_DELAY); // 发送数据 ``` - 如果错误消失,表明原代码有队列使用错误。 4. **参考官方资源**: - 查看 FreeRTOS 官方文档或 GitHub 仓库的 issue 部分。 - 使用调试器(如 JTAG)捕获 HardFault,检查调用栈是否指向第 1026 行。 #### 总结 第 1026 行错误最可能源于 **变量声明位置不当**(编译器报错 `#268`)或 **队列参数无效**。建议优先检查声明顺序和编译器设置[^1][^3]。如果问题未解决,提供更多错误日志(如完整编译器输出)可进一步分析。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值