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. TestNG测试执行顺序

    1.preserve-order属性,之前一直认为preserve-order属性是控制配置方法的执行顺序的,其实不是,preserve-order主要是控制test下节点classes执行顺序的 例 ...

  2. WebSocket贪吃蛇例子学习

    在Tomcat7.0.64下的examples文件夹内,有多人贪吃蛇的例子. Multiplayer snake 这是一个多人在线小游戏,客户端通过操作上下左右键指挥自己的蛇,如果碰到别的蛇就死掉.还 ...

  3. 使用select2插件并添加拼音首字母检索

    项目中要使用下拉检索的时候要支持拼音首字母.本来拼音可以写后台,这里放前台了. 放代码 1. pinyin.js ,最后为了使用方便,直接为string对象添加了扩展方法 /* File Create ...

  4. matlab多行注释的三种方法

    转自:http://icictech.blog.163.com/blog/static/2797425420084595725499/ 1)方法一 选中你要加注释的内容,然后选择工具菜单"t ...

  5. BeeFramework 笔记 四(UISignal 总结)

    http://yang152412.blog.163.com/blog/static/175861582201332321351862/ 一.UISignal的工作模式 1.发送Signal,比如从V ...

  6. (八)ubuntu安装软件提示:Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)

    question: ubuntu@ubuntu:/usr/src/Linux-headers-3.5.0-23-generic$ sudo apt-get install cheese [sudo] ...

  7. Error:Execution failed for task ':bearBabyClient:processDebugManifest'. > Manifest merger failed with multiple errors, see logs

    具体报错如上: 在右侧中 大方块圈中的[com.android.support:support-v4:26.0.0-alpha1] 这个文件导致的,在这的清单文件第27行合并失败,让使用tools:r ...

  8. SQLSERVER2008以上版本的数据恢复

    这哥们真不错... http://blog.csdn.net/dba_huangzj/article/details/8491327

  9. Fiddler抓包7-post请求(json)【转载】

    本篇转自博客:上海-悠悠 原文地址:http://www.cnblogs.com/yoyoketang/tag/fiddler/ 前言上一篇讲过get请求的参数都在url里,post的请求相对于get ...

  10. C#中axWindowsMediaPlayer控件的用法

    属性/方法名: 说明:[基本属性] URL:String; 指定媒体位置,本机或网络地址uiMode:String; 播放器界面模式,可为Full, Mini, None, Invisibleplay ...