1) struct list_head结构体
struct list_head {
struct list_head *next, *prev;
};
List_head 结构包含两个指向List_head的结构体指针prev、next,所以该链表具有双向链表的功能,但该链表没有数据区,该定义只给出了双向链表的结构,在实际使用中采用如下定义
Struct stu
{
Struct List_head list;
Int id;
Char name[20];
};
2 )链表的初始化
#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;
}
LIST_HEAD_INIT(name)将name的地址直接分别赋值给next和prev,那么它们事实上都指向自己,也形成一个空链表,上面的宏提供了定义并初始化一个list_head类型的结构体的功能 ,static修饰的函数就是静态函数,是对函数作用域的限制,指该函数的作用域仅限于本文件,不能被其他文件使用,因此static具有信息隐藏作用,inline修饰的函数的意思是这个函数对编译程序是可见的,编译程序在调用这个函数时就立即展开该函数,而不是保存现场,执行调用函数,恢复现场等繁琐操作,这样可以提高效率
3 )链表的插入
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;
}
该函数指出插入的前一节点prev和后一节点next,让后一节点的prev指向新节点,新的节点的next指向后一节点,让新一节点的prev指向前一节点,让前一节点prev的next指向新节点,从而插入该新节点
通过该函数还实现了以下两个函数分别为头插和尾插
static inline void list_add(struct list_head *new, struct list_head *head)
static inline void list_add_tail(struct list_head *new, struct list_head *head)
是通过调用__list_add函数参数的不同实现的,因为该list_head结构体为双链表,其中实现代码分别为:头插__list_add(new, head, head->next);
尾插__list_add(new, head->prev, head);
4 )链表的删除
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
删除是通过更改,前一节点的next域和后一节点的prev域,从而删除节点,不需需要知道该元素,但必须知道该元素的前仆和后继。操作后,此元素的前仆后继成为彼此的前仆后继,将指定元素挤出队伍但并没有消失,还可以访问
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
此操作删除一个链表中的指定元素。调用该函数后,指定元素从链表中消失,它的前仆后继成为彼此新的前仆后继。其中LIST_POISON1、LIST_POISON2是因为删除的元素其next和prev变量仍然是指向它以前的前仆后继,也就是说将会导致从一个非链表中的对象可以访问链表对象,会引起严重的安全问题
LIST_POISON1和LIST_POISON定义在include/linux/poison.h中:
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
将该被抛弃的元素的内部指针指向两个内核空间不会用到的内存位置,当试图访问该指针时会出现错误
5) 链表的遍历
list_for_each()
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
它实际上是一个for循环,利用传入的pos作为循环变量,从表头head开始,逐项向后(next方向)移动pos,直至又回到head,停止循环,该宏使用两个list_head类型的参数,第一个参数用来指向当前项,第二个参数指向需要检索的链表
当然使用该遍历链表只能得指向list_head结构体的指针,但是无法访问其成员变量,为此linux提供了
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
假设设有一个list_head的链表,且每个链表包含在另外一个结构体的对象里,这些外部结构体的对象通过list_head联系起来。如果ptr指向链表中一个已知的list_head对象,那么list_entry宏会返回包含这个对象的外部结构体对象的指针,从而实现访问其成员变量的遍历
6) 链表的替换
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;
}
一个新的指定list_head对象抢走了旧的指定list_head对象的前仆后继,表现就是该操作并没有对旧的list_head对象做后续处理,也就是说它仍然指向它以前的前仆后继,但原来的前仆后继已经不指向它。
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元素重新初始化,使其不会让它指向原有的链表,让其调用INIT_LIST_HEAD函数指向它自己,保证链表的安全性
7) 链表的移动
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
该函数在把一个list_head对象list从他原来所在的链表删除后,通过list_add函数把他放到另外一个链表指定元素head的后面,即成为head的后继,成为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_head对象list从他原来所在的链表删除后,通过list_add函数把他放到另外一个链表指定元素head的前面,即成为head的前仆,成为head原来前仆的后继