在linux内核中,有一种通用的双向循环链表,构成了各种队列的基础。链表的结构定义和相关函数均在
include/linux/list.h
中,下面就来全面的介绍这一链表的各种API。
循环链表,表头和表中节点都是下面元素结构。有prev,next两个指针分指向链表中前一个节点和后一个节点。
struct list_head{
struct list_head *next,*prev;
}
链表初始化的时候,链表头的prev和next都是指向自身的.
#define LIST_HEAD_INIT(name) {&(name),&(name)}
#define LIST_HEAD(name) \
struct list_head name=LIST_HEAD_INIT(name);
static inline void INIT_LIST_HEAD(struct list_head*list){
list->next=list;
list->prev=list;
}
双向循环链表实现,基本用公共方法处理实现。无论是新加一个节点还是其他操作,使用方法一样。另外iain,链表API实现大致都分为两层:
list_add
、list_add_tail
,用来消除一些例外情况,调用内部实现。__list_add
,往往几个操作公共的部分,或者排除例外的实现。//pre和next 之间插入new
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);
}
static inline void list_add_tail(struct list_head*new,struct list_head*head){
__list_add(new,head->prev,head);
}
list_del
是链表中节点的删除。之所以在调用__list_del后又把被删除元素的next、prev指向特殊的LIST_POSITION1
和LIST_POSITION2
,是为了调试未定义的指针。
static inline __list_del(struct list_head*prev,struct list_head*next){
next->prev=prev;
prev->next=next;
}
//list_del_init是删除节点后,随即把节点中指针再次初始化,这种删除方式更为实用。
static inline void list_del_init(struct list_head*entry){
__list_del(entry->prev,entry->next);
INIT_LIST_HAED(entry);//指向本身
}
//list_del是链表中节点的删除。之所以在调用__list_del后又把被删除元素的next、prev指向特殊的LIST_POSITION1和LIST_POSITION2,是为了调试未定义的指针。
static inline void list_del(struct list_head*entry){
__list_del(entry->prev,entry->next);
//LIST_POSITION1和LIST_POSITION2,是为了调试未定义的指针。
entry->next=LIST_POSTION1;
entry->next=LIST_POTIONS2;
}
list_replace是将链表中一个节点old,替换为另一个节点new。从实现来看,即使old所在地链表只有old一个节点,new也可以成功替换,这就是双向循环链表可怕的通用之处。
list_replace_init将被替换的old随即又初始化。
将list节点从原链表中去除,并加入新的链表head中。
//list_move的作用是把list节点从原链表中去除,并加入新的链表head中。
static inline void list_move(struct list_head*list,struct list_head*head){
__list_del(list->prev,list->next);
list_add(list,head);
}
//list_move_tail只在加入新链表时与list_move有所不同,list_move是加到head之后的链表头部,而list_move_tail是加到head之前的链表尾部。
static inline void list_move_tail(struct list_head*list,struct list_head*head){
__list_del(list->prev,list->next);
list_add_tail(list,head);
}
list_is_last
判断list是否处于head链表的尾部。
list_empty
判断head链表是否为空,为空的意思就是只有一个链表头head。
list_empty_careful
同样是判断head链表是否为空,只是检查更为严格。
list_is_singular
判断head中是否只有一个节点,即除链表头head外只有一个节点。
list_cut_position
用于把head链表分为两个部分。从head->next
一直到entry被从head链表中删除,加入新的链表list。新链表list应该是空的,或者原来的节点都可以被忽略掉。可以看到,list_cut_position
中排除了一些意外情况,保证调用__list_cut_position
时至少有一个元素会被加入新链表。
list_splice
的功能和list_cut_position
正相反,它合并两个链表。list_splice
把list链表中的节点加入head链表中。在实际操作之前,要先判断list链表是否为空。它保证调用__list_splice
时list链表中至少有一个节点可以被合并到head链表中。
地址翻译技巧是Linux拿手好戏,container_of
随处可见。链表节点多被封装在更复杂结构中,使用专门list_entry
定义更为方便。
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#define container_of(ptr,type,memeber) ({ \
const typeof(((type*)0)->member)* __mptr=(ptr); \
(type*)((char*)__mptr - offsetof(type,member));})
//list_entry主要用于从list节点查找内嵌结构。比如定义一个结构struct A{struct list_head list;};如果知道结构中链表的地址,ptrList,进而可以获得整个结构体地址。struct A*ptrA=list_entry(ptrlist,struct A,list);
#define list_entry(ptr,type,member) \
container_of(ptr,type,member)