昨天被告知自己编写的内核函数中链表结构用的太乱,让自己采用内核给定的那套链表机制去编程,答曰不会用。。 因此,今天变抽空把内核中链表的那一套给大致看了一下,发现了Linux中比较有意思的一些设计技巧,便把这些都记下来吧
/**
* list_entry - get the struct for this entry
* @ptr: the &struct lis
t_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
首先是看到了这个宏定义,通过结构体类型type中的链表指针ptr及其在结构体中的命名member,找到结构体所在的地址
以下面的结构体为例
struct my_struct
{
int value;
struct list_head list;
};
在这里是这样计算实现的,首先通过(char *)ptr获取链表结构list的地址,而(&((type *)0)->member)则是计算出list所在的地址与结构体首地址的偏移量,通过把从地址0开始的一段空间强制转换成type类型的变量,则此时结构体的首地址为0,member所在的地址即为便宜地址,最后,将list所在的地址减去list与首地址的偏移量想减,即得到了结构体的首地址
Linux中这样的技巧很多,在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) );})
container_of的用法与list_entry相近,只不过这里作出了意义上的扩展,不再局限于双线链表的结构,而是可以扩展到任意结构体
1.offsetof(type,member)即时通过上面的方法计算出结构体中任一变量与结构体首地址的偏移量
#define offsetof(TYPE, MEMB) ((size_t) &((TYPE *)0)->MEMB)
2.剩下的内容在计算方式上与list_entry基本相当,在这里__mptr既是结构体中的成员member类型的指针,由typeof定义
3.ptr - offsetof()就等于包含该ptr的type结构体父变量的物理起始地址,强制转换成(type *)
typeof也是Linux中定义变量类型的一种方式,它的理解可以借助于sizeof来理解
sizeof(exp)返回的是exp的数据类型的大小,那么typeof(exp)返回的就是exp的数据类型
可以看这样一个例子
int x = 3;
typeof(&x) m;
*m = 5;
虽然不太严谨,但大致能表达出这个意思,x为整形数据,&x为指向整形的指针,那么typeof(&x)即返回的结果为&x的类型 指向整形的指针,因此定义的变量m也就是在定义一个指向整形变量的指针m
更复杂一些的例子
struct my_struct
{
int x;
char y;
};
typeof((struct my_struct *)0->y) m;
m = 'a';
虽然看起来比较复杂一些,但是((struct my_struct *)0)->y 仅仅就是表示了y这个变量,所以typeof的结果就是y元素的类型,也就是char
Linux的C代码写的的确很漂亮,能把c用的出神入化的。。。 呵呵,看了还要继续学习了
阅读(1570) | 评论(0) | 转发(0) |