深入分析Linux内核链表
1. 普通单链表
2. 内核链表
上图是本人从其他博客盗来的,差点被糊弄过去。
下图是本人自己用KeyNote画的(唉!!画图真的是让人心好累啊!!)。
差异是不是很明显啊?!
Read The Fucking Source Code
1. 初始化
/* include/linux/types.h */ struct list_head { struct list_head *next, *prev; };
/* include/linux/list.h */
/*××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××*/
// 一. 如何初始化一个链表,初始化后的链表是什么鸟样?
// 链表初始化的3个方法:
// 1.
#define LIST_HEAD_INIT(name) { &(name), &(name) }
// 使用示例: struct list_head test_list = LIST_HEAD_INIT(test_list);
// 2.
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
// 使用示例: LIST_HEAD(module_bug_list);
// 3.
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
// 使用示例: struct list_head test_list;
INIT_LIST_HEAD(&test_list);
/*××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××*/
就连一个链表的初始化都想的这么周到!!真屌!!
初始化后,链表就是的鸟样:
2. 插入
/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST 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; } #else extern void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next); #endif /** * 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. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }
/**
* list_add 和 list_add_tail的区别是:
* list_add 始终是在链表头后的的第一个位置进行插入:例如链表:head --> 数据1 --> 数据2 --> 数据3,插入新元素后:head --> new --> 数据1 --> 数据2 --> 数据3
* list_add_tail 始终实在链表末尾插入新元素:例如链表:head --> 数据1 --> 数据2 --> 数据3,插入新元素后:head --> 数据1 --> 数据2 --> 数据3 --> new
*/
/**
* 仔细分析上述函数,可以发现其函数抽象的巧妙。
* __list_add 接收三个参数:分别是new, prev, next。任何位置的双链表插入操作,只需这3个参数。那么new元素一定是在prev和next之间进行插入。
* 所以很明显:list_add是在head和head->next之间插入,那就是链表的第一个元素。
* list_add_tail实在head->prev和head之间插入,那就是链表的最后一个元素。
*/
3. 删除
/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ #ifndef CONFIG_DEBUG_LIST 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; } #else extern void __list_del_entry(struct list_head *entry); extern void list_del(struct list_head *entry); #endif
/**
* 上述代码中存在两个宏,在include/linux/poison.h中的定义如下:
*/
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
* 是非空指针,在正常情况下会导致 page faults,用来验证没有人使用未初始化的链表项。
*/
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
/*
* __list_del_entry 和 list_del 的却别是显而易见的。
*/
至此,内核链表有了本质的认识,那么对于其他的链表操作的分析是非常容易的。