FreeRTOS笔记——链表数据结构

2023-05-16

FreeRTOS链表实现

  • 0 概述
  • 1 关键结构体
    • 1.1 链表基础知识
    • 1.2 链表数据结构
    • 1.3 链表操作

0 概述

部分内容参考野火的FreeRTOS相关开发资料,在此做一个学习记录总结。

1 关键结构体

FreeRTOS源码实现中存在很多链表相关操作,理解链表相关操作对掌握FreeRTOS实现原理至关重要。

1.1 链表基础知识

FreeRTOS使用双向链表,与数组通过开辟一段连续内存存储数据不同,链表通过把离散的数据(标准C类型或者是用户自定义结构体)链接成一个表,通过对节点的插入和删除操作来实现对数据的存取。
双向链表是一个圈,没有头尾之分,但是为了方便节点的插入和删除操作,需要人为规定一个根节点(root节点)。
在这里插入图片描述
如上图所示,如果链表为空,这只有一个root节点,node_num为0,next和previous均指向自身。
为了有助于理解后面相关初始化以及插入删除操作,此处需要记住下面两点:
1)root_node->next指向链表的第一项node1(或自身,如果是空链表)
2)root_node->previous指向链表的最后一项noden(或自身,如果是空链表)

1.2 链表数据结构

  • 链表数据节点
struct xLIST_ITEM
{
    TickType_t xItemValue;            /* 辅助值,用于帮助节点做顺序排列 */	
    struct xLIST_ITEM *pxNext;        /* 指向链表下一个节点 */
    struct xLIST_ITEM *pxPrevious;    /* 指向链表前一个节点 */
    void *pvOwner;                    /* 指向拥有该节点的内核对象,通常是TCB */
    void *pvContainer;                /* 指向该节点所在的链表 */
};
typedef struct xLIST_ITEM ListItem_t;
  • 链表根节点
struct xMIN_LIST_ITEM
{
    TickType_t xItemValue;          /* 辅助值,用于帮助节点做顺序排列 */
    struct xLIST_ITEM *pxNext;      /* 指向链表下一个节点 */
    struct xLIST_ITEM *pxPrevious;  /* 指向链表前一个节点 */
};
  • 链表本身
typedef struct xLIST
{
    UBaseType_t uxNumberOfItems;    /* 链表节点计数器 */
    ListItem_t *pxIndex;            /* 链表节点索引指针,初始化后指向链表的最后一个节点或者说是头部节点,即下面的xListEnd节点,称之为root根节点;
                                       pxIndex->pxNext是链表中第一个有效节点(或自身,即空链表),pxIndex->pxPrevious是链表中最后一个有效节点(或自身,即空链表);
                                       任务调度过程中,节点索引会动态变化,指向不同人物的TCB */
    MiniListItem_t xListEnd;        /* 链表中包含最大xItemValue的节点,位于链表末端,也可认为是头部,用作标记 marker */ 
} List_t;

1.3 链表操作

  • 根节点初始化
    在这里插入图片描述
void vListInitialise(List_t * const pxList)
{
    /* 将链表索引指针指向最后一个节点 */
    pxList->pxIndex = (ListItem_t *)(&(pxList->xListEnd));
    /*将链表root根节点的辅助排序值设置为最大,确保该节点是链表中的最后一个或首个 */
    pxList->xListEnd.xItemValue = portMAX_DELAY;
    /*将root根节点的pxNext、pxPrevious均指向节点本身,表示链表为空 */
    pxList->xListEnd.pxNext = (ListItem_t *)(&(pxList->xListEnd));
    pxList->xListEnd.pxPrevious = (ListItem_t *)(&(pxList->xListEnd));
    /* 初始化链表节点计数器的值为0,表示链表为空 */
    pxList->uxNumberOfItems = (UBaseType_t)0U;
}

上面关键的一点是pxList->pxIndex指向根节点,这样后续插入删除操作便可以有了一个其实参考点。

  • 节点初始化
    这里的节点指的是除根节点外的有效挂载数据的节点,初始化就是告诉程序,该节点未挂到任何链表上,实现代码如下:
void vListInitialiseItem(ListItem_t * const pxItem)
{
    /* 初始化该节点所在的链表为空,表示该节点没有插入任何链表,没人能管得了我了!! */
    pxItem->pvContainer = NULL;
}
  • 插入节点至尾部
    从前分析可知,链表的根节点,即xListEnd->pxPrevious指向的就是原链表的尾部节点,节点插入尾部时,就是要让新的节点跟在原来尾部节点之后,可以理解为在原尾部节点与根节点之间插入一个节点,代码实现如下:
void vListInsertEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{
    ListItem_t * const pxIndex = pxList->pxIndex; /* 链表索引节点,链表初始化后指向根节点*/
    pxNewListItem->pxNext = pxIndex; /* 先插入尾部的节点的pxNext指向根节点 */
    pxNewListItem->pxPrevious = pxIndex->pxPrevious; /* pxIndex->pxPrevious 是原来的链表尾部节点 */
    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;
    //pxIndex->pxNext 指向不变化
    /* 记住该节点所在的链表 */
    pxNewListItem->pvContainer = (void *)pxList;
    /* 链表节点计数器++ */
    (pxList->uxNumberOfItems)++;
}
  • 升序插入节点
    根据节点的xItemValue辅助值对接点进行排序,如果xItemValue相同,就根据插入顺序排列了。
    插入后一定要记得更新链表中节点数据计数器pxList->uxNumberOfItems和新插入节点的pxNewListItem->pvContainer(否则节点就是未挂载在任何链表上)。
void vListInsert(List_t * const pxList, ListItem_t * const pxNewListItem)
{
    /* 
     * 记录辅助值比 pxNewListItem-> xItemValue 小的的列表项 ItemX ,
     * ItemX再往后的列表项 ItemX++ 的辅助值会大于或等于 pxNewListItem-> xItemValue
     * 插入的位置即 ItemX 之后
    */
    ListItem_t *pxIterator = NULL;
    /* 获取节点排序辅助值 */
    const TickType_t xValueOfInsertion = 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; //或者 pxIterator->pxNext->pxPrevious = pxNewListItem
    pxNewListItem->pxPrevious = pxIterator;
    pxIterator->pxNext = pxNewListItem;
    /* 记住该节点所在的链表 */
    pxNewListItem->pvContainer = (void *)pxList;
    /* 链表节点计数器++ */
    (pxList->uxNumberOfItems)++;
}
  • 删除节点
    删除节点后需要清除删除节点的pvContain指向,做好已删除节点原pxNext和pxPrevious指向节点的衔接工作,并且链表节点数目要相应自减。
    其中特别注意的是,当删除最后一个节点时,需要调整节点索引指针,即让根节点指向自己,即链表初始化后为空的状态。
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{
    /* 获取节点所在列表 */
    List_t * const pxList = pxItemToRemove->pvContainer;
    /* 类似离职后,做好前上级下级的交接工作 */
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    /* 在使用链表进行任务调度的过程中节点索引会变化,
       如找到当前优先级最高的任务的TCB时就调用了listGET_OWNER_OF_NEXT_ENTRY 来不断遍历节点,
       因此,我如果删除的节点刚好是最终pxIndex停留的节点,则需要重新给pxIndex进行赋值,
       不管是待删除节点的上级还是下级都可以,通过其中一个,总能找到其他人 */
	if( pxList->pxIndex == pxItemToRemove )
	{
		pxList->pxIndex = pxItemToRemove->pxPrevious; // 或者 pxItemToRemove->pxNext,反正都能通过它们遍历其他节点
	}
    /* 初始化节点所在的链表为空,表示节点还没有插入任何链表,没人能管得了我了!! */
    pxItemToRemove->pvContainer = NULL;
    /* 链表节点计数器--*/
    (pxList->uxNumberOfItems)--;
    /* 返回链表中剩余节点的个数 */
    return pxList->uxNumberOfItems;
}
  • 链表宏操作函数小结
/* 初始化节点的拥有者 */
#define listSET_LIST_ITEM_OWNER(pxListItem, pxOwner)  ((pxListItem)->pvOwner = (void *)pxOwner)
/* 获取节点拥有者 */
#define listGET_LIST_ITEM_OWNER(pxListItem)           ((pxListItem)->pvOwner)
/* 初始化节点排序辅助值 */
#define listSET_LIST_ITEM_VALUE(pxListItem, xValue)   ((pxListItem)->xItemValue = xValue)
/* 获取节点排序辅助值 */
#define listGET_LIST_ITEM_VALUE(pxListItem)           ((pxListItem)->xItemValue)
/* 获取链表根节点的节点计数器的值 */
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY(pxList)      (((pxList)->xListEnd).pxNext->xItemValue)
/* 获取链表的入口节点 */
#define listGET_HEAD_ENTRY(pxList)                    (((pxList)->xListEnd).pxNext)
/* 获取链表的第一个节点 */
#define listGET_NEXT(pxListItem)                      ((pxListItem)->pxNext)
/* 获取链表的最后一个节点 */
#define listGET_END_MARKER(pxList)                    ((ListItem_t const *)(&((pxList)->xListEnd)))
/* 判断链表是否为空 */
#define listLIST_IS_EMPTY(pxList)                     ((BaseType_t)(((pxList)->uxNumberOfItems) == (UBaseType_t)0))
/* 获取链表节点数 */
#define listCURRENT_LIST_LENGTH(pxList)               ((pxList)->uxNumberOfItems)

/* 获取下一个节点的Onwer,即 TCB */
#define listGET_OWNER_OF_NEXT_ENTRY(pxTCB, pxList)                                \
{                                                                                 \
    List_t * const pxConstList = (pxList);                                        \
	/* Increment the index to the next item and return the item, */               \
    /* ensuring we don't return the marker used at the end of the list */         \
    (pxConstList)->pxIndex = (pxConstList)->pxIndex->pxNext;                      \
    if((void *)(pxConstList)->pxIndex == (void *)&((pxConstList)->xListEnd))      \
    {                                                                             \
        (pxConstList)->pxIndex = (pxConstList)->pxIndex->pxNext;                  \
    }                                                                             \
    /* 获取节点的OWNER,即TCB */                                                  \
    (pxTCB) = (pxConstList)->pxIndex->pvOwner;                                    \
}

#define listGET_OWNER_OF_HEAD_ENTRY(pxList)   ((&((pxList)->xListEnd))->pxNext->pvOwner)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS笔记——链表数据结构 的相关文章

  • ESP32-CAM实现局域网/远程视频传输

    手上这个ESP32 CAM买回来已经放了一个学期了 xff0c 最近才开始玩 xff0c 试了试用它来实现视频传输 局域网的视频传输比较简单 xff0c 基本能正确把例程烧进去就可以了 xff0c 这篇文章主要记录一下远程视频传输的实现 E
  • Debian 9.5  中文输入问题

    刚安装完debian9 5 发现输入法无法切换 xff0c 网页显示不了中文 找设置找不到修改的地方 xff0c 于是上网查教程 一开始按照教程需要安装 xff1a fcitx ui classic xff0c fcitx ui light
  • Hive_基于Hive的网站日志分析

    文章目录 概述1 引出需要进行数据预处理的必要性 toc 2 使用RegexSerDe处理apache或者ngnix日志文件 toc 3 根据不同业务拆表 toc 3 1 需求分析3 2 拆表 4 数据清洗 toc 4 1 Hive自定义函
  • python如何判断用户输入回车键?--关于input()函数的前世今生

    前言 最近在写代码的时候 xff0c 需要判断一下用户是不是敲了回车键 xff0c 于是写出了这样的代码 xff1a span class token keyword if span span class token builtin inp
  • PyTorch版YOLOv4训练自己的数据集---基于Google Colab

    colab简介 Google Colaboratory是谷歌开放的一款研究工具 xff0c 主要用于机器学习的开发和研究 工具优势 xff1a Google Colab最大的好处是给广大的AI开发者提供了免费的GPU使用 你可以在上面轻松地
  • Efficientnet_pytorch训练自己的数据集,并对数据进行分类

    准备训练的数据集 相关代码已整理至github https github com whisperLiang efficientnet pytorch git 相关代码已整理至码云 xff1a https gitee com whisperl
  • 水下图像色彩还原(基于可见光衰减及图像去雾算法)

    参考源 参考论文 xff1a UnderwaterHazeLines BMVC2017 Github项目地址 xff1a https github com danaberman underwater hl git 对论文的一些重述 水下图像
  • 强化学习TD3算法笔记1——论文解读

    相关论文 TD3 xff1a TD3 Double DQN Double DQN DDPG DDPG TD3论文结构 摘要 xff1a 提出Actor Critic面对的问题 xff0c 概括了TD3算法和效果引言 xff1a 提出当前对于
  • Efficientnet_pytorch_cbam_gui

    大致说明 这是一个基于efficientnet模型的图像分类方案 模型融入了cbam注意力机制模块 xff0c cutmix CrossEntropyLabelSmooth auto augment等tricks帮助原生的effcientn
  • 可靠性udp传输大文件

    高级计算机网络大作业 可靠性udp传输大文件 实验数据zstd压缩1G文件 xff08 延迟100ms 丢包1 xff09 0 1G文件 xff08 延迟100ms 丢包1 xff09 0 01G文件 xff08 延迟100ms 丢包1 x
  • 一些奇怪问题的解决汇总

    vscode ssh远程连接 问题描述 xff1a Setting up SSH Host 192 168 78 133 details Initializing VS Code Server 一开始尝试了网络的各种方式 xff0c 比如删
  • 控制系统--系统结构图

    结构图基本单元 信号线 表示信号流向 引出点 表示信号引出 xff0c 被引出信号与原信号完全相同 或 从同一位置引出信号完全相同 比较点 将所有输入信号做代数运算 方框 表示信号经过传递函数为 H s
  • 字符串及处理之三: 使用TCHAR系列方案

    使用TCHAR系列方案编写程序 TCHAR是一种字符串类型 xff0c 它让你在以MBCS和UNNICODE来build程序时可以使用同样的代码 xff0c 不需要使用繁琐的宏定义来包含你的代码 TCHAR的引入 xff0c 主要是在Tch
  • Chrome解决“github.com拒绝了我们的访问请求”

    目录 1 网站查询特定IP 2 host文件修改 3 刷新DNS 如果你在Chrome访问github com时出现以下错误 xff1a 本博主之前的Chrome和Edge都无法访问github官网 xff0c 然后就来到了万能的C站找到了
  • STC12C5A60S2_LCD1602驱动

    文章目录 LCD1602 HLCD1602 cmain c LCD1602 H 代码如下 xff1a span class token macro property span class token directive hash span
  • 猿创征文|机器学习实战(8)——随机森林

    目录 1 随机森林 2 极端随机树 3 特征重要性 4 提升法 4 1 AdaBoost 4 2 梯度提升 机器学习实战 xff08 7 xff09 中我们已经提到 xff0c 随机森林是决策树的集成 xff0c 通常用bagging方法训
  • 总结2014——迷茫以及迷茫过后的坚持

    首先 xff0c 借用一句话和大家共勉 xff1a 少一些功利主义的追求 xff0c 多一些不为什么的坚持 xff01 xff01 不知不觉15年也快过了1个月了 xff0c 还是想着要为14年做一下总结 xff1a 记录一下自己的历程 今
  • 汇编总结:lea指令

    ea指令变种 按大小分类 leaw 2个字节 leal 4个字节 leaq 8个字节 lea的用法 leaq a b c d rax 首先lea指令是mov指令的变种 xff0c 据说 xff0c lea指令是x86体系结构中 xff0c
  • CMake语法—选项(option)

    CMake语法 选项 xff08 option xff09 1 选项 1 1 定义 1 2 说明 variable 选项名help text 描述 解释 备注value 选项初始化值 xff08 除ON而外全为OFF xff09 2 应用注
  • C++工程:总结 CMake 添加第三方库依赖方式git submodule、 find_library、FetchContent、CPM等

    CMake 已经成为了C 43 43 工程管理的主流方式 xff0c 功能非常强大 xff0c 现在大多数的 C 43 43 库都已经支持CMake xff0c 下面以 jsoncpp 为例 xff0c 介绍几种引入第三方库的方式 1 代码

随机推荐

  • 医学图像——DCMTK、VTK、ITK、RTK、SimpleITK

    1 引言 https github com SINTEFMedtek ITK VTK xff0c 相关童鞋应该很熟悉的 xff0c 而CTK是一个较新的界面库 xff0c 主要用于方便前面两个 TK的界面设计 xff0c 当然也可以作为通用
  • C++中的volatile

    volatile的本意是 易变的 volatile关键字是一种类型修饰符 xff0c 用它声明的类型变量表示可以被某些编译器未知的因素更改 xff0c 比如操作系统 硬件或者其它线程等 遇到这个关键字声明的变量 xff0c 编译器对访问该变
  • 3DTiles】关于GeometricError几何度量误差

    在 3DTiles 的官方文档中详细介绍了关于几何度量误差 Geometric Error 的一些理念和内涵 xff0c 概括来说可以翻译为如下定义 xff1a 几何度量误差 xff0c Geometric Error xff0c 简称 G
  • glPixelStorei 详解 包括像素传输

    3 glPixelStore 像glPixelStorei GL PACK ALIGNMENT 1 这样的调用 xff0c 通常会用于像素传输 PACK UNPACK 的场合 尤其是导入纹理 glTexImage2D 的时候 xff1a C
  • ESLint 简介

    ESLint简介 ESLint是一个用来识别 ECMAScript 并且按照规则给出报告的代码检测工具 xff0c 使用它可以避免低级错误和统一代码的风格 如果每次在代码提交之前都进行一次eslint代码检查 xff0c 就不会因为某个字段
  • IOS VasSonic 粗略见解

    因为项目需求需要在本地缓存html页面 xff0c 优化用户体验 了解到VasSonic 百度了下源码解析但是没有发现IOS的所以只有自己慢慢摸索了 一 类的简单关系 1 SonicEngine 引擎类 代理为 UIWebViewContr
  • axios的详细讲解

    一 axios的特性 axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端 xff0c 简单的理解就是ajax的封装 特性 xff1a 从浏览器中创建 XMLHttpRequests从 node js 创建
  • 无人机飞控算法-姿态估计-欧拉角-旋转矩阵-四元数

    无人机飞控算法 姿态估计 此系列记录了我理解的卡尔曼滤波从0到1的过程 xff0c 从姿态估计到位置估计 xff0c 我们从核心点一个个出发 xff0c 并结合实际模块的应用来一一揭开卡尔曼滤波的神秘面纱 提示 xff1a 在系列文章中 x
  • BMP格式详解

    介绍 数字图像在外存储器设备中的存储形式是图像文件 xff0c 图像必须按照某个已知的 公认的数据存储顺序和结构进行存储 xff0c 才能使不同的程序对图像文件顺利进行打开或存盘操作 xff0c 实现数据共享 图像数据在文件中的存储顺序和结
  • WinHex使用方法详解

    WinHex是由X Ways软件技术公司 xff08 官方网站http www x ways net xff09 开发的一款专业的磁盘编辑工具 xff0c 该工具文如其名 xff0c 是在Windows下运行的十六进制 xff08 hex
  • three.js流动线

    效果 xff1a 先看最基本的 function initThree el options options 61 options const t 61 this appInstance 61 this const width 61 el o
  • OpenGL之FBO(Frame Buffer Object)和多次离屏渲染

    第一次听到离屏渲染的时候觉得很高级 xff0c 遥不可及 xff0c 直到后来做高斯模糊的时候 xff0c 需要通过两次处理来节省性能 xff0c 一直玩一次渲染处理的我这时候才认识FBO xff0c 继而明白了离屏渲染 xff0c 今天抽
  • Android驱动(一)硬件访问服务学习之(四)Android应用程序APP编写

    硬件平台 xff1a tiny4412系统 xff1a Android 5 0 2编译器 xff1a arm linux gcc 4 5 1 xff08 一 xff09 Android通过JNI访问硬件 http blog csdn net
  • gl_FragCoord 的含义

    gl FragCoord 表示当前片元着色器处理的候选片元窗口相对坐标信息 xff0c 是一个 vec4 类型的变量 x y z 1 w xff0c 其中 x y 是当前片元的窗口坐标 xff0c OpenGL 默认以窗口左下角为原点 xf
  • RoboMaster机甲大师——视觉组——计算平台的选型与感想(主流几款)

    RoboMaster机甲大师 视觉组 计算平台 xff08 工控机 xff09 的选型与感想 xff08 主流几款 xff09 FOR THE SIGMA FOR THE GTINDER FOR THE ROBOMASTER 简介 xff1
  • 如何在Linux命令行下发送和接收UDP数据包

    众所周知 在传输层有两个常用的协议 TCP 和 UDP 本文介绍在 Linux 命令行下 如何使用 nc 命令发送或接收 UDP 数据包 这些命令的用法对调试 UDP 通信程序将有所帮助 1 问题的提出 编写了一个使用 raw socket
  • 抽丝剥茧聊Kotlin协程之聊聊Job和SupervisorJob的区别

    1 前言 随着协程的普及 xff0c 协程知识越来越被面试官青睐 首先 xff0c 协程的面试题一般都很简洁 xff0c 一两句简单的话就能把问题描述清楚 xff0c 其次于面试官而言 xff0c 协程框架中精妙的数据结构与算法可以很好的考
  • Android 手机运行 JoyCon Droid 并且使用 Amiibo

    PS 整个过程耗时耗力 xff0c 经常会断开连接 xff0c 有些不想搞那么麻烦的人就不要搞了 xff0c 以免遭受刺激啊 xff0c 哈哈 前提 如果想使用并刷Amiibo xff0c 必须同时满足以下几个条件 xff1a 1 蓝牙版本
  • STM32F10X系列通用OTA bootloader移植与使用指南

    基于STM32F10X系列通用OTA bootloader原理 移植与使用全指南 写在前面这几天我都做了什么呢 xff1f 有什么感受 xff1f 开始移植 写在前面 从2020 1 26到2020 1 30这5天 xff0c 我的较多研究
  • FreeRTOS笔记——链表数据结构

    FreeRTOS链表实现 0 概述1 关键结构体1 1 链表基础知识1 2 链表数据结构1 3 链表操作 0 概述 部分内容参考野火的FreeRTOS相关开发资料 xff0c 在此做一个学习记录总结 1 关键结构体 FreeRTOS源码实现