# kernel study -- list_head 1. [内核基础设施——list_head结构解析](http://linux.laoqinren.net/kernel/list/) 2. [Linux鏈結串列struct list_head 研究](https://myao0730.blogspot.com/2016/12/linux.html) 因為在任意的sturct中加入singly或是doubly linked list是個很常見的架構。但是每個struct結構都不同,很難有同一的API。 ==kernel定義了一個list_head,可以放在任意的struct裡面。定義了一些API來操作list_head。重點就是針對list_head做操作。== ## list_head struct list_head為doubly linked-list。 ```c= struct list_head { struct list_head *next, *prev; }; ``` ==必須使用一個list_head來當dummy head,因為list_for_each才會work。== ![dump list_head as head](http://linux.laoqinren.net/kernel/list/list_head.jpg) ### Declare and initialize ```c= #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) ``` ## list_add/list_add_tail + list_add(new, head) : 把new加到head的後面,因為head常常為dummy head所以等於是把new加到list的開頭。 + list_add_tail(new, head) : 把new加到head的前面,因為head常常為dummy head所以等於是把new加到list的最後。 ```c 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; } void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } void list_add_tail(struct list_head *new, struct list_head *head); ``` ==這個圖表示執行__list_add的結果。把new插在prev和next的中間。== ![__list_add](https://lh6.googleusercontent.com/HQxz5f7Bu-cDtca2X9LTVr1wbcy7vyr3uXz0N7QzTxBzdp6V2CllND9lAN5Tab8VTxZdZlTksALvBXScDM2q9qdufu83qpRI9tFncXaex_0AR54h355QmxBXLIacuw-DTz40BA4h) ## list_del/list_del_init 把這個節點entry從list中拿掉。但是只是鏈結斷開,如果有allocate記憶體必須自己free。list_del_init是斷開後,連entry的鏈結也斷開。 ```c void list_del(struct list_head *entry); void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } ``` 其中斷開的node的prev和next會被指向LIST_POSITION1和LIST_POSITION2。這兩個定義如下。==以往都是指向NULL但是這邊是指向一個non-NULL但是存取它會發生page faults.== ```c= /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) ``` ![list_del](https://lh4.googleusercontent.com/PZJ2VtNfNNOODdWnrfhfMAZAoOFHkTXkU3B5wYwnfMGWEMQ2amq1GZcwl7GXTouFaPwXkIJVxMUOdOlIfdfc6GkQ341ifufxtm4wsF0bD81cm5qhK31qWhra1SbBBRzEHrNbhdoN) ## list_move/list_move_tail 把list從原本的鏈結串上面搬到另一個頭或是尾。 ```c void list_move(struct list_head *list, struct list_head *head); void list_move_tail(struct list_head *list, struct list_head *head) ``` ## list_splice/list_splice_init 把list的內容全部接到head的前面,init版再把list初始化。 ```c void list_splice(struct list_head *list, struct list_head *head); static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head); INIT_LIST_HEAD(list); } } ``` ![list_splice](http://linux.laoqinren.net/kernel/list/list_splice.png) ## list_entry 從struct list_head轉換到存放這個list_head的struct。 ```c= #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define list_entry(ptr, type, member) \ container_of(ptr, type, member) ``` ## list_for_each/list_for_each_prev/list_for_each_safe 順著或逆著走訪這個linked list。 其中pos為目前走到entry,head為list的dummy head。 **由程式碼可以看出必須要有一個dummy head不然會少走一個entry。** ```c #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); pos = pos->prev) ``` 如果使用list_for_each或是list_for_each_prev來del或是move節點會產生問題,因為pos的關係會被你切斷,導致pos = pos->next會找不到下一個節點。所以必須使用list_for_each_safe來刪除或是移動節點。 ```c #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) ``` ==這邊會先把下一個節點存在n,for loop之後再把下一個node指向n。== ## list_for_each_entry/list_for_each_entry_safe ## list_for_each_entry_reverse/list_for_each_entry_safe_reverse 走訪整個linked list但是取出list_head所在的struct(entry)來使用。 pos : 每次取出type \*的pointer。 head : 指向list_head的dummy header。 member : entry中list_head的名稱。 ```c /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) #define list_for_each_entry_reverse(pos, head, member) ``` ## list_for_each_entry_continue/list_for_each_entry_safe_continue 從給定的entry pos的下一個開始,繼續走訪這個linked list。 ```c= /** * list_for_each_entry_continue - iterate over list of given type * continuing after existing point * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) ``` ###### tags: `linux2021`