Double-list的泛型C实现
在linux内核中有一个核心的泛型数据结构list_head,它是一个双链表,大部分的linux内核数据结构都是基于它建立的。
list_head结构很简单:
struct list_head{
struct list_head *prev;
struct list_head *next;
};
你如果需要定义自己的双链表结构,可以把它嵌入到你的结构中而进行重用:
struct fox{
int val;
struct list_head list;
};
这样带来的好处是你可以使用为list_head实现的很多链表操作函数如list_add、list_delete、list_replace以及list_for_each等,从而省去了很多你自己需要实现的链表操作代码。例如下面的例子:
void main(){
struct fox f1, f2, f3, *f;
struct list_head *list;
f1.val = 1;
f2.val = 2;
f3.val = 3;
INIT_LIST_HEAD(&f1.list);
list_add(&f2.list, &f1.list);
list_add(&f3.list, &f2.list);
__list_for_each(list, &f1.list){
f = list_entry(list, struct fox, list);
printf("%d\t", f->val);
}
}
能重用的关键是我们需要能够从list_head中取出自己定义的结构fox,毕竟所有的链表操作都是基于list_head进行的,如:
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
void __list_add(struct list_head *new_node, struct list_head *prev, struct list_head *next){
next->prev = new_node;
new_node->next = next;
new_node->prev = prev;
prev->next = new_node;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
void list_add(struct list_head *new_node, struct list_head *head){
__list_add(new_node, head, head->next);
}
要从list_head中取出父结构struct fox,在C语言中可以通过获取list_head在结构fox中的偏移,然后把list_head指针减去那个偏移(内存是从小到大放置对象的):
unsigned long offset = (unsigned long)(&((struct fox *)0)->list);
struct fox *f = ((struct fox *)((char *)list - offset);
上面可以通过定义宏container_of进行简化:
#define container_of(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
有些编译器支持offsetof宏,如gcc,这样可以简化container_of的编写。