引言
队列是FreeRTOS中最重要的任务间通信机制之一,它不仅实现了数据的安全传递,还提供了任务同步功能。本文将深入分析FreeRTOS队列的实现原理,从数据结构设计到算法实现,揭示其高效性和可靠性的秘密。
1. 队列数据结构深度剖析
1.1 核心数据结构
FreeRTOS队列的核心数据结构定义在queue.c中:
typedef struct QueueDefinition
{
int8_t * pcHead; /* 指向队列存储区域的开始 */
int8_t * pcWriteTo; /* 指向下一个可写入位置 */
union
{
QueuePointers_t xQueue; /* 队列专用数据 */
SemaphoreData_t xSemaphore; /* 信号量专用数据 */
} u;
List_t xTasksWaitingToSend; /* 等待发送的任务链表 */
List_t xTasksWaitingToReceive; /* 等待接收的任务链表 */
volatile UBaseType_t uxMessagesWaiting; /* 当前队列中的消息数量 */
UBaseType_t uxLength; /* 队列长度(项目数) */
UBaseType_t uxItemSize; /* 每个项目的大小 */
volatile int8_t cRxLock; /* 接收锁计数器 */
volatile int8_t cTxLock; /* 发送锁计数器 */
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /* 静态分配标志 */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition * pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
1.2 队列指针结构
队列使用联合体来区分不同的用途:
typedef struct QueuePointers
{
int8_t * pcTail; /* 指向队列存储区域的末尾 */
int8_t * pcReadFrom; /* 指向最后读取位置 */
} QueuePointers_t;
typedef struct SemaphoreData
{
TaskHandle_t xMutexHolder; /* 互斥量持有者 */
UBaseType_t uxRecursiveCallCount; /* 递归调用计数 */
} SemaphoreData_t;
设计亮点:
-
联合体设计:节省内存,同一结构支持队列和信号量
-
双指针系统:pcHead和pcWriteTo实现环形缓冲区
-
等待链表:分离发送和接收等待队列,提高效率
2. 队列创建机制
2.1 动态创建队列
QueueHandle_t xQueueCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize )
{
Queue_t * pxNewQueue;
size_t xQueueSizeInBytes;
uint8_t * pucQueueStorage;
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
/* 计算队列所需的总内存大小 */
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 )
{
if( uxItemSize == ( UBaseType_t ) 0 )
{
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
}
else
{
pxNewQueue->pcHead = ( int8_t * ) ( pxNewQueue + 1 );
}
/* 初始化队列 */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pxNewQueue->pcHead, pxNewQueue );
}
return pxNewQueue;
}
2.2 队列初始化过程
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
Queue_t * pxNewQueue )
{
/* 设置队列参数 */
pxNewQueue->uxLength = uxQueueLength;
pxNewQueue->uxItemSize = uxItemSize;
/* 重置队列到空状态 */
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
{
pxNewQueue->ucQueueType = queueQUEUE_TYPE_BASE;
}
#endif
#if ( configUSE_QUEUE_SETS == 1 )
{
pxNewQueue->pxQueueSetContainer = NULL;
}
#endif
traceQUEUE_CREATE( pxNewQueue );
}
3. 环形缓冲区实现原理
3.1 环形缓冲区的巧妙设计
FreeRTOS队列使用环形缓冲区来存储数据,这种设计的优势:
/* 队列重置函数展示了环形缓冲区的初始化 */
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL();
{
pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
pxQueue->pcWriteTo = pxQueue->pcHead;
pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 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 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
/* 初始化等待链表 */
vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
3.2 指针管理策略
环形缓冲区的关键在于指针的循环管理:
/* 写指针前进函数 */
static void prvCopyDataToQueue( Queue_t * const pxQueue,
const void * pvItemToQueue,
const BaseType_t xPosition )
{

最低0.47元/天 解锁文章
5777

被折叠的 条评论
为什么被折叠?



