当前位置: 首页 > news >正文

【学习日记】【FreeRTOS】链表结构体及函数详解

写在前面

本文主要是对于 FreeRTOS 中链表相关内容的详细解释,代码大部分参考了野火FreeRTOS教程配套源码,作了一小部分修改。

一、结构体定义

主要包含三种结构体:

  1. 普通节点结构体
  2. 结尾节点(mini节点)结构体
  3. 链表结构体

1.普通节点结构体

一个完整的节点,包含五个参数:

  1. 节点的序号,用于确定节点在链表中的位置
  2. 前指针,指向本节点的前一个节点
  3. 后指针,指向本节点的下一个节点
  4. 本节点所属的内核对象,通常是TCB(Task Control Block)是FreeRTOS中用于管理任务的数据结构。
  5. 该节点所属的链表
  • 结构体定义如下:
/* 节点结构体定义 */
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;  /* 节点数据类型重定义 */

2.结尾节点(mini节点)结构体

Mini节点没有所属内核和所属链表信息,主要被用作双向列表的结尾,只有三种信息:

  1. 节点序号值,标识节点在链表中的位置
  2. 前指针
  3. 后指针
  • 结构体定义如下:
/* mini节点结构体定义,作为双向链表的结尾因为双向链表是首尾相连的,头即是尾,尾即是头 */
struct xMINI_LIST_ITEM
{TickType_t xItemValue;                      /* 辅助值,用于帮助节点做升序排列 */struct xLIST_ITEM *  pxNext;                /* 指向链表下一个节点 */struct xLIST_ITEM *  pxPrevious;            /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;  /* 最小节点数据类型重定义 */

3.链表结构体

链表需要三种参数:

  1. 链表节点计数器,统计链表中的节点总数
  2. 链表节点索引指针,指向链表中目前要操作的节点
  3. 链表最后一个节点的指针
  • 结构体定义如下:
/* 链表结构体定义 */
typedef struct xLIST
{UBaseType_t uxNumberOfItems;    /* 链表节点计数器 */ListItem_t *  pxIndex;			/* 链表节点索引指针 */MiniListItem_t xListEnd;		/* 链表最后一个节点 */
} List_t;

二、链表基本函数定义

主要包括下面几种函数:

  1. 节点初始化函数
  2. 空链表初始化函数
  3. 将新节点插入到链表尾部的函数
  4. 将新节点按升序插入链表的函数
  5. 从链表中删除节点的函数

1.节点初始化函数

传入要初始化的节点,将该节点的所属链表参数设置为空即可。

  • 函数定义如下:
//节点初始化
void vListInitialiseItem(ListItem_t * const pxItem)
{pxItem->pvContainer = NULL;		//将该节点所在链表初始化为空,表示该节点还没有插入任何链表
}

2. 空链表初始化函数

需要的步骤如下:

  1. 将指针索引指向最后一个节点
  2. 最后一个节点的排序值设置为最大(取决于portmacro中portMAX_DELAY宏定义)
  3. 最后一个节点的前后指针都指向节点自身
  4. 链表节点计数器设置为 0
  • 代码如下:
//空链表初始化
void vListInitialise(List_t * const pxList)
{//指针索引指向最后一个节点,pxList->xListEnd是一个值,需要取地址后强制类型转换pxList->pxIndex = (ListItem_t *)&pxList->xListEnd;	//最后一个节点的辅助值设置为最大pxList->xListEnd.xItemValue = portMAX_DELAY;		//最后一个节点的前指针和后指针都指向节点自身,表示链表为空pxList->xListEnd.pxNext = (ListItem_t *)&pxList->xListEnd;pxList->xListEnd.pxPrevious = (ListItem_t *)&pxList->xListEnd;//初始化链表节点计数器为0,表示链表为空pxList->uxNumberOfItems = (UBaseType_t)0U;
}

3. 将新节点插入到链表尾部的函数

  • 需要的步骤如下:
  1. 拿出指向链表最后一个节点的指针
  2. 将新的节点插入到原来最后节点的前面
  3. 设置新插入节点所属的链表
  4. 链表节点计数器++
  • 代码如下:
//将节点插入到链表的尾部
void vListInsertEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{//拿出指向链表最后节点的指针ListItem_t * const pxIndex = pxList->pxIndex;//将新的节点插入到原来最后节点前面pxNewListItem->pxNext = pxIndex;pxNewListItem->pxPrevious = pxIndex->pxPrevious;pxIndex->pxPrevious->pxNext = pxNewListItem;pxIndex->pxPrevious = pxNewListItem;//设置插入的新节点所在的链表pxNewListItem->pvContainer = (void *)pxList;//链表节点计数器+1(pxList->uxNumberOfItems)++;
}

4. 将新节点按升序插入链表的函数

  • 需要的步骤如下:
  1. 定义迭代器(节点指针)用于查找插入的位置
  2. 获取新插入节点的序号
  3. 寻找节点插入的位置
  4. 进行节点插入操作
  5. 修改新插入的节点所在的链表
  6. 链表节点计数器++
  • 代码如下:
//将节点按照升序排列插入链表
void vListInsert(List_t * pxList, ListItem_t * const pxNewListItem)
{//1. pxIterator 用于查找插入位置ListItem_t * pxIterator;//2. 获取新插入节点的排序辅助值const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;//3. 寻找节点要插入的位置if(xValueOfInsertion == portMAX_DELAY){	//如果是最大的节点,插入在链表的最后即可pxIterator = pxList->xListEnd.pxPrevious;}else{//for循环寻找插入位置//pxIterator = (ListItem_t *)&pxList->xListEnd 环状链表,尾就是头for(pxIterator = (ListItem_t *)&pxList->xListEnd; pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext);}//4. 插入新节点pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;//5. 修改插入的新节点所在的链表pxNewListItem->pvContainer = (void *)pxList;//6. 链表节点计数器+1(pxList->uxNumberOfItems)++;}

5. 从链表中删除节点的函数

  • 需要的步骤如下:
  1. 获取要删除的节点所在的链表
  2. 进行删除操作
  3. 如果链表索引指向要删除的节点,需更新索引使其指向要删除节点的前一个节点,确保索引指向有效
  4. 设置要删除节点的所属链表为空
  5. 链表节点计数器–
  6. 返回删除操作后链表中剩余的节点的数量
  • 代码如下:
//从链表中删除节点
UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{//1. 获取要删除的节点所在的列表List_t * const pxList = (List_t *)pxItemToRemove->pvContainer;//2. 将节点从链表中删除pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;//3. 如果要删除的节点是目前链表的索引,就得把索引更新为要删除节点的前一个,确保索引指向有效的节点if(pxList->pxIndex == pxItemToRemove){pxList->pxIndex = pxItemToRemove->pxPrevious;}//4. 将要删除的节点所属链表设置为空,表示不属于任何链表pxItemToRemove->pvContainer = NULL;//5. 更新链表节点计数器(pxList->uxNumberOfItems)--;//6. 返回链表中剩余的节点数量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 )/* 获取链表节点的OWNER,即TCB */
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )										\
{																							\List_t * const pxConstList = ( pxList );											    \/* 节点索引指向链表第一个节点调整节点索引指针,指向下一个节点,如果当前链表有N个节点,当第N次调用该函数时,pxInedex则指向第N个节点 */\( 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 )

后记

如果您觉得本文写得不错,可以点个赞激励一下作者!
如果您发现本文的问题,欢迎在评论区或者私信共同探讨!
共勉!

相关文章:

【学习日记】【FreeRTOS】链表结构体及函数详解

写在前面 本文主要是对于 FreeRTOS 中链表相关内容的详细解释&#xff0c;代码大部分参考了野火FreeRTOS教程配套源码&#xff0c;作了一小部分修改。 一、结构体定义 主要包含三种结构体&#xff1a; 普通节点结构体结尾节点&#xff08;mini节点&#xff09;结构体链表结…...

【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA)

【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA) 什么是弹性伸缩 「Autoscaling即弹性伸缩&#xff0c;是Kubernetes中的一种非常核心的功能&#xff0c;它可以根据给定的指标&#xff08;例如 CPU 或内存&#xff09;自动缩放Pod副本&#xff0c;从而可以更好地管…...

Windows、 Linux 等操作系统的基本概念及其常见操作

Windows 和 Linux 是两种常见的操作系统&#xff0c;它们在计算机领域中广泛使用。下面我将为您介绍它们的基本概念以及一些常见的操作。 **Windows 操作系统&#xff1a;** 1. **基本概念&#xff1a;** Windows 是由微软公司开发的操作系统系列&#xff0c;旨在为个人计算机…...

【RabbitMQ】golang客户端教程5——使用topic交换器

topic交换器&#xff08;主题交换器&#xff09; 发送到topic交换器的消息不能具有随意的routing_key——它必须是单词列表&#xff0c;以点分隔。这些词可以是任何东西&#xff0c;但通常它们指定与消息相关的某些功能。一些有效的routing_key示例&#xff1a;“stock.usd.ny…...

SpringBoot对接OpenAI

SpringBoot对接OpenAI 随着人工智能技术的飞速发展&#xff0c;越来越多的开发者希望将智能功能集成到自己的应用中&#xff0c;以提升用户体验和应用的功能。OpenAI作为一家领先的人工智能公司&#xff0c;提供了许多先进的自然语言处理和语言生成模型&#xff0c;其中包括深…...

(C++)继承

目录 1.继承的概念及定义 1.1继承的概念 1.2继承定义 1.2.1定义格式 1.2.2继承方式和访问限定符 1.2.3继承基类成员访问方式的变化 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.复杂的菱形继承及菱形虚拟…...

图像处理技巧形态学滤波之膨胀操作

1. 引言 欢迎回来&#xff0c;我的图像处理爱好者们&#xff01;今天&#xff0c;让我们继续研究图像处理领域中的形态学计算。在本篇中&#xff0c;我们将重点介绍腐蚀操作的反向效果膨胀操作。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 膨胀操作原理 膨胀操作…...

机器学习基础之《特征工程(4)—特征降维》

一、什么是特征降维 降维是指在某些限定条件下&#xff0c;降低随机变量&#xff08;特征&#xff09;个数&#xff0c;得到一组“不相关”主变量的过程 1、降维 降低维度 ndarry 维数&#xff1a;嵌套的层数 0维&#xff1a;标量&#xff0c;具体的数0 1 2 3... …...

学生管理系统(Python版本)

class Student:def __init__(self, id, name, age):self.id idself.name nameself.age ageclass StudentManagementSystem:def __init__(self):self.students []def add_student(self, student):self.students.append(student)print("学生信息添加成功&#xff01;&qu…...

Linux下快速创建大文件的4种方法总结

1、使用 dd 命令创建大文件 dd 命令用于复制和转换文件&#xff0c;它最常见的用途是创建实时 Linux USB。dd 命令是实际写入硬盘&#xff0c;文件产生的速度取决于硬盘的读写速度&#xff0c;根据文件的大小&#xff0c;该命令将需要一些时间才能完成。 假设我们要创建一个名…...

用 Rufus 制作 Ubuntu 系统启动盘时,选择分区类型为MBR还是GPT?

当使用 Rufus 制作 Ubuntu 系统启动盘时&#xff0c;您可以根据您的需求选择分区类型&#xff0c;MBR&#xff08;Master Boot Record&#xff09;还是 GPT&#xff08;GUID Partition Table&#xff09;。 MBR 是传统的分区表格式&#xff0c;适用于大多数旧版本的操作系统和旧…...

Nodejs+vue+elementui汽车租赁管理系统_1ma2x

语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 前端nodejsvueelementui, 课题主要分为三大模块&#xff1a;即管理员模块、用户模块和普通管理员模块&#xff0c;主要功能包括&#…...

Prometheus入门

Prometheus(普罗米修斯) 是一种 新型监控告警工具,Kubernetes 的流行带动了 Prometheus 的应用。 全文参考自 prometheus 学习笔记(1)-mac 单机版环境搭建[1] Mac 上安装 Prometheus brew install prometheus 安装路径在 /usr/local/Cellar/prometheus/2.20.1, 配置文件在 /usr…...

RISC-V云测平台:Compiling The Fedora Linux Kernel Natively on RISC-V

注释&#xff1a;编译Fedora&#xff0c;HS-2 64核RISC-V服务器比Ryzen5700x快两倍&#xff01; --- 以下是blog 正文 --- # Compiling The Fedora Linux Kernel Natively on RISC-V ## Fedora RISC-V Support There is ongoing work to Fedora to support RISC-V hardwar…...

Vim学习(三)—— Git Repo Gerrit

Git、Gerrit、Repo三者的概念及使用 三者各自作用&#xff1a; git&#xff1a;版本管理库&#xff0c;在git库中没有中心服务器的概念&#xff0c;真正的分布式。 repo&#xff1a;repo就是多个git库的管理工具。如果是多个git库同时管理&#xff0c;可以使用repo。当然使用…...

论坛项目之用户部分

注册接口 实现思路 1.特殊字段检查&#xff08;比如性别没有给出需要给出默认值&#xff09; 2.对比检查两次输入的密码是否一致&#xff0c;不一致报错 3.利用UUID生成随机‘盐’值&#xff0c;并使用密码进行MD5加密后与‘盐’进行拼接&#xff0c;生成加密后的密码 4.创建U…...

golang内存对齐

为什么要内存对齐&#xff1f; CPU访问内存时&#xff0c;以CPU的位数为单位进行访问。 如果访问未对齐的内存&#xff0c;处理器需要做两次内存访问&#xff0c;对齐的内存的访问可能仅需要一次&#xff0c;利用内存对齐后提升读取速度。 golang结构体内存对齐规则 在代码编译…...

【CheatSheet】Python、R、Julia数据科学编程极简入门

《Python、R、Julia数据科学编程极简入门》PDF版&#xff0c;是我和小伙伴一起整理的备忘清单&#xff0c;帮助大家10分钟快速入门数据科学编程。 另外&#xff0c;最近 TIOBE 公布了 2023 年 8 月的编程语言排行榜。 Julia 在本月榜单中实现历史性突破&#xff0c;成功跻身 …...

【golang】怎样判断一个变量的类型?

怎样判断一个变量的类型&#xff1f; package mainimport "fmt"var container []string{"zero", "one", "two"} func main() {container : map[int]string{0: "zero", 1: "one", 2: "two"}fmt.Printf…...

怎么学习AJAX相关技术? - 易智编译EaseEditing

学习AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;相关技术可以让你实现网页的异步数据交互&#xff0c;提升用户体验。以下是一些学习AJAX技术的步骤和资源&#xff1a; HTML、CSS和JavaScript基础&#xff1a; 首先&#xff0c;确保你已经掌握了基本的HTML…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...