FreeRTOS 列表 List 源码解析

news2024/11/13 15:12:15

目录

  • 一、链表及链表项的定义
    • 1、链表节点数据结构 xList_ITEM
    • 2、链表精简节点结构 xMINI_LIST_ITEM
    • 3、链表根节点结构 xLIST
  • 二、链表的相关操作
    • 1、初始化
      • 1.1 链表节点初始化
      • 1.2 链表根节点初始化
    • 2、插入
      • 2.1 将节点插入到链表的尾部
      • 2.2 将节点按照升序排列插入到链表
    • 3、删除
    • 4、宏函数


链表是 FreeRTOS 的核心数据结构,有关任务调度、延时、阻塞、事件等操作都是通过对链表进行操作进而实现的。本节将详细分析源码文件 list.clist.h 的内容,为后续的任务队列等的实现奠定基础。

一、链表及链表项的定义

FreeRTOS 使用的链表结构是环形的双向链表,而关于链表节点的数据结构都在 list.h 中定义。

1、链表节点数据结构 xList_ITEM

首先来看链表节点数据结构定义:

struct xLIST_ITEM
{
	// 第一个和最后一个成员值
	// 当 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 被使能的时候会被设定为一个固定值,用来检验一个列表项数据是否完整
    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 
    struct xLIST * configLIST_VOLATILE pxContainer;      // 指向该节点所在的链表
    listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE             
};
typedef struct xLIST_ITEM ListItem_t;

其结构如下:

这里如果使用 configLIST_VOLATILE,其会被替换为 volatile 关键字。

#define configLIST_VOLATILE volatile

volatile 关键字是让编译器不对该变量进行优化,所谓的优化可以理解为其在生成汇编时,若多次读取该变量时其可能会将变量值放入寄存器中以加快读取速度,而不是真正的读取,这使得当某个变量会快速变化时,优化后“读取”的值并不是变量真实的值。当使用 volatile 关键字时,其会强迫编译器每次使用该变量时都真正的对它进行一次读取。

在链表项的结构体构造中值得注意的是 pvOwnerpxContainer 这两个成员变量。

  • pvOwner 指向该节点的拥有者,即该节点内嵌在哪个数据结构中,属于哪个数据结构的一个成员。它提供了一种可以快速访问由此链表项代表的对象的方法。
  • pxContainer 用于指向该节点所在的链表,通常指向链表的根节点。它则提供了一种快速访问其所属链表的方法。

这种处理方式大大提高了链表在任务调度等应用中的处理速度,提高系统效率。

侵入式链表


在 Linux 内核中有很多侵入式的链表的设计,比如在 Linux 中提供的链表项的定义为:

struct list_head
{
        struct list_head *next, *prev;
}

使用链表时只需要将其包含进定义的对象中即可:

struct list_head
{
         // 一些其它成员定义....
         struct list_head *next, *prev;
         // 一些其它成员定义....
}

在此它没有定义类似 ListItem_tpxContainer 这样的成员变量,其获得包含该链表项的对象地址是通过下面一段著名的宏定义实现的:

#define offsetof(s,m) (size_t)&(((s *)0)->m
#define container_of(ptr, type, member) \
({ \
         const typeof( ((type *)0)->member ) *__mptr = (ptr); \
         (type *)( (char *)__mptr - offsetof(type,member) ); \
})

使用实例:

struct node test;
struct list_head *list_item_add = &test.list_item;
struct node *test_add = container_of(list_item_add, struct node, list_item);

container_of() 的实现思路简单概括就是:将成员变量地址减去成员变量在结构体类型中的变量便是实例对象的存储地址。以 struct node 结构体为例,其实例 test 在内存中的存储方式如下图左侧所示。下图右侧给出了如何获得成员在结构体存储中的偏移量,当 &test=0x00 时,其成员的地址便是所需要的偏移量。

因此,offsetof() 宏,其所作的事就是获得偏移量。而 container_of() 宏中的:

(type *)( (char *)__mptr - offsetof(type,member) );

便是用成员地址减去偏移量来获得实例的地址。至于 container_of() 宏中的前一句:

const typeof( ((type *)0)->member ) *__mptr = (ptr);

实时上是起到一个类型检验的作用,拓展关键字 typeof 可以获得变量的类型,如果传入的ptr的类型与成员变量类型不符,那么编译器便会抛出警告,便于检查是否出错。注意 typeof 并不是标准 C 中的关键字,如果所用的编译器不支持,可以将第一句删除,将第二句中的__mptr 替换为 ptr,宏 container_of() 仍然是正确的。

2、链表精简节点结构 xMINI_LIST_ITEM

这个结构是专门用来在下面要讲的 xLIST 表示尾节点,相比于刚才讲到的 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;

3、链表根节点结构 xLIST

typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE       // 校验值
    volatile UBaseType_t uxNumberOfItems;      // 记录该链表里有多少成员(根节点除外)
    ListItem_t * configLIST_VOLATILE pxIndex;  // 链表节点索引指针
    MiniListItem_t xListEnd;                   // 链表尾部(实际也是链表的第一个节点),为节省内存使用Mini 链表项
    listSECOND_LIST_INTEGRITY_CHECK_VALUE      // 校验值
} List_t;

结构图如下:



现在清楚了 List 的基本结构,下面是它的相关操作。

二、链表的相关操作

关于链表的相关操作函数的在 list.c 中实现。

1、初始化

1.1 链表节点初始化

void vListInitialiseItem( ListItem_t * const pxItem )
{
    /* Make sure the list item is not recorded as being on a list. */
    pxItem->pxContainer = NULL;

    /* Write known values into the list item if
     * configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

链表项的初始化十分简单只是将 pxContainer 置为NULL,设置一下校验值。

一个初始化好的节点示意图具体如下:
在这里插入图片描述

1.2 链表根节点初始化

void vListInitialise( List_t * const pxList )
{
    /* 将链表索引指针指向最后一个节点 */
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); 

    /* 将链表最后一个节点的辅助排序的值设置为最大,确保该节点就是链表的最后节点 */
    pxList->xListEnd.xItemValue = portMAX_DELAY;

    /* 将最后一个节点的pxNext 和pxPrevious 指针均指向节点自身,表示链表为空 */
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );     
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

	/* 将链表项数目设为0 */
    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

    /* 写入校验值,用于后续检验,为了保证链表结构体是正确的,没有被覆写 */
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}

2、插入

2.1 将节点插入到链表的尾部

void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )
{
    ListItem_t * const pxIndex = pxList->pxIndex;

    /* 校验链表和链表项 */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* 将链表项嵌入到pxIndex 指向的链表项前 */
    pxNewListItem->pxNext = pxIndex;                   // 1
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;   // 2

    /* 调试测试用的函数,对代码逻辑理解无影响。 */
    mtCOVERAGE_TEST_DELAY();

    pxIndex->pxPrevious->pxNext = pxNewListItem;       // 3
    pxIndex->pxPrevious = pxNewListItem;               // 4

    /* 记录链表项属于该链表 */
    pxNewListItem->pxContainer = pxList;               // 5

	/* 记录链表中的链表项数目 */
    ( pxList->uxNumberOfItems )++;                     // 6
}

代码整体不难理解,主要是弄明白插入的过程中更改指针的指向:

2.2 将节点按照升序排列插入到链表

将节点按照升序排列插入到链表,如果有两个节点的值相同,则新节点在旧节点的后面插入:

void vListInsert( List_t * const pxList,
                  ListItem_t * const pxNewListItem )
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

    /* 校验链表和链表项 */
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    /* 寻找插入位置 */
    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;         // 1
    pxNewListItem->pxNext->pxPrevious = pxNewListItem;  // 2
    pxNewListItem->pxPrevious = pxIterator;				// 3
    pxIterator->pxNext = pxNewListItem;					// 4

    /* 记录链表项属于该链表 */
    pxNewListItem->pxContainer = pxList;				// 5

	/* 记录链表中的链表项数目 */
    ( pxList->uxNumberOfItems )++;						// 6
}

vListInsert() 的实现比起 vListInsertEnd() 也就多了要先查找插入位置再进行插入操作。

在这里插入图片描述

3、删除

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
	/* 调整前后项指针 */
    List_t * const pxList = pxItemToRemove->pxContainer;

    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

    mtCOVERAGE_TEST_DELAY();

    /* 保证当前的链表索引指向有效项 */
    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

 	/* 移除的链表项不再被链表拥有 */
    pxItemToRemove->pxContainer = NULL;

	/* 减少链表项数目 */
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;
}

4、宏函数

list.h 中,还定义了各种各样的带参宏,方便对节点做一些简单的操作。

/* 初始化节点的拥有者 */
#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 )                       ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )

/* 获取链表的节点数 */
#define listCURRENT_LIST_LENGTH( pxList )                 ( ( pxList )->uxNumberOfItems )

/* 获取链表第一个节点的OWNER,即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;                       \
        }                                                                                      \
        ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                         \
    }


/* 获取第一个列表项所属的链表 */
#define listGET_OWNER_OF_HEAD_ENTRY( pxList )            ( ( &( ( pxList )->xListEnd ) )->pxNext->pvOwner )

/* 判断给定 pxListItem 是否属于 pxList */
#define listIS_CONTAINED_WITHIN( pxList, pxListItem )    ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )

/* 判断节点是否属于某个链表 */
#define listLIST_ITEM_CONTAINER( pxListItem )            ( ( pxListItem )->pxContainer )

/* 判断链表是否已初始化 */
#define listLIST_IS_INITIALISED( pxList )                ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(go)线性表的顺序存储

闲来无事&#xff0c;更新一下&#xff0c;线性表的顺序存储&#xff0c;go语言版本&#xff0c;效果都已经测试过&#xff0c;下面给出各部分细节 文章目录 1、生成一个线性表2、查找3、插入4、求长度5、改值6、删除7、遍历8、测试程序9、完整代码总结 package mainimport &q…

VBA技术资料MF195:屏蔽工作表中的粘贴输入

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

关于测试工程师在性能测试工具jmeter的熟悉和精通

经过一周的jmeter接口编写&#xff0c;不不不&#xff0c;是一年1-2次的jmeterd 使用&#xff0c;每次都是新的发现&#xff0c;新的起点&#xff01;&#xff01; 去年10月学习过的东西&#xff0c;现在还是不记得当时怎么这么聪明&#xff0c;于是&#xff0c;每次都是0基础…

笔试训练,牛客.合唱团牛客.kannan与高音牛客.拜访(BFS)牛客.买卖股票的最好时机(四)

目录 牛客.合唱团 牛客.kannan与高音 牛客.拜访&#xff08;BFS&#xff09; 牛客.买卖股票的最好时机(四) 牛客.合唱团 dp[i][j]:从1到i,中挑选最大乘积是多少&#xff0c;但是我们会发现状态转移方程推不出来&#xff0c;我们不知道如何推导的任意两个人&#xff0c; 从[…

[解决]Invalid configuration `aarch64-openwrt-linux‘: machine `aarch64-openwrt

背景 交叉编译libev-4.19 问题 checking host system type… Invalid configuration aarch64-openwrt-linux: machine aarch64-openwrt’ not recognized 解决 打开config.sub&#xff0c;在244行后添加"| aarch64-openwrt \ "

RK 方案u-boot阶段添加驱动

驱动部分&#xff1a; u-boot/drivers/video/drm/gpio_init.c /** (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd** SPDX-License-Identifier: GPL-2.0*/#include <config.h> #include <common.h> #include <errno.h> #include <malloc…

jmeter连接mysql数据,并将查询结果存储到指定txt文件中

1、首先jmeter先进行连接mysql相关的配置&#xff0c;我之前已经有教程了就不赘述了&#xff0c;教程链接如下 jmeter连接mysql数据库以及常规用法-CSDN博客 2、当jmeter成功配置mysql数据库后&#xff0c;在JDBC Request组件中进行如下配置 Variable Name of Pool declared…

关于springboot对接通义千问大模型的尝试

今天正在路上刷手机&#xff0c;突然看到群里有人发了一个链接&#xff0c;内容是Spring Cloud Alibaba AI 的使用&#xff0c;spring cloud AI的使用&#xff0c;于是就想着玩一玩试试&#xff0c;难度不大&#xff0c;就是有些文档的坑&#xff0c;这里做一个记录&#xff0c…

基于RK3588+MCU智能清洁车应用解决方案

智能清洁车应用解决方案 在智慧城市建设发展的过程中&#xff0c;智慧环卫是打造智慧城市的重要组成部分&#xff0c;智能清洁车作为实现环卫智能化、提升作业效率和服务质量的关键工具&#xff0c;发挥着不可或缺的作用。 智能清洁车集成了激光雷达、双目视觉、多重传感器以及…

九月更新|用这5个简单技巧,新手在国内也能轻松使用ChatGPT,GPT新手使用手册(学术教师)

一、 ChatGPT可以做什么&#xff1f; 简单来说&#xff0c;ChatGPT就像一个超级智能的聊天机器人&#xff0c;它可以做很多事情。你可以把它想象成一个非常聪明的助手&#xff0c;随时随地帮你解答问题、提供建议、写文章、甚至讲笑话。以下是几个具体的例子&#xff1a; 1. …

论文浅尝 | 超越实体对齐: 通过实体关系协同实现完整的知识图谱对齐

笔记整理&#xff1a;米尔扎提阿力木&#xff0c;天津大学硕士&#xff0c;研究方向为大模型 论文链接&#xff1a;https://arxiv.org/abs/2407.17745 摘要 知识图谱对齐(Knowledge Graph Alignment, KGA)旨在整合来自多个来源的知识&#xff0c;以解决单个知识图谱在覆盖范围和…

一文带你了解可观测领域中APM与eBPF的技术差异

近年来&#xff0c;随着eBPF技术的兴起&#xff0c;很多人有这样的疑惑&#xff1a;eBPF和APM有什么区别&#xff1f;他们是竞争关系还是合作关系&#xff1f;本文将就此展开讨论&#xff0c;并给出切实有效的落地方案。 01APM APM全称&#xff1a;Application Performance Ma…

vulhub xxe靶机

步骤一&#xff0c;访问浏览器 步骤二&#xff0c;输入/robots.txt 步骤三&#xff0c;发现存在用户登录的一个界面/xxe 我们登录进去 步骤四&#xff0c;随便输入一个数字或者字母打开BP 抓到包之后发送的重放器里边 通过抓包发现是XML数据提交 步骤五&#xff0c;通过php…

【采集软件】抖音根据关键词批量采集搜索结果工具

这是我用Python开发的抖音关键词搜索采集工具软件。 软件界面截图&#xff1a; 爬取结果截图&#xff1a; 软件演示视频&#xff1a; https://www.bilibili.com/video/BV1Fc41147Be 完整讲解文章&#xff1a; https://www.bilibili.com/read/cv33750458

高翔【自动驾驶与机器人中的SLAM技术】学习笔记(九)imu运动学;lambda表达式;bind;function;std::move()

一、IMU运动学 1、测量值&#xff1a; 常用六轴IMU是由陀螺仪&#xff08;Gyroscope&#xff09;和加速度计&#xff08;Acclerator&#xff09;两部分组成。 陀螺仪测量&#xff1a;角速度。加速度计&#xff1a;加速度。 安装要尽量保证IMU的安装位置在车辆中心。避免由I…

基于SOA-BP海鸥优化BP神经网络实现数据预测Python实现

BP神经网络是一种多层前馈神经网络&#xff0c;它通过反向传播算法来训练网络中的权重和偏置&#xff0c;以最小化预测误差。然而&#xff0c;BP神经网络的性能很大程度上依赖于其初始参数的选择&#xff0c;这可能导致训练过程陷入局部最优解。海鸥优化算法因其探索和开发能力…

基于vue框架的残疾人就业帮扶平台97c5w(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,企业,招聘信息,类型,求职信息,投递信息,邀请信息,通知信息,帮扶政策,申请信息,意见反馈 开题报告内容 基于Vue框架的残疾人就业帮扶平台开题报告 一、选题背景与意义 随着社会的文明进步和经济的快速发展&#xff0c;残疾人群体…

flannel,etcd,docker

bridge容器 听有容器连接到桥就可以使用外网&#xff0c;使用nat让容器可以访问外网使用ipas指令查看桥&#xff0c;所有容器连接到此桥&#xff0c;ip地址都是172.17.0.0/16网段&#xff0c;桥是启动docker服务后出现&#xff0c;在centos使用bridge-utils安装 跨主机的容器…

第一次使用PyCharm写C++(失败)

前言&#xff1a; 由于我已经非常习惯使用PyCharm远程连接服务器了&#xff0c;我认为非常方便&#xff0c;所以希望C也能直接用Pycharm。于是尝试在PyCharm上部署C环境。 但是&#xff0c;我失败了。如果您知道问题所在&#xff0c;欢迎给我留言。我认为Pycharm并没有编译C/C…

Windows电脑微信可以登录发消息,但是网页打不开的解决方法:刷新DNS缓存

遇到的问题 今天实验室的电脑突然网页打不开&#xff0c;baidu上不了&#xff0c;chrome浏览器也上不了。但是ping baidu.com能够ping通&#xff0c;github pull也可以&#xff0c;网易云可以听歌。也就是说网络是通的&#xff0c;但是浏览器无法上网。 解决方法 我是通过 W…