---
# System prepended metadata

title: 2023q1 Homework1 (quiz1)
tags: [Linux, ' C']

---

---
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`
:::