FreeRTOS(四)FreeRTOS列表与列表项

news2024/11/16 13:51:06

目录

列表

列表项

迷你列表项

 列表和列表项的关系

列表相关API函数

列表初始化 

列表项初始化

列表项插入

 列表项末尾插入

 列表项删除

列表遍历 


在 FreeRTOS 中,列表(List)和列表项(ListItem)是核心数据结构,用于实现任务、队列、信号量等的管理和调度。这些数据结构是 FreeRTOS 实现其调度算法和同步机制的基础。

列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。

列表项就是存放在列表中的项目

列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表

列表

列表是由多个列表项组成的集合,用于管理一组实体。列表提供了添加、移除、遍历列表项的操作。 (任务状态随时会发生改变)

typedef struct xLIST
{
  	 listFIRST_LIST_INTEGRITY_CHECK_VALUE		/* 校验值 */
   	 volatile UBaseType_t uxNumberOfItems;			/* 列表中的列表项数量 */
   	 ListItem_t * configLIST_VOLATILE pxIndex		/* 用于遍历列表项的指针 */
   	 MiniListItem_t xListEnd					/* 末尾列表项 */
   	 listSECOND_LIST_INTEGRITY_CHECK_VALUE		/* 校验值 */
} List_t;

1、在该结构体中, 包含了两个宏,这两个宏是确定的已知常量, FreeRTOS通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏 ,该功能一般用于调试, 默认是不开启的

2、成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd

3、成员 pxIndex 用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项

4、成员变量 xListEnd 是一个迷你列表项,排在最末尾(这是一个特殊的列表项,用作列表的末尾标记。它通常不用于存储实际的数据,而是用来优化列表的遍历操作。xListEnd 列表项通常被插入到列表的末尾,使得在列表末尾插入和删除操作更加高效)

列表项

列表项(地址非连续、人为连接在一起的)是构成列表的基本单元,每个列表项代表了系统中的一个实体,如任务控制块(TCB)、队列、信号量等。

struct xLIST_ITEM
{
    	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE			/* 用于检测列表项的数据完整性 */
    	configLIST_VOLATILE TickType_t xItemValue				/* 列表项的值 */
     	struct xLIST_ITEM * configLIST_VOLATILE pxNext		/* 下一个列表项 */
  	    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious		/* 上一个列表项 */
    	void * pvOwner							/* 列表项的拥有者 */
    	struct xLIST * configLIST_VOLATILE pxContainer; 			/* 列表项所在列表 */
       	listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE			/* 用于检测列表项的数据完整性 */
};
typedef struct xLIST_ITEM ListItem_t; 

 1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序(通常用于表示列表项的优先级或者用于在有序列表中排序。在任务的列表项中,这个值通常是任务的优先级)譬如:A列表项作业时间是10ms B列表项的作业时间是20ms,则排列顺序是A-->B,像短作业优先调度算法一样

2、成员变量 pxNext pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项

3、成员变量 pxOwner 用于指向包含列表项的对象(通常是任务控制块)

4、成员变量 pxContainer 用于指向列表项所在列表。

 

迷你列表项

 迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项

它通常不用于存储实际的数据,而是用来优化列表的遍历操作。xListEnd 列表项通常被插入到列表的末尾,使得在列表末尾插入和删除操作更加高效

struct xMINI_LIST_ITEM
{
    	listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE 			/* 用于检测数据完整性 */
	configLIST_VOLATILE TickType_t xItemValue;				/* 列表项的值 */
    	struct xLIST_ITEM * configLIST_VOLATILE pxNext;		/* 上一个列表项 */
   	struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; 		/* 下一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;

1、成员变量 xItemValue 为列表项的值,这个值多用于按升序对列表中的列表项进行排序

2、成员变量 pxNext pxPrevious 分别用于指向列表中列表项的下一个列表项和上一个列表项

3、迷你列表项只用于标记列表的末尾和挂载其他插入列表中的列表项,因此不需要成员变量 pxOwner pxContainer,以节省内存开销

 列表和列表项的关系

列表初始状态,以及即将插入的两个列表项如下(根据尾插法插入新的列表项)

列表相关API函数

列表初始化 

void vListInitialise( List_t * const pxList ) 
{ 
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); (1)   
    pxList->xListEnd.xItemValue = portMAX_DELAY;     (2)   

    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );  (3)   
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );  (4)   

    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;     (5)   
    
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );   (6)   

    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );   (7)   

}

(1)这行代码设置 pxIndex 成员,这是一个用于快速索引的指针,它指向列表的末尾。xListEnd 是一个 MiniListItem_t 类型的成员,它作为一个哨兵节点,表示列表的末尾。这样做的目的是为了优化列表的操作,使得在列表末尾插入和删除操作更加高效。

(2)这里将列表结束节点的 xItemValue 设置为 portMAX_DELAYportMAX_DELAY 是一个定义在 FreeRTOS 配置文件中的常量,代表最大的延迟时间。在列表中,它通常用来表示列表项的最大优先级值,用于有序列表的末端。

(3)  pxNext 成员指向下一个列表项。在列表初始化时,列表为空,所以末尾节点的 pxNext 指针指向它自己本身,表示没有更多的元素。

(4)   pxPrevious 成员指向前一个列表项。同样地,初始化时列表为空,所以末尾节点的 pxPrevious 指针也指向它自己本身。

(5)   uxNumberOfItems 成员记录列表中当前的列表项数量。在列表初始化时,列表为空,所以这个值被设置为 0

(6)   listSET_LIST_INTEGRITY_CHECK_1_VALUE 宏用于设置列表的第一个完整性检查值。这是 FreeRTOS 的一种机制,用于在运行时检查列表的完整性是否遭到破坏。如果内存被意外修改,这些值将会改变,从而可以检测到错误。

(7)   listSET_LIST_INTEGRITY_CHECK_2_VALUE 宏用于设置列表的第二个完整性检查值。与第一个检查值一样,它提供了额外的一层保护,以确保列表数据结构没有被损坏

列表项初始化

void vListInitialiseItem( ListItem_t * const pxItem ) 
{ 
    pxItem->pvContainer = NULL;     (1)

    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); (2)
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); (3)
}

(1)这行代码将列表项的 pvContainer 成员初始化为 NULLpvContainer 是一个空指针,用于指向包含该列表项的列表。当列表项没有被包含在任何列表中时,它的 pvContainerNULL。当列表项被添加到列表中时,它的 pvContainer 会被设置为指向该列表。

(2)和(3)

这两行代码用于设置列表项的完整性检查值。这是 FreeRTOS 的一种机制,用于在运行时检查列表项的完整性是否遭到破坏。如果内存被意外修改,这些值将会改变,从而可以检测到错误。

  • listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE:宏用于设置列表项的第一个完整性检查值。
  • listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE:宏用于设置列表项的第二个完整性检查值。

列表项插入

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) 
{ 
    ListItem_t *pxIterator; 
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;   (1) 
  
     listTEST_LIST_INTEGRITY( pxList );        (2) 
     listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); 
 
     if( xValueOfInsertion == portMAX_DELAY )       (3) 
     { 
      pxIterator = pxList->xListEnd.pxPrevious;       (4) 
     } 
     else 
     { 
      for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->\  (5) 
        pxNext->xItemValue <=xValueOfInsertion; pxIterator = pxIterator->pxNext )  
      { 
           //空循环,什么也不做! 
      } 
     }  
     pxNewListItem->pxNext = pxIterator->pxNext;       (6) 
     pxNewListItem->pxNext->pxPrevious = pxNewListItem; 
     pxNewListItem->pxPrevious = pxIterator; 
     pxIterator->pxNext = pxNewListItem; 
     pxNewListItem->pvContainer = ( void * ) pxList;       (7) 
     ( pxList->uxNumberOfItems )++;          (8) 
}

(1)、获取要插入的列表项值,即列表项成员变量xItemValue的值,因为要根据这个值来确 定列表项要插入的位置。

(2)、这一行和下一行代码用来检查列表和列表项的完整性的。其实就是检查列表和列表项中用于完整性检查的变量值是否被改变。这些变量的值在列表和列表项初始化的时候就被写入 了,这两行代码需要实现函数configASSERT()!

(3)、要插入列表项,第一步就是要获取该列表项要插入到什么位置!如果要插入的列表项 的值等于portMAX_DELAY,也就是说列表项值为最大值,这种情况最好办了,要插入的位置 就是列表最末尾了。

(4)、获取要插入点,注意!列表中的 xListEnd 用来表示列表末尾,在初始化列表的时候 xListEnd的列表值也是portMAX_DELAY,此时要插入的列表项的列表值也是portMAX_DELAY。 这两个的顺序该怎么放啊?通过这行代码可以看出要插入的列表项会被放到xListEnd前面。

(5)、要插入的列表项的值如果不等于portMAX_DELAY那么就需要在列表中一个一个的找 自己的位置,这个 for 循环就是找位置的过程,当找到合适列表项的位置的时候就会跳出。由 于这个for循环是用来寻找列表项插入点的,所以for循环体里面没有任何东西。这个查找过程 是按照升序的方式查找列表项插入点的。

(6)、经过上面的查找,我们已经找到列表项的插入点了,从本行开始接下来的四行代码就 是将列表项插入到列表中,插入过程和数据结构中双向链表的插入类似。像 FreeRTOS 这种 RTOS 系统和一些协议栈都会大量用到数据结构的知识,所以建议大家没事的时候多看看数据 结构方面的书籍,否则的话看源码会很吃力的。

(7)、列表项已经插入到列表中了,那么列表项的成员变量 pvContainer 也该记录此列表项 属于哪个列表的了。

(8)、列表的成员变量uxNumberOfItems加一,表示又添加了一个列表项。

 列表项末尾插入

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) 
{ 
    ListItem_t * const pxIndex = pxList->pxIndex; 
    listTEST_LIST_INTEGRITY( pxList );       (1)
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );  
    
    pxNewListItem->pxNext = pxIndex;        (2) 

    pxNewListItem->pxPrevious = pxIndex->pxPrevious; 
    mtCOVERAGE_TEST_DELAY(); 
    pxIndex->pxPrevious->pxNext = pxNewListItem; 
    pxIndex->pxPrevious = pxNewListItem; 
    pxNewListItem->pvContainer = ( void * ) pxList;     (3) 

    ( pxList->uxNumberOfItems )++;          (4)
    
}

(1)、与下面的一行代码完成对列表和列表项的完整性检查。

(2)、从本行开始到(3)之间的代码就是将要插入的列表项插入到列表末尾。使用函数 vListInsert()向列表中插入一个列表项的时候这个列表项的位置是通过列表项的值,也就是列表 项成员变量 xItemValue 来确定。vListInsertEnd()是往列表的末尾添加列表项的,我们知道列表 中的xListEnd 成员变量表示列表末尾的,那么函数vListInsertEnd()插入一个列表项是不是就是 插到 xListEnd 的前面或后面啊?这个是不一定的,这里所谓的末尾要根据列表的成员变量 pxIndex 来确定的!前面说了列表中的pxIndex成员变量是用来遍历列表的,pxIndex所指向的 列表项就是要遍历的开始列表项,也就是说pxIndex 所指向的列表项就代表列表头!由于是个 环形列表,所以新的列表项就应该插入到pxIndex所指向的列表项的前面。

(3)、标记新的列表项pxNewListItem属于列表pxList。

(4)、记录列表中的列表项数目的变量加一,更新列表项数目。

 列表项删除

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) 
{ 
    List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;   (1) 
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; (2) 
     pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; 
     mtCOVERAGE_TEST_DELAY();  
     if( pxList->pxIndex == pxItemToRemove )         
     { 
          pxList->pxIndex = pxItemToRemove->pxPrevious;     (3) 
     } 
     else 
     { 
          mtCOVERAGE_TEST_MARKER(); 
     }  
     pxItemToRemove->pvContainer = NULL;        (4) 
     ( pxList->uxNumberOfItems )--; 
     return pxList->uxNumberOfItems;          (5) 
}

(1)、要删除一个列表项我们得先知道这个列表项处于哪个列表中,直接读取列表项中的成 员变量pvContainer就可以得到此列表项处于哪个列表中。

(2)、与下面一行完成列表项的删除,其实就是将要删除的列表项的前后两个列表项“连接” 在一起。

(3)、如果列表的pxIndex正好指向要删除的列表项,那么在删除列表项以后要重新给 pxIndex找个“对象”啊,这个新的对象就是被删除的列表项的前一个列表项。

(4)、被删除列表项的成员变量pvContainer清零。

(5)、返回新列表的当前列表项数目。

列表遍历 

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )      (1) 
{                  \  
    List_t * const pxConstList = ( pxList );           
     ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;      (2) 
     if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd )(3)     
     {                  
      ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;     (4) 
     }                 
     ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;        (5)
}

(1)、pxTCB 用来保存pxIndex所指向的列表项的pvOwner变量值,也就是这个列表项属于 谁的?通常是一个任务的任务控制块。pxList表示要遍历的列表。

(2)、列表的pxIndex 变量指向下一个列表项。

(3)、如果pxIndex 指向了列表的xListEnd成员变量,表示到了列表末尾。

(4)、如果到了列表末尾的话就跳过 xListEnd,pxIndex 再一次重新指向处于列表头的列表 项,这样就完成了一次对列表的遍历。

(5)、将pxIndex 所指向的新列表项的pvOwner赋值给pxTCB。 此函数用于从多个同优先级的就绪任务中查找下一个要运行的任务。

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

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

相关文章

如何防止订单重复

如何防止订单重复 在整个下单流程中&#xff0c;哪里重复操作影响最大&#xff1f;确认订单只是修改订单状态从未支付改为待支付而已&#xff0c;不会对我们的主要业务产生影响&#xff0c;而在支付的时候&#xff0c;主要由第三方平台&#xff0c;我们也可以不用管&#xff0c…

记一次Windows状态栏不显示问题

文章目录 &#x1fa9f;解决方案☁️单次处理☁️有效处理 &#x1fa9f;现象&#x1fa9f;尝试的操作⭐END&#x1f31f;跋&#x1f31f;交流方式 &#x1fa9f;解决方案 ☁️单次处理 重启explorer.exe 命令行操作 注意&#xff0c;使用命令行操作的时候&#xff0c;出现…

Cocos 3.8.3 实现外描边效果(逃课玩法)

本来想着用Cocos 的Shader Graph照搬Unity的思路来加外描边&#xff0c;发现不行&#xff0c;然后我就想弄两个物体不就行了吗&#xff0c;一个是放大的版本&#xff0c;再放大的版本上加一个材质&#xff0c;这个材质面剔除选择前面的面剔除就行了&#xff0c;果不其然还真行。…

字幕制作软件有哪些?整理了适合新手的5个方法,快速导出srt字幕文件!

字幕制作软件有哪些&#xff1f;平时创作视频作品时&#xff0c;大部分会用到字幕。毕竟字幕是视频中不可或缺的一部分&#xff0c;字幕文件在传达视频内容上达到了重要的作用。 常见的字幕文件有srt、ass和sub格式。市面上比较常用的是srt字幕格式&#xff0c;这种格式几乎适用…

滑动窗口 -- 限制窗口内某元素的数量/种类

目录 长度最小的数组 题解&#xff1a; 将x减到0的最小操作数 题解&#xff1a; 最大连续1的个数 题解&#xff1a; 无重复字符的最长子串&#xff08;限制数量&#xff09; 题解&#xff1a; 水果成篮&#xff08;限制种类&#xff09; 题解&#xff1a; 找到字符串中…

Skywalking告警配置

背景 skywalking 9.7.0&#xff0c;地址&#xff1a;Backend setup | Apache SkyWalking helm&#xff1a;skywalking-helm:4.5.0&#xff0c;地址&#xff1a;skywalking-helm/chart/skywalking/values.yaml at v4.5.0 首先来说一下为什么使用skywalking告警&#xff1f; …

[半导体检测-6]:为什么晶圆缺陷检测精度越高,所需要的光源的波长越短?

目录 前言&#xff1a; 1. 光束的聚焦能力 1.1 概述 1.2 光束的聚焦能力用什么指标来标识&#xff1f; 1. 光束质量因子&#xff08;M因子&#xff09; 2. 衍射极限倍数&#xff08;β因子&#xff09; 3. 斯特列尔比&#xff08;Strehl Ratio&#xff09; 4. 远场发散…

Spring6梳理13——依赖注入之引入集合Bean属性

以上笔记来源&#xff1a; 尚硅谷Spring零基础入门到进阶&#xff0c;一套搞定spring6全套视频教程&#xff08;源码级讲解&#xff09;https://www.bilibili.com/video/BV1kR4y1b7Qc 13 依赖注入之引入集合Bean属性 13.1 创建Lesson类&#xff0c;student类和teacher实体类…

【LeetCode:2535. 数组元素和与数字和的绝对差 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

jmeter性能测试---csv数据文件设置

&#xff08;1&#xff09;什么时候使用CSV数据文件设置&#xff1f; 当不同的用户&#xff0c;或者同一用户多次循环时&#xff0c;都可以获取到不同的值 &#xff08;2&#xff09;使用CSV数据文件设置进行参数化的步骤&#xff1f; 实例&#xff1a; 请求&#xff1a;htt…

基于Node.js+Express+MySQL+VUE实现的计算机毕业设计共享单车管理网站

单车信息选择骑行 骑行状态留言公告/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序 功能如下&#xff1a; 一、开发目标 在共享经济日益盛行的今天&#xff0c;共享单车作为一种绿色、便捷的出行方式&#xff0c;已经深入人们的日常生活。然而&#xff0c;随着共享…

短效IP是网络世界的神秘助力者

伙伴们&#xff0c;我们都知道网络世界神秘莫测&#xff0c;在当今这个高度数字化的时代&#xff0c;网络如同一张无形的大网&#xff0c;将人们的生活和工作紧密相连&#xff0c;成为不可或缺的一部分。而在这庞大的网络背后&#xff0c;有着很多挑战和危险&#xff0c;为了能…

ps快速更换电商图片背景,轻松变成白底图

前言 在电商领域&#xff0c;一张高质量的商品图片往往能吸引更多消费者的目光&#xff0c;提升商品的点击率和转化率。而白底图&#xff0c;以其简洁、清晰、专业的特点&#xff0c;成为电商平台上商品展示的首选。然而&#xff0c;传统的手动抠图方式不仅耗时耗力&#xff0…

Linux中部署Docker环境;Docker常用操作

一&#xff0c;部署Docker环境 官网手册&#xff1a;CentOS | Docker Docs 1.1、查看一下Linux内核版本 uname -r 要求3.10版本及以上。 2.2、卸载老版本docker&#xff0c;避免产生影响 如果服务器安装过docker&#xff0c;没有卸载再次安装会导致安装失败&#xff0c;首…

Latex和Vscode安装和配置

一、Latex安装教程 打开清华大学开源软件镜像站&#xff0c;下载texlive.iso文件 右键点击ios文件&#xff0c;点击装载 配置latex安装 4. 安装过程 二、VScode安装和配置教程 打开Vscode官网&#xff0c;下载安装包 2.右键&#xff0c;以管理员身份运行VSCode安装包&#…

Day.js时间插件的安装引用与常用方法大全

&#x1f680; 个人简介&#xff1a;某大型国企资深软件研发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

C++ 数据类型分类

在C中&#xff0c;数据类型可以大致分为内置类型&#xff08;Built-in Types&#xff09;、标准库类型&#xff08;Standard Library Types&#xff09;和自定义类型&#xff08;User-Defined Types&#xff09;三大类。 内置类型&#xff08;Built-in Types&#xff09; 内置…

Kafka和RabbitMQ比较

Kafka和RabbitMQ都是流行的消息队列系统&#xff0c;它们在分布式系统中扮演着至关重要的角色&#xff0c;用于异步消息传递和解耦应用组件。尽管它们共享一些基本的概念&#xff0c;但它们在设计目标、性能特性、使用场景等方面有着显著的差异。 设计目标 Kafka&#xff1a;Ka…

理解Java引用数据类型(数组、String)传参机制的一个例子

目录 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子理解样例代码输出 参考资料 理解Java引用数据类型&#xff08;数组、String&#xff09;传参机制的一个例子 理解 引用数据类型传递的是地址。用引用类型A给引用类型B赋值&#xff0c;相当于…

ERROR:start workflow error,dolphinscheduler log重复刷屏(死循环)直至磁盘存满

在使用ds过后发现&#xff0c;我虚拟机中的磁盘内存全部沾满了 查看目录下大于100M的文件&#xff1a; find / -size 100M 查看后发现问题在于ds产生的日志文件特别大而且多&#xff0c; 查看日志后发现日志中一直都在死循环错误&#xff1a;start workflow error 等 其中文件…