Linux 内核学习(11) --- Linux 链表结构

文章目录

      • Linked List 简介
      • Linked List 操作方法
        • 链表头结点初始化
        • 创建链表节点
        • 添加节点到链表中
        • 从链表中删除节点
        • 从链表中替换节点
        • 移动链表中的节点
        • 检查链表
        • 链表遍历
        • demo 实例

Linked List 简介

链表是一种数据结构,由一系列节点组成,每个节点包含数据部分和指向下一个节点的指针
链表可以动态增长或者缩小,适合频繁的插入和删除操作,常见的链表类型有单向链表和双向链表

在 linux 内核开发中,开发者无需自己实现链表或者使用第三方库,内核内置了双向链表实现 struct list_head
定义在 linux/list.h

Linked List 操作方法

链表头结点初始化

Linux 内核链表用链表头 list_head 来表示,因为链表初始化其实就是初始化一个链表头节点

LIST_HEAD(demo_list)

LIST_HEAD 宏将创建一个名称为 demo_list 的链表,其实是一个链表头节点,在没有插入如何节点之前,它的首位指针指向自身,也可以认为首尾指针指向自身时链表就是空的

#define LIST_HEAD_INIT(name) {&(name), &(name)};#define LIST_HAED(name) \struct list_head name = LIST_HEAD_INIT(name);
struct list_head {struct list_head *next;struct list_head *prev;
}

注意 LIST_HAED 宏可以用于定义个链表头节点,LIST_HEAD_INIT 只能用于初始化链表头结点

创建链表节点

Linux 内核链表节点也使用 list_head 来表示,通常内嵌在自定义数据结构中:

struct my_node {struct list_head list;int data;
};struct my_node node

链表节点在插入链表之前也需要初始化,使用 INIT_LIST_HEAD

添加节点到链表中

链表有节点初始化后,就可以往链表中添加节点:

static inline void list_add(struct list_head *new, struct list_head *head)
static inline void list_add_tail(struct list_head *new, struct list_head *head)

其中的 head 表示链表头,new 表示要添加的节点,

list_add 将 new 添加到链表头部
list_add_tail 将 new 添加到链表尾部

从链表中删除节点

从链表中删除节点实际上就是修改下一节点及其相邻节点的 prev 和 next 指针指向,并不会释放节点的内存
其中的 list_del_init 删除节点之后还会对该节点重新进行初始化操作

static inline void list_del(struct list_head *entry)
static inline void list_del_init(struct list_head *entry)
从链表中替换节点

和删除节点同理,替换节点也只是修改了 prev 和 next 的指针指向,并且 list_replace_init 还会对替换出来的节点
进行重新的初始化操作

static inline void list_replace(struct list_head *old, struct list_head *new)
static inline void list_replace_init(struct list_head *old, struct list_head *new)
移动链表中的节点

下面的函数中,list 表示要移动的链表,list_move 表示将其移动到链表首部,
list_move_tail 将其移动到链表尾部

static inline void list_move(struct list_head *list, struct list_head *head)
static inline void list_move_tail(struct list_head *list,struct list_head *head)
检查链表

Linux 内核还提供了检查链表的相关函数,例如:

  • list_is_last: 检查节点是否是链表最后一个节点
  • list_empty: 链表是否为空
  • list_is_singular: 链表是否只有一个节点
static inline int list_is_last(const struct list_head *list,const struct list_head *head)
static inline int list_empty(const struct list_head *head)
static inline int list_is_singular(const struct list_head *head)
链表遍历

Linux 提供了一系列函数来遍历和操作链表中的元素:

  • list_entry(ptr, type, member): 通过链表节点的地址获取包含该节点的结构体指针
  • list_for_each(pos, head) : 遍历链表的每个节点
  • list_for_each_entry(pos, head, member) : 遍历链表中的每个结构体实例
  • list_for_each_entry_safe(pos, n, head, member) : 安全的遍历链表中每个结构体实例,可以在遍历过程中安全删除节点
  • list_for_each_prev(pos, head): 逆向遍历链表中的每个节点
  • list_for_each_entry_reverse(pos, head, member): 逆向遍历链表中每个结构体实例
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)#define list_for_each(pos, head) \for (pos = (head)->next; pos != (head); pos = pos->next)#define list_for_each_entry(pos, head, member)				\for (pos = list_first_entry(head, typeof(*pos), member);	\&pos->member != (head);					\pos = list_next_entry(pos, member))#define list_for_each_entry_safe(pos, n, head, member)			\for (pos = list_first_entry(head, typeof(*pos), member),	\n = list_next_entry(pos, member);			\&pos->member != (head); 					\pos = n, n = list_next_entry(n, member))#define list_for_each_prev(pos, head) \for (pos = (head)->prev; pos != (head); pos = pos->prev)#define list_for_each_entry_reverse(pos, head, member)			\for (pos = list_last_entry(head, typeof(*pos), member);		\&pos->member != (head); 					\pos = list_prev_entry(pos, member))

linux linked_list

demo 实例
//static LIST_HEAD(demo_list);
static struct list_head demo_list;static struct demo_linklistnode {char buffer[LINKEDLIST_BUFFER_SIZE];struct list_head list;
};static ssize_t mdrv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {size_t to_read = min(size, BUFFER_SIZE - (size_t)*offset);if(to_read == 0) {return 0;}printk(KERN_DEBUG "%s: to_read size:%zu .\n", __func__, to_read);if(copy_to_user(buf, device_buffer + *offset, to_read)) {return -EFAULT;}struct demo_linklistnode* tempnode, *nextnode;int i = 0;list_for_each_entry_safe(tempnode, nextnode, &demo_list, list) {printk(KERN_DEBUG "Node[%d] buffer:%s", i, tempnode->buffer);if(list_is_first(&tempnode->list, &demo_list)) {printk(KERN_DEBUG "==> fist node");} else if(list_is_last(&tempnode->list, &demo_list)) {printk(KERN_DEBUG "==> last node");}printk(KERN_DEBUG "\n");i++;}*offset += to_read;return to_read;
}static ssize_t mdrv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {size_t to_write = min(size, BUFFER_SIZE - (size_t)*offset);if(to_write == 0) {return -ENOMEM;}printk(KERN_DEBUG "%s: to_write size %zu .\n", __func__, to_write);if(copy_from_user(device_buffer + *offset, buf, to_write)) {return -EFAULT;}struct demo_linklistnode *node = NULL;node = kmalloc(sizeof(struct demo_linklistnode), GFP_KERNEL);if(!node) {printk(KERN_DEBUG "fail to allocate demo_linklistnode buffer\n");return -ENOMEM;}memset(node->buffer, 0, LINKEDLIST_BUFFER_SIZE);memcpy(node->buffer, device_buffer + *offset, to_write);INIT_LIST_HEAD(&node->list);//list_add(&node->list, &demo_list);list_add_tail(&node->list, &demo_list);*offset += to_write;return to_write;
}

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

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

相关文章

一分钟部署nginx-公网IP访问内网

前言 服务器内网下有nacos cluster(3个节点),开放到公网并指定公司网络访问需要配置三次IP白名单,因此需要简化流程,通过nginx反向代理只配置1次IP白名单。 现在通过docker容器模拟环境,准备1台云服务器。…

C 语言分支与循环

目录 一. 分支结构:if 语句与 switch 语句 1. if 语句 2. switch 语句 二、关系操作符、条件操作符与逻辑操作符 1. 关系操作符 2. 条件操作符 3. 逻辑操作符 三、循环结构:while 循环、for 循环与 do - while 循环 1. while 循环 2. for 循…

【一文看懂Spring Boot2.x升级Spring Boot3.x】springboot2.x升级springboot3.x

springboot2.x升级springboot3.x 背景升级jdk版本为17以上springboot版本修改javax包更新mybatis-plus升级swagger升级springdocspringdoc配置背景 当前项目是springboot2.5.9版本的springboot+mybatis-plus项目,需要升级到springboot3.5.0项目。 升级jdk版本为17以上 Spri…

阳台光伏防逆流电表革新者:安科瑞ADL200N-CT/D16-WF

——为家庭能源管理提供高精度、智能化解决方案 一、阳台光伏爆发的背景 在全球能源转型与碳中和目标的驱动下,阳台光伏正以革命性姿态重塑家庭能源消费模式。从欧洲的“微型发电站”到中国的“万亿蓝海”,这一创新技术不仅撬动了能源市场的结构性变革…

美团完整面经

面试岗位 面试的岗位 - 2025春季校招 【转正实习】软件服务工程师-后端方向(成都 - 软硬件服务-SaaS事业部) 一面(业务初试 - 30min) 问题 自我介绍 Java基础 HashMap底层用的数据结构是什么?是线程安全的吗&…

pysnmp 操作流程和模块交互关系的可视化总结

1. SNMP GET 操作序列图 #mermaid-svg-KALvv8WkHJTsNCeu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KALvv8WkHJTsNCeu .error-icon{fill:#552222;}#mermaid-svg-KALvv8WkHJTsNCeu .error-text{fill:#552222;str…

关于 /proc/net/tcp 与 /proc/$pid/net/tcp 的关系分析

关于 /proc/net/tcp 与 /proc/$pid/net/tcp 的关系分析 1. 基础概念 在 Linux 系统中,每个进程必定归属于一个且仅一个网络命名空间(Network Namespace)。这是 Linux 命名空间隔离机制的核心特性之一。 /proc/net/tcp 显示当前网络命名空间…

微信小程序 - 保存手机号等信息到通讯录

主要使用小程序 wx.addPhoneContact 这个api 一、界面 <view class"tab-item" bindtap"addToPhoneContacts">保存</view> 二、js 逻辑文件中 addToPhoneContacts() {wx.addPhoneContact({firstName: this.data.firstName, // 姓名mobilePh…

计算机视觉一些定义解析

1.GCT&#xff08;Gated Channel Transformation&#xff09; 定义 GCT&#xff08;Gated Channel Transformation&#xff09;是一种用于增强卷积神经网络特征提取能力的模块。它的核心思想是通过门控机制对特征图的通道进行动态调整&#xff0c;从而突出对任务更有帮助的特…

美团NoCode的Database 使用指南

系列文章目录 第一篇&#xff1a;美团NoCode设计网站的尝试经验分 第二篇&#xff1a;美团NoCode中的Dev Mode 使用指南 文章目录 系列文章目录Database 适用场景一、什么是 Database&#xff1f;二、准备流程1. 申请账号 三、使用流程1.申请资源的同时可搭建 NoCode 页面&…

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…

1.11 HTTP 文件上传的核心协议

HTTP 文件上传是 Web 开发中的常见需求&#xff0c;涉及到特殊的请求格式和处理机制。 一、HTTP 文件上传的核心协议 1. 两种主要方式 multipart/form-data&#xff08;主流&#xff09; 支持二进制文件和表单字段混合传输&#xff0c;由 Content-Type 头部标识。applicatio…

安装 Poppler(Windows)

下载 Poppler&#xff08;Windows&#xff09;&#xff1a;https://github.com/oschwartz10612/poppler-windows/releases/ 解压在自己目录下 配置系统环境变量&#xff1a;把 poppler-xx.x.x\bin 目录加入你的环境变量 PATH 中。 检查是否配置成功 pdfinfo

Java学习笔记之:初识nginx

Java学习笔记之&#xff1a;初识nginx PS&#xff1a;虽然总结的都很简单&#xff0c;但是作为初学者并且本人记忆力较差所以每次学习新知识点后习惯性记录下来&#xff0c;这样加深一遍记忆并且便于日后复习。 介绍&#xff1a; Nginx是一款轻量级的Web服务器/反向代理服务器…

Middleware

中间件的定义&#xff1a;中间件是位于操作系统和应用程序之间的软件层&#xff0c;用于解决分布式系统中通信、数据共享、资源管理等共性问题。消息队列属于通信中间件&#xff0c;用于在分布式系统中传递消息&#xff0c;实现应用解耦、异步通信和流量削峰。解耦系统&#xf…

Mac如何配置ZSH并使用Oh-my-zsh?让你的终端更加实用、美观

前言 现在&#xff0c;越来越多的人趋向使用ZSH取代(Linux)原本的Bash作为自己的终端Shell。的确&#xff0c;ZSH才是适用于现代的Shell&#xff1a; 更丰富的命令提示更鲜明的演示标记更强大的插件支持 什么是ZSH 回答什么是ZSH前&#xff0c;我们先解释什么是Bash&#x…

C++11新标准

重点 auto 类型推导范围 for 迭代初始化列表变参模板 新类型 C11新增了类型 long long 和 unsigned long long&#xff0c;以支持64位(或更宽)的整型;新增了类型 char16_t和 char32_t&#xff0c;以支持 16位和 32 位的字符表示;还新增了“原始”字符串。 常量 nullptr nu…

SpringAI Prompt提示词

基本概念 Prompts提示词 ❝ 提示词的是引导AI模型输出的输入&#xff0c;提示词的正确性直接影响模型输出的。 Message消息 Message 接口封装了 Prompt 文本内容、一组元数据属性以及称为 MessageType 的分类。Spring AI消息API&#xff1a; 其中最重要的就是角色&#xff1a; …

力扣刷题——二分查找

数组是存放在连续内存空间上的相同类型数据的集合数组下标都是从0开始的数组内存空间的地址是连续的正是因为数组在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;就难免要移动其他元素的地址。 使用二分查找法返回的元素下标可能不是唯一的…

黑群晖NAS部署DeepSeek模型与内网穿透实现本地AI服务

文章目录 前言1.安装Container Manager2. 启动ssh功能3. ssh连接黑群晖4. 安装Ollama5. 安装deepseek模型6. 安装open-webui图形界面7. 安装内网穿透7.1 下载cpolar套件7.2 配置群辉虚拟机7.3 配置公网地址小结 7.4 配置固定公网地址 总结 前言 在追求自建网络存储方案的极客群…