# 2019q1 Homework1 (list)
contributed by <[`ndsl7109256`](https://github.com/ndsl7109256)>
:::danger
注意作業要求,符合格式規範
:notes: jserv
:::
## 作業要求
- 回答「Linux 風格的 linked list」裡頭「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼
- 從 linux-list 學習裡頭的技巧,包含裡頭 unit test 的設計,透過給定的 linked list 操作,實作出 merge sort
* 附上你的修改過程,也需要讓`qtest` 得以支援
* 可將 dict 裡頭的測試資料拿來作效能分析的輸入
* 思考提升排序效率的方法,需要做平均和最壞狀況的分析
## 自我檢查事項:
- [x] 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本?
- [ ] Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量
- [x] GNU extension 的 [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) 有何作用?在程式碼中扮演什麼角色?
- [x] 解釋以下巨集的原理
```Clike
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr); \
(type *) ((char *) __pmember - offsetof(type, member)); \
})
```
- [x] 除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作,為什麼呢?這些有什麼益處?
- [x] `LIST_POISONING` 這樣的設計有何意義?
- [x] linked list 採用環狀是基於哪些考量?
- [ ] 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現
- [ ] 什麼情境會需要需要找到 [第 k 大/小元素](https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/) 呢?又,你打算如何實作?
- [x] `list_for_each_safe` 和 `list_for_each` 的差異在哪?"safe" 在執行時期的影響為何?
- [x] for_each 風格的開發方式對程式開發者的影響為何?
* 提示:對照其他程式語言,如 Perl 和 Python
- [x] 程式註解裡頭大量存在 `@` 符號,這有何意義?你能否應用在後續的程式開發呢?
* 提示: 對照看 Doxygen
- [x] `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何?
- [ ] `tests/` 目錄的 unit test 可如何持續精進和改善呢?
## 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本?
先看一下 gcc 裡對 macro 的定義
> A macro is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro.
> [Macros](https://gcc.gnu.org/onlinedocs/cpp/Macros.html)[color=#9d31ea]
preprocessor 會在 compile 前將每個 macro 替換成事先 define 好的內容。程式可以直接循序執行,不像 function 要做 push 或 pop 的動作。如此一來便可節省時間。
但 macro 的缺點便是因為將每個 macro 展開運作,不像 function 使用固定的 memory ,會隨著使用次數的增長增加占用的 memory space。
- Memory test
寫了段簡單的 [程式(memory.c)](https://gist.github.com/ndsl7109256/44959f81e20f8e452ced6b1375d5c1a6) 並分別運行 macro 和 function 版本,看他們使用了多少 memory。
```
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4570 os2018 20 0 4376 824 760 R 97.0 0.0 19:56.08 macro
4585 os2018 20 0 4376 792 728 R 89.4 0.0 18:16.96 function
```
藉由 ==top== 查看 process 使用 memory 的情形,便可發現 macro 版的比 fuction 版的多用了點 memory
- Time test
[程式(time.c)](https://gist.github.com/ndsl7109256/44959f81e20f8e452ced6b1375d5c1a6)
```
$time ./macro
real 0m0.014s
user 0m0.003s
sys 0m0.000s
$time ./function
real 0m0.039s
user 0m0.014s
sys 0m0.000s
```
下 ==time== 執行兩個程式發現 function 運行時間比 macro 多了 2 到 3 倍。
## Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量
## GNU extension 的 typeof 有何作用?在程式碼中扮演什麼角色
typeof 用於索引變數的 type ,可以吃兩種參數。
- expression
-
- type
-
[Typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html)
## 解釋以下巨集的原理
```c=
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr);\
(type *) ((char *) __pmember - offsetof(type, member));\ })
```
- \_\_extension\_\_
此 macro 主要用於防止 gcc 產生警告,雖然我用 7.3.0 版本的 gcc ,就算把__extension__拿掉也不會產生警告。
- ((type *) 0)->member
((type *) 0) 用於得到一個 type 型態的 pointer ,其中的 0 換成其他**整數**也可以。再指向 member
- offsetof定義在 [<linux/stddef.h>](https://elixir.bootlin.com/linux/latest/source/tools/include/linux/kernel.h#L22) 中,用來計算某一個struct結構的成員相對於該結構起始位址的 offset 。
>#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
將 member 的 addrss 減去 offset 便可得到 struct 的起始位置。
```c=
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr);\
(type *) ((char *) __pmember - offsetof(type, member)); })
typedef struct {
char name[16];
int weight;
char wife1[12];
char wife2[12];
char wife3[12];
} weeb;
int main (void)
{
weeb nerd = {"your_name",87,"Miku","Kotori","Umi"};
printf("address of struct %p\n", &nerd);//0x7ffc3584dc00
printf("address of wife3 %p\n", &nerd.wife3);//0x7ffc3584dc2c
printf("id offset %ld\n", offsetof(weeb,wife3));//output is 44(16 + 4 + 12 +12)
printf("offset of id %ld\n", (long)&nerd.wife3 - (long)&nerd );
printf("get address of struct from that of member %p\n", container_of(&(nerd.wife3),weeb, wife3));//0x7ffc3584dc00
return 0;
}
```
## 除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作,為什麼呢?這些有什麼益處?
[==list.h==](https://elixir.bootlin.com/linux/latest/source/tools/firewire/list.h#L2)
## `LIST_POISONING` 這樣的設計有何意義?
```
```
確保不會使用到還沒初始化的 entry ,如果被 reference 將會產生 page fault
[==poison.h==](https://elixir.bootlin.com/linux/latest/source/include/linux/poison.h)
[Linux鏈結串列struct list_head 研究](https://myao0730.blogspot.com/2016/12/linux.html)
## linked list 採用環狀是基於哪些考量?
在一個特定的排序中不斷循環時就需要使用到 circular linked list,在 CPU 排程就會使用到
可以快速的對尾端進行操作。
若操作不慎使得中間的 link 斷開,有機會復原。
可以實做一些環狀或是沒有明確頭尾的資料結構。
## 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現
## 什麼情境會需要需要找到 [第 k 大/小元素](https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/) 呢?又,你打算如何實作?
## list_for_each_safe` 和 `list_for_each` 的差異在哪?"safe" 在執行時期的影響為何?
>#define list_for_each(pos, head) \\
for (pos = (head)->next; pos != (head); pos = pos->next)
如果 list_for_each 裡面做刪除節點,可能連 head 本身都刪掉了,==pos != (head)==就無法做判斷了。
>#define list_for_each_safe(pos, n, head) \\
for (pos = (head)->next, n = pos->next; pos != (head); \\
pos = n, n = pos->next)
而 safe 版本的多了個 ==n== 指向 pos 的 next ,確保, n 無效的話就會少走一次
## for_each 風格的開發方式對程式開發者的影響為何?
不用了解 array 或 list 的實際物件數也可以做 traverse ,可讀性也比較好一點。
## 程式註解裡頭大量存在 @符號,這有何意義?你能否應用在後續的程式開發呢?(提示: 對照看 Doxygen)
Doxygen 用於產生程式的說明文件,@有幾種常見用法。
- @file 檔案的註解說明
- @author 作者的資訊
- @brief 用於 class 或 function 的註解中,後面為 class 或 function 的簡易說明
- @param
@param arg_name 參數說明
主要用於函式說明中,後面接參數的名字,然後再接關於參數的說明。
- @return 後面接函數回傳值的說明。用於 function 的註解中。說明該函數的傳回值。
- @retval
@retval value 回傳說明
主要用於函式說明中,說明特定傳回值的意義。後面接回傳值,再接該回傳值的說明。
## `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何?
針對 function 逐個做測試,屬於軟體工程裡 ==Implementation== 的過程,確認每個 function 皆正常執行後才能進到下個階段。
在每個 unit test 裡便用了許多 `assert` 確定每步皆正常執行。
## `tests/` 目錄的 unit test 可如何持續精進和改善呢?
###### tags: `sysprog`,`2019spring`