列表与列表项

认识列表和列表项

FreeRTOS 中的 列表(List) 和 列表项(ListItem)是其内核实现的核心数据结构,广泛用于任务调度、队列管理、事件组、信号量等模块。它们通过双向链表实现,支持高效的元素插入、删除和遍历操作。

1. 列表(List)

列表是 FreeRTOS 中用于管理多个列表项的容器,其结构定义在 "list.h" 中

主要成员如下:

  • uxNumberOfItems: 当前列表中包含的列表项数量。

  • pxIndex: 指向列表中的一个列表项,用于遍历列表(如任务切换时遍历就绪列表)。

  • xListEnd: 列表的"结束标记",是一个特殊的列表项,作为链表的头和尾的锚点。

列表特点:

  • 列表是一个双向循环链表,通过 "xListEnd "作为链表的起点和终点。

  • 列表项按 "xItemValue"(列表项的值)"升序排列",便于快速插入和查找(如任务优先级调度)。

2. 列表项(ListItem)

列表项是链表的节点,定义在 "list.h" 中

主要成员如下:

  • pvOwner: 指向列表项的所有者(如任务控制块 `TCB_t`)。

  • pxContainer: 指向该列表项所属的列表。

  • pxPrevious/pxNext: 指向前一个和后一个列表项的指针。

  • xItemValue: 列表项的值,用于排序(如任务的优先级)。

列表项特点:

  • 每个列表项可以动态插入或移除到不同的列表中。

  • 列表项的 "xItemValue"决定了它在列表中的位置(按升序排列)。

3. 列表和列表项的关系

  • 列表通过 "xListEnd"作为根节点,所有列表项通过 "pxPrevious"和 "pxNext" 指针链接成一个环。

  • 例如,FreeRTOS 的任务就绪列表(pxReadyTasksLists)是一个列表,每个任务的控制块(TCB)中的状态列表项(xStateListItem)会插入到对应的就绪列表中。

4. 应用场景

  1. 任务管理
  • 任务的就绪列表、阻塞列表、挂起列表均通过列表和列表项管理。

  • 例如,任务的 "xStateListItem" 会根据任务状态插入到不同的列表中。

  • 任务的优先级由 "xItemValue" 表示,列表按优先级排序。

  1. 事件组、队列、信号量
  • 当任务等待事件或资源时,其事件列表项(如 xEventListItem)会被插入到事件或资源的等待列表中。

5. 常用操作函数

FreeRTOS 提供了一系列 API 操作列表和列表项:

列表操作:

  • vListInitialise(List_t *pxList): 初始化一个空列表。

  • vListInsert(List_t *pxList, ListItem_t *pxNewListItem): 按 "xItemValue" 升序插入列表项。

  • vListRemove(ListItem_t *pxItemToRemove): 从列表中移除某个列表项。

列表项操作:

  • vListInitialiseItem(ListItem_t *pxItem): 初始化列表项。

  • listSET_LIST_ITEM_OWNER(ListItem_t *pxListItem, void *pvOwner): 设置列表项的所有者(如任务句柄)。

  • listGET_LIST_ITEM_VALUE(ListItem_t *pxListItem): 获取列表项的值。

下面用代码演示列表项的插入、删除以及末尾插入:

1.插入列表项1,值为40

复制代码
//列表任务
void list_task(void *pvParameters)
{
   vListInitialise( &TestList );//初始化列表1
   vListInitialiseItem(&ListItem1);//初始化列表项1
   vListInitialiseItem(&ListItem2);//初始化列表项2
   vListInitialiseItem(&ListItem3);//初始化列表项3
	
	 ListItem1.xItemValue = 40;
	 ListItem2.xItemValue = 60;
	 ListItem3.xItemValue = 50;
	
printf("----------------------------列表与列表项地址------------------------- \r\n");
printf("项目                         地址                                    \r\n");
printf("TestList                     %#x                         \r\n",(int)&TestList);
printf("TestList->pxIndex            %#x                 \r\n",(int)TestList.pxIndex);
printf("TestList->xListEnd           %#x             \r\n",(int)&(TestList.xListEnd));
printf("ListItem1                    %#x                   \r\n",(int)&ListItem1);
printf("ListItem2                    %#x                     \r\n",(int)&ListItem2);
printf("ListItem3                    %#x                     \r\n",(int)&ListItem3);

//TestList插入列表项1
vListInsert(&TestList ,&ListItem1);//插入列表项函数
printf("---------------------------插入列表项ListItem1------------------------\r\n");
printf("TestList->xListEnd->pxNext        %#x       \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext                 %#x              \r\n",(int)(ListItem1.pxNext));
printf("----------------------------前后连接分割线----------------------------\r\n");
printf("TestList->xListEnd->pxPrevious    %#x   \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious             %#x           \r\n",(int)(ListItem1.pxPrevious));
printf("------------------------------结束---------------------------------------\r\n");

用串口打印连接结果:

可以看出列表和列表项1进行了环形连接

2.紧接着上面的代码再插入列表项2,值为60

复制代码
	//插入列表项2
  vListInsert(&TestList ,&ListItem2);
printf("--------------------------------再插入列表项ListItem2------------------------\r\n");
printf("TestList->xListEnd->pxNext        %#x       \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext                 %#x               \r\n",(int)(ListItem1.pxNext));
printf("ListItem2->pxNext                 %#x               \r\n",(int)(ListItem2.pxNext));

printf("---------------------------------前后连接分割线-----------------------------\r\n");
printf("TestList->xListEnd->pxPrevious    %#x  \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious             %#x          \r\n",(int)(ListItem1.pxPrevious));
printf("ListItem2->pxPrevious             %#x          \r\n",(int)(ListItem2.pxPrevious));
printf("---------------------------------结束--------------------------------------\r\n");

用串口打印连接结果:

可以看出他们的连接方式

3.紧接着上面的代码再插入列表项3,值为50

复制代码
//插入列表项3
  vListInsert(&TestList ,&ListItem3);
printf("---------------------------再插入列表项ListItem3------------------------\r\n");
printf("TestList->xListEnd->pxNext        %#x      \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext                %#x                \r\n",(int)(ListItem1.pxNext));
printf("ListItem2->pxNext                %#x              \r\n",(int)(ListItem2.pxNext));
printf("ListItem3->pxNext                %#x               \r\n",(int)(ListItem3.pxNext));

printf("----------------------------前后连接分割线---------------------------------\r\n");
printf("TestList->xListEnd->pxPrevious    %#x \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious             %#x          \r\n",(int)(ListItem1.pxPrevious));
printf("ListItem2->pxPrevious             %#x         \r\n",(int)(ListItem2.pxPrevious));
printf("ListItem3->pxPrevious             %#x         \r\n",(int)(ListItem3.pxPrevious));
printf("-----------------------------------结束-----------------------------------\r\n");

用串口打印结果:

可以看出列表项3插在了列表项1和2之间,说明列表项的 "xItemValue"决定了它在列表中的位置(按升序排列)

4.紧接着上面的代码删除列表项2

复制代码
//列表删除列表项2
  uxListRemove(&ListItem2);
printf("--------------------------------列表删除ListItem2------------------------\r\n");
printf("TestList->xListEnd->pxNext       %#x        \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext                %#x                \r\n",(int)(ListItem1.pxNext));
printf("ListItem3->pxNext                %#x                \r\n",(int)(ListItem3.pxNext));

printf("-------------------------------前后连接分割线-----------------------------\r\n");
printf("TestList->xListEnd->pxPrevious    %#x   \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious             %#x           \r\n",(int)(ListItem1.pxPrevious));
printf("ListItem3->pxPrevious             %#x           \r\n",(int)(ListItem3.pxPrevious));
printf("---------------------------------结束--------------------------------------\r\n");

用串口打印结果:

可以看出ListItem3->pxNext 由原来指向列表项2指向了迷你列表向形成新的环

5.紧接着上面的代码末尾插入列表项2

复制代码
//末尾插入列表项2
  TestList.pxIndex = TestList.pxIndex->pxNext;
  vListInsertEnd(&TestList ,&ListItem2);
printf("---------------------------末尾插入列表项ListItem2--------------------------\r\n");
printf("TestList->xListEnd->pxNext        %#x      \r\n",(int)(TestList.xListEnd.pxNext));
printf("ListItem1->pxNext                %#x               \r\n",(int)(ListItem1.pxNext));
printf("ListItem2->pxNext                %#x               \r\n",(int)(ListItem2.pxNext));
printf("ListItem3->pxNext                %#x               \r\n",(int)(ListItem3.pxNext));

printf("-------------------------------前后连接分割线--------------------------------\r\n");
printf("TestList->xListEnd->pxPrevious    %#x   \r\n",(int)(TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious             %#x           \r\n",(int)(ListItem1.pxPrevious));
printf("ListItem2->pxPrevious             %#x           \r\n",(int)(ListItem2.pxPrevious));
printf("ListItem3->pxPrevious             %#x           \r\n",(int)(ListItem3.pxPrevious));
printf("-------------------------------结束-----------------------------------------\r\n");

用串口打印结果:

这时List的pxIdex指向了ListItem1,就是TestList.pxIndex = TestList.pxIndex->pxNext;这时ListItem1指向ListItem3,ListItem3指向迷你列表项,迷你列表项指向ListItem2,ListItem2指向ListItem1,形成环

相关推荐
L小李要学习5 小时前
STM32学习
stm32·嵌入式硬件·学习
正点原子7 小时前
【正点原子STM32MP257连载】第二章 ATK-DLMP257B使用前准备 #串口软件 #MobaXterm
linux·stm32·单片机·嵌入式硬件
ℳ๓. Sweet7 小时前
【STM32】在FreeRTOS下使用硬件SPI收发数据出现的时序耦合问题(WK2124芯片为例)
stm32·单片机·嵌入式硬件
努力创造奇迹10 小时前
STM32 HAL库 实现485通信
stm32·单片机·嵌入式硬件
Tlog嵌入式10 小时前
STM32提高篇: 以太网通讯
网络·stm32·单片机·嵌入式硬件·mcu·iot
番茄老夫子12 小时前
适合stm32 前端adc使用的放大器芯片
stm32·单片机·嵌入式硬件
m0_疾风13 小时前
STM32
stm32·单片机·嵌入式硬件
硬匠的博客13 小时前
C/C++基础
stm32·单片机·嵌入式硬件
LaoZhangGong12313 小时前
MCU屏和RGB屏
经验分享·stm32·单片机·嵌入式硬件·fsmc
格里姆肖14 小时前
LVGL源码(7):渲染
c语言·stm32·单片机