contributed by < ShamrockLee
>
queue.c
函式實作Linux 核心的 list API 使用帶有起始節點( list head )的環狀雙向鍊結串列。
節點以結構 struct list_head
表示,該結構有兩個成員 prev
、 next
,分別存放指向串列中前一個與後一個節點的指標。
每個節點所攜帶的資料,以巨集 list_entry
存取。
list_entry
即 container_of
,可用來存取帶有該節點變數(非指標)的結構的位址,藉以存取該結構中帶有的資料。
前述結構在 queue.h
當中有 element_t
及 queue_contex_t
list.h
當中大部分函式輸入參數被設計來傳入起始節點指標(能看到以 head
命名的輸入參數多次出現)。
然而,那些函式也能用在串列中其他節點上,以插入、讀取或移除該節點兩側的節點。
善用這種操作能在更多地方重用 list.h
所定義的函式,減少手動賦值 prev
、 next
帶來的的維護負擔。
以下是個別函式實作值得注意的部份:
q_free
element_t
的成員,會跟著 element_t
一起釋放;若再以 free
釋放節點,就會出錯。q_delete_mid
q_sort
的合併排序實作也會需要知道中間節點的位址,故將計算中間節點位址的部份另外獨立為函式 q_get_mid
。q_get_mid
以起始節點的後方節點為起點,透過快慢指標求得中間節點的位置。q_delete_dup
q_reverse
prev
與 next
實作。q_reverseK
和 q_swap
呼叫。q_reverseK
struct list_head
變數 temp_head_reverser
與 temp_head_result
作為兩個「暫時串列」的起始節點;前者用以傳入 q_reverse
,後者儲存反轉後的節點。q_swap
k = 2
的 q_reverseK 實現q_merge
q_merge2
,以便 q_sort
合併排序時重用。q_contex_t
的 size
以減少時間複雜度。q_sort
struct list_head temp_head
使用 q_get_mid
尋找中間節點,並以 list_cut_position
把前半部切給 &temp_head
。q_sort
排序,再使用 q_merge2
合併。實作初期,只看到了 list API 所使用的串列結構,並從註解發現大部分函式都是對起始節點的操作,未能理解 list_move
、 list_splice
、 list_cut_position
適用的情境,因此實作 q_swap
、 q_reverseK
、 q_sort
時幾乎都是手動指定 prev
、 next
。
這造成程式碼愈來愈冗長、難以除錯,以至於花費許多時間而缺乏成效,最後卡在合併串列(merge)的部份。又因為怕被發現進度落後,遲遲未上傳程式碼並接受檢核。
在一對一對談後,以「儘可能重用 List API 提供的函式」與「儘可能重用已定義的函式」為目標最佳化。
仔細閱讀 list.h
的函式,花了三小時內實作、重構了所有函式,五小時除錯,達到記憶體使用以外的行為符合期待的結果。
這顯示及早接受檢核的重要性與重用 API 提供的程式碼帶來的效果。