最近想对linux内核有所了解,看到一些有趣的技巧,从书上摘录下来供以后查阅
这个语句只会执行一次,如果写成
或者
在if()
DUMP_WRITE(addr, nr);
else
dosomething();
中无法编译。
另外,对于列表的操作使用偏移可以让列表统一化(list.h)
表的结构
内核队列的结构(mm.h)struct page {
page_flags_t flags;
atomic_t _count;
atomic_t _mapcount;
unsigned long private;
struct address_space *mapping;
pgoff_t index;
struct list_head lru;
#if defined(WANT_PAGE_VIRTUAL)
void *virtual;
#endif /* WANT_PAGE_VIRTUAL */
};
注意内核中用结构内部的成员保存队列信息,这样不必对每种结构定义一个list及对list的操作,取数据的方法如下(mm/page_alloc.c): page = list_entry(area->free_list.next, struct page, lru);
(list.h): #define list_entry(ptr, type, member) \
container_of(ptr, type, member)
(kernel.h):#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
以下为对此函数的说明,转载处http://www.yuanma.org/data/2006/0925/article_1595.htm 及http://blog.csdn.net/yinkaizhong/article/details/4093795
关于offsetof见stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的类型 0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员. 由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.
关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是member的入口地址了.
我们在书写C程序的时候,有时候需要根据结构体成员变量的地址,得到结构体的地址,特别是我们想用C来实现C++的继承特性的时候。
我们对问题的分析如下:
- 输入:一个结构体定义type,这个结构体中某个成员变量的名字member以及它的地址ptr
- 输出:包含此成员变量的结构体的地址
为了便于分析,我们给出一个实例来说明
struct father_t { int a; char *b; double c; }f; char *ptr = &(f.b); //而不是 ptr = f.b; 这里ptr是b的地址,而不是它指向的地址。 |
根据C语言对struct类型的存储特性,我们可以画这么一个图示:
通过分析图示,我们可以看出,我们只需要把当前知道的成员变量的地址ptr,减去它在结构体当中相对偏移4就的到了结构体的地址(ptr-4)。
在linux当中对此有一个很好的宏可以使用,叫做 container_of, 放在 linux/kernel.h当中。它的定义如下所示:
/** * 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) );}) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
- (type *)0->member为设计一个type类型的结构体,起始地址为0,编译器将结构体的起始的地址加上此结构体成员变量的偏移得到此结构体成员变 量的偏移地址,由于结构体起始地址为0,所以此结构体成员变量的偏移地址就等于其成员变量在结构体内的距离结构体开始部分的偏移量。即:&((type *)0->member)就是取出其成员变量的偏移地址。而其等于其在结构体内的偏移量:即为:(size_t)(& ((type *)0)->member)经过size_t的强制类型转换后,其数值为结构体内的偏移量。该偏移量这里由offsetof()求出。
- typeof(((type *)0)->member)为取出member成员的变量类型。用其定义__mptr指针。ptr为指向该成员变量的指针。__mptr为member数据类型的常量指针,其指向ptr所指向的变量处。
- (char*)__mptr转换为字节型指针。(char*)__mptr - offsetof(type,member))用来求出结构体起始地址(为char *型指针),然后(type*)(char*)__mptr - offsetof(type,member))在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。
这就是从结构体某成员变量指针来求出该结构体的首指针。指针类型从结构体某成员变量类型转换为该结构体类型。