FreeRTOS列表和列表项

FreeRTOS内核调度使用了大量的列表(list)和列表项(listitem)数据结构。它的源码中涉及到很多列表的操作,对于FreeRTOS来说,列表就是它最基础的一部分,列表被用作FreeRTOS调度器使用,用于跟踪任务,处于就绪,挂起,延时的任务都会被挂接到各自的列表中,用户程序如果有需要,也可以使用列表,其中就连FreeRTOS的任务调度其实核心也涉及到列表。

  FreeRTOS列表使用指针指向列表项。一个列表(list)下面可能有很多个列表项(list item),每个列表项都有一个指针指向列表。如图所示

列表和列表项

列表项有两种形式,全功能版的列表项xLIST_ITEM和迷你版的列表项xMINI_LIST_ITEM。我们来看一下它们具体的定义,先看全功能版。

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;                                     /*指向一个任务TCB*/void * configLIST_VOLATILE pvContainer;             /*指向包含该列表项的列表 */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*用于检测列表项数据是否完整*/
};
typedef struct xLIST_ITEM ListItem_t;

   宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE用于检查列表项数据是否完整,在projdefs.h中,如果将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替。

      xItemValue是列表项值,通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。如果任务因为等待从队列取数据而进入阻塞状态,则任务的事件列表项的列表项值保存任务优先级有关信息,状态列表项的列表项值保存阻塞时间有关的信息。这个变量被configLIST_VOLATILE修饰,configLIST_VOLATILE被映射成C语言关键字volatile,表明这个变量是“易变的”,告诉编译器不得对这个变量进行代码优化,因为列表项的成员可能会在中断服务程序中被更新。

  pxNext和pxPrevious是列表项类型指针,用来指向列表中下一个和上一个列表项,通过这两个指针,列表项之间可以形成类似双向链表结构。

      指针pvOwner通常指向一个任务TCB。

      指针pvContainer指向包含该列表项的列表。

      迷你版的列表项xMINI_LIST_ITEM是全功能版列表项xLIST_ITEM的一个子集,定义如下所示:
 

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;

    既然有了全功能版的列表项,为什么还要声明迷你版的列表项呢?这是因为列表结构体需要一个列表项成员,但又不需要列表项中的所有字段,所以才有了迷你版列表项。迷你列表项起到的主要作用就是标识列表的末尾,所以它的值也设置成了最大值,列表结构体定义为:

typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE                        /*用于检测列表项数据是否完整*/configLIST_VOLATILE UBaseType_t uxNumberOfItems;ListItem_t * configLIST_VOLATILE pxIndex;                   /*用于遍历列表*/MiniListItem_t xListEnd;                                    /*列表项*/listSECOND_LIST_INTEGRITY_CHECK_VALUE                       /*用于检测列表项数据是否完整*/
}List_t;

 

和列表项定义相同,宏listFIRST_LIST_INTEGRITY_CHECK_VALUE和listSECOND_LIST_INTEGRITY_CHECK_VALUE用于检查列表项数据是否完整,在projdefs.h中,如果将宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替。

      uxNumberOfItems表示该列表中挂接的列表项数目,0表示列表为空。

      列表项类型指针用于遍历列表,列表初始化后,这个指针指向&xListEnd。通过宏listGET_OWNER_OF_NEXT_ENTRY()来获取列表中的下一个列表项。

      列表项xListEnd用于标记列表结束。xListEnd.xItemValue被初始化为一个常数,其值与硬件架构相关,为0xFFFF(16位架构)或者0xFFFFFFFF(32位架构)。
 

关于列表的一些操作

初始化列表

列表结构体中包含一个列表项成员,主要用于标记列表结束。初始化列表就是把这个列表项插入到列表中。

void vListInitialise( List_t * const pxList )
{/*列表索引指向列表项*/pxList->pxIndex = ( ListItem_t * )&( pxList->xListEnd );                  /* 设置为最大可能值 */pxList->xListEnd.xItemValue =portMAX_DELAY;/* 列表项xListEnd的pxNext和pxPrevious指针指向了它自己 */pxList->xListEnd.pxNext = (ListItem_t * ) &( pxList->xListEnd );pxList->xListEnd.pxPrevious= ( ListItem_t * ) &( pxList->xListEnd );pxList->uxNumberOfItems = ( UBaseType_t) 0U;/* 设置为已知值,用于检测列表数据是否完整*/listSET_LIST_INTEGRITY_CHECK_1_VALUE(pxList );listSET_LIST_INTEGRITY_CHECK_2_VALUE(pxList );
}

如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listSET_LIST_INTEGRITY_CHECK_1_VALUE()和listSET_LIST_INTEGRITY_CHECK_2_VALUE被一个已知值代替,默认为0x5a5a(16位架构)或者0x5a5a5a5a(32位架构)。

      假设禁止列表数据完整性检查,初始化后的列表如图1-2所示,uxNumberOfItems被初始化为0,xListEnd.xItemValue初始化为0xffffffff,pxIndex、xListEnd.pxNext和xListEnd.pxPrevious初始化为指向列表项xListEnd。
 

 初始化列表项

列表项的初始化很简答, 只需要声明该列表项不属于任何一个列表就可以了。

void vListInitialiseItem( ListItem_t * const pxItem )
{pxItem->pvContainer = NULL;/*设置为已知值,用于检测列表项数据是否完整*/listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE(pxItem );
}

如果宏configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES设置为1,则使能列表项数据完整性检查,则宏listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE和listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE会被两个已知的数值代替,默认为0x5a5a(16位架构)或者0x5a5a5a5a(32位架构)。

      假设禁止列表项数据完整性检查,初始化后的列表项如图1-3所示。仅是将指针pvContainer设置为空指针,该指针用于指向包含该列表项的列表,这里设置为NULL表示这个列表项不属于任何列表。
 

 

列表插入列表项

 

每个列表项对象都有一个列表项值(xItemValue),通常是一个被跟踪的任务优先级或是一个调度事件的计数器值。调用API函数vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem)可以将pxNewListItem指向的列表项插入到pxList指向的列表中,列表项在列表的位置由pxNewListItem->xItemValue决定,按照升序排列。

void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/* 检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );/*将新的列表项插入到列表,根据xItemValue的值升序插入列表。*/if( xValueOfInsertion == portMAX_DELAY){pxIterator =pxList->xListEnd.pxPrevious;}else{for( pxIterator = (ListItem_t * ) &( pxList->xListEnd );pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator =pxIterator->pxNext ){/* 这里为空 */}}pxNewListItem->pxNext =pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious= pxNewListItem;pxNewListItem->pxPrevious =pxIterator;pxIterator->pxNext = pxNewListItem;pxNewListItem->pvContainer = ( void* ) pxList;( pxList->uxNumberOfItems )++;
}

 列表项末尾插入

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t* const pxIndex = pxList->pxIndex;/*检查列表和列表项数据的完整性,仅当configASSERT()定义时有效。*/listTEST_LIST_INTEGRITY( pxList );listTEST_LIST_ITEM_INTEGRITY(pxNewListItem );/*向列表中插入新的列表项*/pxNewListItem->pxNext = pxIndex;pxNewListItem->pxPrevious =pxIndex->pxPrevious;mtCOVERAGE_TEST_DELAY();pxIndex->pxPrevious->pxNext =pxNewListItem;pxIndex->pxPrevious = pxNewListItem;pxNewListItem->pvContainer = ( void* ) pxList;( pxList->uxNumberOfItems )++;
}

Tips: 这个函数是最容易造成误解的一个函数,字面理解,在我开始学的时候我以为就是插入到最后一个迷你列表项的前面,所谓末尾插入肯定是最后一项嘛,阅读源码之后,其实不然,因为列表中有一项成员 

ListItem_t * configLIST_VOLATILE pxIndex; 

该成员主要作用就是用来遍历列表的。阅读源码发现它是插入在pxIndex所指的列表项的前面。这里体现了FreeRTOS的哲学理念,“公平”,如果pxIndex,指向的是当前的索引的列表项表示正在使用,这时比如顺序是1->2->3,现在pxIndex指向2,要插入4,这时你4肯定是要最后遍历访问的,意味着就是访问顺序应该是2->3->1->4,所以它要插入在2前面,我的方法是记住一个口诀,末尾插入就是插入pxIndex所指列表项的前一项的后面,可能听着有点别扭(不就是所指列表项的前面嘛🤣 ,细细体会,有公平的哲学思想)

重点

重点:一开始学习的时候,一直不明白这个pxIndex有什么用,因为我在有关列表的list.c中的API函数中根本没发现有改变它的代码,以为它一直是初始化的值,就是一直指向迷你列表项,其实不然,作为一名程序员,一切从源码中得到答案。

搜索代码之后发现,中间对pxIndex赋值的地方只有listGET_OWNER_OF_NEXT_ENTRY这个接口(list.h中的一个有参宏)

 每调用一次listGET_OWNER_OF_NEXT_ENTRY,列表的pxIndex会指向下一个列表项。
而调用listGET_OWNER_OF_NEXT_ENTRY,主要是

 

FreeRTOS的列表和列表项采用了一种统一的列表管理,不像我以前学的数据结构中的链表操作一样,其中的节点都是具体的结构体的内容,所以是针对具体的一类结构体,比如struct people,这样导致的后果就是所有有关链表操作的内容都是针对这类结构体,如果稍微改成struct dog,这样就需要全部重写链表的所有操作了。FreeRTOS采用一种方法,写出了通用的链表操作,让我不得不感叹这代码确实是写的好!🤣 

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

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

相关文章

JVM 堆外内存详解

Java 进程内存占用除了JVM 运行时数据区&#xff0c;还有直接内存&#xff08;Direct Memory&#xff09;区域及 JVM 程序自身也会占用内存 直接内存&#xff08;Direct Memory&#xff09;区域&#xff1a;直接内存通过使用Native堆外内存来存储数据&#xff0c;这意味着数据…

在中国企业出海的大浪潮下,亚马逊云科技提供遍及全球的基础设施和技术支持

中国技术出海是中国企业更高层次更高质量的全球化。在人类文明发展史上&#xff0c;凝聚中国古人智慧结晶的造纸术、印刷术、火药、指南针等&#xff0c;曾为中国技术出海写下过浓墨重彩的一笔。在今天&#xff0c;如金山办公、店匠科技、ADVANCE.AI等公司又以技术立业&#xf…

C语言每日一题(35)有效的括号

力扣网 20 有效的括号 题目描述 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右…

一个月B站涨粉200万,品牌号不可错过的吸粉秘籍

越来越多品牌为了持续在B站营销而创建品牌官方账号&#xff0c;发布原创作品融入B站UP主中&#xff0c;吸引B站用户塑造品牌形象&#xff0c;提高品牌传播度、品牌声量。 据飞瓜数据&#xff08;B站版&#xff09;统计&#xff0c;B站有着超过2万个品牌号&#xff0c;本篇文章…

C# Onnx PP-HumanSeg 人像分割

目录 效果 模型信息 项目 代码 下载 效果 图片源自网络侵删 模型信息 Inputs ------------------------- name&#xff1a;x tensor&#xff1a;Float[1, 3, 192, 192] --------------------------------------------------------------- Outputs -------------------…

好用的博客评论系统 Valine 使用及避坑指南

评论系统&#xff0c;即网站的一个小功能&#xff0c;展示评论内容和用户输入框。开源免费的评论系统可不多&#xff0c;原来很火的"多说"评论系统都关闭了&#xff0c;而Disqus又是国外的访问受限。无意间发现了Valine&#xff0c;挺不错的&#xff0c;分享给大家。…

内测分发平台的未来发展和趋势如何

大家好&#xff0c;我是咕噜-凯撒&#xff0c;随着软件开发行业的快速发展和更新迭代的频率不断加快&#xff0c;内测分发平台作为软件测试和发布的重要环节&#xff0c;将在未来扮演更加关键的角色。未来内测分发平台发展将呈现出一系列的新趋势,都有哪些方面呢。图片来源:new…

基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码

基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于向量加权平均算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于向量加权平均优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xf…

DB2—03(DB2中常见基础操作)

DB2—03&#xff08;DB2中常见基础操作&#xff09; 1. 前言1.1 oracle和mysql相关 2. db2中的"dual"2.1 SYSIBM.SYSDUMMY12.2 使用VALUES2.3 SYSIBM.SYSDUMMY1 "变" dual 3. db2中常用函数3.1 nvl()、value()、COALESCE()3.2 NULLIF() 函数3.3 LISTAGG() …

Echarts实现散点图

效果图如下&#xff1a; <html><head><meta charsetutf-8><link rel"stylesheet" href"js/leaflet1.7.1/dist/leaflet.css"/><script src"js/leaflet1.7.1/dist/leaflet.js"></script><script src"…

2023年G2电站锅炉司炉证考试题库及G2电站锅炉司炉试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年G2电站锅炉司炉证考试题库及G2电站锅炉司炉试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲…

SpringBoot:邮件发送

官网文档&#xff1a;39. Sending Email (spring.io)。 Sending Email Spring框架提供了JavaMailSender实例&#xff0c;用于发送邮件。 如果SpringBoot项目中包含了相关的启动器&#xff0c;那么就会自动装配一个Bean实例到项目中。 在SpringBoot项目中引入如下Email启动器&a…