/*本篇由真胖子同志打造,原创辛苦,转载请标明出处http://blog.csdn.net/figtingforlove/article/details/20124267*/
(一)双链表
内核几乎每个稍大的数据结构都会包含双链表结构,用于管理内核的各个数据结构,内核为实现双链表提供了若干通用函数和结构,方便程序设计时使用。
1.双链表的定义
linux/types.h
185 struct list_head {
186 struct list_head *next, *prev;//前驱和后继节点指针
187 };
2.双链表的初始化(指向自己)
linux/list.h
/*静态初始化宏*/
19 #define LIST_HEAD_INIT(name) { &(name), &(name) }
20
21 #define LIST_HEAD(name) \
22 struct list_head name = LIST_HEAD_INIT(name)
23 /*动态初始化函数*/
24 static inline void INIT_LIST_HEAD(struct list_head *list)
25 {
26 list->next = list;
27 list->prev = list;
28 }
3.插入(add a new entry)
37 static inline void __list_add(struct list_head *new,
38 struct list_head *prev,
39 struct list_head *next)
40 {
41 next->prev = new;
42 new->next = next;
43 new->prev = prev;
44 prev->next = new;
45 }
/* 插入到链表头*/
52 /**
53 * list_add - add a new entry
54 * @new: new entry to be added
55 * @head: list head to add it after
56 *
57 * Insert a new entry after the specified head.
58 * This is good for implementing stacks.
59 */
60 static inline void list_add(struct list_head *new, struct list_head *head)
61 {
62 __list_add(new, head, head->next);//(新节点,新节点的前驱,新节点的后继
63 }
/*插入到链表尾*/
66 /**
67 * list_add_tail - add a new entry
68 * @new: new entry to be added
69 * @head: list head to add it before
70 *
71 * Insert a new entry before the specified head.
72 * This is useful for implementing queues.
73 */
74 static inline void list_add_tail(struct list_head *new, struct list_head *head)
75 {
76 __list_add(new, head->prev, head);
77 }
4.删除
80 /* Delete a list entry by making the prev/next entries
81 * point to each other.
82 *
83 * This is only for internal list manipulation where we know
84 * the prev/next entries already!
85 */
86 static inline void __list_del(struct list_head * prev, struct list_head * next)
87 {
88 next->prev = prev;
89 prev->next = next;
90 }
91
92 /**
93 * list_del - deletes entry from list.
94 * @entry: the element to delete from the list.
95 * Note: list_empty() on entry does not return true after this, the entry is
96 * in an undefined state.
97 */
98 #ifndef CONFIG_DEBUG_LIST
99 static inline void __list_del_entry(struct list_head *entry)
100 {
101 __list_del(entry->prev, entry->next);
102 }
103
104 static inline void list_del(struct list_head *entry)
105 {
106 __list_del(entry->prev, entry->next);
107 entry->next = LIST_POISON1;
108 entry->prev = LIST_POISON2;
109 }
110 #else
/*没有上面的配置选项,__list_del_entry函数会有一些告警信息的输出,但函数的主体是一样的*/
111 extern void __list_del_entry(struct list_head *entry);
112 extern void list_del(struct list_head *entry);
113 #endif
上面有两个宏定义的常数值LIST_POISON1和LIST_POISON2,这是为了确保不能使用已经删除的节点,内核做了如下定义
7 /* Architectures might want to move the poison pointer offset
8 * into some well-recognized area such as 0xdead000000000000,
9 * that is also not mappable by user-space exploits:
10 */
11 #ifdef CONFIG_ILLEGAL_POINTER_VALUE
12 # define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
13 #else
14 # define POISON_POINTER_DELTA 0
15 #endif
16
17 /*
18 * These are non-NULL pointers that will result in page faults
19 * under normal circumstances, used to verify that nobody uses
20 * non-initialized list entries.
21 */
22 #define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
23 #define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
可见这两个值代表内存位置 0x00100100 和 0x00200200,其实相当于一个内存位置标记。在内核中0xC0000000以下(内存为4G,高地址1G作为内核空间使用,低地址3G作为用户空间使用,此3G内核空间申请不到)的地址内核是不能申请到的。为了防止有的节点申请内存的时候使用不可能被初始化的指针来当作标记比如NULL,而0x00200200 这样的低地址是内核空间申请内存不会出现的地址可以拿来当标记。因为0x00200200不能被内核申请,所以当我们在内核空间引用该指针时,就会出现BUG。
把entry->prev 设置为 LIST_POISON2,这样做有一个好处,就是:当你一旦不小心错误的引用了 entry->prev 这个指针的时候,就会打印如下的出错信息:Unable to handle kernel paging request at virtual address 00200200这样由出错信息中的“00200200”,你就可以知道是错误的引用了已经被删除了的链表节点了!相反,如果把entry->prev 设置为 NULL,当你一旦不小心错误的引用了 entry->prev 这个指针的时候,打印的出错信息如下:Unable to handle kernel paging request at virtual address 00000000这样我们从出错地址“00000000”看不出出错的原因是错误的引用了已经被删除了的链表节点,因为NULL在很多地方都会被用到。
此外,内核还定义了另一个删除节点的函数
138 /**
139 * list_del_init - deletes entry from list and reinitialize it.
140 * @entry: the element to delete from the list.
141 */
142 static inline void list_del_init(struct list_head *entry)
143 {
144 __list_del_entry(entry);
145 INIT_LIST_HEAD(entry);
146 }
这个函数在删除了链表节点后,对她又进行了初始化。这样就不会访问到未初始化的地址而出现错误,一般我们用到的大多是list_del 而不是 list_del_init。5.判空
182 /**
183 * list_empty - tests whether a list is empty
184 * @head: the list to test.
185 */
186 static inline int list_empty(const struct list_head *head)
187 {
188 return head->next == head;
189 }
190
/*
不安全方式(并行时)判断链表是否为空
*/
191 /**
192 * list_empty_careful - tests whether a list is empty and not being modified
193 * @head: the list to test
194 *
195 * Description:
196 * tests whether a list is empty _and_ checks that no other CPU might be
197 * in the process of modifying either member (next or prev)
198 *
199 * NOTE: using list_empty_careful() without synchronization
200 * can only be safe if the only activity that can happen
201 * to the list entry is list_del_init(). Eg. it cannot be used
202 * if another CPU could re-list_add() it.
203 */
204 static inline int list_empty_careful(const struct list_head *head)
205 {
206 struct list_head *next = head->next;
207 return (next == head) && (next == head->prev);
208 }
6.遍历查找内核提供了许多函数和宏用于链表的遍历,非常方便我们安全的使用/*取链表中的一项*/
344 /**
345 * list_entry - get the struct for this entry
346 * @ptr: the &struct list_head pointer.指向结构体中成员的指针
347 * @type: the type of the struct this is embedded in.结构体的类型
348 * @member: the name of the list_struct within the struct.这个成员在结构体中的名字
349 */
350 #define list_entry(ptr, type, member) \
351 container_of(ptr, type, member)
/*从结构体的成员来获得包含成员的结构体实例,这个宏在内核中的应用非常广泛*/
791 #define container_of(ptr, type, member) ({ \
792 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
793 (type *)( (char *)__mptr - offsetof(type,member) );})
((type *)0)表示将空指针转换为type类型的指针,也就是该结构体类型的指针,这是允许的因为我们并不反引用该指针。lixnux/kernel.h中这样定义offset宏
14 #ifndef offsetof
15 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
16 #endif
实际就是取MEMBER成员的相对地址,因为当TYPE类型的结构体地址为0时,取其地址&((TYPE *)0)->MEMBER就是它的相对偏移量。定义了一个指向该成员类型的指针__mptr,将它转换为char*指针相当于指向字节类型的地址,再减去MEMBER的偏移量得到的就是TYPE类型结构体的首字节指针,然后把这指针转回TYPE类型,perfect!
6.1遍历一個链表的方法
402 /**
403 * list_for_each - iterate over a list
404 * @pos: the &struct list_head to use as a loop cursor.
405 * @head: the head for your list.
406 */
407 #define list_for_each(pos, head) \
408 for (pos = (head)->next; pos != (head); pos = pos->next)
还有另一个安全的方法
418 /**
419 * list_for_each_safe - iterate over a list safe against removal of list entry
420 * @pos: the &struct list_head to use as a loop cursor.
421 * @n: another &struct list_head to use as temporary storage
422 * @head: the head for your list.
423 */
424 #define list_for_each_safe(pos, n, head) \
425 for (pos = (head)->next, n = pos->next; pos != (head); \
426 pos = n, n = pos->next)
两者的区别在于,list_for_each_safe缓存了pos->next,这个优势可以体现在删除链表想的时候。当在遍历的过程中需要删除结点时,会出现什么情况
list_for_each():list_del(pos)将pos的prev/next指向undefine stat(这里指的是前面提到的LIST_POSITION1),这样下一次迭代的时候
pos = pos->next
pos将指向一个不可用的位置,导致kernel panic,出错;
list_del_init(pos)将pos的prev/next指向指向自身,这样下一次迭代的时候
pos = pos->next
pos将指向自身,导致死循环。list_for_each_safe():首先将pos的next指针缓存到n,处理一个流程后再赋回pos,避免了这种情况发生。
因此之遍历链表不删除结点时,可以使用list_for_each(),而当有删除结点操作时,则要使用list_for_each_safe()。其他带safe的处理也是基于这个原因。
6.2遍历由链表项链接结构体
439 /**
440 * list_for_each_entry - iterate over list of given type
441 * @pos: the type * to use as a loop cursor.
442 * @head: the head for your list.
443 * @member: the name of the list_struct within the struct.
444 */
445 #define list_for_each_entry(pos, head, member) \
446 for (pos = list_first_entry(head, typeof(*pos), member); \
447 &pos->member != (head); \
448 pos = list_next_entry(pos, member))
list_first_entry and list_next_entry
353 /**
354 * list_first_entry - get the first element from a list
355 * @ptr: the list head to take the element from.
356 * @type: the type of the struct this is embedded in.
357 * @member: the name of the list_struct within the struct.
358 *
359 * Note, that list is expected to be not empty.
360 */
361 #define list_first_entry(ptr, type, member) \
362 list_entry((ptr)->next, type, member)
386 /**
387 * list_next_entry - get the next element in list
388 * @pos: the type * to cursor
389 * @member: the name of the list_struct within the struct.
390 */
391 #define list_next_entry(pos, member) \
392 list_entry((pos)->member.next, typeof(*(pos)), member)
(二)哈希链表
linux还提供了一个双线链表的修改版,非常适合实现散列表中的益处链表
详细分析请看博客http://blog.csdn.net/cwcmcw/article/details/17124205