链表
在学习《Linux内核设计与实现》中链表结构时,对<scripts\kconfig\list.h>(我看的内核是2.6的,在其他版本对链表的定义可能不在这个目录中)中的宏定义:container_of()很困惑,所以google一番,现记录自己对其的理解:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
宏定义的三个参数的含义分别为:
type:一般是个结构体,也就是包含用户数据和链表节点的结构体
ptr:是指向type中链表节点的指针
member:是type中定义链表节点时用的名字
比如:
struct student
{
int id;
char* name;
struct list_head list;
};
- type是struct student
- ptr是指向struct list_head的指针,也就是指向member类型的指针
- member就是list
对于container_of宏:
// 步骤1:将数字0强制转型为type*,然后取得其中的member元素
((type *)0)->member // 相当于((struct student *)0)->list
// 步骤2:定义一个临时变量__mptr,并将其也指向ptr所指向的链表节点
const typeof(((type *)0)->member)*__mptr = (ptr);
// 步骤3:计算member字段距离type中第一个字段的距离,也就是type地址和member地址之间的差
// offset(type, member)也是一个宏,定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
// 步骤4:将__mptr的地址 - type地址和member地址之间的差
// 其实也就是获取type的地址
步骤1很好理解,就是强制类型转换。
步骤2中的typeof()的用法说明:
//把y定义成x指向的数据类型:
typeof(*x) y;
步骤3:(TYPE *)0,将 0 强制转换为 TYPE 型指针,记 p = (TYPE *)0,p是指向TYPE的指针,它的值是0。那么 p->MEMBER 就是 MEMBER 这个元素了,而&(p->MEMBER)就是MENBER的地址,而基地址为0,这样就巧妙的转化为了TYPE中的偏移量。再把结果强制转换为size_t型的就OK了,size_t其实也就是int。
typedef __kernel_size_t size_t;
typedef unsigned int __kernel_size_t;
步骤4就是为了获得type的地址。
通过container_of()宏,我们定义一个简单的函数就可以返回包含list_head的父类型结构体:
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
因为我们获得一个指向链表结构的指针通常是无用的,我们要操作的是用户数据,是一个指向包含list_head的结构体指针,如之前的student结构体。而通过宏定义container_of()就可以让我们获得该结构体的首地址。