FreeRTOS 实时操作系统第九讲 - 链表 (数据结构)

一、链表简述

  链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列节点(链表中每一个元素称为节点)组成,节点可以在运行时动态生成。每个节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域。

  链表作为 C 语言的一种基础数据结构,在平时写程序中用得并不多,但在操作系统中使用得非常多。如果需要读懂 FreeRTOS 系统的源码,必须弄懂链表,如果只是应用 FreeRTOS 系统,简要了解即可。

  如下图:链表好比一个圆形的晾衣架,晾衣架上有很多钩子,钩子首尾相连;链表也是,链表由节点组成,节点与节点之间也是首尾相连。

  晾衣架的钩子本身不能代表很多东西,但钩子却可以挂载很多东西;同样,链表也类似,链表的节点本身不能储存很多内容,但节点跟晾衣架的钩子一样,可以挂载很多数据。

  另外,链表分为单向链表与双向链表,单向链表很少用,用得较多的是双向链表。

二、单向链表与双向链表

1、单向链表

  单向链表如下图,该链表中共有 n 个节点,前一个节点都有一个指针指向后一个节点,首尾相连,组成一个圈。

2、双链链表

  双向链表如下图,该链表中共有 n 个节点,前一个节点都有两个指针分别指向前后节点,首尾相连,组成一个圈。

3、链表与数组的差异

  链表是通过节点把离散的数据 (比如操作系统中任务) 链接成一个表,通过对节点的插入与删除操作实现对数据的储存。 而数组是通过开辟一段连续的内存来储存数据,这是数组与链表的最大区别。

三、FreeRTOS 中链表实现代码

  说明:FreeRTOS 操作系统中的列表与列表项,分别对应 C 语言中的链表与节点。

1、列表项定义

/** Definition of the only type of object that a list can contain.*/
struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */configLIST_VOLATILE TickType_t xItemValue;			/*< The value being listed.  In most cases this is used to sort the list in descending order. */struct xLIST_ITEM * configLIST_VOLATILE pxNext;		/*< Pointer to the next ListItem_t in the list. */struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;	/*< Pointer to the previous ListItem_t in the list. */void * pvOwner;										/*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */void * configLIST_VOLATILE pvContainer;				/*< Pointer to the list in which this list item is placed (if any). */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE			/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t;					/* For some reason lint wants this as two separate definitions. */

列表项结构体参数含义如下:

  1. 用于检测列表数据是否完整
  2. 辅助值 (比如用于任务的优先级),用于帮助节点进行顺序排列
  3. 指向下一个节点的指针
  4. 指向上一个节点的指针
  5. 指向拥有该节点的内核对象,通常是 TCB(任务控制块 / 任务句柄)
  6. 指向该节点所在的链表
  7. 用于检测列表数据是否完整

2、列表定义

/** Definition of the type of queue used by the scheduler.*/
typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */volatile UBaseType_t uxNumberOfItems;ListItem_t * configLIST_VOLATILE pxIndex;			/*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */MiniListItem_t xListEnd;							/*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */listSECOND_LIST_INTEGRITY_CHECK_VALUE				/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;

列表结构体参数含义如下:

  1. 用于检测列表数据是否完整
  2. 链表节点计数器,用于记录该链表下有多少个节点,根节点除外
  3. 链表节点索引指针,用于遍历节点
  4. 链表最后一个节点。 链表是一个圈,首尾相连的,首就是尾,尾也是首。 从字面理解就是链表的最后一个节点,其实也是链表的第一个节点,称之为生产者。 该生产者的数据类型是一个精简的节点,具体如下图。
  5. 用于检测列表数据是否完整

节点 (列表项) 精简定义:

struct xMINI_LIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */configLIST_VOLATILE TickType_t xItemValue;struct xLIST_ITEM * configLIST_VOLATILE pxNext;struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

四、链表与节点初始化函数

说明:FreeRTOS 操作系统中的列表与列表项,分别对对 C 语言中的链表与节点。

1、列表项初始化函数

void vListInitialiseItem( ListItem_t * const pxItem )
{/* Make sure the list item is not recorded as being on a list. */pxItem->pvContainer = NULL;/* Write known values into the list item ifconfigUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

说明: 列表项初始化,只需将 pvContainer 初始化为 NULL 即可,表示该节点还没有插入到任何链表。 初始化后如下图:

2、列表初始化函数

void vListInitialise( List_t * const pxList )
{/* The list structure contains a list item which is used to mark theend of the list.  To initialise the list the list end is insertedas the only list entry. */pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );			/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. *//* The list end value is the highest possible value in the list toensure it remains at the end of the list. */pxList->xListEnd.xItemValue = portMAX_DELAY;/* The list end next and previous pointers point to itself so we knowwhen the list is empty. */pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );	/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */pxList->uxNumberOfItems = ( UBaseType_t ) 0U;/* Write known values into the list ifconfigUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

说明: 列表初始化,主要初始化索引指针,链表计数值,与内部精简列表项。初始化后如下图:

五、链表操作函数 (尾部插入、升序插入、移除)

说明:FreeRTOS 操作系统中的列表与列表项,分别对对 C 语言中的链表与节点。

1、将节点插入链表尾部

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;/* Only effective when configASSERT() is also defined, these tests may catchthe list data structures being overwritten in memory.  They will not catchdata errors caused by incorrect configuration or use of FreeRTOS. */listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );/* Insert a new list item into pxList, but rather than sort the list,makes the new list item the last item to be removed by a call tolistGET_OWNER_OF_NEXT_ENTRY(). */pxNewListItem->pxNext = pxIndex;pxNewListItem->pxPrevious = pxIndex->pxPrevious;/* Only used during decision coverage testing. */mtCOVERAGE_TEST_DELAY();pxIndex->pxPrevious->pxNext = pxNewListItem;pxIndex->pxPrevious = pxNewListItem;/* Remember which list the item is in. */pxNewListItem->pvContainer = ( void * ) pxList;( pxList->uxNumberOfItems )++;
}

分析如下:

  1. 将新节点的 pxNext 指向根节点内的精简节点;
  2. 将新节点的 pxPrevious 指向之前的最后一个节点;
  3. 将之前最后一个节点的 pxNext 指向新节点;
  4. 将根节点内的精简节点 pxPrevious 指向新节点;
  5. 新节点的 pvContaner 指向链表;
  6. 链表的节点计数值加 1

尾部插入详情如下:

2、将节点按照升序插入链表

说明:如果两个节点的辅助值相同,则新节点在旧节点的后面插入。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/* Only effective when configASSERT() is also defined, these tests may catchthe list data structures being overwritten in memory.  They will not catchdata errors caused by incorrect configuration or use of FreeRTOS. */listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );/* Insert the new list item into the list, sorted in xItemValue order.If the list already contains a list item with the same item value then thenew list item should be placed after it.  This ensures that TCB's which arestored in ready lists (all of which have the same xItemValue value) get ashare of the CPU.  However, if the xItemValue is the same as the back markerthe iteration loop below will not end.  Therefore the value is checkedfirst, and the algorithm slightly modified if necessary. */if( xValueOfInsertion == portMAX_DELAY ){pxIterator = pxList->xListEnd.pxPrevious;}else{/* *** NOTE ***********************************************************If you find your application is crashing here then likely causes arelisted below.  In addition see http://www.freertos.org/FAQHelp.html formore tips, and ensure configASSERT() is defined!http://www.freertos.org/a00110.html#configASSERT1) Stack overflow -see http://www.freertos.org/Stacks-and-stack-overflow-checking.html2) Incorrect interrupt priority assignment, especially on Cortex-Mparts where numerically high priority values denote low actualinterrupt priorities, which can seem counter intuitive.  Seehttp://www.freertos.org/RTOS-Cortex-M3-M4.html and the definitionof configMAX_SYSCALL_INTERRUPT_PRIORITY onhttp://www.freertos.org/a00110.html3) Calling an API function from within a critical section or whenthe scheduler is suspended, or calling an API function that doesnot end in "FromISR" from an interrupt.4) Using a queue or semaphore before it has been initialised orbefore the scheduler has been started (are interrupts firingbefore vTaskStartScheduler() has been called?).**********************************************************************/for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM.  This is checked and valid. */{/* There is nothing to do here, just iterating to the wantedinsertion position. */}}pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;/* Remember which list the item is in.  This allows fast removal of theitem later. */pxNewListItem->pvContainer = ( void * ) pxList;( pxList->uxNumberOfItems )++;
}

分析如下:

  1. 查找插入位置;
  2. 调整指向关系
  3. 新节点的 pvContaner 指向链表
  4. 链表的节点计数值加 1

升序插入详情如下:

3、移除节点

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in.  Obtain the list from the list
item. */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;/* Only used during decision coverage testing. */mtCOVERAGE_TEST_DELAY();/* Make sure the index is left pointing to a valid item. */if( pxList->pxIndex == pxItemToRemove ){pxList->pxIndex = pxItemToRemove->pxPrevious;}else{mtCOVERAGE_TEST_MARKER();}pxItemToRemove->pvContainer = NULL;( pxList->uxNumberOfItems )--;return pxList->uxNumberOfItems;
}

分析如下:

  1. 通过节点获取链表;
  2. 调整指向关系
  3. 调整链表的索引指针
  4. 将删除节点的 pvContainer 指向 NULL;
  5. 链表的节点计数值减 1
  6. 返回链表的节点计数值

移除详情如下:

六、链表编程测试

说明:软件模拟仿真

1、只创建 1 个任务,在任务中进行链表测试

2、列表与列表项定义
说明:watch 中查看变量值,需要定义为全局变量

3、任务 1 代码

4、设置为模拟仿真,避免仿真错误,删除硬件相关的初始化代码


5、增加断点


6、开始仿真,并在 watch 添加列表与列表项

7、全速仿真至任务 1,再按 F10 单步执行,同时查看 watch 窗口

8、验证 OK。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/601320.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring整合MyBatis框架!!!

搭建环境&#xff1a; pom.xml: <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></pro…

指定linux文件夹下所有文件赋权命令“chmod -R 755”

仓库&#xff1a;Ai-trainee/GPT-Prompts-Hub 下面我们假设要为&#xff1a;/opt/robot/lib/robot_control/下所有子文件赋权 如果要为 robot_control 目录中的所有文件分配权限&#xff08;在 Linux 术语中也称为“更改文件权限”或“chmod”&#xff09;&#xff0c;则可以…

残疾大学生找工作好难

有点肢体残疾且普通话不太标准的大学生好难找工作啊&#xff0c;怎么办&#xff1f;难道得去捡垃圾了&#xff1f;求学多年&#xff0c;好容易读了个大学(省内一本)&#xff0c;我咋这么命苦&#xff0c;找了800多家&#xff0c;面试好几家&#xff0c;都没一个要我的。

Microsoft Visual Studio 2022 install Project 下载慢

1. 关闭Internet 协议版本6 2. 如果没有效果&#xff0c;打开Internet 协议版本4&#xff0c;更改DNS 3. 在浏览器中下载后安装&#xff0c;下载地址如下&#xff1a; Microsoft Visual Studio Installer Projects 2022 - Visual Studio Marketplace 4. 安装时注意关闭vs&…

2024CISA开门红,凌晨通过

祝各位新年快乐&#xff0c;万事顺遂 听说最近it内审有很多甲方开始裁员&#xff0c;为了防止波及到各位&#xff0c;想必各位也在考虑考取证书提高自己的权重&#xff0c;就算后面波及到了自己&#xff0c;去换工作的时候也会快人一步 但是大家都知道&#xff0c;最近都忙得…

一种DevOpts的实现方式:基于gitlab的CICD(一)

写在之前 笔者最近准备开始入坑CNCF毕业的开源项目&#xff0c;看到其中有一组开源项目的分类就是DevOpts。这个领域内比较出名的项目是Argocd&#xff0c;Argo CD 是一个用于 Kubernetes 的持续交付 (Continuous Delivery) 工具&#xff0c;它以声明式的方式实现了应用程序的…

五、HTML 标题

在 HTML 文档中&#xff0c;标题很重要。 一、HTML 标题 标题&#xff08;Heading&#xff09;是通过 <h1> - <h6> 标签进行定义的。<h1> 定义最大的标题。 <h6> 定义最小的标题。 <h1>这是一个标题。</h1> <h2>这是一个标题。&l…

第九节HarmonyOS 常用基础组件9-TextArea

1、描述 多行文本输入框组件&#xff0c;当输入的文本内容超过组件宽度时会自动换行显示。 2、接口 TextArea(value?:{placeholder?: ResourceStr, text?: ResourceStr, controller?: TextAreaController}) 3、参数 参数名 参数类型 必填 描述 placeholder Resour…

使用mysql查询当天、近一周、近一个月及近一年的数据以及各种报表查询sql

1.mysql查询当天的数据 1 select * from table where to_days(时间字段) to_days(now()); 2.mysql查询昨天的数据 1 select * from table where to_days(now( ) ) - to_days( 时间字段名) < 1 3.mysql查询近一个月的数据 1 SELECT * FROM table WHERE date(时间字段) …

迁移数据mysql到clickhouse

场景&#xff1a; 项目上需要将mysql表中数据迁移到clickhouse。 理论&#xff1a; 借助MaterializeMySQL 说明&#xff1a; 首先该方案实施需要启动mysql的binlog配置否则同步不了&#xff0c;尽管MaterializeMySQL官方说是在实验阶段&#xff0c;不应该在生产上使用&#x…

主浏览器优化之路2——Edge浏览器的卸载与旧版本的重新安装

Edge浏览器的卸载与旧版本的重新安装 引言开整寻找最年轻的她开始卸载原本的Edge工具下载后新版本的安装 结尾 引言 &#xff08;这个前奏有点长&#xff0c;但是其中有一些我的思考顿悟与标题的由来&#xff0c;望耐心&#xff09; 我在思考这个系列的时候 最让我陷入困得是…

Spring Boot依赖版本声明

链接 官网 Spring Boot文档官网&#xff1a;​​​​​​https://docs.spring.io/spring-boot/docs/https://docs.spring.io/spring-boot/docs/ Spring Boot 2.0.7.RELEASE Spring Boot 2.0.7.RELEASE reference相关&#xff1a;https://docs.spring.io/spring-boot/docs/2.…

文件夹重命名方法:提高效率减少错误,中英文批量翻译文件夹名称

在日常生活和工作中&#xff0c;经常要处理大量的文件夹&#xff0c;无论是整理电脑上的文件&#xff0c;还是为项目分类。如何快速、准确地重命名这些文件夹&#xff0c;对于提高工作效率和减少错误至关重要。现在来看下云炫文件管理器一些实用的文件夹重命名方法&#xff0c;…

Python+Requests实现接口自动化测试(超详细的)

一般对于自动化的理解&#xff0c;有两种方式的自动化。 第一&#xff0c;不需要写代码&#xff0c;完全由工具实现&#xff0c;这种方式的工具一般是公司自己研发的&#xff0c;方便黑盒测试人员使用。这种工具的特点是学习成本低&#xff0c;方便使用&#xff0c;但是通用性…

高压放大器输出接法及其注意事项

高压放大器应用场景非常广泛&#xff0c;非常适用于半导体高压驱动、TFT产业高压驱动、各种高压工程等应用&#xff1b;也很适用当作音频信号产生器或函数波形产生器的波形放大使用。使用场景广泛&#xff0c;放大器的输出接法也多种&#xff0c;对于不同的放大器也有对应的输出…

C++补充内容--EasyX-UI界面

esay x 其他 地图打印(利用二维数组) 双缓冲 当我们绘制一张图 然后另一张图盖住前一张图的某个部分的时候 由于while的存在 会导致 两张图不停的闪烁 所以加入双缓冲可以解决这个问题 开启双缓冲 之后等待Flush或者End 才会进行图片的绘制 不然不会进行图片的绘制,这样就可…

东信免驱系列身份证阅读器串口通讯协议解析示例,适用于单片机、ARM等系统开发集成使用

完整的一次读卡流程包括&#xff1a; 身份证寻卡 > 身份证选卡 > 身份证读卡&#xff0c;三个步骤 缺一不可&#xff08;见通讯协议&#xff09;。 寻卡&#xff1a;EA EB EC ED 04 00 B0 B4 BB 返回&#xff1a;EA EB EC ED 05 00 00 B0 B5 BB 选卡&#xff1a;EA …

1.4 Unity协程

一、先说接口 接口是不能实例化的&#xff0c;想实例化接口&#xff0c;只能实例化继承了接口的类。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace InterfaceTest {interface IMyInterfa…

基于Springboot的旅游管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的旅游管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

使用STM32微控制器驱动LCD1602显示器

驱动LCD1602显示器是嵌入式系统常见的任务之一&#xff0c;而STM32微控制器因其灵活性和丰富的外设而成为了广泛采用的解决方案。在这篇文章中&#xff0c;我们将探讨如何使用STM32微控制器来驱动LCD1602显示器。我们将从STM32的GPIO配置、延时函数以及LCD1602的初始化和写入数…