struct list_head结构体是linux实现数据结构双向链表的基础。其定义:
struct list_head {
struct list_head *next, *prev;
};
可见链表里面的成员还是链表,每个链表都指向了前面和后面的链表。
一般将struct list_head作为一个成员,放到一个结构体中,其作用是可以从当前的结构体指针,获取到下一个链表元素的地址。一般用list_entry()来实现。具体按照下面的链接:
http://www.cnblogs.com/bastard/archive/2012/10/19/2731107.html
ok,下面来看看list_head为我们提供的一些接口:
先看一个基础的:
添加add:
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;
}
这是在原来的链表中添加一个元素。两个接口,一个是添加为当前list_head的next,一个是添加到prev。对应的函数分别为:list_add和list_add_tail.
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);
}
如果不好理解的话,自己画个图,应该就能明白了。
删除delet:
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
删除就是把某一节点从链表中删除。entry->next = LIST_POISON1; entry->prev = LIST_POISON2; LIST_POISON1/2在linux/poison.h中定义。删除后,为什么还要设置删除掉的节点指针呢?因为删除后,该选节点已不在链表当中,因此不会再使用。LIST_POISON1/2是两个不会存在于内核空间的地址,如果使用,就会报错。因此,设置指针,是强制禁用该节点。
inline void list_del_init 把entry从链表中delete,并将其初始化为一个空的链表。
替代replace:
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);
}
移动move:
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
从原链表中删除,并添加到新的链表中。list_move添加到head后面,list_move_tail添加到head的前面。
//将head和它的next互换
static inline void list_rotate_left(struct list_head *head)
{
struct list_head *first;
if (!list_empty(head)) {
first = head->next;
list_move_tail(first, head);
}
}
/**
* list_is_singular - tests whether a list has just one entry. //测试是否只有一个节点,除了头节点外。
* @head: the list to test.
*/
static inline int list_is_singular(const struct list_head *head)
{
return !list_empty(head) && (head->next == head->prev);
}
链表一分为二cut:
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
注意这个是从entry处分开,list到entry为新的链表。head、entry->next、、、为老的链表。
举例:原来的链表是 head->1->2->3->4->5(只列出next方向,previous方向就是反过来),如果entry是2,那么新的链表是list->1->2,原来的链表则变为head->3->4->5。
看代码的一些小心得:一,阅读英文注释,不要害怕。二,静下心。
合并:
static inline void __list_splice(const struct list_head *list,
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
就是将其中的一个链表,放到另一个链表当中。上面的代码就是将list放到放到prev和next之间。
主要的函数接口有:
static inline void list_splice(const 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_empty(list))
__list_splice(list, head->prev, head);
}
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
上面都比较简单,不细谈,不行可以去看看函数的解释。
下面是一些获取指针的操作:
基础:
#define list_entry(ptr, type, member)
container_of(ptr, type, member)
container_of这个函数,一定要牢记,它是通过已知的指针ptr,去获取它存在于那个type类型的结构体的指针,它在这个type结构体里面的成员名叫member。
之前说了一般链表的第一个是做连接用的,没什么实际意义,因此衍生的函数有:
#define list_first_entry(ptr, type, member)
list_entry((ptr)->next, type, member)
获取第一个链表成员的指针。
遍历链表:
正向:
#define list_for_each(pos, head)
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_safe(pos, n, head)
for (pos = (head)->next, n = pos->next; pos != (head);
pos = n, n = pos->next)
反向:
#define list_for_each_prev(pos, head)
for (pos = (head)->prev; pos != (head); pos = pos->prev)
#define list_for_each_prev_safe(pos, n, head)
for (pos = (head)->prev, n = pos->prev;
pos != (head);
pos = n, n = pos->prev)
关于safe的说明:如果是list_for_each,在处理的过程中,将pos的节点给删除了,那就引起了错误,因为pos->next就成了我们上面说的LISTPOISON1,操作的时候就引起页错误。(list_del(pos)将pos的前后指针指向undefined state,导致kernel panic,list_del_init(pos)将pos前后指针指向自身,导致死循环)
safe机制在于提供了一个临时存放区,可以对其进行操作后,再返回到原链表中。思考:如果在处理过程中,删除了n,那是不是也会引起错误呢?????