# 2018q3 Homework3 (list)
contributed by < [`jason53415`](https://github.com/jason53415) >
## 自我檢查清單
* 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本?
* 參考 [Macro vs Function in C](https://stackoverflow.com/questions/9104568/macro-vs-function-in-c) :
* Macro:在 pre-process 時會將 macro 直接展開取代掉原程式碼,因此執行時不需要 function call 的時間,但多次呼叫時比起 function 會佔用更多記憶體空間,通常適合簡短且頻繁出現的函式。
* Function:在執行時需要將目前位址存入 stack,再跳躍到 function 所在的記憶體處,因此速度較慢,但因為在記憶體中 function 只會存在一個地方,如果多次呼叫的話比起 macro 可以節省許多空間,適合用在較長的函式。
* 因為 linked list 的函式大多簡短且頻繁使用,因此才會選擇使用 macro,犧牲掉一些記憶體空間來加快執行速度。
* Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量
* [Linux kernel 排程機制](https://tampub.uta.fi/bitstream/handle/10024/96864/GRADU-1428493916.pdf)
* 以下為部份 runqueue 的資料結構,其中第 19 行便是使用到 list.h 中定義的 circular doubly linked list
```c=
#define MAX_USER_RT_PRIO 100
#define MAX_RT_PRIO (MAX_USER_RT_PRIO + 1)
#define ISO_PRIO (MAX_RT_PRIO)
#define NORMAL_PRIO (MAX_RT_PRIO + 1)
#define IDLE_PRIO (MAX_RT_PRIO + 2)
#define PRIO_LIMIT ((IDLE_PRIO) + 1)
/* There can be only one */
static struct global_rq grq;
/*
* The global runqueue data that all CPUs work off. Data is protected
* either by the global grq lock, or the discrete lock that precedes the
* data in this struct.
*/
struct global_rq {
raw_spinlock_t lock;
unsigned long nr_running;
unsigned long nr_uninterruptible;
…
struct list_head queue[PRIO_LIMIT];
DECLARE_BITMAP(prio_bitmap, PRIO_LIMIT + 1);
u64 niffies; /* Nanosecond jiffies */
}
```
* GNU extension 的 [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) 有何作用?在程式碼中扮演什麼角色?
* 可以宣告一個與其他變數相同資料型態的變數,例如:
```c
int a;
typeof(a) b;
```
* 可以使函式依據傳入變數的 type 的不同而產生不同的行為,以下列 `max()` 函式為例:
```c
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
```
* 解釋以下巨集的原理
```Clike
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr); \
(type *) ((char *) __pmember - offsetof(type, member)); \
})
```
* 根據 [Alternate Keywords](https://gcc.gnu.org/onlinedocs/gcc-3.0.1/gcc_5.html#SEC109),某些 [GNU C extensions](https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html) 的關鍵字會因為編譯參數 `-traditional`、`-ansi`、`-std=c99` 等而失效,解決的方法是可以如上方的程式碼一樣,在關鍵字的前後加上 `__`,變成 `__extension__` 和 `__extension__`。
* `container_of()` 這個 macro 的功能是當我們只知道一個結構中某成員的位址時,即可透過這個 macro 得到這個結構的起始位址。
* 輸入的三個參數中,`ptr` 表示某個成員的位址,`type` 為成員所屬的結構,`member` 則是成員的名稱。
* 在函式中首先使用 `((type *) 0)->member` 表示一個位址在 `0` 的 `type` 結構中,名稱為 `member` 的成員,主要目的是使用 `__typeof__` 函數來取得那個成員的資料型態,並且宣告一個指向同資料型態的指標 `__pmember` 並指向 `ptr`。
* 根據 [Linux Programmer's Manual](http://man7.org/linux/man-pages/man3/offsetof.3.html), `offsetof(type, member)` 可以回傳某個成員 `member` 與 `type` 結構的起始位址間的 offset。
* 接下來會將 `__pmember` 轉型為 `char *`,並減掉藉由 `offsetof()` 求得的 offset,來取得結構的起始位址,並轉型為指向結構的指標 `type *`。
* 除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作,為什麼呢?這些有什麼益處?
* `list.h` 中還定義了以下的操作:
* LIST_HEAD:宣告一個 list 並初始化
* INIT_LIST_HEAD:初始化一個空的 list
* list_add_tail:在 list 的尾端插入新節點
* list_del_init:刪除節點並將其初始化,可以避免之後錯誤的操作
* list_empty:回傳 list 是否是空的
* list_is_singular:回傳是否只有一個節點
* list_splice:把一個 list 的所有節點插入到另一個 list 開頭
* list_splice_tail:把一個 list 的所有節點插入到另一個 list 結尾
* list_splice_init:把一個 list 的所有節點插入到另一個 list 開頭,並且把第一個 list 的 head 做 INIT_LIST_HEAD
* list_cut_position:將一個 list 的開頭移到另一個 list
* list_move:把一個指定的節點移到 list 的開頭
* list_move_tail:把一個指定的節點移到 list 的尾端
* 許多操作都有針對頭尾的兩種不同版本,可以使 list 在使用上更為便利且有效率。
* `LIST_POISONING` 這樣的設計有何意義?
* list.h 中的 `list_del()` 有這段註解及操作:
```c
/**
* LIST_POISONING can be enabled during build-time to provoke an invalid memory
* access when the memory behind the next/prev pointer is used after a list_del.
* This only works on systems which prohibit access to the predefined memory
* addresses.
*/
#ifdef LIST_POISONING
node->prev = (struct list_head *) (0x00100100);
node->next = (struct list_head *) (0x00200200);
#endif
```
* 因為在執行 `list_del()` 時,節點只是從 list 上被移掉,並不是真的被刪除,因此當 `LIST_POISONING` 被定義的時候,會將被移掉節點的 `prev` 和 `next` 指向預先定義的位址,以避免非法的存取。
* linked list 採用環狀是基於哪些考量?
* 環狀 linked list 的優勢主要就是不需要特別記錄及處理尾端的指標,使得在操作上更為容易,搭配上 doubly linked list 的話,可以允許從 linked list 上任意節點朝任意方向 traverse 整個 list,增加了許多使用上的彈性與效率。
* [參考資料](https://www.quora.com/Why-are-all-the-linked-lists-circular-in-the-Linux-Kernel)
* `list_for_each_safe` 和 `list_for_each` 的差異在哪?"safe" 在執行時期的影響為何?
* 最主要的差異是在 `list_for_each_safe` 中多使用了一個指標 `safe` 指向目前 `node` 所指向的節點的下一個節點。
* 在 `list_for_each_safe` 的執行時期中,`node` 所指向的節點在是可以被移除的,因為會有 `safe` 保存下一個節點的位址,而不會產生未定義行為。
* for_each 風格的開發方式對程式開發者的影響為何?
* Perl 中的 for 迴圈:
```perl
foreach $a (@list) {
print "$a\n";
}
```
* Python 中的 for 迴圈:
```python
for a in list:
print(a)
```
* for_each 風格的開發方式可以使程式碼變得更為簡潔,可讀性也較高。
* 在 C 語言 for 迴圈的寫法中,可以自訂變數的初始動作、條件、以及每次迴圈後的操作,這使它的彈性比較大,但寫法也顯得較為複雜,較不具可讀性。
* 程式註解裡頭大量存在 `@` 符號,這有何意義?你能否應用在後續的程式開發呢?
* [Doxygen](https://en.wikipedia.org/wiki/Doxygen) 是一個可以抓取程式的內容與註解,來自動產生說明文件的工具。
* Doxygen 有定義了許多[指令](http://www.stack.nl/~dimitri/doxygen/manual/commands.html)來幫助生成說明文件,而 `@` 或 `\` 就是用來表示指令的開頭,例如 `@brief` 可以用於 function 的簡易說明,`@return` 則可以用來說明函數的回傳值。
* 使用 Doxygen 生成的 list.h 的說明文件:

* `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何?
* tests/ 中有許多用來測試的檔案,用來檢驗 list.h 中的 macro 是否正確運作,如果運作失敗或結果不符合預期會被 `assert()` 擋下來。
* 根據[維基百科](https://en.wikipedia.org/wiki/Unit_testing)的解釋, unit test 指的是隔離程式各個部份的實做,並測試這些小部份函式或程式碼是否可以正確執行,目的是為了快速定位並修復錯誤,並使得在軟體開發過程的早期就確保程式碼的正確性。