# 【Linux】Linked List 筆記 > 資料整理 Corn ## 議題 * 偵測環狀結構 * 排序問題確保空間複雜度為 $O(1)$ * Traverse ## Linked List 在 Linux 核心程式碼 在 [linux/list.h](https://github.com/torvalds/linux/blob/master/include/linux/list.h) 為 Linked List 中 以 **circular doubly-linked list ( 雙向環狀鏈結串列 )** 進行實作,只要在自定義的資料結構中加入 `struct list_head` 便可以透過Linux List 的 API 進行操作。如下圖所示,也就是將其**嵌入**自定義的資料結構。 ![image](https://hackmd.io/_uploads/S1tdZnbAp.png) ## Linux核心原始程式碼巨集 `container_of` & `offsetof` [【Linux】Linux核心原始程式碼巨集 container_of & offsetof](/CYV1UFMNRJ6v3FeKZrxF_g) ## Circular Doubly-Linked List 環狀鏈結串列 (circular Doubly-linked list) 為 linux kernel 中實現 `list` 方式,包含了以下特性 * `head` 到 `tail` 的時間複雜度從 $O(n)$ 到 $O(1)$ * `node` 具有上個節點與下個節點的特性 * 任何節點都可以為開始位置 圖解如下: - [ ] `list_head` 結構體 ```c struct list_head { struct list_head *prev; struct list_head *next; }; ``` ```graphviz digraph list_head { rankdir=LR; node[shape=record, style=bold]; head [label="{<prev>prev|<next>next}"]; } ``` - [ ] 自定義結構體嵌入 `list_head` 自定義的結構體 ( container struct ) 被稱作 entry, 我們可以使用 `list_entry` 來求得自定義結構體的起始位址。 ```graphviz digraph list{ rankdir=LR; node[shape=record, style=bold]; subgraph cluster_0{ node [shape=record]; value [label="{value}"]; head [label="{<prev>prev|<next>next}"]; style=bold; label=my_data } } ``` - [ ] `LIST_HEAD` - Declare list head and initialize it ```c #define LIST_HEAD(head) struct list_head head = {&(head), &(head)} ``` ```graphviz digraph list_init { rankdir=LR; node[shape=record]; style=bold node [shape=record]; head [label="{<left>prev|<right>next}", style="bold"]; head:right:e -> head:left:w[arrowhead=normal, arrowtail=normal, dir=both]; } ``` - [ ] `list_entry` - Calculate address of entry that contains list node `list_entry` 就是 `container_of` 的封裝而已喔! `list_first_entry`為取得在`head->next`的自定義結構體的起始位置,也就是第一個`node` `list_first_last`為取得在`head->prev`的自定義結構體的起始位置,也就是最後一個`node` ```c #define list_entry(node, type, member) container_of(node, type, member) #define list_first_entry(head, type, member) \ list_entry((head)->next, type, member) #define list_last_entry(head, type, member) \ list_entry((head)->prev, type, member) ``` - [ ] `list_for_each` iterate over list nodes 尋訪整個list,但不能對`node` 與 `head` 進行修改,否則會造成未定義! ```c #define list_for_each(node, head) \ for (node = (head)->next; node != (head); node = node->next) ``` ```graphviz digraph ele_list { rankdir=LR; node[shape=record]; head [label="head"] nnode [label="node"] head:bottom:e -> e1:top:w nnode:bottom:e -> e1:top:w[arrowhead=normal, arrowtail=normal, style=dashed] e1 [label="head node|{<left>prev|<right>next}", style="bold", color=grey]; e2 [label="cat|{<left>prev|<right>next}", style="bold"]; e3 [label="...", style="bold"]; e4 [label="eat|{<left>prev|<right>next}", style="bold"]; e5 [label="fat|{<left>prev|<right>next}", style="bold"]; e1:right:e -> e2:left:w[arrowhead=normal, arrowtail=normal, dir=both]; e2:right:e -> e3:left:w[arrowhead=normal, arrowtail=normal, dir=both]; e3:right:e -> e4:left:w[arrowhead=normal, arrowtail=normal, dir=both]; e4:right:e -> e5:left:w[arrowhead=normal, arrowtail=normal, dir=both]; e5:right:e -> e1:left:w[arrowhead=normal, arrowtail=normal, dir=both]; } ``` - [ ] `list_for_each` iterate over list nodes and allow deletions 尋訪整個 list,但可以針對 `node` 進行操作,因為有了 `safe` 確保下一個節點的位置 ```c #define list_for_each_safe(node, safe, head) \ for (node = (head)->next, safe = node->next; node != (head); \ node = safe, safe = node->next) ```