閱讀539 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Linux內核中的list.h淺談

[十月往昔]——Linux內核中的list.h淺談

為什麼要叫做“十月往昔”呢,是為了紀念我的原博客。

不知道為什麼,突然想來一個新的開始——而那個博客存活至今剛好十個月,也有十個月裏的文檔。

十月往昔,總有一些覺得珍貴的,所以搬遷到這裏來。

而這篇文章是在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 行定義了一個list_head 結構,隻有兩個指向list_head 結構的指針,一個next ,一個prev ,作用顯而易見。

23 行的宏LIST_HEAD_INIT(name)25 行的宏LIST_HEAD(name) 組合進行鏈表的初始化,即nextprev 都指向自身。

25 行的靜態內聯函數INIT_LIST_HEAD(struct list_head *list) 同樣是用來初始化鏈表,效果同上述一點。GNU 下的C 語言對C 進行了擴充,不再是ANSI C ,它裏麵增添了很多C++ 的特性,所以對內核進行編譯隻能選用相應的GCC

INIT_LIST_HEAD 在有的文獻中是以宏的形式出現:

#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

這段程序在兩個已知的節點中間插入一個新節點。這裏選擇的是條件編譯,如果沒有對CONFIG_DEBUG_LIST 進行宏定義,則定義了__list_add 這個靜態內聯函數,便於以下兩個函數使用。

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}

該函數在指定的head 節點後麵插入一個新節點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}

該函數將一個節點new 插在指定的節點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}

該函數通過設置prevnext 指針指向彼此,實現了刪除二者之間節點的功能。但是這裏我有個疑惑,刪除的指針的釋放在哪裏實現。

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

該函數通過調用上麵的內聯函數實現節點的刪除,這裏的LIST_POISON1 LIST_POISON2 是在linux / poison . h 定義的。此處仍然是條件編譯。

 

 

鏈表節點的置換

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}

靜態內聯函數list_replace 接受兩個參數:oldnew ,並通過new 替換old 。而list_replace_init 函數則是通過調用list_replace 進行替換,之後調用INIT_LIST_HEAD 對被替換的old 進行鏈表初始化。

 

 

鏈表的移動

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 函數接受兩個參數,第一個參數list 為想要移動的節點指針,第二個參數為目的地節點指針。該函數通過調用__list_del 函數實現list 節點的prevnext 兩個指針互指實現刪除list 節點的效果,並且調用list_addlist 節點插入到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 函數將指定節點移到指定鏈表的尾部,成為尾節點。並且由於鏈表是循環的,所以移動的節點指向該鏈表head 節點。具體實現是通過目標節點的prevnext 互指實現從原始鏈表中刪除list 節點,之後通過調用list_add_taillist 節點插入到以head 為表首的鏈表尾部。

 

判斷節點是否為鏈表的最後一個

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}

通過判斷節點的next 指向是否為表首來確定是否為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}

通過判斷head 節點是否指向自身來判斷鏈表是否為空。

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}

此處函數的作用並不十分理解,對於綠色注釋說明部分的DescriptionNOTE 部分也是一知半解。單純地翻一下NOTE 部分:如果沒有經過同步化處理,那麼如果要達到安全地使用list_empty_careful 這個函數必須限定當前能對指定節點發生的操作僅僅為list_del_init() ,比如當一個CPU 對它進行add 操作的時候不能使用該函數。

該函數能達到的效果是檢查鏈表是否為空,並且檢測是否有CPU 在修改當前指定節點的prevnext 指針。

這裏引用一段解釋,來自楊沙洲:

Linux 鏈表另行提供了一個 list_empty_careful() 宏,它同時判斷頭指針的 next prev ,僅當兩者都指向自己時才返回真。這主要是為了應付另一個 cpu 正在處理同一個鏈表而造成 next prev 不一致的情況。但代碼注釋也承認,這一安全保障能力有限:除非其他 cpu 的鏈表操作隻有 list_del_init() ,否則仍然不能保證安全,也就是說,還是需要加鎖保護。

 

 

判斷鏈表是否隻有唯一的一個節點

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}

空表並不是一個節點都沒有,唯一的節點也不是指隻有一個節點,具體看函數代碼我們便可以了解。當一個節點指針被執行LIST_HEAD 了以後,它的prevnext 指針都指向自身,這便稱為空表;而如果它的prevnext 指針都指向僅有的第二個節點,那麼它便稱為僅有一個節點。

 

 

鏈表的切割

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}

這裏有三個參數,list,head,entry

假設原先有鏈表:head <-> node1 <-> node2 <-> node3 <-> entry <-> node4 <-> head

那麼最後會得到鏈表1head <-> node4 <-> head 和鏈表2list <-> 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}

假設有兩條鏈表:head <-> node1 <-> node2 <-> node3 <-> head

和:last <-> list <-> first

那麼合並的結果是取代了headlast <-> 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}

以下的合並函數都是調用第一個合並內聯函數__list_splice ,區別隻在於合並取代的位置以及是否對空出來的head 進行初始化,即調用INIT_LIST_HEAD 等宏。

最後更新:2017-04-02 04:01:43

  上一篇:go Linux內核中的內存管理淺談
  下一篇:go 控製gridview顯示字段中顯示的字數