list_for_each_entry()函数分析

news2024/11/14 21:55:09

在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。

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

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

相关文章

9个已开源的GPT4平替分享(附开源代码+论文)

资料整理自网络,有误欢迎指正 对于想要研究大模型的同学来说,目前ChatGPT无疑是最好的学习对象,但等它开源估计是不太可能了,所以学姐今天整理了一些开源的类GPT模型,帮助大家更好的理解大模型背后的机理。 PS&#x…

io,nio,aio区别

文章目录 前言io类型介绍同步阻塞io同步非阻塞ioio多路复用异步io 普通ionioChannelChannel实现基本的 Channel代码 示例 BufferBuffer的基本用法Buffer的capacity,position和limitcapacitypositionlimit Buffer的类型Buffer的分配向Buffer中写数据从Buffer中读取数据 Selector…

CSS 实现任意角度圆环

参考链接: css 制作圆环 - 掘金 主要思路: 利用 CSS 的 clip-path 属性进行裁剪 clip-path 具体信息参考 polygon() - MDN (mozilla.org) 该属性原理是:利用多边形进行对图形的裁剪。 根据具体代码,去分析 clip-path: polyg…

JavaWeb:Servlet、ServletContext、HttpServletResponse、HttpServletRequest 的详细内容

文章目录 JavaWeb - 02一、Servlet1. 简介2. HelloServlet3. Servlet 原理4. Mapping 原理 二、ServletContext1. 共享数据2. 获取初始化参数3. 请求转发4. 读取资源文件 三、HttpServletResponse1. 方法介绍2. 应用:下载文件3. 应用:创建验证码4. 应用&…

office web apps在线office文件预览部署及问题处理

文件下载链接网盘: 链接: https://pan.baidu.com/s/1OmWM5END0jyWESGzFCniEw 提取码: ejpg 基本环境需要两台机,1台为域控,1台为 (office web apps ,需要加入到域) 主机1:添加域控服务 安装完…

设备树简介

设备树 设备树简介 设备树是一种描述硬件的数据结构,它起源于OpenFirmware(OF)。 在Linux 2.6中, ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx中,采用设备树后,许多硬件…

python操作字典

# 字典 score{"张三":"23","王五":"45"} print(score) dctdict(name张三,age20) print(dict) print(type(score)) # 字典元素的获取 print(score[张三]) print(score.get(张三)) # 判断是否是字典中的元素 print(王五 in score) # 为字…

浅析AI视频智能识别技术如何助力智慧平安校园建设

校园安全一直是学生健康成长、全面发展的前提与保障。校园门口伤害事件的频发与校园内应急事件的突发,让建设平安校园的任务愈加急迫。校园人流量大、监控点多,安保人员无法同时盯住上百个视频画面,亦无法保证24小时有效监控。传统的校园安防…

软件测试简历项目经验怎么写?一篇足矣解决

一、前言:浅谈面试 面试是我们进入一个公司的门槛,通过了面试才能进入公司,你的面试结果和你的薪资是息息相关的。那如何才能顺利的通过面试,得到公司的认可呢?面试软件测试要注意哪些问题呢?下面和笔者一起来看看吧。这里分享一…

OpenGL(十一)——材质

目录 一、前言 二、物体材质 三、光源材质 一、前言 OpenGL材质是模拟现实世界中不同材质物体表面,如木制箱子和钢制箱子对光的反射程度不同。物体材质对接受光散射程度不同,较少散射产生较小高光点,较多散射则会产生较大高光点。前面章节…

如何节约ChatGPT消耗的token

如何节约GPT的token.md 原文链接:小回博客 如何节约GPT的token 一、模拟一下携带上下文的流程: 第1次问答: 你:帮我写一个1000字的文案(13) gpt: xxxxxx (1000)第2次问答: 你:谢…

《我命由我不由天》蔡志忠——笔记三

目录 经典摘录 1、大脑是用来思考的 2、养生主 3、自己的问题,自己找答案 4、42岁自学英文 5、终身阅读 6、打不垮我们的终究使我们更强大 7、大环境下失业 8、生命只能兑现此刻 经典摘录 1、大脑是用来思考的 罗素非常反对制式教育,他说&#…

有哪些比较好的测试用例管理工具?

“新入职小型创业公司,想要一个比Excel高效且好用的工具。”我预料很多人会提TestLink、Jira、PingCode 等一堆平台,都2023年了,若还是复制粘贴的10年前这一套,那就让人看不下去了。为了让大家少走弯路,所以我写了这篇…

【自用】配置minGW、vscode配置ESP-IDF环境

步骤总览 1.配置minGW 2.下载安装esp-idf软件 3.将vscode esp-idf插件 和 esp-idf软件进行关联 一、配置minGW 1.下载 链接:https://pan.baidu.com/s/1j6ITlNDDyivKwpWNBjASvg?pwd0108 提取码:0108 2.解压 解压上面下载的压缩包即可 3.配置环境变…

EFDC建模方法及在地表水环境评价、水源地划分、排污口论证应用

目录 专题一 软件安装 专题二 EFDC模型讲解 专题三 一维河流模拟实操(上机操作) 专题四 建模前处理(上机操作) 专题五 EFDC网格剖分介绍(上机操作) 专题六 EFDC二维湖库水动力模拟/非保守染色剂模拟&…

prometheus监控数据持久化

前置条件 1.规划两台主机安装prometheus # kubectl get nodes --show-labels | grep prometheus nm-foot-gxc-proms01 Ready worker 62d v1.23.6 beta.kubernetes.io/archamd64,beta.kubernetes.io/oslinux,kubernetes.io/archamd64,kubernetes.io…

5.Redis持久化

5.Redis持久化 总体介绍持久双雄一图:Redis persistence RDB(Redis Database)官网介绍RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。能干嘛?案例演示:需求…

图像处理——连接IP摄像头上传到服务器实现目标识别

前言 1.项目的需求是,本地连接IP摄像头,然后把图像上传到图像处理服务器器进行处理,得到的结果返回本地。 2.IP摄像头使用的是大华的摄像头,目标识别用的yolov5的模型,服务器用的是flask,实现语言是pytho…

【闪击Python】字符串的创建和驻留机制

💌 博客内容:字符串的创建和驻留机制 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准前端,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家&#x…