【Linux内核解析-linux-5.14.10-内核源码注释】内核常用链表宏解释

news2025/5/25 2:01:17

1、list_for_each_entry_safe

链表
这段代码是一个宏定义,用于遍历一个链表中所有的元素,并且在遍历过程中可以安全地删除元素。具体来说,这个宏定义的功能是:

  1. 遍历链表中所有的元素,从头节点开始,直到尾节点结束。

  2. 对于每个元素,使用给定的结构体成员变量名找到它所属的结构体对象,并且将该对象的指针赋值给给定的变量名。

  3. 在遍历过程中,可以安全地删除当前元素,因为它在删除前会先保存下一个元素的指针,保证不会影响遍历的正确性。

下面是这个宏定义的详细解释:

  1. 参数解释:
  • pos:用于保存当前遍历到的元素的指针。
  • tmp:用于保存下一个元素的指针,以便在删除当前元素后继续遍历。
  • head:链表的头节点。
  • member:链表节点所在的结构体成员变量名。
  1. 宏定义解释:

首先,使用 __container_of 宏定义将头节点的下一个节点转换为链表节点的指针,再使用 __container_of 宏定义将该节点的成员变量 member 转换为其所属的结构体对象,并且将该对象的指针赋值给 pos 变量。

接着,使用 __container_of 宏定义将 pos 的成员变量 member.next 转换为下一个节点的指针,并且将该节点的指针赋值给 tmp 变量。

然后,使用 & 操作符判断当前节点是否为链表的尾节点。如果不是尾节点,就执行循环体中的代码,否则跳出循环。

在循环体中,首先将 tmp 的成员变量 member.next 转换为下一个节点的指针,并且将该节点的指针赋值给 tmp 变量,以便在删除当前节点后继续遍历。

然后,执行用户自定义的代码,该代码可以访问当前节点所属的结构体对象,并且可以安全地删除当前节点。

举例说明:

假设有一个链表,链表节点的结构体定义如下:

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

其中,list 是链表节点的成员变量名。如果要遍历该链表并且可以安全地删除节点,可以使用 list_for_each_entry_safe 宏定义,代码如下:

struct list_head head;
struct my_node *pos, *tmp;

// 遍历链表中的所有元素,并且可以安全地删除元素
list_for_each_entry_safe(pos, tmp, &head, list) {
// 访问当前节点所属的结构体对象
printf(“data = %d\n”, pos->data);

// 安全地删除当前节点
list_del(&pos->list);
free(pos);

}

在上面的代码中,list_del 函数用于删除当前节点,并且在删除前会先保存下一个节点的指针,以保证遍历的正确性。

2、container_of

在这里插入图片描述
这是一个非常常见的宏定义,用于从某个结构体成员变量的指针中获取整个结构体的指针。这个宏在内核编程中非常常用,因为内核中经常需要通过成员变量的指针来访问整个结构体的其他成员变量。

宏定义的格式如下:

#define container_of(ptr, type, member) \
    (type *)((char *)(ptr) - (char *) &((type *)0)->member)

宏定义了三个参数:

  • ptr:指向某个结构体成员变量的指针;
  • type:结构体的类型;
  • member:结构体中的成员变量名。

宏的实现原理是利用了C语言中的指针算术运算和结构体的内存布局。在结构体中,每个成员变量的地址相对于结构体的起始地址都是固定的。因此,我们可以通过计算某个成员变量相对于结构体起始地址的偏移量,来计算出整个结构体的地址。

宏定义的实现步骤如下:

  • 将0强制转换为type类型的指针;
  • 取出member成员变量的地址;
  • 用指向该成员变量的指针减去该员变量在结构体中的偏移量,得到整个结构体的起始地址;
  • 将该地址强制转换为type类型的指针,并返回。

例如,假设有一个结构体struct my_struct,其中包含一个成员变量int my_field。如果我们有一个指向my_field的指针int *ptr,我们可以使用container_of宏来计算整个结构体的地址:

struct my_struct *s = container_of(ptr, struct my_struct, my_field);

这样,指针s就指向了包含my_field成员变量的整个结构体。从而我们可以通过s来访问整个结构体的其他成员变量。

举例:

为了更好地理解这个宏的实现原理,我们可以通过一个例子来说明。假设有如下的结构体定义:

struct my_struct {
    int a;
    int b;
    int c;
};

现在我们定义一个指向b成员变量的指针int *ptr,我们想要通过ptr来获取整个结构体的指针。我们可以使用container_of宏来计算整个结构体的地址:
假设我们定义了一个指向b成员变量的指针int *ptr,我们想要通过ptr来获取整个结构体的指针。我们可以使用container_of宏来计算整个结构体的地址:

struct my_struct *s = container_of(ptr, struct my_struct, b);

现在我们来逐步分析一下container_of宏的实现过程:

  1. 首先,我们将0强制转换为struct my_struct *类型的指针,得到一个指向结构体的空指针。

    (struct my_struct *)0
    
  2. 然后,我们取出b成员变量的地址,这可以通过&((struct my_struct *)0)->b来实现。由于我们已经将0强制转换为struct my_struct *类型的指针,因此这个表达式实际上是取出了结构体中b成员变量的地址相对于结构体起始地址的偏移量。

    &((struct my_struct *)0)->b
    
  3. 接下来,我们将指向b成员变量的指针ptr强制转换为char *类型的指针,然后减去上一步中计算出的偏移量。由于指针减法的结果是以指针类型的大小为单位的,因此我们需要将结果强制转换为struct my_struct *类型的指针。

    (struct my_struct *)((char *)ptr - (char *)&((struct my_struct *)0)->b)
    

    这个表达式相当于将ptr指向的地址向前偏移了b成员变量相对于结构体起始地址的偏移量,从而得到整个结构体的起始地址。

  4. 最后,我们将上一步中计算出的地址强制转换为struct my_struct *类型的指针,并返回该指针。

    (struct my_struct *)((char *)ptr - (char *)&((struct my_struct *)0)->b)
    

    这样,我们就得到了一个指向整个结构体的指针,从而可以通过该指针来访问结构体的其他成员变量。

3、list_for_each_entry_rcu

在这里插入图片描述
这是一个宏定义,用于在读取RCU(Read-Copy-Update)保护的链表中遍历每个元素。

具体来说,这个宏定义了一个循环,其中:

  • pos 是当前元素的指针;
  • head 是链表头的指针;
  • member 是链表节点结构体中表示下一个元素的成员变量的名称;
  • cond... 是可选的条件语句,用于在遍历时进行过滤。

宏定义的实现中,首先调用了一个名为 __list_check_rcu 的函数,用于检查 cond 中是否有语法错误。这个函数的具体实现可以参考 Linux 内核源码中的 include/linux/list.h 文件。

然后,在循环中,首先将 pos 初始化为链表头的下一个元素,即第一个要遍历的元素。这里使用了 list_entry_rcu 宏,将链表节点结构体的指针转换为包含该节点的数据结构的指针,以便于访问该元素的数据。

接着,在循环条件中,检查当前元素的下一个元素是否为链表头,如果是,则说明已经遍历完了整个链表,循环结束。

最后,在循环体中,使用 list_entry_rcu 宏获取当前元素的下一个元素,并将其赋值给 pos,继续循环。这里同样使用了包含节点的数据结构的指针,以便于访问下一个元素的数据。

总之,这个宏定义非常方便地实现了在读取RCU保护的链表中遍历每个元素的功能,而且还支持条件过滤,非常实用。

4、list_last_entry

在这里插入图片描述
这是一个宏定义,用于获取链表中最后一个元素的数据结构指针。

具体来说,这个宏定义了三个参数:

  • ptr 是链表的指针,表示要获取最后一个元素的链表;
  • type 是数据结构的类型,表示链表节点所包含的数据结构的类型;
  • member 是链表节点结构体中表示下一个元素的成员变量的名称。

宏定义的实现中,使用了 list_entry 宏,将链表最后一个节点的指针转换为包含该节点的数据结构的指针。具体来说,宏定义使用了链表最后一个节点的前一个节点的指针 ptr->prev,然后将其转换为包含该节点的数据结构的指针,即 list_entry((ptr)->prev, type, member)

这个宏定义非常方便地实现了获取链表中最后一个元素的数据结构指针的功能,可以在需要遍历链表时,从最后一个元素开始往前遍历。需要注意的是,这个宏定义假设链表不为空,否则在空链表上调用它将会导致错误。

5、WARN相关

在这里插入图片描述这些都是 Linux 内核中的宏定义,在处理错误和警告时使用。

  1. #define WARN_ON_ONCE(condition) WARN_ON(condition)

这个宏定义表示:如果条件 condition 为真,则打印警告信息。与 WARN_ON() 不同的是,这个宏只会打印一次警告信息,即便 condition 多次为真也只会打印一次。

  1. #define WARN_ONCE(condition, format…) WARN(condition, format)

这个宏定义表示:如果条件 condition 为真,则打印警告信息。与 WARN() 不同的是,这个宏只会打印一次警告信息,即便 condition 多次为真也只会打印一次。同时,这个宏还可以传入格式化字符串 format,用于打印更详细的警告信息。

  1. #define WARN_TAINT(condition, taint, format…) WARN(condition, format)

这个宏定义表示:如果条件 condition 为真,则打印警告信息,并将污点标记 taint 与之关联。污点标记是一种标记机制,用于标记内核中的某些数据或操作是不可信的或者有风险的。通过将污点标记与警告信息关联,可以更加精确地追踪和排查问题。

  1. #define WARN_TAINT_ONCE(condition, taint, format…) WARN(condition, format)

这个宏定义表示:如果条件 condition 为真,则打印警告信息,并将污点标记 taint 与之关联。与 WARN_TAINT() 不同的是,这个宏只会打印一次警告信息,即便 condition 多次为真也只会打印一次。同时,这个宏还可以传入格式化字符串 format,用于打印更详细的警告信息。

6、offset_in_page

在这里插入图片描述
这是一个宏定义,定义的作用是计算指针 p 所指向的地址在所在页面中的偏移量。

具体来说,PAGE_SIZE 是一个常量,表示页面的大小,通常为 4KB。而 offset_in_page(p) 宏定义中的 (unsigned long)p 表示将指针 p 转换为一个无符号长整型数,然后对 PAGE_SIZE 取模运算,得到的结果就是指针 p 所指向的地址在所在页面中的偏移量。

例如,假设 PAGE_SIZE 为 4KB,指针 p 指向地址 0x12345678,那么 offset_in_page(p) 的值就是 0x678,即指针 p 所指向的地址在所在页面中的偏移量为 0x678。这个宏定义通常用于内核中对页面进行操作时,需要计算出页面中某个地址的偏移量,以便进行相应的处理。

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

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

相关文章

读SQL进阶教程笔记15_SQL编程思维

1. 还原论 1.1. 认为可以把高级现象还原为低级基本现象的学说 1.2. 将复杂的东西看成是由简单单元组合而成的 1.2.1. 以赋值、条件分支、循环等作为基本处理单元,并将系统整体分割成很多这样的单元的思维方式 1.2.2. 文件系统也是将大量的数据分割成记录这样的小…

DOM事件(中)

常见的事件分类(了解) ●我们在写页面的时候经常用到的一些事件 ●大致分为几类,浏览器事件 / 鼠标事件 / 键盘事件 / 表单事件 / 触摸事件 ●不需要都记住,但是大概要知道 鼠标事件 ●click :点击事件 ●dblclick &a…

Python小姿势 - # 如何使用Python爬取网页数据

如何使用Python爬取网页数据 今天我们来学习一下如何使用Python来爬取网页数据。 首先,我们需要准备一个空白的文件,在文件中输入以下代码: import requests url http://www.baidu.com r requests.get(url) print(r.text) 上面的代码中&…

宝宝腹泻怎么办?儿科医生分享小儿腹泻的辩证和处理方法

小儿腹泻病是婴幼儿时期的常见病。面对腹泻,很多父母往往不知所措,甚至不知道该怎么处理,只能带宝宝去医院治疗。由于腹泻具有反复性,稍有护理不当,甚至会加重病情。那么,小儿腹泻药如何处理呢?…

微服务---Redis实用篇-黑马头条项目-登录功能(短信验证缓存,用户信息缓存)

黑马头条项目-登录功能(短信验证缓存,用户信息缓存) 1、短信登录 1.1、导入黑马点评项目 1.1.1 、导入SQL 1.1.2、有关当前模型 手机或者app端发起请求,请求我们的nginx服务器,nginx基于七层模型走的事HTTP协议,可以实现基于Lua直接绕开t…

Android之WindowManager介绍

WindowManager android中真正展示给用户的是window和view. activity在android中所其的作用主要是处理一些逻辑问题,比如生命周期的管理、建立窗口等。 在android中,窗口的管理还是比较重要的一块,因为他直接负责把内容展示给用户&#xff…

11.streamFile

1.Stream流 1.1体验Stream流【理解】 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合,存储多个字符串元素把集合中所有以"张"开头的元素存储到一个新的集合把"张"开头的集合中的长度为3的元素存储到一个新的集合遍历上一步得到的集…

05-微服务部署2023系列-centos+docker部署redis(单机版)

1、创建数据挂载目录 mkdir /root/docker/redisCluster/redis1/data -p 2、部署并启动命令 docker run -itd --privileged=true --name redisMaster -p 19000:6379 -v /root/docker/redisCluster/redis1/data:/data redis --appendonly yes --requirepass "myRedisPass123…

美颜sdk的开发流程及其在不同平台上的适用性比较

当下,在开发美颜功能时,美颜sdk成为了不可或缺的工具。近期,很多开发者向小编提问开发美颜的一些专业技术问题。本篇文章,小编将为大家统一解答一下近期的热门问题。 一、开发流程 1、确定美颜算法 美颜算法是美颜sdk的核心&a…

【Linux下】进程间通信

文章目录 进程间通信进程间通信的目的进程间通信的分类进程间通信的本质 管道初识管道匿名管道创建匿名管道理解协同机制和原子性写入 命名管道命名管道创建的俩种方式使用命名管道实现俩个不同进程之间通信 **管道实现进程间通信的本质**匿名管道vs命名管道 system Vsystem V共…

【Linux】进程概述和进程状态转换(查看进程、实时显示进程动态、杀死进程等)

目录 进程概述进程状态转换进程的状态进程相关命令 橙色 进程概述 进程是正在运行的程序的实例,是基本的分配单元也是基本的执行单元。 可以用一个程序来创建多个进程,进程是由内核定义的抽象实体,并为该实体分配用以执行程序的各项系统资源…

【Jmeter快速入门】

Jmeter快速入门 Jmeter快速入门1.安装Jmeter1.1.下载1.2.解压1.3.运行 2.快速入门2.1.设置中文语言2.2.基本用法 Jmeter快速入门 1.安装Jmeter Jmeter依赖于JDK,所以必须确保当前计算机上已经安装了JDK,并且配置了环境变量。 1.1.下载 可以Apache Jm…

git在vs可视化界面下变基操作

vs版本:vs2022 天天都在使用git,听说过变基这个名词,但是并没有实操过变基,正好今天有个同事说起一件事情:提交代码的时候有太多的自动合并,如果需要回退版本,操作起来很困难,理想状…

如何在项目中实现登录时的验证码校验功能?

如何在项目中实现登录时的验证码校验功能? 第一步:创建项目,添加依赖第二步:验证码配置(CaptchaConfig类)第三步:创建CaptchaController第四步:测试注意问题 这里介绍一款老牌的验证…

使用@Resource注解和@Inject注解

1.Resource和Inject概述 1.1.Resource注解 Resource注解是Java规范里面的,也可以说它是JSR250规范里面定义的一个注解。该注解默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时&…

KCC南京成立会议如期举办

1915年9月15日,陈独秀等人在上海创立了《新青年》杂志,掀起了中华新文化思潮。接着,在1919年5月4日,陈独秀、李大钊等人发起了五四运动,唤醒了沉睡多年的东方巨狮。从此,五四青年节便成为了青年人的节日。 …

上市后首份财报,紫燕食品去年净利下降超3成

近日,上海紫燕食品股份有限公司(下称“紫燕食品”,603517.SH)交出了上市后的首份年报。 去年9月26日,紫燕食品在上交所主板上市,成为继绝味食品(603517.SH)、周黑鸭(01458.HK&#…

Jetpack Compose 中的Deep Linking — Android

Jetpack Compose 中的Deep Linking — Android 在本文中,我们将学习如何在 Jetpack Compose 中轻松实现深度链接。 什么是深度链接? 深层链接允许用户直接从外部来源(例如网站或其他应用程序)导航到应用程序内的特定内容。 添…

联想笔记本系统更新中断后变成蓝屏怎么U盘重装系统?

联想笔记本系统更新中断后变成蓝屏怎么U盘重装系统?有用户使用的联想笔记本电脑在系统更新的过程中,因为自己进行了一些操作,导致系统更新中断了,重启系统之后变成了蓝屏的情况。那么这个时候怎么去通过U盘重装系统来恢复电脑使用…

2d游戏人物动作实现(C语言)

没有接触制作小游戏前,感觉做游戏很不可思议,游戏里的人物是怎么移动的,怎么攻击,释放技能。。。。。。现在逐渐了解到之后,发现2d游戏人物的动作更多是图片的拼接,动作是否精细,由这个动作的帧…