双链表的应用在内核中随处可见,list.h头文件集中定义了双链表(struct list_head结构体)的相关操作。比如这里的一个头文件中就有大量的struct list_head型的数据。
关于list.h的分析,网上资料很多,这里只是记录我在分析list.h中遇到的问题。
0.struct list_head结构体
可能这样写,更让我们习惯:
2 | struct list_head *next; |
3 | struct list_head *prev; |
这个结构经常作为成员与其他数据类型一起组成一个新的结构体(后文若无特别提示,“新结构体”均指类似下面举例的嵌套型结构体),比如:
我们已经看到,struct list_head这个结构比较特殊,它内部没有任何数据,只是起到链接链表的作用。对于它当前所在的这个结点来说,next指向下一个结点,prev指向上一个结点。通常我们通过指向struc list_head的指针pos来获取它所在结点的地址,尽而获取其他数据。也许你现在还比较困惑这一过程,别着急,后面有特别解释。
1.链表的初始化
其实可以从后往前看,这样更容易理解。INIT_LIST_HEAD函数形成一个空链表。这个list变量一般作为头指针(非头结点)。
1 | 28static inline void INIT_LIST_HEAD( struct list_head *list) |
下面的宏生成一个头指针name,如何生成?请看LIST_HEAD_INIT(name)。
1 | 25#define LIST_HEAD(name) \ |
2 | 26 struct list_head name = LIST_HEAD_INIT(name) |
LIST_HEAD_INIT(name)将name的地址直接分别赋值给next和prev,那么它们事实上都指向自己,也形成一个空链表。现在再回头看宏LIST_HEAD(name),它其实就是一个定义并初始化作用。
1 | 23#define LIST_HEAD_INIT(name) { &(name), &(name) } |
3.添加元素
这两个函数分别给链表头结点后,头结点前添加元素。前者可实现栈的添加元素,后者可实现队列的添加元素。
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);
这两个函数如何实现的?它们均调用的下面函数:
1 | 41static inline void __list_add( struct list_head * new , |
2 | 42 struct list_head *prev, |
3 | 43 struct list_head *next) |
现在我们要关注的是,list_add和list_add_tail两函数在调用__list_add函数时,对应的各个参数分别是什么?通过下面所列代码,我们可以发现这里的参数运用的很巧妙,类似JAVA中的封装。
1 | 64static inline void list_add( struct list_head * new , struct list_head *head) |
3 | 66 __list_add( new , head, head->next); |
6 | 78static inline void list_add_tail( struct list_head * new , struct list_head *head) |
8 | 80 __list_add( new , head->prev, head); |
注意,这里的形参prev和next是两个连续的结点。这其实是数据结构中很普通的双链表元素添加问题,在此不再赘述。下面的图可供参考,图中1~4分别对应__list_add函数的四条语句。
![Screenshot](https://i-blog.csdnimg.cn/blog_migrate/94d6aa0a99478383a19a75c78e943dce.png)
3.删除元素
这里又是一个调用关系,__list_del函数具体的过程很简单,分别让entry节点的前后两个结点(prev和next)“越级”指向彼此。请注意这个函数的后两句话,它属于不安全的删除。
1 | 103static inline void list_del( struct list_head *entry) |
3 | 105 __list_del(entry->prev, entry->next); |
4 | 106 entry->next = LIST_POISON1; |
5 | 107 entry->prev = LIST_POISON2; |
想要安全的删除,那么可以调用下面函数。还记得INIT_LIST_HEAD(entry)吗,它可以使entry节点的两个指针指向自己。
1 | 140static inline void list_del_init( struct list_head *entry) |
3 | 142 __list_del(entry->prev, entry->next); |
4 | 143 INIT_LIST_HEAD(entry); |
4.替换元素
用new结点替换old结点同样很简单,几乎是在old->prev和old->next两结点之间插入一个new结点。画图即可理解。
1 | 120static inline void list_replace( struct list_head *old, |
2 | 121 struct list_head * new ) |
4 | 123 new ->next = old->next; |
5 | 124 new ->next->prev = new ; |
6 | 125 new ->prev = old->prev; |
7 | 126 new ->prev->next = new ; |
同样,想要安全替换,可以调用:
1 | 129static inline void list_replace_init( struct list_head *old, |
2 | 130 struct list_head * new ) |
4 | 132 list_replace(old, new ); |
5 | 133 INIT_LIST_HEAD(old); |
5.移动元素
理解了删除和增加结点,那么将一个节点移动到链表中另一个位置,其实就很清晰了。list_move函数最终调用的是__list_add(list,head,head->next),实现将list移动到头结点之后;而list_move_tail函数最终调用__list_add_tail(list,head->prev,head),实现将list节点移动到链表末尾。
01 | 151static inline void list_move( struct list_head *list, struct list_head *head) |
03 | 153 __list_del(list->prev, list->next); |
04 | 154 list_add(list, head); |
08 | 162static inline void list_move_tail( struct list_head *list, |
09 | 163 struct list_head *head) |
11 | 165 __list_del(list->prev, list->next); |
12 | 166 list_add_tail(list, head); |
6.测试函数
接下来的几个测试函数,基本上是“代码如其名”。
list_is_last函数是测试list是否为链表head的最后一个节点。
1 | 174static inline int list_is_last( const struct list_head *list, |
2 | 175 const struct list_head *head) |
4 | 177 return list->next == head; |
下面的函数是测试head链表是否为空链表。注意这个list_empty_careful函数,他比list_empty函数“仔细”在那里呢?前者只是认为只要一个结点的next指针指向头指针就算为空,但是后者还要去检查头节点的prev指针是否也指向头结点。另外,这种仔细也是有条件的,只有在删除节点时用list_del_init(),才能确保检测成功。
01 | 184static inline int list_empty( const struct list_head *head) |
03 | 186 return head->next == head; |
06 | 202static inline int list_empty_careful( const struct list_head *head) |
08 | 204 struct list_head *next = head->next; |
09 | 205 return (next == head) && (next == head->prev); |
下面的函数是测试head链表是否只有一个结点:这个链表既不能是空而且head前后的两个结点都得是同一个结点。
1 | 226static inline int list_is_singular( const struct list_head *head) |
3 | 228 return !list_empty(head) && (head->next == head->prev); |
7.将链表左转180度
正如注释说明的那样,此函数会将这个链表以head为转动点,左转180度。整个过程就是将head后的结点不断的移动到head结点的最左端。如果是单个结点那么返回真,否则假。
1 | 212static inline void list_rotate_left( struct list_head *head) |
3 | 214 struct list_head *first; |
5 | 216 if (!list_empty(head)) { |
6 | 217 first = head->next; |
7 | 218 list_move_tail(first, head); |
上述函数每次都调用 list_move_tail(first, head);其实我们将其分解到“最小”,那么这个函数每次最终调用的都是:__list_del(first->prev,first->next);和__list_add(list,head->prev,head);这样看起来其实就一目了然了。
8.将链表一分为二
这个函数是将head后至entry之间(包括entry)的所有结点都“切开”,让他们成为一个以list为头结点的新链表。我们先从宏观上看,如果head本身是一个空链表则失败;如果head是一个单结点链表而且entry所指的那个结点又不再这个链表中,也失败;当entry恰好就是头结点,那么直接初始化list,为什么?因为按照刚才所说的切割规则,从head后到entry前事实上就是空结点。如果上述条件都不符合,那么就可以放心的“切割”了。
01 | 257static inline void list_cut_position( struct list_head *list, |
02 | 258 struct list_head *head, struct list_head *entry) |
04 | 260 if (list_empty(head)) |
06 | 262 if (list_is_singular(head) && |
07 | 263 (head->next != entry && head != entry)) |
09 | 265 if (entry == head) |
10 | 266 INIT_LIST_HEAD(list); |
12 | 268 __list_cut_position(list, head, entry); |
具体如何切割,这里的代码貌似很麻烦,可是我们画出图后,就“一切尽在不言中”了。
01 | 231static inline void __list_cut_position( struct list_head *list, |
02 | 232 struct list_head *head, struct list_head *entry) |
04 | 234 struct list_head *new_first = entry->next; |
05 | 235 list->next = head->next; |
06 | 236 list->next->prev = list; |
07 | 237 list->prev = entry; |
08 | 238 entry->next = list; |
09 | 239 head->next = new_first; |
10 | 240 new_first->prev = head; |
图示:
![Screenshot-2](https://i-blog.csdnimg.cn/blog_migrate/81118e594212a8f579014e9b99eb83aa.png)
9.合并链表
既然我们可以切割链表,那么当然也可以合并了。先看最基本的合并函数,就是将list这个链表(不包括头结点)插入到prev和next两结点之间。这个代码阅读起来不困难,基本上是“见码知意”。
01 | 271static inline void __list_splice( const struct list_head *list, |
02 | 272 struct list_head *prev, |
03 | 273 struct list_head *next) |
05 | 275 struct list_head *first = list->next; |
06 | 276 struct list_head *last = list->prev; |
08 | 278 first->prev = prev; |
09 | 279 prev->next = first; |
11 | 281 last->next = next; |
12 | 282 next->prev = last; |
理解了最基本的合并函数,那么将它封装起来,就可以形成下面两个函数了,分别在head链表的首部和尾部合并。这里的调用过程类似增加,删除功能。
01 | 290static inline void list_splice( const struct list_head *list, |
02 | 291 struct list_head *head) |
04 | 293 if (!list_empty(list)) |
05 | 294 __list_splice(list, head, head->next); |
08 | 302static inline void list_splice_tail( struct list_head *list, |
09 | 303 struct list_head *head) |
11 | 305 if (!list_empty(list)) |
12 | 306 __list_splice(list, head->prev, head); |
合并两个链表后,list还指向原链表,因此应该初始化。在上述两函数末尾添加初始化语句INIT_LIST_HEAD(list);后,就安全了。
10.遍历
下面我们要分析链表的遍历。虽然涉及到遍历的宏比较多,但是根据我们前面分析的那样,掌握好最基本的宏,其他宏就是进行“封装”。便利中的基本宏是:
1 | 381#define __list_for_each(pos, head) \ |
2 | 382 for (pos = (head)->next; pos != (head); pos = pos->next) |
head是整个链表的头指针,而pos则不停的往后移动。但是你有没有觉得,这里有些奇怪?因为我们在上篇文章中说过,struct list_head结构经常和其他数据组成新的结构体,那么现在我们只是不停的遍历新结构体中的指针,如何得到其他成员?因此我们需要搞懂list_entry这个宏:
1 | 348#define list_entry(ptr, type, member) \ |
2 | 349 container_of(ptr, type, member) |
这个宏的作用是通过ptr指针获取type结构的地址,也就是指向type的指针。其中ptr是指向member成员的指针。这个list_entry宏貌似很简单的样子,就是再调用container_of宏,可是当你看了container_of宏的定义后……
1 | 443#define container_of(ptr, type, member) ({ \ |
2 | 444 const typeof( ((type *)0)->member ) *__mptr = (ptr); \ |
3 | 445 (type *)( ( char *)__mptr - offsetof(type,member) );}) |
是不是让人有点抓狂?别急,我们一点点来分析。
首先这个宏包含两条语句。第一条:const typeof( ((type *)0)->member ) *__mptr = (ptr);首先将0转化成type类型的指针变量(这个指针变量的地址为0×0),然后再引用member成员(对应就是((type *)0)->member ))。注意这里的typeof(x),是返回x的数据类型,那么 typeof( ((type *)0)->member )其实就是返回member成员的数据类型。那么这条语句整体就是将__mptr强制转换成member成员的数据类型,再将ptr的赋给它(ptr本身就是指向member的指针)。
第二句中,我们先了解offsetof是什么?它也是一个宏被定义在:linux/include/stddef.h中。原型为:
1 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER); |
这个貌似也很抓狂,不过耐心耐心:((TYPE *)0)->MEMBER)这个其实就是提取type类型中的member成员,那么&((TYPE *)0)->MEMBER)得到member成员的地址,再强制转换成size_t类型(unsigned int)。但是这个地址很特别,因为TYPE类型是从0×0开始定义的,那么我们现在得到的这个地址就是member成员在TYPE数据类型中的偏移量。
我们再来看第二条语句, (type *)( (char *)__mptr – offsetof(type,member) )求的就是type的地址,即指向type的指针。不过这里要注意__mptr被强制转换成了(char *),为何要这么做?因为如果member是非char型的变量,比如为int型,并且假设返回值为offset,那么这样直接减去偏移量,实际上__mptr会减去sizeof(int)*offset!这一点和指针加一减一的原理相同。
有了这个指针,那么就可以随意引用其内的成员了。关于此宏的更具体了解,不妨亲自动手测试这里的程序。
好了,现在不用抓狂了,因为了解了list_entry宏,接下来的事情就很简单了。
下面这个宏会得到链表中第一个结点的地址。
1 | 359#define list_first_entry(ptr, type, member) \ |
2 | 360 list_entry((ptr)->next, type, member) |
真正遍历的宏登场了,整个便利过程看起来很简单,可能你对prefetch()陌生,它的作用是预取节点,以提高速度。
1 | 367#define list_for_each(pos, head) \ |
2 | 368 for (pos = (head)->next; prefetch(pos->next), pos != (head); \ |
我们再来看一开始我们举例的那个便利宏。注意它和上述便利宏的区别就是没有prefetch(),因为这个宏适合比较少结点的链表。
1 | 381#define __list_for_each(pos, head) \ |
2 | 382 for (pos = (head)->next; pos != (head); pos = pos->next) |
接下来这个遍历宏貌似长相和上面那几个稍有不同,不过理解起来也不困难,倒着(从最后一个结点)开始遍历链表。
1 | 389#define list_for_each_prev(pos, head) \ |
2 | 390 for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ |
下面两个宏是上述两个便利宏的安全版,我们看它安全在那里?它多了一个与pos同类型的n,每次将下一个结点的指针暂存起来,防止pos被释放时引起的链表断裂。
1 | 399#define list_for_each_safe(pos, n, head) \ |
2 | 400 for (pos = (head)->next, n = pos->next; pos != (head); \ |
3 | 401 pos = n, n = pos->next) |
5 | 409#define list_for_each_prev_safe(pos, n, head) \ |
6 | 410 for (pos = (head)->prev, n = pos->prev; \ |
7 | 411 prefetch(pos->prev), pos != (head); \ |
8 | 412 pos = n, n = pos->prev) |
前面我们说过,用在list_for_each宏进行遍历的时候,我们很容易得到pos,我们都知道pos存储的是当前结点前后两个结点的地址。而通过list_entry宏可以获得当前结点的地址,进而得到这个结点中其他的成员变量。而下面两个宏则可以直接获得每个结点的地址,我们接下来看它是如何实现的。为了方便说明以及便于理解,我们用上文中的结构struct stu来举例。pos是指向struct stu结构的指针;list是一个双链表,同时也是这个结构中的成员,head便指向这个双链表;member其实就是这个结构体中的list成员。
在for循环中,首先通过list_entry来获得第一个结点的地址;&pos->member != (head)其实就是&pos->list!=(head);它是用来检测当前list链表是否到头了;最后在利用list_entry宏来获得下一个结点的地址。这样整个for循环就可以依次获得每个结点的地址,进而再去获得其他成员。理解了list_for_each_entry宏,那么list_for_each_entry_reverse宏就显而易见了。
1 | 420#define list_for_each_entry(pos, head, member) \ |
2 | 421 for (pos = list_entry((head)->next, typeof(*pos), member); \ |
3 | 422 prefetch(pos->member.next), &pos->member != (head); \ |
4 | 423 pos = list_entry(pos->member.next, typeof(*pos), member)) |
6 | 431#define list_for_each_entry_reverse(pos, head, member) \ |
7 | 432 for (pos = list_entry((head)->prev, typeof(*pos), member); \ |
8 | 433 prefetch(pos->member.prev), &pos->member != (head); \ |
9 | 434 pos = list_entry(pos->member.prev, typeof(*pos), member)) |
下面这两个宏是从当前结点的下一个结点开始继续(或反向)遍历。
1 | 456#define list_for_each_entry_continue(pos, head, member) \ |
2 | 457 for (pos = list_entry(pos->member.next, typeof(*pos), member); \ |
3 | 458 prefetch(pos->member.next), &pos->member != (head); \ |
4 | 459 pos = list_entry(pos->member.next, typeof(*pos), member)) |
6 | 470#define list_for_each_entry_continue_reverse(pos, head, member) \ |
7 | 471 for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ |
8 | 472 prefetch(pos->member.prev), &pos->member != (head); \ |
9 | 473 pos = list_entry(pos->member.prev, typeof(*pos), member)) |
与上述宏不同的是,这个宏是从当前pos结点开始遍历。
1 | 483#define list_for_each_entry_from(pos, head, member) \ |
2 | 484 for (; prefetch(pos->member.next), &pos->member != (head); \ |
3 | 485 pos = list_entry(pos->member.next, typeof(*pos), member)) |
接下来几个宏又分别是上述几个宏的安全版。安全原因上面已经说过,在此不再赘述。
1 | list_for_each_entry_safe(pos, n, head, member) |
2 | list_for_each_entry_safe_continue(pos, n, head, member) |
3 | list_for_each_entry_safe_from(pos, n, head, member) |
4 | list_for_each_entry_safe_reverse(pos, n, head, member) |
以上即是list.h头文件中的大部分内容分析。关于hash表部分在此暂不分析。