# 2021q1 Homework2 (quiz2)
contributed by < [`DokiDokiPB`](https://github.com/a1091150) >
## 相關資訊
- [課程資訊](http://wiki.csie.ncku.edu.tw/sysprog/schedule)
- [交作業區](https://hackmd.io/@sysprog/linux2021-homework2)
- [測驗題目](https://hackmd.io/@sysprog/linux2021-quiz2)
- [筆記觀摩](https://hackmd.io/@toastcheng/w2-quiz2)
## 測驗一
### 選擇題
#### COND1 && COND2
- ( c ) fast->next == list
- ( b ) fast->next->next == list
- fast 移動距離為 slow 兩倍,因為 queue 採取 circular queue 的方式,透過判斷否走回 head 的方式中斷 list_for_each。
- fast 的起始點為 list->next
- 如果 queue node 數量為偶數,當 fast->next == list head 時, slow 就會在中間。
- 如果 queue node 數量為奇數,當 fast->next->next == list head 時, slow 就會在中間。
#### MMM
- (e) &get_middle(&q->list)->list
#### TTT
- 這裡要求傳回 middle,依據上面 CONT1 & CONT2 的結果,也就是傳回 slow。
### 解釋程式碼運作原理
#### container_of
- [container_of](https://hackmd.io/@sysprog/linux2021-quiz2)
- [gcc gnu typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html)
- [你所不知道的 C 語言:技巧篇 善用 GNU extension 的 typeof](https://hackmd.io/@sysprog/c-trick?type=view#善用-GNU-extension-的-typeof)
#### list_head
```clike=
struct list_head {
struct list_head *prev, *next;
};
#define LIST_HEAD(head) struct list_head head = {&(head), &(head)}
static inline void INIT_LIST_HEAD(struct list_head *head)
{
head->next = head; head->prev = head;
}
```
- 依據測驗題目筆記的說明,只要在自己的 struct 引入 list_head,無須自己維護一套 doubly-linked list
- 當 head 指向自己時候,表示此 head 為空,沒有東西。
在本程式碼實作,會有兩種情況
- queue_t 中的 list,用於記錄 head,不用於記錄資料內容
- list_ele_t 中的 node,用於記錄 node,不用於記錄 linked list.
在 function 中的命名有反應此情況:
- 以 struct list_head *node 表示其為 node,非 head
- 以 struct list_head *head 表示其為 head, head->next 為其 node
- 以 struct list_head *list 表示其為 head 並且很多 node
#### list_add_tail
```
static inline void list_add_tail(struct list_head *node, struct list_head *head)
{
struct list_head *prev = head->prev;
prev->next = node;
node->next = head;
node->prev = prev;
head->prev = node;
}
```
- head->prev 就是取得 list tail,並將新的 node 接上 list tail 之後。
#### list_del
```
static inline void list_del(struct list_head *node)
{
struct list_head *next = node->next, *prev = node->prev;
next->prev = prev; prev->next = next;
}
```
- 刪除 node 的方式就是將 prev 跟 next 相連結
- 如果 node == prev 或是 node == next, list_del 不會有作用
- 只是將目標 node 移除該 list
#### list_empty
```
static inline int list_empty(const struct list_head *head)
{
return (head->next == head);
}
```
- 就是判斷 list_head 是否只有自己(head)
#### list_is_singular
```
static inline int list_is_singular(const struct list_head *head)
{
return (!list_empty(head) && head->prev == head->next);
}
```
- head->prev == head->next 表示指向 head 或是指向同一個 node,再透過 !list_empty 排除指向 head 的情況,即可獲得 head 中只有一個 node
#### list_splice_tail
```
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
struct list_head *head_last = head->prev;
struct list_head *list_first = list->next, *list_last = list->prev;
if (list_empty(list))
return;
head->prev = list_last;
list_last->next = head;
list_first->prev = head_last;
head_last->next = list_first;
}
```
- list_splice_tail 的目的是將 list 整串加入 head 尾端中
- 因為 list_head 為 doubly-linked list,也就是取得頭尾,並且重新串接。
#### list_cut_position
```
static inline void list_cut_position(struct list_head *head_to,
struct list_head *head_from,
struct list_head *node)
{
struct list_head *head_from_first = head_from->next;
if (list_empty(head_from))
return;
if (head_from == node) {
INIT_LIST_HEAD(head_to);
return;
}
head_from->next = node->next;
head_from->next->prev = head_from;
head_to->prev = node;
node->next = head_to;
head_to->next = head_from_first;
head_to->next->prev = head_to;
}
```
- list_cut_position 的目標是切割成兩串 list
- head_to 為存放切割過後的左半 list,切割後存放範圍為原本 head_from->next 至 node
- head_from 為被切割的 list,切割後的存放右半 list,存放範圍為 node->next 至 head_from->prev
- node 為切割的點
#### list_entry
```
#define list_entry(node, type, member) container_of(node, type, member)
```
- list_head 新增至使用者自定義的結構中,原本可以透過自定義的結構體取得 list_head,但是 list_head 不能反查自定義的結構。
- 透過 container_of 的特性,可以反查結構,並取得該結構。
### 改進部份 第一版本
#### 移除不必要的程式碼
- 在 list_head 中,有明顯的分別 list_head node 跟 list_head head 兩者操作方式不一樣,需要修改名稱以符合定義
- 修改 list_ele_t 中 list 改為 node
- queue_t 保持原樣
- queue_t 中不需要去額外新增 head, tail
- queue_t 中 size 沒有使用,也是可以移除。
```diff
typedef struct __element {
char *value;
- struct list_head list;
+ struct list_head node;
} list_ele_t;
typedef struct {
- list_ele_t *head; /* Linked list of elements */
- list_ele_t *tail;
- size_t size;
struct list_head list;
} queue_t;
```
#### function 名稱錯誤
- delete 一字的意思為刪除,表示會刪除其資料內容,應改為 remove
- 操作的對象為 node,名稱應修正成 list_remove_node 表示這是移除 node
```diff
- static inline void list_del(struct list_head *node)
+ static inline void list_remove_node(struct list_head *node)
```
### 研讀 Linux 核心的 lib/list_sort.c 原始程式碼
- 請參閱筆記 [**研讀 Linux 核心的 lib/list_sort.c 原始程式碼**](https://hackmd.io/@DokiDokiPB/SJfES914O)
:::info
TODO:
- ~~閱讀程式碼並且回答選擇題~~
- 回答延伸問題
- ~~解釋上述程式碼運作原理,指出改進空間並著手實作~~
- 研讀 Linux 核心的 lib/list_sort.c 原始程式碼,學習 sysprog21/linux-list 的手法,將 lib/list_sort.c 的實作抽離為可單獨執行 (standalone) 的使用層級應用程式,並設計效能評比的程式碼,說明 Linux 核心的 lib/list_sort.c 最佳化手法
- 將你在 quiz1 提出的改進實作,和 Linux 核心的 lib/list_sort.c 進行效能比較,需要提出涵蓋足夠廣泛的 benchmark
:::
## 測驗二
:::info
- 閱讀程式碼並且回答選擇題
- 回答延伸問題
- 解釋上述程式碼運作原理
- 在 The Linux Kernel API 頁面搜尋 “power of 2”,可見 is_power_of_2,查閱 Linux 核心原始程式碼,舉例說明其作用和考量,特別是 round up/down power of 2
- 特別留意 __roundup_pow_of_two 和 __rounddown_pow_of_two 的實作機制
- 研讀 slab allocator,探索 Linux 核心的 slab 實作,找出運用 power-of-2 的程式碼和解讀
:::
## 測驗三
:::info
- 閱讀程式碼並且回答選擇題
- 回答延伸問題
- 解釋上述程式碼運作原理,並嘗試重寫為同樣功能但效率更高的實作
- 在 Linux 核心原始程式碼找出逐 bit 進行資料複製的程式碼,並解說相對應的情境
:::
## 測驗四
:::info
- 閱讀程式碼並且回答選擇題
- 回答延伸問題
- 上述程式碼使用到 gcc atomics builtins,請透過 POSIX Thread 在 GNU/Linux 驗證多執行緒的環境中,string interning 能否符合預期地運作?若不能,請提出解決方案
- chriso/intern 是個更好的 string interning 實作,請探討其特性和設計技巧,並透過內建的 benchmark 分析其效能表現
:::