Freertos列表和列表项详解

张开发
2026/5/17 14:42:22 15 分钟阅读
Freertos列表和列表项详解
Freertos列表和列表项详解一、列表和列表项详解1、核心概念2、数据结构定义 (以 FreeRTOS V10.x 为例)3、关键点解析4、常用操作函数/宏5、应用场景举例6、总结与注意事项二、代码示例一、列表和列表项详解FreeRTOS 中的列表 (List)和**列表项 (ListItem)**是 FreeRTOS 内核中实现任务调度、事件管理如信号量、队列、事件组等核心功能的基础数据结构。1、核心概念列表 (List):本质一个双向链表的管理结构。它本身并不存储数据而是管理链表的头、尾以及链表中的元素数量。作用用于组织和管理一系列相关的列表项。例如就绪列表 (Ready List):存放所有处于就绪状态的任务控制块TCB。阻塞列表 (Blocked List):存放所有因等待事件如信号量、队列消息、延时而阻塞的任务控制块。挂起列表 (Suspended List):存放所有被显式挂起的任务控制块。事件列表 (Event List):在信号量、队列等对象内部用于管理等待该事件发生的任务。列表项 (ListItem):本质一个双向链表的节点元素。它包含指向前驱和后继节点的指针。作用作为连接点将包含它的数据结构通常是任务控制块tskTCB或事件对象的结构体链接到某个列表中。关键特性列表项通常作为宿主结构体如tskTCB的一个成员变量存在。FreeRTOS 通过container_of宏或类似机制从列表项指针反向获取到其宿主结构体如任务控制块的指针。2、数据结构定义 (以 FreeRTOS V10.x 为例)/* 列表项 (ListItem) 结构体 */structxLIST_ITEM{TickType_t xItemValue;/* 辅助排序的值 (主要用于延时列表) */structxLIST_ITEM*pxNext;/* 指向链表中下一个列表项的指针 */structxLIST_ITEM*pxPrevious;/* 指向链表中前一个列表项的指针 */void*pvOwner;/* 指向包含此列表项的宿主对象 (通常是 TCB) 的指针 */void*pvContainer;/* 指向此列表项所属的列表 (List) 的指针 */};/* 简化定义 (常用) */typedefstructxLIST_ITEMListItem_t;/* 列表 (List) 结构体 */typedefstructxLIST{UBaseType_t uxNumberOfItems;/* 当前列表中列表项的数量 */ListItem_t*pxIndex;/* 遍历列表时使用的当前指针 */MiniListItem_t xListEnd;/* 列表的末端标记 (也是一个特殊的列表项) */}List_t;/* 简化的末端列表项 (用于 List_t 的 xListEnd) */structxMINI_LIST_ITEM{TickType_t xItemValue;structxLIST_ITEM*pxNext;structxLIST_ITEM*pxPrevious;};typedefstructxMINI_LIST_ITEMMiniListItem_t;3、关键点解析双向链表列表项 (ListItem_t) 通过pxNext和pxPrevious指针构成双向链表。这使得在链表中插入和删除节点的操作非常高效时间复杂度O ( 1 ) O(1)O(1)。列表末端 (xListEnd):列表结构 (List_t) 包含一个特殊的末端列表项 (xListEnd)。它本身是一个MiniListItem_t只有排序值和前后指针没有pvOwner和pvContainer。这个末端项不存储实际数据它的pxNext指向链表的第一个实际项pxPrevious指向链表的最后一个实际项。初始化时pxNext和pxPrevious都指向它自己表示空列表。uxNumberOfItems:记录列表中有效列表项的数量不包括末端项。这使得快速获取列表长度的时间复杂度为O ( 1 ) O(1)O(1)。pxIndex:用于遍历列表的当前指针。当调用listGET_OWNER_OF_NEXT_ENTRY这类宏时会移动此指针并返回下一个列表项的所有者如 TCB。xItemValue:列表项中的一个重要成员。它主要用于根据某个值对列表项进行排序在延时列表中xItemValue存储的是任务的唤醒时间绝对 tick 计数。列表按xItemValue升序排列保证链表头部的任务最先到期。在优先级就绪列表中xItemValue通常存储任务的优先级但 FreeRTOS 的实现更高效通常直接使用优先级作为数组索引数组元素是List_t。在其他列表中如阻塞在同一个信号量上的任务列表顺序可能不重要xItemValue可能未被使用或用于其他目的。pvOwner:指向包含此列表项的结构体宿主的指针。对于任务这就是tskTCB *。这是 FreeRTOS 从列表项找到其所属任务的关键。pvContainer:指向此列表项当前所属的列表 (List_t *) 的指针。这有助于快速知道一个列表项在哪个列表中。4、常用操作函数/宏FreeRTOS 提供了丰富的 API 来操作列表和列表项初始化:vListInitialise( List_t * const pxList ): 初始化一个列表设置末端项、计数器、索引。vListInitialiseItem( ListItem_t * const pxItem ): 初始化一个列表项清空指针。插入:vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ): 根据pxNewListItem-xItemValue的值将新项按升序插入到列表的正确位置。这是最常用的插入方式确保有序如延时列表。vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ): 将新项插入到列表的末端实际上是在末端项之前插入。当顺序不重要时使用如同一个信号量的阻塞列表。移除:uxListRemove( ListItem_t * const pxItemToRemove ): 从它当前所在的列表中移除指定的列表项并返回移除后原列表剩余项的数量。遍历/获取:listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ): 这是一个非常重要的宏。它移动列表的pxIndex指针到下一个列表项并通过pvOwner返回该列表项所属的宿主对象如 TCB。常用于公平地遍历就绪列表中的任务进行调度。它会循环遍历整个列表。listCURRENT_LIST_LENGTH( pxList ): 获取列表当前长度 (uxNumberOfItems)。listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ): 获取列表第一个有效项的xItemValue常用于检查延时列表中最早到期的任务时间。listGET_HEAD_ENTRY( pxList ): 获取指向列表第一个有效列表项的指针。状态检查:listLIST_IS_EMPTY( pxList ): 检查列表是否为空。5、应用场景举例任务状态管理:每个任务控制块 (tskTCB) 至少包含两个ListItem_t成员xStateListItem: 用于将任务链接到就绪列表、阻塞列表、挂起列表或事件列表中的一个。xEventListItem: 专门用于当任务阻塞在某个事件如信号量、队列上时链接到该事件对象本身的等待列表中。它的xItemValue通常用于优先级继承或优先级排队。调度器根据任务状态将其xStateListItem插入到相应的全局状态列表中。当任务等待事件时其xEventListItem会被插入到事件对象的等待列表中。延时管理:xDelayedTaskList1和xDelayedTaskList2是两个列表用于管理因vTaskDelay或vTaskDelayUntil而阻塞的任务。任务按唤醒时间 (xStateListItem.xItemValue) 排序插入。pxOverflowDelayedTaskList指针用于处理 tick 计数器溢出时的列表切换。定时器服务:FreeRTOS 的软件定时器也使用列表 (xActiveTimerList) 来管理活动的定时器按下次到期时间排序。事件对象:信号量 (SemaphoreHandle_t)、队列 (QueueHandle_t)、事件组 (EventGroupHandle_t) 等对象内部都有一个List_t成员通常叫xTasksWaitingToSend/xTasksWaitingToReceive或xTasksWaitingToReceive/xTasksWaitingToSend或xTasksWaiting。用于管理那些因等待该事件发生如等待获取信号量、等待队列消息而阻塞的任务。任务按优先级或 FIFO 排队。6、总结与注意事项核心作用列表和列表项是 FreeRTOS 高效管理任务和各种内核对象事件的基石。它们提供了灵活的任务状态转换和事件等待机制。有序性通过vListInsert按xItemValue排序插入是实现时间管理如延时和某些调度策略的关键。效率双向链表的设计使得插入和删除操作高效。uxNumberOfItems使得获取列表长度高效。pvOwner的重要性它是连接列表项和其宿主对象任务/事件的桥梁。pvContainer的用途快速判断列表项所属的列表并在移除操作时自动置空。内存布局理解ListItem_t在tskTCB等结构体中的位置有助于理解 FreeRTOS 的工作原理。列表项所有权一个列表项 (ListItem_t) 同时只能属于一个列表 (List_t)。在将其插入新列表前必须先将其从旧列表中移除如果它属于某个列表的话。uxListRemove和插入函数会自动处理pvContainer的更新。二、代码示例#includeFreeRTOS.h#includelist.h// 定义自定义数据结构作为列表项typedefstruct{intvalue;ListItem_t listItem;// 必须包含ListItem_t成员}DataItem_t;voidlist_example(void){// 1. 创建列表List_t myList;vListInitialise(myList);// 2. 创建列表项DataItem_t item1{.value10};DataItem_t item2{.value20};DataItem_t item3{.value30};// 3. 插入列表项按优先级顺序vListInsert(myList,item1.listItem);vListInsert(myList,item2.listItem);vListInsert(myList,item3.listItem);// 4. 遍历列表ListItem_t*current;for(currentlistGET_HEAD_ENTRY(myList);current!listGET_END_MARKER(myList);currentlistGET_NEXT(current)){// 获取包含列表项的结构体DataItem_t*datacontainer_of(current,DataItem_t,listItem);printf(Value: %d\n,data-value);}// 5. 删除指定项uxListRemove(item2.listItem);}关键说明ListItem_t是FreeRTOS内置的列表项结构vListInitialise()初始化空列表vListInsert()按优先级升序插入数值越小优先级越高listGET_HEAD_ENTRY()获取列表头节点container_of宏实现从列表项指针反推包含结构体指针uxListRemove()从列表中移除指定项内存注意事项实际使用时需动态分配列表项内存删除列表项后需手动释放内存示例中为简化使用静态变量

更多文章