【学习日记】【FreeRTOS】链表结构体及函数详解
写在前面
本文主要是对于 FreeRTOS 中链表相关内容的详细解释,代码大部分参考了野火FreeRTOS教程配套源码,作了一小部分修改。
一、结构体定义
主要包含三种结构体:
- 普通节点结构体
- 结尾节点(mini节点)结构体
- 链表结构体
1.普通节点结构体
一个完整的节点,包含五个参数:
- 节点的序号,用于确定节点在链表中的位置
- 前指针,指向本节点的前一个节点
- 后指针,指向本节点的下一个节点
- 本节点所属的内核对象,通常是TCB(Task Control Block)是FreeRTOS中用于管理任务的数据结构。
- 该节点所属的链表
- 结构体定义如下:
/* 节点结构体定义 */
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节点没有所属内核和所属链表信息,主要被用作双向列表的结尾,只有三种信息:
- 节点序号值,标识节点在链表中的位置
- 前指针
- 后指针
- 结构体定义如下:
/* mini节点结构体定义,作为双向链表的结尾因为双向链表是首尾相连的,头即是尾,尾即是头 */
struct xMINI_LIST_ITEM
{TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列 */struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 最小节点数据类型重定义 */
3.链表结构体
链表需要三种参数:
- 链表节点计数器,统计链表中的节点总数
- 链表节点索引指针,指向链表中目前要操作的节点
- 链表最后一个节点的指针
- 结构体定义如下:
/* 链表结构体定义 */
typedef struct xLIST
{UBaseType_t uxNumberOfItems; /* 链表节点计数器 */ListItem_t * pxIndex; /* 链表节点索引指针 */MiniListItem_t xListEnd; /* 链表最后一个节点 */
} List_t;
二、链表基本函数定义
主要包括下面几种函数:
- 节点初始化函数
- 空链表初始化函数
- 将新节点插入到链表尾部的函数
- 将新节点按升序插入链表的函数
- 从链表中删除节点的函数
1.节点初始化函数
传入要初始化的节点,将该节点的所属链表参数设置为空即可。
- 函数定义如下:
//节点初始化
void vListInitialiseItem(ListItem_t * const pxItem)
{pxItem->pvContainer = NULL; //将该节点所在链表初始化为空,表示该节点还没有插入任何链表
}
2. 空链表初始化函数
需要的步骤如下:
- 将指针索引指向最后一个节点
- 最后一个节点的排序值设置为最大(取决于portmacro中portMAX_DELAY宏定义)
- 最后一个节点的前后指针都指向节点自身
- 链表节点计数器设置为 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. 将新节点插入到链表尾部的函数
- 需要的步骤如下:
- 拿出指向链表最后一个节点的指针
- 将新的节点插入到原来最后节点的前面
- 设置新插入节点所属的链表
- 链表节点计数器++
- 代码如下:
//将节点插入到链表的尾部
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. 将新节点按升序插入链表的函数
- 需要的步骤如下:
- 定义迭代器(节点指针)用于查找插入的位置
- 获取新插入节点的序号
- 寻找节点插入的位置
- 进行节点插入操作
- 修改新插入的节点所在的链表
- 链表节点计数器++
- 代码如下:
//将节点按照升序排列插入链表
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. 从链表中删除节点的函数
- 需要的步骤如下:
- 获取要删除的节点所在的链表
- 进行删除操作
- 如果链表索引指向要删除的节点,需更新索引使其指向要删除节点的前一个节点,确保索引指向有效
- 设置要删除节点的所属链表为空
- 链表节点计数器–
- 返回删除操作后链表中剩余的节点的数量
- 代码如下:
//从链表中删除节点
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 中链表相关内容的详细解释,代码大部分参考了野火FreeRTOS教程配套源码,作了一小部分修改。 一、结构体定义 主要包含三种结构体: 普通节点结构体结尾节点(mini节点)结构体链表结…...
【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA)
【云原生•监控】基于Prometheus实现自定义指标弹性伸缩(HPA) 什么是弹性伸缩 「Autoscaling即弹性伸缩,是Kubernetes中的一种非常核心的功能,它可以根据给定的指标(例如 CPU 或内存)自动缩放Pod副本,从而可以更好地管…...
Windows、 Linux 等操作系统的基本概念及其常见操作
Windows 和 Linux 是两种常见的操作系统,它们在计算机领域中广泛使用。下面我将为您介绍它们的基本概念以及一些常见的操作。 **Windows 操作系统:** 1. **基本概念:** Windows 是由微软公司开发的操作系统系列,旨在为个人计算机…...
【RabbitMQ】golang客户端教程5——使用topic交换器
topic交换器(主题交换器) 发送到topic交换器的消息不能具有随意的routing_key——它必须是单词列表,以点分隔。这些词可以是任何东西,但通常它们指定与消息相关的某些功能。一些有效的routing_key示例:“stock.usd.ny…...
SpringBoot对接OpenAI
SpringBoot对接OpenAI 随着人工智能技术的飞速发展,越来越多的开发者希望将智能功能集成到自己的应用中,以提升用户体验和应用的功能。OpenAI作为一家领先的人工智能公司,提供了许多先进的自然语言处理和语言生成模型,其中包括深…...
(C++)继承
目录 1.继承的概念及定义 1.1继承的概念 1.2继承定义 1.2.1定义格式 1.2.2继承方式和访问限定符 1.2.3继承基类成员访问方式的变化 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.复杂的菱形继承及菱形虚拟…...
图像处理技巧形态学滤波之膨胀操作
1. 引言 欢迎回来,我的图像处理爱好者们!今天,让我们继续研究图像处理领域中的形态学计算。在本篇中,我们将重点介绍腐蚀操作的反向效果膨胀操作。 闲话少说,我们直接开始吧! 2. 膨胀操作原理 膨胀操作…...
机器学习基础之《特征工程(4)—特征降维》
一、什么是特征降维 降维是指在某些限定条件下,降低随机变量(特征)个数,得到一组“不相关”主变量的过程 1、降维 降低维度 ndarry 维数:嵌套的层数 0维:标量,具体的数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("学生信息添加成功!&qu…...
Linux下快速创建大文件的4种方法总结
1、使用 dd 命令创建大文件 dd 命令用于复制和转换文件,它最常见的用途是创建实时 Linux USB。dd 命令是实际写入硬盘,文件产生的速度取决于硬盘的读写速度,根据文件的大小,该命令将需要一些时间才能完成。 假设我们要创建一个名…...
用 Rufus 制作 Ubuntu 系统启动盘时,选择分区类型为MBR还是GPT?
当使用 Rufus 制作 Ubuntu 系统启动盘时,您可以根据您的需求选择分区类型,MBR(Master Boot Record)还是 GPT(GUID Partition Table)。 MBR 是传统的分区表格式,适用于大多数旧版本的操作系统和旧…...
Nodejs+vue+elementui汽车租赁管理系统_1ma2x
语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具:Navicat 开发软件:VScode 前端nodejsvueelementui, 课题主要分为三大模块:即管理员模块、用户模块和普通管理员模块,主要功能包括&#…...
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
注释:编译Fedora,HS-2 64核RISC-V服务器比Ryzen5700x快两倍! --- 以下是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三者的概念及使用 三者各自作用: git:版本管理库,在git库中没有中心服务器的概念,真正的分布式。 repo:repo就是多个git库的管理工具。如果是多个git库同时管理,可以使用repo。当然使用…...
论坛项目之用户部分
注册接口 实现思路 1.特殊字段检查(比如性别没有给出需要给出默认值) 2.对比检查两次输入的密码是否一致,不一致报错 3.利用UUID生成随机‘盐’值,并使用密码进行MD5加密后与‘盐’进行拼接,生成加密后的密码 4.创建U…...
golang内存对齐
为什么要内存对齐? CPU访问内存时,以CPU的位数为单位进行访问。 如果访问未对齐的内存,处理器需要做两次内存访问,对齐的内存的访问可能仅需要一次,利用内存对齐后提升读取速度。 golang结构体内存对齐规则 在代码编译…...
【CheatSheet】Python、R、Julia数据科学编程极简入门
《Python、R、Julia数据科学编程极简入门》PDF版,是我和小伙伴一起整理的备忘清单,帮助大家10分钟快速入门数据科学编程。 另外,最近 TIOBE 公布了 2023 年 8 月的编程语言排行榜。 Julia 在本月榜单中实现历史性突破,成功跻身 …...
【golang】怎样判断一个变量的类型?
怎样判断一个变量的类型? 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(Asynchronous JavaScript and XML)相关技术可以让你实现网页的异步数据交互,提升用户体验。以下是一些学习AJAX技术的步骤和资源: HTML、CSS和JavaScript基础: 首先,确保你已经掌握了基本的HTML…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
