了解container_of宏在Linux内核(Understanding container_of

2019-08-19 18:43发布

当我在浏览Linux内核,我发现了一个container_of其定义如下宏:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

我明白了什么呢container_of做的,但我不明白的是最后一句,这是

(type *)( (char *)__mptr - offsetof(type,member) );})

如果我们使用宏如下:

container_of(dev, struct wifi_device, dev);

最后一句的相应部分是:

(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);

它看起来像什么都不做。 任何人可以在这里填写的空白?

Answer 1:

您的使用示例container_of(dev, struct wifi_device, dev); 因为你是有混合两种命名空间可能会有点误导。

虽然第一dev在您的示例指的是指针的名称的第二dev是指一种结构构件的名称。

最大的可能是这种混合被挑起所有的头痛。 事实上, member在您的报价参数是指给予容器结构成员的名字。

以这个容器,例如:

struct container {
  int some_other_data;
  int this_data;
}

和指针int *my_ptrthis_data会员你会使用宏来得到一个指向struct container *my_container使用:

struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);

考虑的偏移this_data到结构的开始考虑是要得到正确的指针位置至关重要。

有效地你只需要减去偏移成员的this_data从你的指针my_ptr ,以获得正确的位置。

这正是宏的最后一行呢。



Answer 2:

最后一句投:

(type *)(...)

一个指向一个给定的type 。 指针被计算为从一个给定的偏移量指针dev

( (char *)__mptr - offsetof(type,member) )

当您使用cointainer_of宏,要检索包含给定字段的指针结构。 例如:

struct numbers {
    int one;
    int two;
    int three;
} n;

int *ptr = &n.two;
struct numbers *n_ptr;
n_ptr = container_of(ptr, struct numbers, two);

你必须在一个结构的中间点指针(你知道这是一个指向提交two [ 字段名在结构 ]),但要恢复整个结构( numbers )。 所以,你计算偏移的申请的two的结构:

offsetof(type,member)

并减去这从给定的指针偏移量。 结果是指针结构的开始。 最后,你施放此指针结构类型有一个有效的变量。



Answer 3:

这是一个GCC扩展的的利用率报表表达式 。 如果您看到返回值宏的东西,然后最后一行是:

return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);

对于复合语句的解释,请参阅链接的页面。 下面是一个例子:

int main(int argc, char**argv)
{
    int b;
    b = 5;
    b = ({int a; 
            a = b*b; 
            a;});
    printf("b %d\n", b); 
}

输出是

b 25



Answer 4:

有一点真实的语境说更清晰,下面用红黑树为例 ,这是我理解的方式container_of

作为Documentation/rbtree.txt状态,在linux内核代码,这不是rb_node包括数据录入,而

在rbtree树数据节点是含有一个结构rb_node构件结构。

struct vm_area_struct (在文件include/linux/mm_types.h:284 )是这样的结构,

在同一文件中,有一个宏rb_entry其被定义为

#define rb_entry(ptr, type, member) container_of(ptr, type, member)

显然, rb_entry是一样container_of

mm/mmap.c:299内部函数定义browse_rb ,还有的使用rb_entry

static int browse_rb(struct mm_struct *mm)
{
    /* two line code not matter */
    struct rb_node *nd, *pn = NULL; /*nd, first arg, i.e. ptr. */
    unsigned long prev = 0, pend = 0;

    for (nd = rb_first(root); nd; nd = rb_next(nd)) {
        struct vm_area_struct *vma;
        vma = rb_entry(nd, struct vm_area_struct, vm_rb);   
        /* -- usage of rb_entry (equivalent to container_of) */
        /* more code not matter here */

现在很清楚,在container_of(ptr, type, member)

  • type是容器结构,在此struct vm_area_struct
  • member是其成员的名称type实例,这里vm_rb ,它的类型的rb_node
  • ptr是一个指针指向member的的type实例,这里rb_node *nd

什么container_of做的是,在这个例子中,

  • 的给定的地址obj.member (这里obj.vm_rb ),返回的地址obj
  • 由于结构是连续的存储器的块, 的地址obj.vm_rb减去offset between the struct and member将是容器的地址。

include/linux/kernel.h:858 -的定义container_of

include/linux/rbtree.h:51 -的定义rb_entry

mm/mmap.c:299 -的使用rb_entry

include/linux/mm_types.h:284 - struct vm_area_struct

Documentation/rbtree.txt: -红黑树的文档

include/linux/rbtree.h:36 -的定义struct rb_node

PS

上述文件是在当前开发的版本,即4.13.0-rc7

file:k的意思是在第k线file



Answer 5:

conatainer_of在Linux内核()宏 -

当涉及到管理代码的几个数据结构,你几乎总是需要嵌入一个结构到另一个,并随时检索它们没有被有关内存偏移或边界问的问题。 比方说,你有一个结构的人,这里定义:

 struct person { 
     int age; 
     int salary;
     char *name; 
 } p;

通过只对年龄和薪水的指针,你可以检索整个结构包裹(含),该指针。 正如它的名字所说,container_of宏用于寻找结构的某一领域的容器。 宏是在include / linux / kernel.h当定义,如下所示:

#define container_of(ptr, type, member) ({               \ 
   const typeof(((type *)0)->member) * __mptr = (ptr);   \ 
   (type *)((char *)__mptr - offsetof(type, member)); })

不要害怕指针的; 只是看到他们如下:

container_of(pointer, container_type, container_field); 

下面是上述代码段中的元素:

  • 指针:这是指针字段在结构
  • CONTAINER_TYPE:这是结构包裹(含)的指​​针的类型
  • container_field:这是字段的名称,其指针指向所述结构的内部

让我们考虑以下容器:

struct person { 
    int age; 
    int salary; 
    char *name; 
}; 

现在,让我们考虑它的实例之一,具有指针年龄成员一起:

struct person somebody; 
[...] 
int *age_ptr = &somebody.age; 

随着指针名成员(age_ptr),你可以使用container_of宏观为了得到一个指向它包装使用以下这个成员的整体结构(容器):

struct person *the_person; 
the_person = container_of(age_ptr, struct person, age); 

container_of采取的年龄结构体开始偏移考虑,以获得正确的指针位置。 如果减去指针age_ptr场岁的偏移量,你会得到正确的位置。 这是宏的最后一行做了什么:

(type *)( (char *)__mptr - offsetof(type,member) ); 

将其应用到一个真实的例子,给出如下:

struct family { 
    struct person *father; 
    struct person *mother; 
    int number_of_sons; 
    int family_id; 
} f; 

/*   
 * Fill and initialise f somewhere   */      [...]

 /* 
  * pointer to a field of the structure 
  * (could be any (non-pointer) member in the structure) 
  */ 
   int *fam_id_ptr = &f.family_id; 
   struct family *fam_ptr; 

   /* now let us retrieve back its family */ 
   fam_ptr = container_of(fam_id_ptr, struct family, family_id); 

该container_of宏主要用于在内核的通用容器。

这是所有关于内核container_of宏。



文章来源: Understanding container_of macro in the Linux kernel