contributed by < Welly0902 >
延伸問題
Circular doubly-linked list 節點 structure 定義如下:
再來是節點內含值比較的函式:
可看到 cmpint
並不是直接 return *p1 - *p2;
,故接下來會觀察一下 uint16_t
這個資料型態是否有它必要的意義。
再來看一下 sorting 的程式:
一段一段拆解來看,首先:
參照 list.h 說明,我們可以看到list_empty
的程式碼:
return 1
代表 list 是空的,也就是說的 head->next == head
時 list 是空的,這裡我不太確定 circular doubly-linked list 當一開始空的時候長怎樣,所以我們回去看 list.h
中的 INIT_LIST_HEAD
:
畫成圖為:
所以從上圖我們可看出 head->next == head
確實為只有 head 的初始狀態,為空。
接下來看一下 list_is_singular
function,同樣查看 list.h
了解它的目的是為了去檢驗是否剛好只有一個節點在此。看一下下方程式碼:
可看條件為非空且 head->prev
與 head->next
指到同一個 node。
所以回到 sorting 原程式,//1
先檢查如果 input list 為空或只有一個節點,那直接回傳即為排列好的 list。再來 //2
的部分 initialize 了兩個 pointer list_less
和 list_greater
:
再來 //3
的部分首先先看 list_first_entry
macro 的程式碼:
可以看到運用了另一個 macro list_entry
:
裡面又連結到另一個 list.h
中的 macro container_of
,並根據 ifdef, ifndef 定義加上 indentation 以方便閱讀:
對於 __LIST_HAVE_TYPEOF
檢查是否允許 GNUC extension 有關條件的說明可參考之前同學的說明。這裡直接看
而這裡透過詢問 chatgpt,可快速了解 container_of
macro 所代表的意義
Q: container_of in linux kernel
ChatGPT: container_of is a powerful and commonly used macro in the Linux kernel that allows a developer to obtain a pointer to a structure containing a given field.
The container_of macro takes three arguments: a pointer to a field in a structure, the name of the structure containing the field, and the name of the field itself. The macro then computes the offset of the field within the structure and returns a pointer to the containing structure.
:::
所以我們可知 list_entry
可讓我們得到一個 member pointer 用來取用目前 pointer 指向節點內的 member,以取用節點內 member進行操作。而list_first_entry
則可得到指向目前 linked list 中第一個節點的 mpointer 來對其進行操作。
回到 //3
的程式碼,從說明可以快速推出 AAA 與 BBB 為 item
和 list
。
再來看程式碼:
首先第一行:
再來第二行的 list_del
程式碼如下:
LIST_POISONING can be enabled during build-time to provoke an invalid memory
access when the memory behind the next/prev pointer is used after a list_del.
This only works on systems which prohibit access to the predefined memory addresses.
這裡做 deletion 是為了先將選中的 pivot (此程式是選 list 的第一個元素當作 pivot) 當作分隔 list 中 node 的 i 值大小的依據。Deletion 過程如下圖:
由上圖可以看出 n2 及 n3 經過 pivot 指向的 node n1 deletion 後已經成為
一個 circular doubly-linked list,不過從此處程式碼看不出:
再來先繼續往下看主程式:
首先先建立等等 for each 會用到的 member pointer:
接下來先看一下 list_move_tail
的程式:
以及 list_add_tail
的程式:
那我們假設目前的 list 如下圖:
則當我們利用 cmpint
與 pivot相減後,以上圖例子來看,首先3-6<0
,故會執行list_move_tail (&itm->list, &list_less);
:
先執行 list_del(&itm->list)
(當下節點為 3):
接著 list_add_tail(&itm->list, &list_less)
:
當執行完 主程式的 //4
,可得下圖:
接著進行到 //5
時,會將 list_less
和 list_greater
中的值遞迴排序好。
排序好後會剩下 list_less
和 list_greater
的 list 和 pivot node 並執行 //6
。
在執行前先看一下 list_splice
的程式碼:
以及 list_splice_tail
的程式碼:
在 //6
中先執行 list_add(&pivot->list, head)
將 pivot 加到 head pointer 最前面:
list_splice_tail
時從 head->prev 也就是 head 這個 linked list的最後一個節點(6)進行操作:
延伸問題
延伸問題
1
和 2
提及的快速排序演算法,注意要引入你改進效能的版本xorlist_destroy
,使其不急著釋放記憶體,而搭配 atexit(3) 並在程式即將結束執行時,才批次處理資源釋放