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. Sth about 函数式编程(Functional Programming)

    今天开会提到了函数式编程,针对不同类型的百年城方式,查阅了一部分资料,展示如下: 编程语言一直到近代,从汇编到C到Java,都是站在计算机的角度,考虑CPU的运行模式和运行效率,以求通过设计一个高效的 ...

  2. Lettcode Kth Largest Element in an Array

    Lettcode Kth Largest Element in an Array 题意:在无序数组中,寻找第k大的数字,注意这里考虑是重复的. 一直只会简单的O(nlogn)的做法,听说这题有O(n) ...

  3. Codeforces Round #359 (Div. 2) A

    A. Free Ice Cream time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  4. Fiddler抓取HTTP请求

    参考链接:http://blog.csdn.net/ohmygirl/article/details/17849983/ http://www.cnblogs.com/kingwolf_JavaScr ...

  5. [bzoj1026][SCOI2009]windy数——数位dp

    题目 求[a,b]中的windy数个数. windy数指的是任意相邻两个数位上的数至少相差2的数,比如135是,134不是. 题解 感觉这个题比刚才做的那个简单多了...这个才真的应该是数位dp入门题 ...

  6. vue elementui 递归 sidebar可伸缩侧边栏

    最近在学习vue 做了一个可伸缩的 侧边栏 记录下 在很多管理系统都用到 管理系统一般都长的差不多 首先是收起时候 展开时候 首先是新建一个Layout.vue <template> &l ...

  7. struct timeval和gettimeofday()

    http://www.cppblog.com/lynch/archive/2011/08/05/152520.html struct timeval结构体在time.h中的定义为: struct ti ...

  8. 01深入理解C指针之---指针含义符号

    该系列文章源于<深入理解C指针>的阅读与理解,由于本人的见识和知识的欠缺可能有误,还望大家批评指教. 1.指针的含义: 指针本身也是变量,与其他一般变量不同的是:指针变量中没有存储具体类型 ...

  9. CppCMS1.0.3 Build by VS2012

    1.CppCMS简介 CppCMS是一个C++的Web开发框架(不是一个CMS).它不同于大多数其他Web开发框架,如巨蟒Django , Java的Servlets ,或C++ Wt因为它在设计和调 ...

  10. 牛客网 暑期ACM多校训练营(第二场)D.money-贪心 or 动态规划

    D.money 贪心,直接贴官方的题解吧. 题目大意 你要按照顺序依次经过n个商店,每到达一个商店你可以购买一件商品,也可以出售你手中的商品. 同一时刻你手上最多拿一件商品.在第i个商店购买和出售的代 ...