嵌入式链表操作原理详解

news2025/6/9 7:10:19

嵌入式链表操作原理详解

链表是嵌入式软件开发中最基础的数据结构之一,其设计采用嵌入式链表节点的思想,实现了高度通用的链表管理机制。以下是核心原理和操作的全面解析:


一、基础数据结构
struct list_head {
    struct list_head *next, *prev;  // 双向指针
};

设计特点

  1. 独立于数据类型的纯链表节点
  2. 通过嵌入到自定义结构体实现数据关联
  3. 双向循环链表结构(头节点的prev指向尾节点)

二、核心操作宏详解
1. 链表初始化
// 静态初始化
LIST_HEAD(my_list);

// 动态初始化
struct list_head my_list;
INIT_LIST_HEAD(&my_list);
2. 节点插入
// 头插法(插入到head之后)
list_add(struct list_head *new, struct list_head *head);

// 尾插法(插入到head之前)
list_add_tail(struct list_head *new, struct list_head *head);

图示

头插法: head → new → node1 → node2
尾插法: node1 → node2 → new → head
3. 节点删除
// 基础删除
list_del(struct list_head *entry);

// 安全删除(删除后初始化节点)
list_del_init(struct list_head *entry);

注意:删除后节点指针被设为LIST_POISON1/2(用于调试)

4. 链表遍历
// 基础遍历
#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

// 安全遍历(允许删除节点)
#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
         pos = n, n = pos->next)

// 反向遍历
list_for_each_prev(pos, head)
5. 获取父结构体 - list_entry
#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

实现原理

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

参数解析

  • ptr:链表节点指针(如&data->list
  • type:父结构体类型(如struct my_data
  • member:链表成员名(如list

计算过程

  1. 计算membertype中的偏移量(offsetof
  2. 将节点指针减去偏移量得到父结构体地址
6. 其他关键操作
// 链表判空
list_empty(const struct list_head *head)

// 节点移动
list_move(struct list_head *list, struct list_head *head)  // 移动到head后
list_move_tail(struct list_head *list, struct list_head *head) // 移动到head前

// 链表合并
list_splice(const struct list_head *list, struct list_head *head) // 合并到head后

三、完整使用示例
// 1. 定义数据结构
struct task_info {
    pid_t pid;
    char name[16];
    struct list_head list;  // 嵌入链表节点
};

// 2. 初始化链表头
LIST_HEAD(task_list);

// 3. 添加节点
struct task_info *task1 = kmalloc(sizeof(*task1), GFP_KERNEL);
task1->pid = 1001;
strcpy(task1->name, "init");
INIT_LIST_HEAD(&task1->list);
list_add_tail(&task1->list, &task_list);  // 添加到链表尾部

// 4. 遍历链表
struct list_head *pos;
struct task_info *task;

list_for_each(pos, &task_list) {
    task = list_entry(pos, struct task_info, list);
    printk("PID: %d, Name: %s\n", task->pid, task->name);
}

// 5. 安全删除所有节点
struct list_head *n;
list_for_each_safe(pos, n, &task_list) {
    task = list_entry(pos, struct task_info, list);
    list_del(pos);
    kfree(task);
}

四、设计优势与原理
  1. 类型无关性

    • 链表操作只处理list_head,与具体数据类型解耦
    • 通过container_of实现类型安全转换
  2. 内存高效

    • 每个节点仅增加8字节(32位)或16字节(64位)开销
    • 无额外内存分配(节点包含在父结构中)
  3. O(1)时间复杂度

    • 插入/删除操作只需修改相邻节点的指针
    // list_add内部实现
    new->next = head->next;
    new->prev = head;
    head->next->prev = new;
    head->next = new;
    
  4. 循环链表设计

    • 头节点的prev指向尾节点,尾节点的next指向头节点
    • 避免遍历时的边界条件检查

五、高级应用技巧
  1. 多链表嵌入

    struct process {
        struct list_head ready_list;  // 就绪队列
        struct list_head wait_list;   // 等待队列
        // ...
    };
    
  2. 哈希链表

    struct hlist_head *htable;  // 哈希表头
    struct hlist_node node;     // 哈希节点
    
  3. RCU安全遍历

    list_for_each_entry_rcu(pos, head, member)
    

六、注意事项
  1. 删除安全

    • 遍历中删除节点必须使用_safe版本
    • 删除后节点指针不可再使用
  2. 内存屏障

    • 多核环境下需使用smp_rmb()/smp_wmb()保证可见性
  3. 对齐要求

    • container_of依赖结构体成员对齐,不可随意填充

这种链表设计被广泛应用于操作系统内核(如进程调度、文件系统、网络协议栈等),其通用性和高效性使其成为系统编程的经典范式。理解其原理对深入操作系统和嵌入式开发至关重要。

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

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

相关文章

从“人找政策”到“政策找人”:智能退税ERP数字化重构外贸生态

离境退税新政核心内容与外贸企业影响 (一)政策核心变化解析 退税商店网络扩容 新政明确鼓励在大型商圈、旅游景区、交通枢纽等境外旅客聚集地增设退税商店,并放宽备案条件至纳税信用M级企业。以上海为例,静安区计划新增1000家退…

以人类演示视频为提示,学习可泛化的机器人策略

25年5月来自清华大学、上海姚期智研究院和星动纪元(RoboEra)公司的论文“Learning Generalizable Robot Policy with Human Demonstration Video as a Prompt”。 最近的机器人学习方法通​​常依赖于从通过遥操作收集的大量机器人数据集中进行模仿学习…

SOC-ESP32S3部分:36-适配自己的板卡

飞书文档https://x509p6c8to.feishu.cn/wiki/RP4UwPrsKi4xuQkKLAAcKxD3n1b 如果你自己画了PCB板,需要把自己绘制的板卡配置小智AI工程,可以参考此文档。 下载源码 克隆或下载源码到本地,这里以1.5.5为例,大家可以自行修改其它版…

LLMs 系列科普文(8)

八、模型的自我认知 接下来我们聊聊另一种问题,即模型的自我认知。 网上经常经常可以看到人们会问大语言模型一些关于认知方面的问题,比如“你是什么模型?谁创造了你?” 说实话,其实这个问题有点无厘头。 之所以这么…

机器学习基础相关问题

机器学习相关的基础问题 K-means是否一定会收敛 K-means是否一定会收敛 K-means算法在有限步数内一定会收敛,但收敛到的可能是局部最优解而非全局最优解。以下是详细分析: K-means 的优化目标是最小化 样本到其所归属簇中心的距离平方和(SSE…

验证负载均衡与弹性伸缩

什么是弹性伸缩(Auto Scaling)? 弹性伸缩是指 云计算平台根据实时负载自动调整计算资源(如服务器实例、容器Pod)数量,以确保系统在高峰时保持稳定,在低谷时节省成本。 什么时候会触发弹性伸缩&…

Three.js中AR实现详解并详细介绍基于图像标记模式AR生成的详细步骤

文档地址 Three.js中AR实现详解 以下是Three.js中实现AR功能的详细解析,涵盖技术原理、实现步骤、核心组件及优化策略: 🧩 一、技术基础 AR.js框架的核心作用 AR.js是Three.js实现AR的基石,提供以下核心能力: 多模…

GeoBoundaries下载行政区划边界数据(提供中国资源shapefile)

要下载山东省济南市各个区的行政区划边界数据,你可以通过 geoBoundaries 提供的数据来实现。下面是详细步骤,包括网页操作和可选的 Python 自动化方式。 目录 ✅ 一、通过 geoBoundaries 官网手动下载1. 打开官网:2. 查找中国数据&#xff1a…

大模型如何选型?嵌入模型如何选型?

欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。 目录 引言模型优劣认知与模型选择大模型(L…

开源大模型网关:One API实现主流AI模型API的统一管理与分发

以下是对One API的简单介绍: One API是一个使用go语言开发的大语言模型 API 管理与分发系统支持Docker一键快速部署,且资源占用小,高性能开箱支持多平台大模型快速接入,包括OpenAI、Gemini、xAI、Grop、Anthropic Claude、Ollama…

智慧充电:新能源汽车智慧充电桩的发展前景受哪些因素影响?

全球能源结构转型与碳中和目标的推进,新能源汽车产业迎来爆发式增长,而智慧充电桩作为其核心基础设施,发展前景备受关注。智慧充电不仅关乎用户充电体验的优化,更是电网平衡、能源效率提升的关键环节。 然而,其发展并…

【网站建设】不同类型网站如何选择服务器?建站项目实战总结

做了几个建站项目后,深刻体会到一件事:不同类型的网站,所采用的服务器策略是完全不同的。 如果选错了服务器方案,可能带来过高的成本、过低的性能,甚至上线失败。 这篇文章分享一下我在实战中的经验,供正在做建站项目的朋友参考。 🚩 1️⃣ 纯展示型网站 —— 静态服务…

iptables实验

实验一:搭建web服务,设置任何人能够通过80端口访问。 1.下载并启用httpd服务器 dnf -y install httpd 开启httpd服务器 systemctl start httpd 查看是否启用 下载并启用iptables,并关闭firewalld yum install iptable…

前后端分离开发 和 前端工程化

来源:黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)_哔哩哔哩_bilibili 前后端混合开发: 需要使用前端的技术栈开发前端的功能,又需要使用Java的技术栈…

web端rtmp推拉流测试、抽帧识别计数,一键式生成巡检报告

本文旨在实现无人机城市交通智慧巡检中的一个模块——无人机视频实时推拉流以及识别流并在前端展示,同时,统计目标数量以及违停数量,生成结果评估,一并发送到前端展示。对于本文任何技术上的空缺,可在博主主页前面博客…

Excel 表格内批量添加前缀与后缀的实用方法

我们经常需要为 Excel 表格中的内容统一添加前缀或后缀,例如给编号加“NO.”、给姓名加“会员_”等。手动操作效率低,本文将介绍几种实用的方法,帮助你快速完成批量添加前缀和后缀的操作。 使用“&”运算符添加前缀或后缀(推…

2024 CKA题库+详尽解析| 15、备份还原Etcd

目录 免费获取题库配套 CKA_v1.31_模拟系统 15、 备份还原Etcd 题目: 开始操作: 1)、切换集群 2)、登录master并提权 3)、备份Etcd现有数据 4)、验证备份数据快照 5)、查看节点和Pod状态 6&am…

西门子 S7-1200 PLC 海外远程运维技术方案

西门子 S7-1200 PLC 海外远程运维技术方案 一、面向海外场景的核心优势 针对跨国企业、海外项目及远程技术支持需求,本方案基于巨控GRM552Y-CHE模块提供无缝的全球化远程PLC运维能力,突破地域及时差限制,显著提升国际项目响应效率。 二、海…

嵌入式学习--江协stm32day5

USART 1. 引脚与接口层 异步引脚: TX:发送数据输出;RX:接收数据输入;SW_RX:单线半双工模式的接收引脚(替代 RX)。 同步引脚:SCLK:同步模式下的时钟输出&…

(LeetCode 动态规划(基础版))96. 不同的二叉搜索树 (递推 || 递归)

题目:96. 不同的二叉搜索树 思路:二叉树长度为n时,枚举每个点u作为根节点root,那么root左边的数构成左子树种数left,root右边的数构成右子树种数right,那么当前u为根节点下,二叉树的种数为left*…