我想从头开始实现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) );})
我相信任何人都可以解释。
答案 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
希望有帮助.. :)