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.hqwc.cn/news/326082.html

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

相关文章

C++11 14 17线程

线程类封装 #include<thread> #include<iostream> #include<string>using namespace std::chrono_literals; class MyThread { public:void Main() {std::cout << "MyThread Main" << name << ":" << age;} pr…

数据结构 模拟实现LinkedList双向不循环链表

目录 一、双向不循环链表的概念 二、链表的接口 三、链表的方法实现 &#xff08;1&#xff09;display方法 &#xff08;2&#xff09;size方法 &#xff08;3&#xff09;contains方法 &#xff08;4&#xff09;addFirst方法 &#xff08;5&#xff09;addLast方法 …

【HBase】——优化

1 RowKey设计 重要&#xff1a;一条数据的唯一标识就是 rowkey&#xff0c;那么这条数据存储于哪个分区&#xff0c;取决于 rowkey 处于 哪个一个预分区的区间内&#xff0c;设计 rowkey的主要目的 &#xff0c;就是让数据均匀的分布于所有的 region 中&#xff0c;在一定程度…

西电期末1025.平滑滤波

一.题目 二.分析与思路 别光看公式&#xff0c;读题干&#xff1a;“位置i的输出为距离i最近的三个输入的平均值”&#xff0c;再看示例&#xff0c;输入几个&#xff0c;输出几个&#xff0c;所以就是输出每个位置距离最近的三个输入的平均值&#xff0c;中间没什么问题&…

Service Weaver:Google开源基于分布式应用程序开发的框架,重新定义微服务边界

大家好&#xff0c;我是萧楚河&#xff0c;公众号&#xff1a;golang面试经典讲解&#xff0c;感谢关注&#xff0c;一起学习一起成长。一、前言 今年6月&#xff0c;一群谷歌员工&#xff08;由谷歌软件工程师Michael Whittaker领导&#xff09;发表了一篇名为“Towards Mode…

PyTorch基础操作

一、Tensor 在 PyTorch 中&#xff0c;张量&#xff08;Tensor&#xff09;是一个核心概念&#xff0c;它是一个用于存储和操作数据的多维数组&#xff0c;类似于 NumPy 的 ndarray&#xff0c;但与此同时&#xff0c;它也支持 GPU 加速&#xff0c;这使得在大规模数据上进行科…

vue简单实现滚动条

背景&#xff1a;产品提了一个需求在一个详情页&#xff0c;一个form表单元素太多了&#xff0c;需要滚动到最下面才能点击提交按钮&#xff0c;很不方便。他的方案是&#xff0c;加一个滚动条&#xff0c;这样可以直接拉到最下面。 优化&#xff1a;1、支持滚动条&#xff0c;…

吉林大学19、21级计算机学院《计算机网络》期末真题试题

一、21级&#xff08;考后回忆&#xff09; 一、不定项选择&#xff08;一共10个选择题&#xff0c;一个两分&#xff0c;选全得满分&#xff09; 不定项&#xff1a;可以选择1~4个 考点有&#xff1a; ①协议、服务 ②码分多路复用通过接受码片序列&#xff0c;求哪个站点发送…

Java反射篇----第三篇

系列文章目录 文章目录 系列文章目录前言一、反射使用步骤(获取 Class 对象、调用对象方法)二、获取 Class 对象有几种方法三、利用反射动态创建对象实例前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章…

分布式协调系统

分布式协调系统 分布式协调系统解决的进程间的通信和协作&#xff0c;根据是否在同一时间和是否相互引用分为四个模型。 示例系统Chubby 主功能&#xff1a;让客户端实现同步&#xff0c;方法是加锁服务 介绍一下系统&#xff1a; 系统由五台服务器构成&#xff0c;通过pax…

STM32通用定时器-输入捕获-脉冲计数

一、知识点 编码器   两相编码器&#xff08;正交编码器&#xff09;&#xff1a;两相编码器由 A 相和 B 相组成&#xff0c;相位差为 90 度。当旋转方向为顺时针时&#xff0c;A 相先变化&#xff0c;然后 B 相变化&#xff1b;当旋转方向为逆时针时&#xff0c;B 相先变化…

【leetcode】字符串中的第一个唯一字符

题目描述 给定一个字符串 s &#xff0c;找到 它的第一个不重复的字符&#xff0c;并返回它的索引 。如果不存在&#xff0c;则返回 -1 。 用例 示例 1&#xff1a; 输入: s “leetcode” 输出: 0 示例 2: 输入: s “loveleetcode” 输出: 2 示例 3: 输入: s “aabb”…