Linux内核链表使用方法

简介:

        链表是linux内核中最简单,同时也是应用最广泛的数据结构。内核中定义的是双向链表。

        linux的链表不是将用户数据保存在链表节点中,而是将链表节点保存在用户数据中。linux的链表节点只有2个指针(pre和next),这样的话,链表的节点将独立于用户数据之外,便于实现链表的共同操作。

一、链表函数介绍

Linux内核链表源码路径: include/linux/list.h

常用函数、宏介绍:

函数作用备注
初始化LIST_HEAD_INITINTI_LIST_HEAD初始化链表头常用
LIST_HEAD
添加list_add头部添加常用
list_add_tail尾部添加
删除list_del删除节点,指向特定的位置常用
list_del_init删除节点后,反初始化
遍历list_entry根据list倒推宿主结构体的首地址常用
list_for_each正向遍历获取list
list_for_each_entry正向遍历,获取list数组结构
list_for_each_prev反向遍历获取list
list_for_each_entry_reverse反向遍历,获取list数组结构
搬移list_move将链表的某个节点插入到新的链表上
list_move_tail
合并list_splice将2条链表合并
list_splice_init合并后原有的list反初始化
list_splice_tail
list_splice_tail_init
替换list_replace将新节点和链表上某位置的节点替换
list_replace_init将新节点和链表上某位置的节点替换,替换后将旧节点反初始化

定义链表结构体:

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

1、初始化

1.1 创建链表头 并用 INIT_LIST_HEAD 初始化

struct list_head listHead;static inline void INIT_LIST_HEAD(struct list_head *list)
{list->next = list;list->prev = list;
}

1.2 也可以直接用 LIST_HEAD 创建并初始化链表头

/* 初始化 */
#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD(name) \struct list_head name = LIST_HEAD_INIT(name)

2、添加

  • list_add

list_add(struct list_head *new, struct list_head *head)

功能:将new添加到链表头head的下一个位置

参数:

        new:添加的链表节点

        head:链表头

  • list_add_tail

list_add_tail(struct list_head *new, struct list_head *head)

功能:将new添加到链表头head的尾部

参数:

        new:添加的链表节点

        head:链表头

函数原型:

/* 添加 */
static inline void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{next->prev = new;new->next = next;new->prev = prev;prev->next = new;
}static inline void list_add(struct list_head *new, struct list_head *head)
{__list_add(new, head, head->next);
}static inline void list_add_tail(struct list_head *new, struct list_head *head)
{__list_add(new, head->prev, head);
}

3、删除

  • list_del

list_del(struct list_head *entry)

功能:删除某节点

参数:

        entry:entry所在的链表头中将entry节点删除

  • list_del_init

函数原型:

/* 删除 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{next->prev = prev;prev->next = next;
}static inline void __list_del_entry(struct list_head *entry)
{__list_del(entry->prev, entry->next);
}static inline void list_del(struct list_head *entry)
{__list_del(entry->prev, entry->next);entry->next = NULL;entry->prev = NULL;
}static inline void list_del_init(struct list_head *entry)
{__list_del_entry(entry);INIT_LIST_HEAD(entry);
}

4、遍历

4.1 container_of 解析

list.h文件中,最复杂的就是获取用户数据的宏定义list_entry,其功能是根据结构体中已知的list链表成员变量的地址,来倒推宿主结构体的首地址。

#define list_entry(ptr, type, member) \container_of(ptr, type, member)

调用container_of这个宏

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

分析一下container_of宏

// 步骤1:将数字0强制转型为type*,然后取得其中的member元素
((type *)0)->member  // 相当于((struct student *)0)->list// 步骤2:定义一个临时变量__mptr,并将其也指向ptr所指向的链表节点
const typeof(((type *)0)->member)*__mptr = (ptr);// 步骤3:计算member字段距离type中第一个字段的距离,也就是type地址和member地址之间的差
// offset(type, member)也是一个宏,定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)// 步骤4:将__mptr的地址 - type地址和member地址之间的差
// 其实也就是获取type的地址

4.2 遍历宏原型

/* 遍历 */
#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_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = 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_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))

list_for_eachlist_for_each_safe 差异:

        list_for_each_safe 可防止删除链表条目。如:list_for_each执行的for循环中,如果删除条目会导致段错误"Segmentation fault (core dumped)"报错。而 list_for_each_safe就可以解决此问题。

5、判断链表为空

  • list_empty
static inline int list_empty(const struct list_head *head)
{return head->next == head;
}

二、使用方法

链表数据结构在内核态和用户态都能使用,使用方法如下:

1、定义 struct list_head 链表头 head

2、初始化 LIST_HEAD(head)

3、添加entry到链表 list_add_tail(&entry, &head)

4、遍历链表头

struct list_head *cursor, *next;list_for_each_safe(cursor, next, &tx_req_list) {stpHead_Addr = list_entry(cursor, struct ipcl_req, list);        //根据我们结构体中的已知的成员变量的地址,来寻求该结构体的首地址...        //我们自己定义功能list_del_init(cursor);                //链表删除cursor,并初始化 cursor}

示例:

代码下载路径:https://download.csdn.net/download/hinewcc/89522091

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"struct list_head listHead;      //定义链表头//LIST_HEAD(listHead);        /* 含链表的结构体 */
struct list_member {char name[32];struct list_head entry;
};#define MEMBER_NUM  5int main(int argc, char **argv)
{int i;if (argc != 2) {printf("usage: ./app name");return -1;}printf("search name: %s\n", argv[1]);/* 1.初始化listHead链表 */INIT_LIST_HEAD(&listHead);                                              struct list_member stMember[MEMBER_NUM] = {0};struct list_head *cursor, *next;/* 2.listHead链表添加 */for (i = 0; i < MEMBER_NUM; i++) {printf("addr[%d]: %p\n", i, &stMember[i]);sprintf(stMember[i].name, "name%d", i);list_add_tail(&stMember[i].entry, &listHead);          //listHead链表添加成员}/* 3.listHead链表轮询并比较 */list_for_each_safe(cursor, next, &listHead) {              //轮询listHead链表头/*  功能:根据结构体中的已知的 entry 成员变量的地址,来寻求该结构体的首地址参数1: entry成员指针参数2: 结构体类型参数3: 结构体中entry的成员名*/struct list_member *member = list_entry(cursor, struct list_member, entry);if (strcmp(member->name, argv[1]) == 0) {           //比较printf("search OK: addr: %p\n", member);break;}}/* 4.测试 list_del 删除, list_empty 检测链表空 */list_for_each_safe(cursor, next, &listHead) {struct list_member *member = list_entry(cursor, struct list_member, entry);printf("del %s\n", member->name);list_del(cursor);if (list_empty(&listHead)) {printf("list empty!!!\n");}}return -1;
}

编译:$ gcc -o test_app -I ./ main.c

运行:

$ ./test_app name1

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

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

相关文章

【Linux】记录一起网站劫持事件

故事很短&#xff0c;处理也简单。权当记录一下&#xff0c;各位安全大大们手下留情。 最近一位客户遇到官网被劫持的情况&#xff0c;想我们帮忙解决一下&#xff08;本来不关我们的事&#xff0c;毕竟情面在这…还是无偿地协助一下&#xff09;&#xff0c;经过三四轮“谦让…

多线程(进阶)

前言&#x1f440;~ 上一章我们介绍了线程池的一些基本概念&#xff0c;今天接着分享多线程的相关知识&#xff0c;这些属于是面试比较常见的&#xff0c;大部分都是文本内容 常见的锁策略 乐观锁 悲观锁 轻量锁 重量级锁 自旋锁 挂起等待锁 可重入锁和不可重入锁 互斥…

Python结合MobileNetV2:图像识别分类系统实战

一、目录 算法模型介绍模型使用训练模型评估项目扩展 二、算法模型介绍 图像识别是计算机视觉领域的重要研究方向&#xff0c;它在人脸识别、物体检测、图像分类等领域有着广泛的应用。随着移动设备的普及和计算资源的限制&#xff0c;设计高效的图像识别算法变得尤为重要。…

MPI hello world SSH 免密互联

目标&#xff1a; 我们想实现2台主机免密互联&#xff0c;将MPI Hello World跑起来 假设hostname是node01,node02,&#xff08;Linux shell窗口一般是UserNameHostName&#xff0c;node1和node2一定要和HostName一样&#xff09; hostname是/etc/hosts中的配置&#xff0c;如下…

并发编程-05AQS原理

并发编程-深入理解AQS之ReentrantLock 一 认识AQS 在讲解AQS原理以及相关同步器之前&#xff0c;我们需要对AQS有一些基本的认识&#xff0c;了解下它有什么样的机制&#xff0c;这样追踪源码的时候就不会太过于迷茫&#xff01; 1.1 什么是AQS java.util.concurrent包中的大…

JavaWeb—js(2)

函数 /* 创建一个方法形参不需要写参数类型&#xff0c;多个参数直接用逗号隔开就可以 */function show(a,b,c){console.log(我是show方法,a,b,c)return ab;}// 调用方法//调用方法时 形参个数如果和实参个数对不上不会有异常show();show(你好)show(1,2);let add show(1,2,3…

君方智能设计平台-事务管理技术方案

1.背景介绍 事务处理是指对数据进行一组操作&#xff0c;这些操作要么全部成功&#xff0c;要么全部失败&#xff0c;以确保数据的一致性和完整性。软件的事务管理主要实现方案主要涉及以下几个方面&#xff1a; &#xff08;1&#xff09;数据一致性&#xff1a;在CAD软件中…

JCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断

JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断 目录 JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断分类效果格拉姆矩阵图GAF-PCNN-MATTGASF-CNNGADF-CNN 基本介绍程序设计参考资料 分…

npm 淘宝镜像证书过期,错误信息 Could not retrieve https://npm.taobao.org/mirrors/node/latest

更换 npm 证书 问题描述报错原因更换步骤1 找到 nvm 安装目录2 发现证书过期3 更换新地址4 保存后&#xff0c;重新安装成功 问题描述 在使用 nvm 安装新版本时&#xff0c;未成功&#xff0c;出现报错&#xff1a; Could not retrieve https://npm.taobao.org/mirrors/node/l…

Python酷库之旅-第三方库Pandas(009)

目录 一、用法精讲 19、pandas.read_xml函数 19-1、语法 19-2、参数 19-3、功能 19-4、返回值 19-5、说明 19-6、用法 19-6-1、数据准备 19-6-2、代码示例 19-6-3、结果输出 20、pandas.DataFrame.to_xml函数 20-1、语法 20-2、参数 20-3、功能 20-4、返回值 …

AI Earth ——开发者模式案例10:基于 CNN 的 AI 分类模型开发

基于 CNN 的 AI 分类模型开发 本案例主要介绍如何快速利用 AIE Python SDK 创建机器学习建模流程。我们主要使用到 Python SDK的Machine Learning Proxy 模块(下文简称 AieMlProxy )。该模块涵盖了一系列用户与训练集群之间的交互接口,包括:鉴权、数据加载、训练任务提交、…

Shell Expect自动化交互(示例)

Shell Expect自动化交互 日常linux运维时&#xff0c;经常需要远程登录到服务器&#xff0c;登录过程中需要交互的过程&#xff0c;可能需要输入yes/no等信息&#xff0c;所以就用到expect来实现交互。 关键语法 ❶&#xff3b;#!/usr/bin/expect&#xff3d; 这一行告诉操…

MySQL之备份与恢复和MySQL用户工具(一)

备份与恢复 备份脚本化 为备份写一些脚本是标准做法。展示一个示例程序&#xff0c;其中必定有很多辅助内容&#xff0c;这只会增加篇幅&#xff0c;在这里我们更愿意列举一些典型的备份脚本功能&#xff0c;展示一些Perl脚本的代码片段。你可以把这些当作可重用的代码块&…

io流 多线程

目录 一、io流 1.什么是io流 2.流的方向 i.输入流 ii.输出流 3.操作文件的类型 i.字节流 1.拷贝 ii.字符流 ​3.字符流输出流出数据 4.字节流和字符流的使用场景 5.练习 6.缓冲流 1.字节缓冲流拷贝文件 2.字符缓冲流特有的方法 1.方法 2.总结 7.转换流基本用法…

数字信号处理及MATLAB仿真(3)——量化的其他概念

上回书说到AD转换的两个步骤——量化与采样两个步骤。现在更加深入的去了解以下对应的概念。学无止境&#xff0c;要不断地努力才有好的收获。万丈高楼平地起&#xff0c;唯有打好基础&#xff0c;才能踏实前行。 不说了&#xff0c;今天咱们继续说说这两个步骤&#xff0c;首先…

【国产开源可视化引擎Meta2d.js】网格

画布背景网格 在线体验&#xff1a; 乐吾乐2D可视化 示例&#xff1a; // 设置默认缺省网格属性 meta2d.store.options.grid true; // 开启 meta2d.store.options.gridColor eeeeee; // 网格线条颜色 meta2d.store.options.gridSize 10; // 格子大小// 设置单个图纸的网格…

pnpm的坑

请问pnpm的两个坑怎么解决&#xff1a; 第一个坑&#xff1a;没有节省磁盘空间 我已经配置了依赖的存储位置&#xff0c; 但我在项目里pnpm install以后&#xff0c;发现依赖包还是很大&#xff0c; 然后发现里面的链接并不是指向先前配置的依赖存储位置&#xff0c;而是指…

java核心-泛型

目录 概述什么是泛型分类泛型类泛型接口泛型方法 泛型通配符分类 泛型类型擦除分类无限制类型擦除有限制类型擦除 问题需求第一种第二种 概述 了解泛型有利于学习 jdk 、中间件的源码&#xff0c;提升代码抽象能力&#xff0c;封装通用性更强的组件。 什么是泛型 在定义类、接…

VSCode设置好看清晰的字体!中文用鸿蒙,英文用Jetbrains Mono

一、中文字体——HarmonyOS Sans SC 1、下载字体 官网地址&#xff1a;https://developer.huawei.com/consumer/cn/design/resource/ 直接下载&#xff1a;https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20230517…

加装德国进口高精度主轴 智能手机壳「高质量高效率」钻孔铣槽

在当前高度智能化的社会背景下&#xff0c;智能手机早已成为人们生活、工作的必备品&#xff0c;智能手机壳作市场需求量巨大。智能手机壳的加工过程涉及多个环节&#xff0c;包括钻孔和铣槽等。钻孔要求精度高、孔位准确&#xff0c;而铣槽则需要保证槽位规整、深度适宜。这些…