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采用一种方法,写出了通用的链表操作,让我不得不感叹这代码确实是写的好!🤣 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言--判断年月日是否合理

一.题目描述 比如输入2001&#xff0c;2&#xff0c;29&#xff0c;输出&#xff1a; 不合理 。因为平年的二月只有28天 比如输入2000&#xff0c;6&#xff0c;31&#xff0c;输出&#xff1a;不合理。因为6月是小月&#xff0c;只有30天。 二.思路分析 本题主要注意两个问…

Android : ListView + BaseAdapter-2简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 实体类 News.java package com.example.mylistviewadapter2.entity;public class News {private String title;private String content;private int img;public News(String title, String conte…

CentOS 7 使用pugixml 库

安装 pugixml Git下载地址&#xff1a;https://github.com/zeux/pugixml 步骤1&#xff1a;首先&#xff0c;你需要下载pugixml 的源代码。你可以从Github或者源代码官方网站下载。并上传至/usr/local/source_code/ 步骤2&#xff1a;下载完成后&#xff0c;需要将源代码解压…

【MySQL】多表查询、子查询、自连接、合并查询详解,包含大量示例,包你会。

复合查询 前言正式开始一些开胃菜多表查询自连接子查询单行子查询多行子查询in关键字all关键字any关键字多列子查询在from中使用子查询 合并查询union 和 union all 前言 我前面博客讲的所有的查询都是在单表中进行的&#xff0c;从这里开始就要专门针对查询这个话题进行进一步…

STM32-标准库和HAL库-不同容量系列的代码移植

使用STM32单片机过程中经常会涉及到不同芯片间的代码转换&#xff0c;手头上熟悉的工程需要稍作处理才能用到新的板子上。常见的是STM32F103xE、STM32F103xC&#xff08;大容量&#xff09;和STM32F103x8、STM32F103xB&#xff08;中容量&#xff09;的转换。这里做一下总结&am…

93.STL-系统内置仿函数

目录 算术仿函数 关系仿函数 逻辑仿函数 C 标准库中提供了一些内置的函数对象&#xff0c;也称为仿函数&#xff0c;它们通常位于 <functional> 头文件中。以下是一些常见的系统内置仿函数&#xff1a; 算术仿函数 功能描述&#xff1a; 实现四则运算其中negate是一元…

PTA-6-45 工厂设计模式-运输工具

题目如下&#xff1a; 工厂类用于根据客户提交的需求生产产品&#xff08;火车、汽车或拖拉机&#xff09;。火车类有两个子类属性&#xff1a;车次和节数。拖拉机类有1个子类方法耕地&#xff0c;方法只需简单输出“拖拉机在耕地”。为了简化程序设计&#xff0c;所有…

依托数据、平台、知识增强等优势 夸克大模型大幅降低问答幻觉率

“大模型时代&#xff0c;夸克有巨大机会创造出革新性搜索产品。”11月22日&#xff0c;夸克大模型公布了其面向搜索、生产力工具和资产管理助手的大模型技术布局。数据显示&#xff0c;夸克千亿级参数大模型登顶C-Eval和CMMLU两大权威榜单&#xff0c;夸克百亿级参数大模型同样…

Linux-编译器

编译器 gcc-arm-linux-gnueabihf gcc-arm-linux-gnueabihf 是一个针对 ARM 架构 Linux 系统的交叉编译工具链&#xff0c;它包括了 C、C、Objective-C 和 Fortran 编译器以及一些辅助工具&#xff0c;用于将源代码编译成可在 ARM 架构的 Linux 系统上运行的二进制程序。arm架…

2023年,人工智能在医疗行业领域的应用场景

本期行业洞察将带领大家了解人工智能在医疗行业领域的应用&#xff0c;主要了解在患者治疗和运营中的应用、人工智能作为预防工具以及大型医院目前如何使用人工智能。未来的智慧医疗时代已经悄然到来。 人工智能在患者治疗和机构运营中的应用 人工智能有望彻底改变医疗护理的…

基本的弹层,点击弹出

<div class"role"><el-button type"primary" size"mini" click"showDialog true">添加角色</el-button></div><!--控制弹框的显示隐藏visible .sync可以点击X关闭弹框 --> <el-dialog width"…

C语言从入门到实战——数组和指针的强化练习题

数组和指针的强化练习题 前言1. sizeof和strlen的对比1.1 sizeof1.2 strlen1.3 sizeof和strlen的对⽐ 2. 数组和指针笔试题解析2.1 一维数组2.2 字符数组2.3 二维数组 3. 指针运算笔试题解析3.1 题目1&#xff1a;3.2 题目23.3 题目33.4 题目43.5 题目53.6 题目63.7 题目7 前言…

如何解决msvcp110.dll丢失问题,分享5个有效的解决方法

最近&#xff0c;我在使用电脑时遇到了一个令人头疼的问题——msvcp110.dll丢失。这个错误通常会导致某些应用程序无法正常运行。为了解决这个问题&#xff0c;我们需要采取一些有效的方法来修复丢失的msvcp110.dll文件。那么&#xff0c;msvcp110.dll到底是什么呢&#xff1f;…

【iOS】实现评论区展开效果

文章目录 前言实现行高自适应实现评论展开效果解决cell中的buttom的复用问题 前言 在知乎日报的评论区中&#xff0c;用到了Masonry行高自适应来实现评论的展开&#xff0c;这里设计许多控件的约束问题&#xff0c;当时困扰了笔者许久&#xff0c;特此撰写博客记录 实现行高自…

Globalsign证书

Globalsign证书是一种被广泛应用于各个领域的网络安全解决方案。它提供了一系列的功能&#xff0c;包括保证在线交易的安全性、管理大量的数字身份以及自动验证和加密等。由于其全面的安全保障功能&#xff0c;许多大型公司、云服务供应商以及互联网创业者都选择了Globalsign证…

接口自动化测试的价值是什么?

接口自动化的内容写了很多了&#xff0c;本来以为没什么东西再聊。这两天和两个不同团队的测试负责人交流&#xff0c;发现大家对于接口自动化的落地还是很多疑问&#xff0c;接口自动化到底能不能在短期内帮助到团队呢&#xff1f; 01 它不是救命稻草 自动化并不是提升效率…

大数据湖及应用平台建设解决方案:PPT全39页,附下载

关键词&#xff1a;大数据湖建设&#xff0c;集团大数据湖&#xff0c;大数据湖仓一体&#xff0c;大数据湖建设解决方案 一、大数据湖定义 大数据湖是一个集中式存储和处理大量数据的平台&#xff0c;主要包括存储层、处理层、分析层和应用层四个部分。 1、存储层&#xff…

028 - STM32学习笔记 - ADC结构体学习(二)

028 - STM32学习笔记 - 结构体学习&#xff08;二&#xff09; 上节对ADC基础知识进行了学习&#xff0c;这节在了解一下ADC相关的结构体。 一、ADC初始化结构体 在标准库函数中基本上对于外设都有一个初始化结构体xx_InitTypeDef&#xff08;其中xx为外设名&#xff0c;例如…

d3dx9_43.dll缺失怎么办?教你一分钟修复d3dx9_43.dll丢失问题

今天&#xff0c;与大家分享关于“d3dx9_43.dll丢失的5个解决方法”的主题。在我们的日常生活和工作中&#xff0c;我们可能会遇到各种各样的问题&#xff0c;而d3dx9_43.dll丢失就是其中之一。那么&#xff0c;什么是d3dx9_43.dll呢&#xff1f;它为什么会丢失&#xff1f;又该…

Ubuntu安装PCAN-View

目录 一. Hardware 二. Software 2.1 安装驱动 2.2 安装PCAN-View QA 本文介绍如何安装linux版的PCAN-View。 PCAN-View&#xff1a;用来抓包分析CAN/CANFD报文。Hardware: PEAK-System Linux generic #37~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Oct 9 15:34:04 UTC 2…