Linux内核设计与实现中看到的链表相关内容进行记录
内容参考: C语言高级用法—container_of().
Linux内核中链表的实现
Linux中将链表节点插入数据结构而非在数据结构中塞入链表
链表结构如下
struct list_head{
struct list_head *next;
struct list_head *prev;
};
以数据结构举例如下
struct fox{
int data;
struct list_head list;
}
container_of
函数原型如下
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr,type,member)({ \
const typeof( ((type *) 0->member ) *__mptr =(ptr); \
(type *)( (char *)__mptr - offsetof( type,member ) );})
参数如下
ptr:结构体变量中某个成员的地址
type:结构体类型
member:该结构体变量的具体名字
接下来一步步分析代码
typeof()
typeof() 是GUN C提供的一种特性,可参考C-Extensions,它可以取得变量的类型,或者表达式的类型。
typeof() 返回参数的类型,如果参数为函数,不执行函数而是得到函数返回类型
struct a* foo(){
...
do something
}
typeof(foo()) bar;
bar得到的类型为 a*;
在宏的用法中
#define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \//如果调用者传参时,两者类型不一致,在编译时就会发出警告。
_max1 > _max2 ? _max1 : _max2; })
首先定义和x相同类型的变量 _max1,再将x的值赋给_max1
(void) (&_max1 == &_max2);
typeof( ((type *)0)->member )
ANSI C标准允许值为0的常量被强制转换成任何一种类型的指针,并且转换的结果是个NULL,因此((type *)0)的结果就是一个类型为*type 的NULL指针.
如果利用这个NULL指针来访问type的成员当然是非法的,但*typeof( ((type *)0)->member )*是想取该成员的类型,编译器不会生成访问type成员的代码,类似的代码&( ((type *)0)->member )在最前面有个取地址符&,它的意图是想取member的地址,所以编译器同样会优化为直接取地址。
struct apple{
int weight;
int color;
};
int main(int argc, char *argv[])
{
int weight = 5;
typeof(((struct apple *)0)->weight) *ap1 = &weight;//定义一个指针变量ap1, ap1的类型为apple成员weight的类型。
printf("ap1's value:%d\n", *ap1);
}
ap1’s value:5
offset_of()
offset_of()的作用是获取结构体中某个成员相对于该结构体首元素地址的偏移量。
#define offset_of(type, memb) \
((unsigned long)(&((type *)0)->memb))
内核中链表的遍历
如前所示,内核将链表节点嵌入数据结构,已有list的地址情况下,为了获取数据结构的成员,通过container_of()可以获取到父结构中包含的变量。
举例如下
{
struct ipstore ip1;
struct ipstore *p1;
p1 = container_of(&ip1.list, struct ipstore, list);
printf("ip1's addr:0x%0x\n", &ip1);
printf("p1's addr:0x%0x\n", p1);
}
ip1’s addr:0xa5fe1fe0
p1’s addr:0xa5fe1fe0