[十月往昔]——Linux内核中的list.h浅谈
為什么要叫做“十月往昔”呢,是為了紀(jì)念我的原博客。
不知道為什么,突然想來(lái)一個(gè)新的開始——而那個(gè)博客存活至今剛好十個(gè)月,也有十個(gè)月里的文檔。
十月往昔,總有一些覺得珍貴的,所以搬遷到這里來(lái)。
而這篇文章是在09.04.10里寫的。
終歸是一家之談。
Jason Lee
————————————–cut-line
/*-------------------------------
include/linux/list.h -2.6.29
*/
該文件包含:
1#ifndef _LINUX_LIST_H 2#define _LINUX_LIST_H 3 4#include <linux/stddef.h> 5#include <linux/poison.h> 6#include <linux/prefetch.h> 7#include <asm/system.h>
鏈表的初始化
19struct list_head { 20 struct list_head *next, *prev; 21}; 22 23#define LIST_HEAD_INIT(name) { &(name), &(name) } 24 25#define LIST_HEAD(name) / 26 struct list_head name = LIST_HEAD_INIT(name) 27 28static inline void INIT_LIST_HEAD(struct list_head *list) 29{ 30 list->next = list; 31 list->prev = list; 32}
19-21 行定義了一個(gè)list_head 結(jié)構(gòu),只有兩個(gè)指向list_head 結(jié)構(gòu)的指針,一個(gè)next ,一個(gè)prev ,作用顯而易見。
23 行的宏LIST_HEAD_INIT(name) 與25 行的宏LIST_HEAD(name) 組合進(jìn)行鏈表的初始化,即next 和prev 都指向自身。
25 行的靜態(tài)內(nèi)聯(lián)函數(shù)INIT_LIST_HEAD(struct list_head *list) 同樣是用來(lái)初始化鏈表,效果同上述一點(diǎn)。GNU 下的C 語(yǔ)言對(duì)C 進(jìn)行了擴(kuò)充,不再是ANSI C ,它里面增添了很多C++ 的特性,所以對(duì)內(nèi)核進(jìn)行編譯只能選用相應(yīng)的GCC 。
INIT_LIST_HEAD 在有的文獻(xiàn)中是以宏的形式出現(xiàn):
#define INIT_LIST_HEAD(ptr) do { / (ptr)->next = (ptr); (ptr)->prev = (ptr); / } while (0)
鏈表的插入
34/* 35 * Insert a new entry between two known consecutive entries. 36 * 37 * This is only for internal list manipulation where we know 38 * the prev/next entries already! 39 */ 40#ifndef CONFIG_DEBUG_LIST 41static inline void __list_add(struct list_head *new, 42 struct list_head *prev, 43 struct list_head *next) 44{ 45 next->prev = new; 46 new->next = next; 47 new->prev = prev; 48 prev->next = new; 49} 50#else 51extern void __list_add(struct list_head *new, 52 struct list_head *prev, 53 struct list_head *next); 54#endif
這段程序在兩個(gè)已知的節(jié)點(diǎn)中間插入一個(gè)新節(jié)點(diǎn)。這里選擇的是條件編譯,如果沒有對(duì)CONFIG_DEBUG_LIST 進(jìn)行宏定義,則定義了__list_add 這個(gè)靜態(tài)內(nèi)聯(lián)函數(shù),便于以下兩個(gè)函數(shù)使用。
56/** 57 * list_add - add a new entry 58 * @new: new entry to be added 59 * @head: list head to add it after 60 * 61 * Insert a new entry after the specified head. 62 * This is good for implementing stacks. 63 */ 64static inline void list_add(struct list_head *new, struct list_head *head) 65{ 66 __list_add(new, head, head->next); 67}
該函數(shù)在指定的head 節(jié)點(diǎn)后面插入一個(gè)新節(jié)點(diǎn)new 。
70/** 71 * list_add_tail - add a new entry 72 * @new: new entry to be added 73 * @head: list head to add it before 74 * 75 * Insert a new entry before the specified head. 76 * This is useful for implementing queues. 77 */ 78static inline void list_add_tail(struct list_head *new, struct list_head *head) 79{ 80 __list_add(new, head->prev, head); 81}
該函數(shù)將一個(gè)節(jié)點(diǎn)new 插在指定的節(jié)點(diǎn)head 之前。
鏈表的刪除
83/* 84 * Delete a list entry by making the prev/next entries 85 * point to each other. 86 * 87 * This is only for internal list manipulation where we know 88 * the prev/next entries already! 89 */ 90static inline void __list_del(struct list_head * prev, struct list_head * next) 91{ 92 next->prev = prev; 93 prev->next = next; 94}
該函數(shù)通過(guò)設(shè)置prev 和next 指針指向彼此,實(shí)現(xiàn)了刪除二者之間節(jié)點(diǎn)的功能。但是這里我有個(gè)疑惑,刪除的指針的釋放在哪里實(shí)現(xiàn)。
96/** 97 * list_del - deletes entry from list. 98 * @entry: the element to delete from the list. 99 * Note: list_empty() on entry does not return true after this, the entry is 100 * in an undefined state. 101 */ 102#ifndef CONFIG_DEBUG_LIST 103static inline void list_del(struct list_head *entry) 104{ 105 __list_del(entry->prev, entry->next); 106 entry->next = LIST_POISON1; 107 entry->prev = LIST_POISON2; 108} 109#else 110extern void list_del(struct list_head *entry); 111#endif
該函數(shù)通過(guò)調(diào)用上面的內(nèi)聯(lián)函數(shù)實(shí)現(xiàn)節(jié)點(diǎn)的刪除,這里的LIST_POISON1 和LIST_POISON2 是在linux / poison . h 定義的。此處仍然是條件編譯。
鏈表節(jié)點(diǎn)的置換
113/** 114 * list_replace - replace old entry by new one 115 * @old : the element to be replaced 116 * @new : the new element to insert 117 * 118 * If @old was empty, it will be overwritten. 119 */ 120static inline void list_replace(struct list_head *old, 121 struct list_head *new) 122{ 123 new->next = old->next; 124 new->next->prev = new; 125 new->prev = old->prev; 126 new->prev->next = new; 127} 128 129static inline void list_replace_init(struct list_head *old, 130 struct list_head *new) 131{ 132 list_replace(old, new); 133 INIT_LIST_HEAD(old); 134}
靜態(tài)內(nèi)聯(lián)函數(shù)list_replace 接受兩個(gè)參數(shù):old 和new ,并通過(guò)new 替換old 。而list_replace_init 函數(shù)則是通過(guò)調(diào)用list_replace 進(jìn)行替換,之后調(diào)用INIT_LIST_HEAD 對(duì)被替換的old 進(jìn)行鏈表初始化。
鏈表的移動(dòng)
146/** 147 * list_move - delete from one list and add as another’s head 148 * @list: the entry to move 149 * @head: the head that will precede our entry 150 */ 151static inline void list_move(struct list_head *list, struct list_head *head) 152{ 153 __list_del(list->prev, list->next); 154 list_add(list, head); 155}
List_move 函數(shù)接受兩個(gè)參數(shù),第一個(gè)參數(shù)list 為想要移動(dòng)的節(jié)點(diǎn)指針,第二個(gè)參數(shù)為目的地節(jié)點(diǎn)指針。該函數(shù)通過(guò)調(diào)用__list_del 函數(shù)實(shí)現(xiàn)list 節(jié)點(diǎn)的prev 和next 兩個(gè)指針互指實(shí)現(xiàn)刪除list 節(jié)點(diǎn)的效果,并且調(diào)用list_add 將list 節(jié)點(diǎn)插入到head 之后。
157/** 158 * list_move_tail - delete from one list and add as another’s tail 159 * @list: the entry to move 160 * @head: the head that will follow our entry 161 */ 162static inline void list_move_tail(struct list_head *list, 163 struct list_head *head) 164{ 165 __list_del(list->prev, list->next); 166 list_add_tail(list, head); 167}
List_move_tail 函數(shù)將指定節(jié)點(diǎn)移到指定鏈表的尾部,成為尾節(jié)點(diǎn)。并且由于鏈表是循環(huán)的,所以移動(dòng)的節(jié)點(diǎn)指向該鏈表head 節(jié)點(diǎn)。具體實(shí)現(xiàn)是通過(guò)目標(biāo)節(jié)點(diǎn)的prev 和next 互指實(shí)現(xiàn)從原始鏈表中刪除list 節(jié)點(diǎn),之后通過(guò)調(diào)用list_add_tail 將list 節(jié)點(diǎn)插入到以head 為表首的鏈表尾部。
判斷節(jié)點(diǎn)是否為鏈表的最后一個(gè)
169/** 170 * list_is_last - tests whether @list is the last entry in list @head 171 * @list: the entry to test 172 * @head: the head of the list 173 */ 174static inline int list_is_last(const struct list_head *list, 175 const struct list_head *head) 176{ 177 return list->next == head; 178}
通過(guò)判斷節(jié)點(diǎn)的next 指向是否為表首來(lái)確定是否為last 。
判斷鏈表是否為空
180/** 181 * list_empty - tests whether a list is empty 182 * @head: the list to test. 183 */ 184static inline int list_empty(const struct list_head *head) 185{ 186 return head->next == head; 187}
通過(guò)判斷head 節(jié)點(diǎn)是否指向自身來(lái)判斷鏈表是否為空。
189/** 190 * list_empty_careful - tests whether a list is empty and not being modified 191 * @head: the list to test 192 * 193 * Description: 194 * tests whether a list is empty _and_ checks that no other CPU might be 195 * in the process of modifying either member (next or prev) 196 * 197 * NOTE: using list_empty_careful() without synchronization 198 * can only be safe if the only activity that can happen 199 * to the list entry is list_del_init(). Eg. it cannot be used 200 * if another CPU could re-list_add() it. 201 */ 202static inline int list_empty_careful(const struct list_head *head) 203{ 204 struct list_head *next = head->next; 205 return (next == head) && (next == head->prev); 206}
此處函數(shù)的作用并不十分理解,對(duì)于綠色注釋說(shuō)明部分的Description 和NOTE 部分也是一知半解。單純地翻一下NOTE 部分:如果沒有經(jīng)過(guò)同步化處理,那么如果要達(dá)到安全地使用list_empty_careful 這個(gè)函數(shù)必須限定當(dāng)前能對(duì)指定節(jié)點(diǎn)發(fā)生的操作僅僅為list_del_init() ,比如當(dāng)一個(gè)CPU 對(duì)它進(jìn)行add 操作的時(shí)候不能使用該函數(shù)。
該函數(shù)能達(dá)到的效果是檢查鏈表是否為空,并且檢測(cè)是否有CPU 在修改當(dāng)前指定節(jié)點(diǎn)的prev 和next 指針。
這里引用一段解釋,來(lái)自楊沙洲:
“ Linux 鏈表另行提供了一個(gè) list_empty_careful() 宏,它同時(shí)判斷頭指針的 next 和 prev ,僅當(dāng)兩者都指向自己時(shí)才返回真。這主要是為了應(yīng)付另一個(gè) cpu 正在處理同一個(gè)鏈表而造成 next 、 prev 不一致的情況。但代碼注釋也承認(rèn),這一安全保障能力有限:除非其他 cpu 的鏈表操作只有 list_del_init() ,否則仍然不能保證安全,也就是說(shuō),還是需要加鎖保護(hù)。 ”
判斷鏈表是否只有唯一的一個(gè)節(jié)點(diǎn)
208/** 209 * list_is_singular - tests whether a list has just one entry. 210 * @head: the list to test. 211 */ 212static inline int list_is_singular(const struct list_head *head) 213{ 214 return !list_empty(head) && (head->next == head->prev); 215}
空表并不是一個(gè)節(jié)點(diǎn)都沒有,唯一的節(jié)點(diǎn)也不是指只有一個(gè)節(jié)點(diǎn),具體看函數(shù)代碼我們便可以了解。當(dāng)一個(gè)節(jié)點(diǎn)指針被執(zhí)行LIST_HEAD 了以后,它的prev 和next 指針都指向自身,這便稱為空表;而如果它的prev 和next 指針都指向僅有的第二個(gè)節(jié)點(diǎn),那么它便稱為僅有一個(gè)節(jié)點(diǎn)。
鏈表的切割
217static inline void __list_cut_position(struct list_head *list, 218 struct list_head *head, struct list_head *entry) 219{ 220 struct list_head *new_first = entry->next; 221 list->next = head->next; 222 list->next->prev = list; 223 list->prev = entry; 224 entry->next = list; 225 head->next = new_first; 226 new_first->prev = head; 227} 228 229/** 230 * list_cut_position - cut a list into two 231 * @list: a new list to add all removed entries 232 * @head: a list with entries 233 * @entry: an entry within head, could be the head itself 234 * and if so we won’t cut the list 235 * 236 * This helper moves the initial part of @head, up to and 237 * including @entry, from @head to @list. You should 238 * pass on @entry an element you know is on @head. @list 239 * should be an empty list or a list you do not care about 240 * losing its data. 241 * 242 */ 243static inline void list_cut_position(struct list_head *list, 244 struct list_head *head, struct list_head *entry) 245{ 246 if (list_empty(head)) 247 return; 248 if (list_is_singular(head) && 249 (head->next != entry && head != entry)) 250 return; 251 if (entry == head) 252 INIT_LIST_HEAD(list); 253 else 254 __list_cut_position(list, head, entry); 255}
這里有三個(gè)參數(shù),list,head,entry 。
假設(shè)原先有鏈表:head <-> node1 <-> node2 <-> node3 <-> entry <-> node4 <-> head
那么最后會(huì)得到鏈表1 :head <-> node4 <-> head 和鏈表2 :list <-> node1 <-> node2 <-> node3 <-> entry <-> list 。
這里最好自己畫圖模擬一下。
鏈表的合并
257static inline void __list_splice(const struct list_head *list, 258 struct list_head *prev, 259 struct list_head *next) 260{ 261 struct list_head *first = list->next; 262 struct list_head *last = list->prev; 263 264 first->prev = prev; 265 prev->next = first; 266 267 last->next = next; 268 next->prev = last; 269}
假設(shè)有兩條鏈表:head <-> node1 <-> node2 <-> node3 <-> head
和:last <-> list <-> first
那么合并的結(jié)果是取代了head :last <-> list <-> first <-> node1 <-> node2 <-> node3 <-> last
271/** 272 * list_splice - join two lists, this is designed for stacks 273 * @list: the new list to add. 274 * @head: the place to add it in the first list. 275 */ 276static inline void list_splice(const struct list_head *list, 277 struct list_head *head) 278{ 279 if (!list_empty(list)) 280 __list_splice(list, head, head->next); 281} 282 283/** 284 * list_splice_tail - join two lists, each list being a queue 285 * @list: the new list to add. 286 * @head: the place to add it in the first list. 287 */ 288static inline void list_splice_tail(struct list_head *list, 289 struct list_head *head) 290{ 291 if (!list_empty(list)) 292 __list_splice(list, head->prev, head); 293} 294 295/** 296 * list_splice_init - join two lists and reinitialise the emptied list. 297 * @list: the new list to add. 298 * @head: the place to add it in the first list. 299 * 300 * The list at @list is reinitialised 301 */ 302static inline void list_splice_init(struct list_head *list, 303 struct list_head *head) 304{ 305 if (!list_empty(list)) { 306 __list_splice(list, head, head->next); 307 INIT_LIST_HEAD(list); 308 } 309} 310 311/** 312 * list_splice_tail_init - join two lists and reinitialise the emptied list 313 * @list: the new list to add. 314 * @head: the place to add it in the first list. 315 * 316 * Each of the lists is a queue. 317 * The list at @list is reinitialised 318 */ 319static inline void list_splice_tail_init(struct list_head *list, 320 struct list_head *head) 321{ 322 if (!list_empty(list)) { 323 __list_splice(list, head->prev, head); 324 INIT_LIST_HEAD(list); 325 } 326}
以下的合并函數(shù)都是調(diào)用第一個(gè)合并內(nèi)聯(lián)函數(shù)__list_splice ,區(qū)別只在于合并取代的位置以及是否對(duì)空出來(lái)的head 進(jìn)行初始化,即調(diào)用INIT_LIST_HEAD 等宏。
總結(jié)
以上是生活随笔為你收集整理的[十月往昔]——Linux内核中的list.h浅谈的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python---字符串函数
- 下一篇: [十月往昔]——Linux内核中的内存管