list_for_each与list_for_each_entry详解

一、list_for_each

1.list_for_each原型

#define list_for_each(pos, head) \

for (pos = (head)->next, prefetch(pos->next); pos != (head); \

pos = pos->next, prefetch(pos->next))

它实际上是一个 for 循环,利用传入的pos 作为循环变量,从表头 head开始,逐项向后(next方向)移动 pos ,直至又回到 head (prefetch() 可以不考虑,用于预取以提高遍历速度)。

注意:此宏必要把list_head放在数据结构第一项成员,至此,它的地址也就是结构变量的地址。

2.使用方法(以访问当前进程的子进程为例):

struct list_head {

struct list_head *next, *prev;

};

在struct task_struct 中有如下定义:

struct list_head children;

所以

struct task_struct *task;

struct list_head *list;

list_for_each(list,&current->chilidren) {

task = list_entry(list, struct task_struct, sibling);/*task指向当前的某个子进程*/

}

其中用到了函数list_entry():

这个函数的作用在图1中表示就是可以通过已知的指向member子项的指针,获得整个结构体的指针(地址)

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

二、list_for_each_entry:

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

意思大体如下:

假设只有两个结点,则第一个member代表head,

list_for_each_entry的作用就是循环遍历每一个pos中的member子项。

图1:

pos:                                                           pos:

___________                                        ____________

|                       |                                    
|                          |

|                       |                                    
|                          |

|    ...........        |                                     |   ................      
|

|                       |                                    
|                           |

|                       |                                    
|                           |

|     member:    |                 
 _________|__> member    |

|   {                  |                |                   
 |  {                       |

|         *prev;    |                |                    
|       *prev;        |

|         *next;        --|----------                    |        *next;-------------

|    }                |                                      |  } 
                    |             |

|—^———— |                                      |____________|             |

|                                                       
                                              |

|                                                       
                                             |

|_____________________________________________|

宏list_for_each_entry:

#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))

list_entry((head)->next, typeof(*pos), member)返回(head)->next物理指针所处位置向前减去offsetof()个字节数据之后, 其父变量pos的物理地址,父变量的类型在编译时由typeof(*pos)自动返回.

所以list_for_each_entry遍历head 下面挂接的类型为typeof(*pos)的childs结构体们,当然每个child结构体包含struct list_head node之类相似的双向链表list_head类型项,就这样通过循环pos将依次指向双向链表上的各个child.(member就是child类型中被定义的变量名)

其中用到了函数list_entry():

这个函数的作用在图1中表示就是可以通过已知的指向member子项的指针,获得整个结构体的指针(地址)

#define list_entry(ptr, type, member) \

container_of(ptr, type, member)

和函数prefetch:

#define prefetch(x) __builtin_prefetch(x)

其中用到了builtin_prefetch:

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

其中用到了函数container_of():

493#define container_of(ptr, type, member) ({                      \

494        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \

495        (type *)( (char *)__mptr - offsetof(type,member) );})

其中又用到了offsetof()函数:

lxr上找到的源码:

#define offset_of(type, memb) \

47        ((unsigned long)(&((type *)0)->memb))

offsetof(TYPE, MEMBER)

该宏在Linux内核代码(版本2.6.22)中定义如下:

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

分析:

(TYPE *)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么 p->MEMBER 就是 MEMBER 这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转换为size_t型的就OK了,size_t其实也就是int。

typedef __kernel_size_t  size_t;

typedef unsigned int __kernel_size_t;

可见,该宏的作用就是求出MEMBER在TYPE中的偏移量。

list_for_each与list_for_each_entry详解,布布扣,bubuko.com

时间: 2024-08-25 10:27:29

list_for_each与list_for_each_entry详解的相关文章

Linux 内核 hlist 详解

在Linux内核中,hlist(哈希链表)使用非常广泛.本文将对其数据结构和核心函数进行分析. 和hlist相关的数据结构有两个:hlist_head 和 hlist_node //hash桶的头结点 struct hlist_head { struct hlist_node *first;//指向每一个hash桶的第一个结点的指针 }; //hash桶的普通结点 struct hlist_node { struct hlist_node *next;//指向下一个结点的指针 struct hl

Apache Spark源码走读之16 -- spark repl实现详解

欢迎转载,转载请注明出处,徽沪一郎. 概要 之所以对spark shell的内部实现产生兴趣全部缘于好奇代码的编译加载过程,scala是需要编译才能执行的语言,但提供的scala repl可以实现代码的实时交互式执行,这是为什么呢? 既然scala已经提供了repl,为什么spark还要自己单独搞一套spark repl,这其中的缘由到底何在? 显然,这些都是问题,要解开这些谜团,只有再次开启一段源码分析之旅了. 全局视图 上图显示了java源文件从编译到加载执行的全局视图,整个过程中最主要的步

Linux的i2c驱动详解

目录(?)[-] 简介 架构 设备注册 I2C关键数据结构和详细注册流程 关键数据结构 详细注册流程 使用I2C子系统资源函数操作I2C设备 Gpio模拟i2c总线的通用传输算法 总结 理清i2c中的个结构体关系 i2c驱动的编写建议 1 简介 I2C 总线仅仅使用 SCL . SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用.因此, I2C 总线被非常广泛地应用在 EEPROM .实时钟.小型 LCD 等设备与 CPU 的接口中. Linux I2

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

转载:DenseNet算法详解

原文连接:http://blog.csdn.net/u014380165/article/details/75142664 参考连接:http://blog.csdn.net/u012938704/article/details/53468483 本文这里仅当学习笔记使用,具体细节建议前往原文细度. 论文:Densely Connected Convolutional Networks 论文链接:https://arxiv.org/pdf/1608.06993.pdf 代码的github链接:h

MariaDB(MySQL)创建、删除、选择及数据类型使用详解

一.MariaDB简介(MySQL简介略过) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品.在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB. MariaDB由MySQL的创始人Michael Widenius(英语:Michael Widenius)主导开发,他早前曾以10亿美元的价格,将自己创建的公司MySQL A

HttpServletResponse和HttpServletRequest详解

HttpServletResponse,HttpServletRequest详解 1.相关的接口 HttpServletRequest HttpServletRequest接口最常用的方法就是获得请求中的参数,这些参数一般是客户端表单中的数据.同时,HttpServletRequest接口可以获取由客户端传送的名称,也可以获取产生请求并且接收请求的服务器端主机名及IP地址,还可以获取客户端正在使用的通信协议等信息.下表是接口HttpServletRequest的常用方法. 说明:HttpServ

POSIX 线程详解(经典必看)

总共三部分: 第一部分:POSIX 线程详解                                   Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  2000 年 7 月 01 日 第二部分:通用线程:POSIX 线程详解,第 2部分       Daniel Robbins ([email protected]), 总裁/CEO, Gentoo Technologies, Inc.  20

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频