返回列表 发帖

[站长原创] 从list_entry的linux内核实现看container_of()的基本原理

[站长原创] 从list_entry的linux内核实现看container_of()的基本原理

从list_entry的linux内核实现看container_of()的基本原理



嵌入式开发联盟 - www.mcuos.com


站长osboy原创,qq:82475491


email:mcuos.com@gmail.com


未经允许不得转载





内核链表函数中有如下代码:

  1. #define list_entry(ptr, type, member) \
  2.         container_of(ptr, type, member)
复制代码

  1. #define container_of(ptr, type, member) ({                        \
  2.         const typeof( ((type *)0)->member ) *__mptr = (ptr);        \
  3.         (type *)( (char *)__mptr - offsetof(type,member) );})
复制代码



这里使用的是一个利用编译器技术的小技巧,即先求得结构成员在与结构中的偏移量,然后根据成员变量的地址反过来得出属主结构变量的地址。

container_of()和offsetof()并不仅用于链表操作,这里最有趣的地方是((type *)0)->member,它将0地址强制"转换"为type结构的指针,再访问到type结构中的member成员。在container_of宏中,它用来给typeof()提供参数(typeof()是gcc的扩展,和sizeof()类似),以获得member成员的数据类型;在offsetof()中,这个member成员的地址实际上就是type数据结构中member成员相对于结构变量的偏移量。为了更加清晰的了解,osboy站长为你画了一幅图:




图中我们以__wake_up_common为例子来说明了container_of()的功能,等待队列元素的数据结构为:

  1. struct __wait_queue {
  2. unsigned int        flags;
  3. void        *private;
  4. wait_queue_func_tfunc;
  5. struct list_head task_list;
  6. }; wait_queue_t
复制代码


该结构体包含了一个成员变量struct list_head task_list;, 上过osboy的培训课程的人都应该知道,在讲解等待队列那个章节的时候,在唤醒队列的函数中我们会遍历互斥和非互斥的函数,如下代码,调用层次为:__wake_up_common--> list_for_each_entry_safe-->list_first_entry-->list_entry-->container_of。

  1. static void __wake_up_common
  2. (wait_queue_head_t *q, unsigned int mode,
  3. int nr_exclusive, int wake_flags, void *key)
  4. {
  5. wait_queue_t *curr, *next;
  6. list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
  7. unsigned flags = curr->flags;
  8. if (curr->func(curr, mode, wake_flags, key) &&
  9. (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
  10.         break;
  11. } }
复制代码

  1. #define list_for_each_entry_safe(pos, n, head, member)                        \
  2.         for (pos = list_first_entry(head, typeof(*pos), member),        \
  3.                 n = list_next_entry(pos, member);                        \
  4.              &pos->member != (head);                                         \
  5.              pos = n, n = list_next_entry(n, member))
复制代码

  1. #define list_first_entry(ptr, type, member) \
  2.         list_entry((ptr)->next, type, member)
复制代码



有上面的分析可以计算出一个恒定的值offsetof(type,member),然后我们可以通过等待队列头的真实地址找到下一个等待队列元素成员struct list_head task_list的地址,然后减去这个offset就得出这个等待队列结构体的地址了。
附件: 您需要登录才可以下载或查看附件。没有帐号?本站只开放邀请码注册,QQ:82475491,索要邀请码
分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服