RTOS:创建队列(含源码分析)
文章目录
- 前言
- 一、创建队列
- 二、详细逻辑分析
- 2.1、参数检查
- 2.2、内存分配
- 2.3、初始化队列
- 2.4、错误处理
- 三、源码复杂度解读
- 3.1、traceENTER_xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType );
- 3.2、( uxQueueLength > ( UBaseType_t ) 0 )
- 3.3、( SIZE_MAX / uxQueueLength ) >= uxItemSize )
- 3.4、( SIZE_MAX - sizeof( Queue_t ) ) >= ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize ) )
- 3.5、xQueueSizeInBytes = ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize );
- 3.6、pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
- 3.7、pxNewQueue != NULL
- 3.8、pucQueueStorage = ( uint8_t * ) pxNewQueue;
- 3.9、pucQueueStorage += sizeof( Queue_t );
- 3.10、pxNewQueue->ucStaticallyAllocated = pdFALSE;
- 3.11、pxNewQueue->ucStaticallyAllocated = pdFALSE;
- 3.12、prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
- 3.13、return pxNewQueue;
- 四、注意事项
前言
操作系统,在入行嵌入式j接触它之前,感觉那是多么高深、神圣的技术,感觉它是高不可攀的。曾经也幻想过它有多么的复杂,在裸机编程时无数次想去应用这个技术,但无奈,无人指引,以致于每每都会望而却步。如今,也在多种OS的基础上做过各行各业的软件开发,所以想在闲暇之时,将使用过的OS内核软件逐行阅读,以提升自我编程能力、去了解更多软件编程思想。写此专栏文章的目的也仅仅是为了让这个过程留痕。
本文所有代码源于RTOS-Keenel
一、创建队列
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType )
{Queue_t * pxNewQueue = NULL;size_t xQueueSizeInBytes;uint8_t * pucQueueStorage;traceENTER_xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType );if( ( uxQueueLength > ( UBaseType_t ) 0 ) &&/* Check for multiplication overflow. */( ( SIZE_MAX / uxQueueLength ) >= uxItemSize ) &&/* Check for addition overflow. *//* MISRA Ref 14.3.1 [Configuration dependent invariant] *//* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-143. *//* coverity[misra_c_2012_rule_14_3_violation] */( ( SIZE_MAX - sizeof( Queue_t ) ) >= ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize ) ) ){/* Allocate enough space to hold the maximum number of items that* can be in the queue at any time. It is valid for uxItemSize to be* zero in the case the queue is used as a semaphore. */xQueueSizeInBytes = ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize );/* MISRA Ref 11.5.1 [Malloc memory assignment] *//* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#rule-115 *//* coverity[misra_c_2012_rule_11_5_violation] */pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );if( pxNewQueue != NULL ){/* Jump past the queue structure to find the location of the queue* storage area. */pucQueueStorage = ( uint8_t * ) pxNewQueue;pucQueueStorage += sizeof( Queue_t );#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){/* Queues can be created either statically or dynamically, so* note this task was created dynamically in case it is later* deleted. */pxNewQueue->ucStaticallyAllocated = pdFALSE;}#endif /* configSUPPORT_STATIC_ALLOCATION */prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );}else{traceQUEUE_CREATE_FAILED( ucQueueType );mtCOVERAGE_TEST_MARKER();}}else{configASSERT( pxNewQueue );mtCOVERAGE_TEST_MARKER();}traceRETURN_xQueueGenericCreate( pxNewQueue );return pxNewQueue;
}
二、详细逻辑分析
2.1、参数检查
首先检查 uxQueueLength 是否大于0、uxQueueLength 和 uxItemSize 的乘积是否会溢出。此外还检查了分配的总内存大小加上队列结构体大小是否会溢出。
2.2、内存分配
如果参数检查通过,就计算所需的总内存大小,并使用 pvPortMalloc 函数分配内存。分配的内存包括队列结构体本身和存储队列元素的存储器。
2.3、初始化队列
首先检查内存分配是否成功,当内存分配成功后,便初始化队列,包括设置队列的长度、大小、队列类型等
2.4、错误处理
如果参数检查失败或内存分配失败,函数将返回NULL,反之返回队列句柄。
三、源码复杂度解读
3.1、traceENTER_xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType );
跟踪函数宏,调试和性能分析时用,实际应用时处于kong.
3.2、( uxQueueLength > ( UBaseType_t ) 0 )
确保 uxQueueLength 大于0。
3.3、( SIZE_MAX / uxQueueLength ) >= uxItemSize )
确保 uxQueueLength 和 uxItemSize 的乘积不会溢出。
3.4、( SIZE_MAX - sizeof( Queue_t ) ) >= ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize ) )
确保分配的总内存大小加上队列结构大小不会溢出。
3.5、xQueueSizeInBytes = ( size_t ) ( ( size_t ) uxQueueLength * ( size_t ) uxItemSize );
计算队列存储区所需的总内存大小(字节为单位)。
3.6、pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
为队列即将创建的队列申请内存。
3.7、pxNewQueue != NULL
判断内存申请是否成功。
3.8、pucQueueStorage = ( uint8_t * ) pxNewQueue;
记录内存申请的起始地址。
3.9、pucQueueStorage += sizeof( Queue_t );
起始地址偏移一个 Queue_t。
3.10、pxNewQueue->ucStaticallyAllocated = pdFALSE;
如果启用了静态分配支持的宏定义 configSUPPORT_STATIC_ALLOCATION ,便标记队列为动态创建。
3.11、pxNewQueue->ucStaticallyAllocated = pdFALSE;
表示队列创建为动态创建,以便后续删除。
3.12、prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
初始化新对队列。
3.13、return pxNewQueue;
返回新队列句柄。如果创建失败便返回 NULL。
四、注意事项
- 在使用该函数之前,需要确保FreeRTOS系统已经初始化。
- 需要确保传入的形参值合理,以避免内存溢出。
- 创建队列时,应考虑系统的内存限制,避免因队列过大而导致内存不足。
- 在多任务环境中使用队列时,需要注意队列的访问冲突,FreeRTOS会自动处理这些冲突,但开发人员还需要注意。