--- tags: Linux, C --- # 2023q1 Homework1 (quiz1) contributed by <[huanginch](https://github.com/huanginch/)> >[題目](https://hackmd.io/@sysprog/linux2023-quiz1) ## 測驗一 ### struct item ```c #include <stdint.h> #include "list.h" struct item { uint16_t i; struct list_head list; }; ``` 此段程式碼為宣告item結構,item 包含一個數值 i 與一個嵌入至 item 的 list_head。 ### cmpint ```c static inline int cmpint(const void *p1, const void *p2) { const uint16_t *i1 = (const uint16_t *) p1; const uint16_t *i2 = (const uint16_t *) p2; return *i1 - *i2; } ``` 此段程式碼進行節點的大小比較,透過相減的方式,若 i1 的值大於 i2 會回傳正數(i1 - i2 > 0),小於回傳負數(i1 - i2 < 0),相等則回傳 0。 ### list_sort ```c if (list_empty(head) || list_is_singular(head)) return; ``` 首先此段程式碼判斷 list 是否為 empty 或只有一個節點,是的話就直接 return (不需要 sort )。 ```c struct list_head list_less, list_greater; INIT_LIST_HEAD(&list_less); INIT_LIST_HEAD(&list_greater); ``` 使用 INIT_LIST_HEAD 這個巨集來產生 list_less 與 list_greater 兩個 list,分別用來儲存小於 pivot 的 list 與大於 pivot 的 list。 ```c struct item *pivot = list_first_entry(head, AAA, BBB); ``` 使用 list_first_entry 巨集從 head (也就是我們的排序目標) 取得第一個節點,並將 pivot 指向此節點。 參考 [list_first_entry](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.list_first_entry)的說明可以得知,此巨集第二個參數需要自訂 struct 的名稱,也就是 `struct item`,第三個變數為這個 struct 中 `list_head` 的名稱,也就是 `list`。 :::info 此處 AAA 要填入 ```sruct item```, BBB 要填入 ```list``` ::: ```c list_del(&pivot->list); ``` 將 pivot 的 list 移走,因為在做quick sort 時,pivot 會重新連接 list_less 和 list_greater,所以舊的指向的 list 必須移除。 ```c struct item *itm = NULL, *is = NULL; CCC (itm, is, head, list) { if (cmpint(&itm->i, &pivot->i) < 0) DDD (&itm->list, &list_less); else EEE (&itm->list, &list_greater); } ``` 這段在做 quick sort 中的比大小,針對目標 list (head) 中的每個節點,如果值比 pivot 的值小就放入 list_less,大於或等於放入 list_greater。 同時依照傳入的參數來判斷,CCC使用的是 `list_for_each_entry_safe` 需傳入四個參數,其中前兩個分別用途為 *itm 作為 cursor、*is 用來做 temporary storage。 可參考 [API文件](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.list_for_each_entry_safe) :::info CCC 要填入 `list_for_each_entry_safe` DDD 要填入 `list_add` EEE 要填入 `list_add` ::: #### stable sorting :::warning 為了實現 stable sorting ,值與 pivot 相等的節點不能放入 list_less ::: 考慮以下情況: 因為我們使用 head list 中的第一個節點當作 pivot,同時他的值為3,我們暫時將它稱為 $3_1$,此時 head 中第二個節點的值也是3,我們暫時稱它為$3_2$,而 stable sorting 所要求的是排序後的 list 也能維持 $3_1$, $3_2$的順序,因為我們排序後會依照 list_less、pivot、list_greater的順序連上,所以假設我們將等於 pviot 的值放入 list_less,會導致 $3_2$, $3_1$。 ```c list_qsort(&list_less); list_qsort(&list_greater); ``` 這兩段程式碼有錯,這邊該執行的是遞迴呼叫 list_sort 以排序剛剛分好的兩個 list。 ```c list_sort(&list_less); list_sort(&list_greater); ``` 以上才是正確寫法。 ```c list_add(&pivot->list, head); // head->pivot list_splice(&list_less, head); // list_less->head FFF(&list_greater, head); // head->list_greater ``` 最後將排序好的 list_less、pviot、list_greater 依照順序接上,就會得到排序好的 list。 * 因為 pivot 是一個節點,所以使用 list_add * 因為要將 list_less 接在 head 之前,所以使用 list_splice * 因為要將 list_greater 接在 head 之後,所以使用 list_splice_tail :::info FFF 要填入 `list_splice_tail` :::