相关博文:
linux中的list.h(2) ——>内核链表的创建、LIST_HEAD()方法
linux中的list.h(3) ——>内核链表的增加、删除、遍历
在之前我接触的数据结构中的链表,通常的做法是在结构体的内部添加一个指向数据next节点的指针,才能串在链表中。
比如,现有一个fox数据结构:
struct fox{
unsigned long weight;
bool is_cute;
}
存储这个结构到链表里的通常做法是在数据结构中嵌入一个链表指针。
struct fox{
unsigned long weight;
bool is_cute;
struct fox *next; /* 指向下一个狐狸 */
}
但是linux内核中,它不是将数据结构塞入链表,而是将链表节点塞入数据结构。
linux内核中的链表数据结构。链表代码在< linux/list.h>中声明,其数据结构为:
struct list_head{
struct list_head *prev;
struct list_head *next;
}
list_head本身没有意义,它需要被嵌入到我们自己的结构体中才会生效。
list_head结构的使用:
struct fox{
unsigned long weight;
bool is_cute;
struct list_head list; /* 所有的fox结构体形成链表 */
}
这样将linux的链表结构体嵌入到使用链表的对象中(链表宿主)的组合方式,实现了链表和宿主的解耦。于是所有链表的操作都是针对struct list_head这个链表数据结构,而链表的设计者不会知道链表宿主的任何情况。因此,必须在指向链表结构体的指针和指向宿主的指针之间进行转换。
宏list_entry
用于返回包含list_head
的宿主结构体。实现在指向链表结构体的指针和指向宿主的指针之间进行转换。
/usr/src/kernels/include/linux/kernel.h
container_of
的用法,是在已知结构体的某个成员的指针(首地址)时,求出整个结构体的首指针地址。
ptr是成员的指针(首地址),type是结构体类型,member是成员。
struct list_head *ptr;
...
/* list_entry(ptr, type, member) */
struct fox *pnode=list_entry(ptr, struct fox, list);
ptr为指向当前fox宿主对象中内嵌list成员的指针。
struct fox为宿主类型名。
list为linux链表结构体在宿主中的成员变量名。
((type*)0)
将0转型为type类型指针,即fox类型。((type*)0)->member
访问结构体中的数据成员member,即fox中的list成员。typeof(((type*)0)->member)
,typeof的作用是根据变量名获取变量的类型。在这里获取的类型为struct list_head
。即为const struct list_head *__mptr
。将fox结构体变量中的成员list的地址赋给临时变量__mptr。offsetof(type,member)
是求成员在结构体中的偏移量。这里成员list在fox中的偏移量。(char*)__mptr
减去member在结构体中的偏移量,得到的值就是整个结构体变量的首地址。简单的来说,就是先定义一个临时的数据类型与ptr相同的指针变量__mptr,用它来保存ptr的值。再用(char*)__mptr
减去member在结构体中的偏移量,即可得到整个结构体的首地址。
offsetof(type,member)求成员在结构体中的偏移量。
/usr/src/kernels/include/linux/stddef.h
#include
#include
#ifdef __compiler_offsetof
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#endif
struct Test{
int a;
char b;
float c;
};
int main()
{
printf("%d\n",offsetof(struct Test,a));
printf("%d\n",offsetof(struct Test,b));
printf("%d\n",offsetof(struct Test,c));
return 0;
}