列表与列表项

认识列表和列表项

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,形成环

相关推荐
promising-w33 分钟前
【stm32开发板】单片机最小系统原理图设计
stm32·单片机·嵌入式硬件
韩立 •37 分钟前
GD32F103系列工程模版创建记录
stm32·单片机·嵌入式硬件
DIY机器人工房2 小时前
[9-2] USART串口外设 江协科技学习笔记(9个知识点)
笔记·科技·stm32·单片机·学习·江协科技
小宋同学在不断学习8 小时前
STM32 串口通信①:USART 全面理解 + 代码详解
stm32·单片机·嵌入式硬件
百里东风9 小时前
STM32CubeMX定时器配置
stm32·单片机·嵌入式硬件
长流小哥10 小时前
STM32:ESP8266 + MQTT 云端与报文全解析
stm32·单片机·物联网·mqtt
想搞嵌入式的小白12 小时前
硬件I2C和软件I2C的区别
stm32·单片机·嵌入式硬件
HelloTonyGo13 小时前
在MDK中自动部署LVGL,在stm32f407ZGT6移植LVGL-8.3,运行demo,显示label
stm32·嵌入式硬件·lvgl
apolloyhl13 小时前
1-Wire 一线式总线:从原理到实战,玩转 DS18B20 温度采集
arm开发·stm32·单片机·嵌入式硬件
DIY机器人工房15 小时前
[9-3] 串口发送&串口发送+接收 江协科技学习笔记(26个知识点)
笔记·科技·stm32·单片机·学习·江协科技