list_for_each_entry()函数分析

2023-05-16

在Linux内核源码中,经常要对链表进行操作,其中一个很重要的宏是list_for_each_entry:

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     prefetch(pos->member.next), &pos->member != (head);	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

这个宏本质上是一个for循环,用于遍历链表,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head,循环遍历每一个pos中的member子项。

在这里插入图片描述
比如:

int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
			      bool find_req_seq, struct udevice **devp)
{
	struct uclass *uc;
	struct udevice *dev;
	int ret;

	*devp = NULL;
	pr_debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq);
	if (seq_or_req_seq == -1)
		return -ENODEV;
	ret = uclass_get(id, &uc);
	if (ret) 
		return ret;

	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
		pr_debug("   - %d %d '%s'\n", dev->req_seq, dev->seq, dev->name);
		if ((find_req_seq ? dev->req_seq : dev->seq) ==
				seq_or_req_seq) {
			*devp = dev;
			pr_debug("   - found\n");
			return 0;
		}
	}
	pr_debug("   - not found\n");
	
	return -ENODEV;
}
struct udevice {
	const struct driver *driver;
	const char *name;
	void *platdata;
	void *parent_platdata;
	void *uclass_platdata;
	ofnode node;
	ulong driver_data;
	struct udevice *parent;
	void *priv;
	struct uclass *uclass;
	void *uclass_priv;
	void *parent_priv;
	struct list_head uclass_node;
	struct list_head child_head;
	struct list_head sibling_node;
	uint32_t flags;
	int req_seq;
	int seq;
#ifdef CONFIG_DEVRES
	struct list_head devres_head;
#endif
};

以上代码就是利用list_for_each_entry()来循环遍历寻找uclass_node成员。

对程序中for循环的三步分析:
我们将for循环分解为一下三点:

  1. for循环初始化 pos = list_entry((head)->next, typeof(*pos), member);

  2. for循环执行条件 &pos->member != (head);

  3. 每循环一次执行 pos = list_entry(pos->member.next, typeof(*pos), member))

typeof()是取变量的类型,这里是取指针pos所指向数据的类型。

1、pos = list_entry((head)->next, typeof(*pos), member)

pos相当于循环中返回的循环变量,这里就是返回一个结构体指针。实现过程如下:

函数list_entry():

/** 
 * list_entry - get the struct for this entry 
 * @ptr:    the &struct list_head pointer. 
 * @type:   the type of the struct this is embedded in. 
 * @member: the name of the list_struct within the struct. 
 */  
#define list_entry(ptr, type, member) \  
    container_of(ptr, type, member)  

container_of这个函数:这个不做重点分析,这个函数的做用是:就是根据一个结构体变量中的一个域成员变

量的地址来获取指向整个结构体变量的地址。

/** 
 * container_of - cast a member of a structure out to the containing structure 
 * @ptr:    the pointer to the member. 
 * @type:   the type of the container struct this is embedded in. 
 * @member: the name of the member within the struct. 
 * 
 */  
#define container_of(ptr, type, member) ({          \  
    const typeof(((type *)0)->member)*__mptr = (ptr);    \  
             (type *)((char *)__mptr - offsetof(type, member)); })  

讲讲container_of:作用:根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。

所以(type *)0)就是将0强转为一个地址,这个地址(0x0000)指向的是类型type的数据。当然,这里是一个技巧,并不是真的在地址0x0000存放了我们的数据。

((type *)0)->member的作用,这里的‘->’很显然是通过指针指取结构体成员的操作。指针就是刚才通过0强转的地址。所以也就是相当于地址0x0000 是结构体类型type的首地址,通过->’取其中的成员变量member。

typeof( ((type *)0)->member ) *__mptr = (ptr):知道member成员的类型,定义一个指针变量__mptr,指向的类型是member的类型,其初始化为ptr的值。

offsetof(type,member): 求出member在结构体中的偏移量

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

根据优先级的顺序,最里面的小括号优先级最高,TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0,这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。再将这个偏移量强制转换成size_t型数据(无符号整型)。

(char *)__mptr - offsetof(type,member)就是__mptr - offset,即member类型的指针减去member在结构体中的偏移量。

小结:通过上面的分析,和定义出的注释,container_of的作用很明显了 – 获得结构体的地址。那么我们需要给他提供三个参数,分别是:ptr:member成员的指针 type:结构体类型 member:成员member的名字。
这样我们就能通过container_of(ptr, type, member)的返回值,得到结构体的地址。

2、 prefetch(pos->member.next),&pos->member!= (head);

prefetch的含义是告诉cpu那些元素有可能马上就要用到,告诉cpu预取一下,这样可以提高速度,用于预取以提

高遍历速度,&pos->member !=(head) ,这个判断循环条件。

3、 pos= list_entry(pos->member.next, typeof(*pos), member))

和第1实现相似,用于逐项向后(next 方向)移动 pos。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

list_for_each_entry()函数分析 的相关文章

随机推荐

  • 关于STL的一些理解

    1 集合set 定义 xff1a set lt int gt s1 类型可选 se begin 返回指向第一个元素的迭代器 se clear 清除所有元素 常用 se count 返回某个值元素的个数 常用 xff0c 一般用来查这个元素在
  • ModuleNotFoundError: No module named ‘...’:报错解决方案

    1 module包没安装 这个 就是包的名字 xff0c 看问题定位 xff0c import源文件 xff0c 然后去site packages里找那个模块 xff0c 如果是空的没安装 xff0c 这时候可以在对应的环境里使用pip i
  • C语言----结构体,枚举,共用体

    1 xff09 结构体 span class token comment 例 xff1a 一个描述学生的结构体 span span class token keyword struct span span class token class
  • Nvidia jetson agx xavier can通讯失败,修改时钟源pllaon

    一 问题详情 在调试Xavier can的过程中 xff0c can通信始终有问题 xff0c 收发都会报错 后来发现时钟源导致波特率不匹配 xff0c 所以不能通信 二 修改时钟源 在虚拟机使用官方sdkmanager xff0c 下载安
  • linux下实现https访问

    http转https 适用于linux服务器 linux下nginx 43 ssl实现https访问 xff08 一 xff09 环境准备 xff08 二 xff09 下载nginx源码 xff0c 编译nginx并添加ssl模块 xff0
  • Nvidia Jetson nano 安装Archiconda、gpu版torch、踩坑记录

    Nvidia Jetson nano 安装Archiconda gpu版torch 踩坑记录 jetson nano 属于aarch64架构 xff0c 不同于一般的x86 64的linux系统架构 xff0c 而torch官网上面 htt
  • linux下的yum,vim,gcc,gdb

    我们在windows系统下我们能够很简单进行粘贴复制下载安装 xff0c 也能通过vs进行我们缩写代码的编译运行 xff0c 但是我们在linux下我们应该怎样去操作我们的这些命令呢 xff0c 这章我们就讲解一下我们linux下常用的一些
  • 【Keil】Keil5添加源程序和头文件

    xxx c就是源程序 xxx h就是头文件 源程序添加方法 双击文件夹 xff0c 例如图片上的Source xff0c 跳出弹窗 xff0c 选择需要添加的源程序即可 添加头文件的方法 1 首先点击图片红框处 xff0c 或是在文件夹te
  • C 标准库 string常用函数 笔记

    文章目录 64 TOC 文章目录 前言字符串长度strlen sizeof 字符串拼接strcat strncat 字符串拷贝strcpy strncpy memcpy 内存填充memset 字符串比较strcmp strncmp 字符串查
  • node.js中的http.request方法使用说明_node.js

    方法说明 xff1a 函数的功能室作为客户端向HTTP服务器发起请求 语法 xff1a 复制代码 代码如下 http get options callback 由于该方法属于http模块 xff0c 使用前需要引入http模块 xff08
  • ARM中SP、LR、PC三个寄存器介绍

    寄存器定义和用途 定义 寄存器是中央处理器内的组成部份 寄存器是有限存贮容量的高速存贮部件 xff0c 它们可用来暂存指令 数据和位址 用途 可将寄存器内的数据执行算术及逻辑运算 xff1b 存于寄存器内的地址可用来指向内存的某个位置 xf
  • Linux buffer/cache介绍

    free 命令 与 buffer cache 在 Linux 系统中 xff0c 我们经常用 free m命令来查看系统内存的使用状态 xff1a m 显示单位为MB free m 各个参数的说明 total 内存总数 used 已经使用的
  • 数字签名算法RSA

    RSA RSA数字签名算法源于RSA公钥密码算法的思想 xff0c 将RSA公钥密码算法按照数字签名的方式运用 RSA数字签名算法是迄今为止应用最为广泛的数字签名算法 RSA数字签名算法的实现如RSA加密算法一致 RSA数字签名算法主要可分
  • AXI接口协议详解-AXI总线、接口、协议

    转自 xff1a https cloud tencent com developer article 1695010 AXI接口协议详解 AXI总线 接口 协议 AXI 总线 上面介绍了AMBA总线中的两种 xff0c 下面看下我们的主角
  • USB主机是如何检测到设备的插入的呢?

    转自 xff1a https www cnblogs com wangh0802PositiveANDupward archive 2013 05 06 3061241 html USB设备的插入检测机制 首先 xff0c 在USB集线器的
  • 电路图中的那些类似于箭头的是什么意思?

    这个双箭头在画图软件中称为 off connect xff0c 作用是连接一个工程中的两张原理图里网络标号相同的网络 即 xff1a 这张原理图中网络标号为 PCIE20 2 REFCLKP 网络 xff0c 与其他原理图中的网络标号为 P
  • linux的socket CAN驱动介绍

    https blog csdn net linyangspring article details 27186911 在linux中 xff0c CAN总线的驱动有两种实现方式 xff1a 字符设备以及socket can驱动 Socket
  • 一文讲透Linux网络设备驱动框架及编写步骤

    https blog 51cto com u 14592069 5785977 本文阐述了网络架构模型 xff0c 特别是Linux系统中网络子设备框架4层结构 xff0c 反别阐述了各层的作用 重点讲解了sk buff及net devic
  • linux下的项目管理工具make和git的使用

    在linux下我们不能向在windows下这样去快速的进行编译 xff0c 在以前我们都是使用一个命令一个命令地去将 c文件生成程序 xff0c 在这里我们介绍项目自动化辅助构建工具make以及项目版本管理工具git make make说白
  • list_for_each_entry()函数分析

    在Linux内核源码中 xff0c 经常要对链表进行操作 xff0c 其中一个很重要的宏是list for each entry xff1a span class token comment list for each entry iter