1 list_entry作用就是通过list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是type型的,
而ptr在其宿主结构中定义为member成员。定义在内核源文件include/linux/list.h中,
对比list_entry(ptr,type,member)可知有以下结果:
其中list相当于member成员,struct example_struct相当于type成员,ptr相当于ptr成员。而list{}成员嵌套于example_struct{}里面。ptr指向example_struct{}中的list成员变量的。在list_entry()作用下,将ptr指针回转指向struct example_struct{}结构体的开始处
#define list_entry(ptr, type, member) /
container_of(ptr, type, member)
container_of是个宏,定义如下:
#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); / // typeof是gcc扩展,用于得知数据类型的,这句话作用大
//概是看ptr是否是结构体里成员变量member的类型,不是编译时将报错,类型检测的
(type *)( (char *)__mptr - offsetof(type,member) );})
offsetof是计算成员member在结构体的type里面的偏移数,用__mptr的地址减去偏移地址就可以得到type结构体的开始地址
container_of向上面这样定义展开后可以直接赋值如下面这样
#include
int main(int argc,char argv[])
{
int x = 1,y = 2;
int m =
({
x++;
x + y;
});
fprintf(stdout,"----%d/n",m);
}
这种用法应该是gcc的扩展
2
在linux内核中,list是无处不在,好多代码都include了list.h。在linux源代码中,由于list的运用过于频繁,所以就把 list 单独定义了,如果有哪个结构要成为列表,就把结构的第一个成员定义成list_head,然后初始化就可以了。
list.h应该是在include/linux/list.h,里面的代码很易懂,大家翻翻吧。当然数据结构要过关先,起码要知道
list是双向链表还有双向链表的结构是如何的。
一部分代码:
typedef struct list_head {
struct list_head *next, *prev; //从定义中可以看出是个双向队列
} list_t;
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) /
struct list_head name = LIST_HEAD_INIT(name) //定义一个空的列表
#define INIT_LIST_HEAD(ptr) do { / //初始化一个已定义的列表
(ptr)->next = (ptr); (ptr)->prev = (ptr); /
} while (0)
.
.
前三个宏用来初始化一个next和prev指针皆指向自身的空链表,宏可以用到的地方明显受限于c语法约束。例如,LIST_HEAD_INIT()用来初始化结构元素,第二个宏用来初始化静态变量,第三个用于函数内部。
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head) //加入一个新节点
{
__list_add(new, head, head->next);
}
在list.h中还有一个比较有趣的宏
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_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)))
从上面我们知道list_entry的作用是返回类型type的数据结构地址,但是具体怎么实现的呢?看代码怪怪的。其实我们只要知道
&((type *)0)->member 的意思就明白了。它是取结构成员偏移量的一种方法,将常数0强制为结构指针,取其成员的地址,结果就是成员的偏移量。
list_entry宏根据list_head型指针ptr换算成其宿主结构的起始地址,该宿主结构是 type型的,而ptr在其宿主结构中定义为member成员。如下图:
req-->|type型对象起始地址
|
|... ...
ptr-->|ptr指针所指的member成员地址
|
|... ...
ptr指向图中所示的位置,通过(unsigned long)(&((type*)0)->member)得到ptr 和req之间的差值,ptr减去这个差值就得到了type型宿主结构的指针req,返回 类型为(type*)。
了解上面那个宏,你就
知道了 list_head 是如何工作的了。首先,我们先定义并初始化一个 list_head,然后声明一个结构,如下
struct demo {
struct list_head list;
int i;
}
struct demo demo1;
list_add(&demo1->list, list_head) //把demo1加入列表,
list_head仅仅就是两个指针
这样的列表是非常灵活的,不同类型的变量都可以加到列表中去。如下图所示:
_________________________________________________
| list | 1 | 2 | 3 |
|_head |____________|___________|____________ |____
|| || ||
|| || ||
demo1 demo2 struct dev dev1
所以,我们只要知道了,list_head的地址,里面的东西就好找了。
注意:
队列头list_head不能被嵌入到结构中。
list_add(n,p):把n指向的元素插入p所指向的特定元素之后
list_add_tail(n,h):把n所指向的元素插到第一个元素的地址h所指定的链表尾
list_empty(p):检查由第一个元素的地址指定的链表是否为空
list_for_each(p,h):对第一个元素的地址h指定的链表进行扫描
//摘自 http://answes.spaces.live.com/blog/cns!76AAAD406604238A!179.entry?_c=BlogPart
3 上面对list所讲的东西,都是适用于内核的(module),在用户态下好像不可以