Linux链表操作全解析

Linux C语言链表深度解析与实战技巧

    • 一、链表基础概念与内核链表优势
      • 1.1 为什么使用链表?
      • 1.2 Linux 内核链表与用户态链表的区别
    • 二、内核链表结构与宏解析
      • 常用宏/函数
    • 三、内核链表的优点
    • 四、用户态链表示例
    • 五、双向循环链表在内核中的实现优势
      • 5.1 插入效率
      • 5.2 安全遍历删除
    • 六、典型应用场景
    • 七、调试技巧与常见陷阱
      • 7.1 打印链表内容
      • 7.2 常见错误
    • 八、实战案例:Linux 内核模块中的链表使用
      • 8.1 模块源码
      • 8.2 Makefile 编译模块
      • 8.3 插入与卸载模块
    • 九、总结:开发建议

本文系统讲解 Linux 下 C 语言链表的使用原理、最佳实践及内核模块中的实战示例,适合嵌入式开发者、驱动工程师和系统软件开发者。


一、链表基础概念与内核链表优势

1.1 为什么使用链表?

  • 动态插入/删除效率高(相比数组不需要整体移动元素)
  • 内存利用率高(按需分配)
  • 常用于队列、任务调度、资源池、驱动设备列表等场景

1.2 Linux 内核链表与用户态链表的区别

项目用户态实现内核态实现
指针结构自定义指针结构使用 struct list_head
安全性程序员自行维护提供安全宏/内联函数
插入/删除API手动实现提供统一接口如 list_add
迭代方式手动循环宏如 list_for_each_entry

二、内核链表结构与宏解析

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

常用宏/函数

  • INIT_LIST_HEAD(ptr)
  • list_add(new, head):头插法
  • list_add_tail(new, head):尾插法
  • list_del(entry)
  • list_empty(head)
  • list_for_each_entry(pos, head, member)
  • list_for_each_entry_safe(pos, n, head, member)

三、内核链表的优点

  • 双向循环结构:从任意节点出发都能遍历完整链表
  • 插入删除不涉及内容拷贝:仅修改指针
  • 接口统一、安全可靠:可结合 container_of 获取真实结构体指针

四、用户态链表示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct student {int id;char name[20];struct student *next;
} student_t;void add_student(student_t **head, int id, const char *name) {student_t *new_node = malloc(sizeof(student_t));new_node->id = id;strncpy(new_node->name, name, sizeof(new_node->name));new_node->next = *head;*head = new_node;
}void print_students(student_t *head) {while (head) {printf("ID: %d, Name: %s\n", head->id, head->name);head = head->next;}
}

五、双向循环链表在内核中的实现优势

5.1 插入效率

在头部插入只需两次指针操作:

list_add(&new_node->list, &head);

5.2 安全遍历删除

list_for_each_entry_safe(ptr, tmp, &head, list) {list_del(&ptr->list);kfree(ptr);
}

六、典型应用场景

场景描述
设备驱动管理管理设备结构体(如 net_device)
定时器链表内核定时器统一调度
slab 分配器链表管理对象缓存池
进程调度/等待队列管理 PCB / wait queue
协议栈管理TCP/UDP 的 socket 链表

七、调试技巧与常见陷阱

7.1 打印链表内容

#define print_list(head) \list_for_each_entry(ptr, head, list) \printk(KERN_INFO "Node: %s\n", ptr->name);

7.2 常见错误

  • 忘记初始化 INIT_LIST_HEAD
  • 使用已释放节点(UAF)
  • 非安全删除遍历(未使用 list_for_each_entry_safe

八、实战案例:Linux 内核模块中的链表使用

8.1 模块源码

// mylist_module.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>MODULE_LICENSE("GPL");struct student {int id;char name[20];struct list_head list;
};static LIST_HEAD(student_list);static int __init mylist_init(void)
{int i;struct student *stu;printk(KERN_INFO "Loading student list module...\n");for (i = 1; i <= 5; ++i) {stu = kmalloc(sizeof(*stu), GFP_KERNEL);stu->id = i;snprintf(stu->name, sizeof(stu->name), "Student%d", i);INIT_LIST_HEAD(&stu->list);list_add_tail(&stu->list, &student_list);}return 0;
}static void __exit mylist_exit(void)
{struct student *stu, *tmp;printk(KERN_INFO "Unloading student list module...\n");list_for_each_entry_safe(stu, tmp, &student_list, list) {list_del(&stu->list);kfree(stu);}
}module_init(mylist_init);
module_exit(mylist_exit);

8.2 Makefile 编译模块

obj-m += mylist_module.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

8.3 插入与卸载模块

$ make
$ sudo insmod mylist_module.ko
$ dmesg | tail -n 10
$ sudo rmmod mylist_module
$ dmesg | tail -n 10

九、总结:开发建议

建议项内容
一定初始化链表头使用 INIT_LIST_HEAD 初始化
删除节点用安全宏list_for_each_entry_safe 防止遍历时删除崩溃
内存管理责任明确kmalloc/kfree 成对使用
多线程环境加锁必要时配合 spinlock 或 mutex
定位 bug 用 printk输出 prev, next, 数据字段调试链表结构

本文涵盖了从用户态链表构造到 Linux 内核模块链表的实战应用,帮助你在驱动开发和内核开发中熟练掌握链表的构造与使用。

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

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

相关文章

SQL进阶之旅 Day 19:统计信息与优化器提示

【SQL进阶之旅 Day 19】统计信息与优化器提示 文章简述 在数据库性能调优中&#xff0c;统计信息和优化器提示是两个至关重要的工具。统计信息帮助数据库优化器评估查询成本并选择最佳执行计划&#xff0c;而优化器提示则允许开发人员对优化器的行为进行微调。本文深入探讨了…

安宝特方案丨船舶智造AR+AI+作业标准化管理系统解决方案(维保)

船舶维保管理现状&#xff1a;设备维保主要由维修人员负责&#xff0c;根据设备运行状况和维护计划进行定期保养和故障维修。维修人员凭借经验判断设备故障原因&#xff0c;制定维修方案。 一、痛点与需求 1 Arbigtec 人工经验限制维修效率&#xff1a; 复杂设备故障的诊断和…

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…

基于区块链的供应链溯源系统:构建与实践

前言 在当今全球化的经济环境中&#xff0c;供应链的复杂性不断增加&#xff0c;商品从原材料采购到最终交付给消费者的过程涉及多个环节和众多参与者。如何确保供应链的透明度、可追溯性和安全性&#xff0c;成为企业和消费者关注的焦点。区块链技术以其去中心化、不可篡改和透…

Web攻防-SQL注入数据格式参数类型JSONXML编码加密符号闭合

知识点&#xff1a; 1、Web攻防-SQL注入-参数类型&参数格式 2、Web攻防-SQL注入-XML&JSON&BASE64等 3、Web攻防-SQL注入-数字字符搜索等符号绕过 案例说明&#xff1a; 在应用中&#xff0c;存在参数值为数字&#xff0c;字符时&#xff0c;符号的介入&#xff0c…

探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面

在开发鸿蒙应用时&#xff0c;你是否也曾为一个页面的布局反复调整&#xff1f;是否还在为查 API、写模板代码而浪费大量时间&#xff1f;今天带大家实战体验一下鸿蒙官方的 AI 编程助手——CodeGenie&#xff08;代码精灵&#xff09; &#xff0c;如何从 0 到 1 快速构建一个…

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…

使用Whisper本地部署实现香港版粤语+英语混合语音转文字方案

今天要一个非常好的朋友有个工作&#xff0c;就是要把医院医生诊断的说话记录转成文字&#xff0c;之前都是她本人一句一句的听&#xff0c;然后记录下来的&#xff0c;我想通过ai 来解决这个问题。 她的需求如下&#xff1a; 不能把数据传到网上&#xff0c;隐私问题所以需要…

案例分享--汽车制动卡钳DIC测量

制动系统是汽车的主要组成部分&#xff0c;是汽车的主要安全部件之一。随着车辆性能的不断提高&#xff0c;车速不断提升&#xff0c;对车辆的制动系统也随之提出了更高要求&#xff0c;因此了解车辆制动系统中每个部件的动态行为成为了制动系统优化的主要途径&#xff0c;同时…

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;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, …

SQL SERVER 数据库迁移的三种方法!

要将SQL Server从研发环境的把数据库结构(不含数据)迁移至生产环境,可通过以下几种方法实现。以下是具体操作步骤及适用场景: ⚙️ 一、使用SSMS图形界面生成结构脚本(推荐新手) 通过SQL Server Management Studio的生成脚本向导,仅导出数据库架构: ​​连接测试库​​…

C# 快速检测 PDF 是否加密,并验证正确密码

引言&#xff1a;为什么需要检测PDF加密状态&#xff1f; 在批量文档处理系统&#xff08;如 OCR 文字识别、内容提取、格式转换&#xff09;中&#xff0c;加密 PDF 无法直接操作。检测加密状态可提前筛选文件&#xff0c;避免流程因密码验证失败而中断。 本文使用 Free Spire…

(33)课54:3 张表的 join-on 连接举例,多表查询总结。数据库编程补述及游标综合例题。静态 sqL与动态sqL(可带参数)

&#xff08;112&#xff09;3 张表的 join-on 连接举例 &#xff1a; &#xff08;113&#xff09; 多表查询总结 &#xff1a; &#xff08;114&#xff09;数据库编程补述 &#xff1a; 综合例题 &#xff1a; 以上没有动手练习&#xff0c;不知道这样的语法是否…

再见 Navicat!一款开源的 Web 数据库管理工具!

大家好&#xff0c;我是 Java陈序员。 在日常的开发工作中&#xff0c;常常需要与各种数据库打交道。而为了提高工作效率&#xff0c;常常会使用一些可视化工具进行操作数据库。 今天&#xff0c;给大家介绍一款开源的数据库管理工具&#xff0c;无需下载安装软件&#xff0c…

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…

函数中的Callable

在编程中&#xff0c;​Callable&#xff08;可调用对象&#xff09;​​ 是指任何可以通过 () 操作符调用的对象。在函数和类设计的上下文中&#xff0c;Callable 通常指代可以被调用的实体&#xff0c;例如函数、方法、Lambda表达式或实现了 __call__ 方法的对象。以下是详细…

MySQL学习之触发器

文章目录 前言什么是触发器&#xff08;Trigger&#xff09;&#xff1f;触发器的特点 MySQL中触发器的用法创建NEW 与 OLD举例其他操作 注意事项后续内容参考目录 前言 阅读本文前请注意最后编辑时间&#xff0c;文章内容可能与目前最新的技术发展情况相去甚远。欢迎各位评论…

AIGC 基础篇 Python基础 04 for循环与while循环

今天&#xff0c;我们来讲Python里面的循环部分 1.for循环 for i in range(1,10,2):print(i) 这是一个简单但是完整的for循环&#xff0c;里面包含了for循环的所有结构 首先注意格式是“for 变量 in range(取得到的起始值&#xff0c;取不到的终点值&#xff0c;步长)” …