关于container_of和list_for_each_entry 及其相关函数的分析
- offsetof /**/ #define
offsetof(TYPE, MEMBER) ((size_t)&((TYPE
*)0)->MEMBER)
理解offsetof的关键在于&((TYPE
*)0)->MEMBER,几乎可以说只要理解了这一部分,后面的几个函数都能够解决,那么我们看看这一部分究竟完成了怎样的工作。根据优先级的顺
序,最里面的小括号优先级最高,TYPE*将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中
MEMBER成员进行取址,而整个TYPE结构体的首地址是0,这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。再将这个偏移量强制转换成
size_t型数据(无符号整型)。所以整个offsetof的功能就是获取MEMBER成员在TYPE型数据中的偏移量。接下来我们可以讲一下container_of了。
- 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)->member定义一个MEMBER型的指针__mptr,这个指针指向ptr,所以第一句话获取到了我们要求的结构体,它的成员
member的地址,接下来我们用这个地址减去成员member在结构体中的相对偏移量,就可以获取到所求结构体的地址, (char *)__mptr
-
offsetof(type,member)就实现了这个过程,最后再把这个地址强制转换成type型指针,就获取到了所求结构体指针,define预定
义返回最后一句话的值,将所求结构体指针返
回。所以整个container_of的功能就是通过指向结构体成员member的指针ptr获取指向整个结构体的指针。container_of清楚了,那
list_entry就更是一目了然了。 - 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) list_entry
的功能等同于container_of。接下来分析我们最终想要知道的list_for_each_entry的实现过程。 - 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); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
在理解了list_entry的基础上分析list_for_each_entry本来是一件比较轻松的事情,但在这里还是要强调一下双向链表及链表头的
概念,否则对list_for_each_entry的理解还是一知半解。建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般是不含有实
体数据的,必须用INIT_LIST_HEAD()进行初始化,表头建立以后,就可以将带有数据结构的实体链表成员加入到链表中,链表头和链表的关系如图
所示:链表头和链表的关系清楚了,我们才能完全理解list_for_each_entry。list_for_each_entry被预定义成一个
for循环语句,for循环的第一句话获取(head)->next指向的member成员的数据结构指针,也就是将pos初始化为除链表头之外的
第一个实体链表成员,for的第三句话通过pos->member.next指针遍历整个实体链表,当pos->member.next再次
指向我们的链表头的时候跳出for循环。整个过程没有对链表头进行遍历(不需要被遍历),所以使用list_for_each_entry遍历链表必须从
链表头开始。因此可以看出,list_for_each_entry的功能就是遍历以head为链表头的实体链表,对实体链表中的数据结构进行处理。
- list_for_each_entry_safe
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
相比于list_for_each_entry,list_for_each_entry_safe用指针n对链表的下一个数据结构进行了临时存储,所以
如果在遍历链表的时候可能要删除链表中的当前项,用list_for_each_entry_safe可以安全的删除,而不会影响接下来的遍历过程(用n
指针可以继续完成接下来的遍历, 而list_for_each_entry则无法继续遍历)。
关于container_of和list_for_each_entry 及其相关函数的分析的更多相关文章
- 【内核】linux内核启动流程详细分析
Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...
- 【内核】linux内核启动流程详细分析【转】
转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...
- 有关ActiveXObject的兼容性问题(浏览器的特有属性)
这个问题还得从一开始时候学习有关javascript中有关对文件的一些操作. 对于每个前端的人应该都清楚有关File对象,其中包括多种方法,就不一一描述了,比如说她是通过FileSystemObjec ...
- javascript学习笔记_1
1.JSON的遍历 for(var i in json){ alert(json[i]; }2.arguments 可以理解为是一个数组,并且建有json的部分能力 css(obj,attr,val ...
- Swoole源代码学习记录(十三)——Server模块具体解释(上)
Swoole版本号:1.7.5-stable Github地址:https://github.com/LinkedDestiny/swoole-src-analysis 最终能够正式进入Server. ...
- linux中OTG识别到一个U盘后产生一个sg节点的全过程
注:本篇文章暂时不做流程图,如果有需求后续补做. 1. 需要准备的源码文件列表: base部分: kernel\base\core.c kernel\base\bus.c kernel\base\dd ...
- 关于宏:container_of和 offsetof以及list_for_each_entry
1.offsetof(TYPE, MEMBER) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) offse ...
- list_for_each_entry()函数分析
list_for_each原型: #define list_for_each(pos, head) \ for (pos = (head)->next, prefetch(pos->nex ...
- container_of宏定义分析---linux内核
问题:如何通过结构中的某个变量获取结构本身的指针??? 关于container_of宏定义在[include/linux/kernel.h]中:/*_** container_of - cast a ...
随机推荐
- [bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席树]
题面 bzoj上的强制在线版本 思路 首先可以确定,这类联通块相关的询问问题,都可以$LCT$+可持久化记录解决 用LCT维护生成树作为算法基础 具体而言,从前往后按照边的编号顺序扫一遍边 如果这条边 ...
- nginx+php-fpm配置后页面显示空白的解决方法以及用nginx和php-fpm解决“502 Bad Gateway”问题
For reference, I am attaching my location block for catching files with the .php extension: location ...
- vue-cli安装sass
npm install node-sass --save npm install sass-loader --save 也可以使用淘宝镜像 npm install -g cnpm --registry ...
- ACMUniversity
描述 在大学里,很多单词都是一词多义,偶尔在文章里还要用引申义.这困扰Redraiment很长的时间. 他开始搜集那些单词的所有意义.他发现了一些规律,例如 “a”能用“e”来代替, “c”能用“f” ...
- this bind apply call
this 是当前函数运行时所属的对象bind 是指定一个函数运行时的上下文,也就是说把这个函数的this指向绑定到相应对象上,默认的暴露在全局御中的函数this指向widow对象, 严格模式下全局的t ...
- 行为型设计模式之备忘录模式(Memento)
结构 意图 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 适用性 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时 ...
- 使用QML创建界面(转)
原文转自 https://blog.csdn.net/rl529014/article/details/51378307 在Qt编程中,我们可以使用纯C++代码,或C++和XML结合的方式来创建GUI ...
- Java中UML图
原文出处:http://blog.sina.com.cn/s/blog_7b3a17130100r95y.html 类(Class) 类(图A)是对象的蓝图,其中包含3个组成部分.第一个是Java中定 ...
- 为何url地址不是直接发送到服务器,而是被编码后再发送
首先,先说一下,关于为何必须将url地址,去编码后,再发送,是因为相关的协议规范:RFC 1738,定义了url地址中不能包含除了0-9的数字,大小写字母(a-zA-Z),短横线’-‘ 之外的字母.换 ...
- 应用程序已被Java安全阻止
提示 您的安全设置已阻止自签名的应用程序运行 控制面板-JAVA-安全-例外站点-https://域名或IP/或http://域名或IP/,注意结尾必须要加/否则还是会一直提示被阻止