如何在linux内核中从头开始实现container_of

时间:2015-06-16 12:55:47

标签: c linux struct linux-kernel linux-device-driver

我想从头开始实现container_of宏/函数,就像linux内核中可用的那样,从父结构的成员中获取父结构的地址。

e.g。如果父结构是

  

struct parent {         int id;         struct list_head list; };

我在结构中有list_head元素的地址。

所以我想获取struct parent的地址,以便我可以访问父级的id。

  

我只有三个已知信息   1.父结构的类型   2. struct list_head的类型   3.识别/ list_head变量的名称。

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})
我相信任何人都可以解释。

1 个答案:

答案 0 :(得分:0)

首先,你的问题不合适。根据我的理解,你想要理解宏的工作,而不是实现它。

继续前进,

Linux Kernel Linked List Explained

中很好地解释了linux内核中使用的链接列表

在linux内核中,列表包含在列表节点中。 例如:

struct list_head {  /* Kernel list structure */
    struct list_head *next, *prev;
}

struct my_list {
    int to;
    struct list_head list; /* list is contained in the node */
    int from;
}

所以我们使用list_head变量迭代链表。棘手的部分是我们使用list变量来获取节点结构(包含它)

MIT FAQ中的Q. 2.14和2.15。这个问题解释了如果我们在结构中有变量的偏移量,我们如何能够检索指向CONTAINING结构的指针。

所以用外行来说,我们可以说,

struct s address = <address of struct variable> - <offset of that variable in the struct>

来到宏,考虑宏的这个定义。 (简单的实现,我在drivers / gpu / drm / nouveau / include / nvif / list.h中找到)

#define container_of(ptr, type, member) \
    (type *)((char *)(ptr) - (char *) &((type *)0)->member)

因此将左操作数视为结构VARIABLE(通常是list_head变量)。现在来到正确的运营商。要在包含strict(例如struct my_list)中获取该变量的OFFSET(比如list_head变量),我们创建一个归零结构(即一个地址为0x0的临时结构),因此结构中任何变量的地址都对应于该变量的偏移量。

现在要理解的最后一部分是为什么要转换为char *。那么这个基本的指针算术。 char * addtion将值一次递增1(char *指向1个字节的char)。

((char*)0) + 1  -> 0x1

如果它是int *指针的加1将使偏移量增加4,因为int *指向大小为4的int(在我的计算机上)。

((int*)0) + 1  -> 0x4

希望有帮助.. :)