Linux代码看的比较多了,经常会遇到container_of和list_for_each_entry,特别是 list_for_each_entry比较多,因为Linux经常用到链表,虽然知道这些函数的大概意思,但一旦出现一个类似的函数比如 list_for_each_entry_safe就又会感到头大,所以下定决心分析总结一下这些函数的用法,以后再看到这些面孔的时候也会轻松很多,读 Linux代码的时候不会那么吃力。

我们知道list_for_each_entry会用到list_entry,而list_entry用到container_of,所以首先讲讲container_of。
在讲container_of之前我们不得不提到offsetof,因为在container_of中会使用到它,所以我们看下来,把list_for_each_entry函数的用法理顺我们对整个Linux中经常用到的一些函数就会比较清楚了。
  1. 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了。

  2. 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就更是一目了然了。

  3. 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的实现过程。
  4. 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为链表头的实体链表,对实体链表中的数据结构进行处理。

  5. 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 及其相关函数的分析的更多相关文章

  1. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  2. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  3. 有关ActiveXObject的兼容性问题(浏览器的特有属性)

    这个问题还得从一开始时候学习有关javascript中有关对文件的一些操作. 对于每个前端的人应该都清楚有关File对象,其中包括多种方法,就不一一描述了,比如说她是通过FileSystemObjec ...

  4. javascript学习笔记_1

    1.JSON的遍历 for(var i in json){  alert(json[i]; }2.arguments 可以理解为是一个数组,并且建有json的部分能力 css(obj,attr,val ...

  5. Swoole源代码学习记录(十三)——Server模块具体解释(上)

    Swoole版本号:1.7.5-stable Github地址:https://github.com/LinkedDestiny/swoole-src-analysis 最终能够正式进入Server. ...

  6. linux中OTG识别到一个U盘后产生一个sg节点的全过程

    注:本篇文章暂时不做流程图,如果有需求后续补做. 1. 需要准备的源码文件列表: base部分: kernel\base\core.c kernel\base\bus.c kernel\base\dd ...

  7. 关于宏:container_of和 offsetof以及list_for_each_entry

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

  8. list_for_each_entry()函数分析

    list_for_each原型: #define list_for_each(pos, head) \ for (pos = (head)->next, prefetch(pos->nex ...

  9. container_of宏定义分析---linux内核

    问题:如何通过结构中的某个变量获取结构本身的指针??? 关于container_of宏定义在[include/linux/kernel.h]中:/*_** container_of - cast a ...

随机推荐

  1. Codeforces Round #384 (Div. 2) 734E(二分答案+状态压缩DP)

    题目大意 给定一个序列an,序列中只有1~8的8个整数,让你选出一个子序列,满足下列两个要求 1.不同整数出现的次数相差小于等于1 2.子序列中整数分布是连续的,即子序列的整数必须是1,1,1.... ...

  2. 【距离GDOI:130天】 AC自动机ing

    弄完后缀数组,终于能安心来复习AC自动机了..其实当时学的很不好,非常不好..模版都是有问题的...今天花了第一节晚修和一节自习算是把AC自动机的基础弄好了...切掉3道基础题,然后就被某道坑爹题坑掉 ...

  3. hdu 5111 树上求交

    hdu 5111 树上求交(树链剖分 + 主席树) 题意: 给出两棵树,大小分别为\(n1\),\(n2\), 树上的结点权值为\(weight_i\) 同一棵树上的结点权值各不相同,不同树上的结点权 ...

  4. 类复制 MemberwiseClone与Clone(深 浅 Clone)

    MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象.如果字段是值类型的,则对该字段执行逐位复制.如果字段是引用类型,则复制引用但 ...

  5. poj 2186 强连通入门题目

    每头牛的梦想就是成为牛群中最受欢迎的牛. 在一群N(1 <= N <= 10,000)母牛中, 你可以得到M(1 <= M <= 50,000)有序的形式对(A,B),告诉你母 ...

  6. HDU1263 map二维运用

    #include <iostream> #include <cstdio> #include <cstring> #include <map> #inc ...

  7. Cocos2D研究院之CCNode详解(三)

    http://www.xuanyusong.com/archives/950 上一章我们了解了cocos2d的项目路径以及工作原理,这次作者要真刀真枪地讲解代码了,咱们先来看看cocos2d最常用.也 ...

  8. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

  9. 关于js无法设置input的value的问题

    html内容来自: <script type="text/html" id="theTemplate"> <input id="in ...

  10. 牛客网练习赛18 A 【数论/整数划分得到乘积最大/快速乘】

    链接:https://www.nowcoder.com/acm/contest/110/A 来源:牛客网 题目描述 这题要你回答T个询问,给你一个正整数S,若有若干个正整数的和为S,则这若干的数的乘积 ...