3.4. linux 通用双向循环链表
在linux内核中,有一种通用的的双向循环链表,构成了各种队列的基础。链表的结构定义和相关函数均在 include/linux/list.h
中。
include/linux/types.h 中对 list_head
做出了定义
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->prev = list;
list->next = list;
}
初始化时,链表头的prev和next都是指向自身的。
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
{
new->next = next;
next->prev = new;
prev->next = new;
new->prev = prev;
}
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);
}
双向循环链表的实现,很少有例外的情况,基本都可以用公共的方式来处理。
链表API实现时大致都是分为两层:一层是外部的,如 list_add
list_add_tail
,用来消除一些例外情况,调用内部实现;
一层是内部的,函数名前会加双下划线,如 __list_add
,往往都是几个操作公共的部分,或者排除例外后的实现。
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
prev->next = next;
next->prev = prev;
}
static inline void list_del(struct list_head *head)
{
__list_del(head->prev, head->next);
}
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
LIST_INIT_HEAD(entry);
}
list_del是将链表中的节点删除,list_del_init则是将链表中的节点删除后再次把节点指针初始化。
static inline void list_replace(struct list_head *old, struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
static inline void list_replace_init(struct list_head *old, struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
list_replace 将链表中的一个节点old,替换为另一个节点new。
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, 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_move 的作用是将list节点从原链表中删除,并加入新的链表head中。
static inline int list_is_last(struct list_head *list, struct list_head *head)
{
return (list->next == head);
}
list_is_last 判断list是否处于head链表的尾部。
static inline int list_empty(struct list_head *head)
{
rerurn (head->next == head);
}
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
list_empty 判断head链表是否为空
static inline int list_is_singular(const list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
list_is_singular 判断head是否只有一个节点,即除链表头head外是否只有一个节点。
static inline void __list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry)
{
entry->next->prev = head->prev;
head->prev->next = entry->next;
list->next = head;
head->prev = list;
entry->next = list;
list->prev = entry;
}
static line void lis_cut_position(struct list_head *list, struct list_head *head, struct list_head entry)
{
if(list_entry(head))
return;
if(list_is_singular(head) && (list->next != entry && head != entry))
return;
if(entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
list_cut_position 用于把head链表分为两部分,从head->next 一直到entry被从head链表中删除,加入新的链表list。 新的链表应该是空的,或者原来的节点可以被忽略掉。
static inline void __list_splice(struct list_head *list, struct list_head *prev, struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
prev->next = first;
first->prev = prev;
last->next = next;
next->prev = last;
}
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if(!list_empty(list))
__list_splice(list, head, head->next);
}
static inline void list_splice_tail(struct list_head *list, struct list_head *head)
{
if(!list_emptu=y(list))
__list_splice(list, head->prev, head);
}
list_splict 的功能和list_cut_posiotion相反,它合并两个链表。list_splice把list链表中的节点加入到head链表中。
list_splict是把原来list链表中的节点全部插入到head链表的头部,list_splice_tail则是把原来list链表中的节点全部加到head链表的尾部。
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
list_entry 主要用于从list节点查找其内嵌在的结构。比如定义了一个结构struct A{struct list_head list;},如果知道结构中链表的地址ptrlist, 就可以从ptrlist进而获取整个结构的指针,struct A *Ptr = list_entry(ptrlist, struct A, list);
这种地址翻译的技巧是linux的拿手好戏,container_of随处可见,只是链表节点多被封装在更复杂的结构中,使用专门的list_entry定义也是对了使用方便
#define __list_for_each(pos,head) \
for(pos = (head)->next; pos != head; pos = pos->next)
list_for_each 遍历链表中的每一个节点。
#define list_for_each_prev(pos,head) \
for(pos = (head)->prev; pos != head; pos = pos->prev)
list_for_each_prev 遍历链表中的每一个节点,不同的是它是逆序的.
#define list_for_each_safe(pos, n, head) \
for(pos = (head)->next, n = pos->next;pos != head; pos = n,n = pos->next)
list_for_each_safe 也是链表顺序遍历,只是它更加安全,即使在遍历过程中喔咕,当前节点从链表中删除,也不会影响链表的遍历。参数中需要加一个暂存的链表节点指针n
#define list_for_each_entry(pos, head, member) \
for(pos = list_entry(head->next, typeof(*pos), member);
&pos->member != head; \
pos = list_entry(pos->memer.prev, typeof(*pos), member)
)
list_for_each_entry 不是遍历链表节点,而是遍历链表节点所嵌套进的结list_for_each_entry 不是遍历链表节点,而是遍历链表节点所嵌套进的结构.