# 2019q1 Homework1 (list) contributed by < `yunchi0921` > ###### tags: `linux_kernel` ## 1. 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本? macro 在 compile 之前,preprocessor 會進行文字的替換,執行時不像 function 運用 stack 的 push & pop,是類似空間換取時間的概念。 >如何證明macro是使用空間換取時間?[color=#ad2016][color=#e00f04] 這邊參考 [ChiAnLin0607同學的共筆](https://hackmd.io/Hwtv4V-zT1GzT3EoG9lZTg?view),透過指令分別對macro與function部份進行objdump反組譯,可以看出macro佔用空間較多。 時間部份寫了小程式測試,實測出macro在時間上是稍短於function ```clike= #include<stdio.h> #include<stdlib.h> #include<time.h> #ifdef MACRO #define add(a,b) a+b #define sub(a,b) a-b #define mul(a,b) a*b #else int add(a,b){return a+b;} int sub(a,b){return a-b;} int mul(a,b){return a*b;} #endif int main(){ add(6,6); sub(6,6); mul(6,6); add(6,6); sub(6,6); mul(6,6); add(6,6); . . . add(6,6); sub(6,6); mul(6,6); printf("Elapsed Time :%f sec",(double)clock()/CLOCKS_PER_SEC); return 0; ``` ![](https://i.imgur.com/f01tleY.png) ## 2.Linux應用 linked list 在哪些場合?舉三個按例並附上對應程式碼,需要解說,以及揣摩對應考量 ## 3.GNU extension 的 typeof 有何作用?再程式碼中扮演什麼角色? [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html)是GNU於標準C上的擴充,用於傳回object的type類型。 >There are two ways of writing the argument to typeof: with an expression or with a type. Here is an example with an expression: > `typeof (x[0](1))` 儲存 array of pointer to function 回傳的 type。 `typeof (int *)` 儲存 type 為 pointer to int 。 >typeof is often useful in conjunction with statement expressions . Here is how the two together can be used to define a safe “maximum” macro which operates on any arithmetic type and evaluates each of its arguments exactly once: > ``` #define max(a,b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a > _b ? _a : _b; }) ``` 這裡使用了typeof得知a與b的type,也解決了macro沒有typechecking的問題。 ## 4 解釋以下巨集原理 ```clike= #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) ``` * `(type *) 0)` 將0轉型成 pointer to type * `()(type *) 0)->member)` 將轉型為pointer to type 0 指向 member * `__typeof__(((type *) 0)->member) *__pmember = (ptr);` `__pmember`利用typeof取得該type,並餵`ptr`給該指標。 * `offsetof(type,member)` 取得該member在type這個structure內的offset * `(type *) ((char *) __pmember - offsetof(type, member));` 將pmember(指向member的pointer to type),減去掉這個member在type中的offset,就是type這個structure 的起始位置。 以下利用一個例子試試看: ```clike= #include<stddef.h> #include<stdio.h> #include<stdlib.h> #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) struct grade{ int math; int eng; int chi; }; int main(){ struct grade yunchi; yunchi.math=80; yunchi.eng=90; yunchi.chi=30; int* ptr=&yunchi.eng; struct grade *pos; pos= container_of(ptr,struct grade, eng); printf("Compute result:%p\nBegin of struture:%p\n",pos,&yunchi); } ``` 結果 ![](https://i.imgur.com/n4aImkt.png) ## 5. 除了你熟悉的 add 和 delete 操作,list.h 還定義一系列操作,為什麼呢?這些有什麼益處? `list move`: 移動一個node到一個list的開頭。 `list_splice`: 加入一系列的lists nodes 到另一個list上。 ## 6. `LIST_POISONING` 這樣的設計有何意義? > 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. ```clike= static inline void list_del(struct list_head *node) { struct list_head *next = node->next; struct list_head *prev = node->prev; next->prev = prev; prev->next = next; #ifdef LIST_POISONING node->prev = (struct list_head *) (0x00100100); node->next = (struct list_head *) (0x00200200); #endif } ``` 在 linux/poison.h 的註解當中 >These are non-NULL pointers that will result in page faults under normal circumstances, used to verify that nobody uses non-initialized list entries. 當list_del執行的時候,將會把node的prev&next指向一個特定的位置,當access到這個位置時,會產生pagefault。 至於是為什麼,不曉得是否跟0xDEADBEAF 一樣是magic number有待查證。 ## 7. linked list 採用環狀是基於哪些考量? 環狀的 linked list 可以使每個節點都做一樣的事,不用考慮head及tail的問題。 ## 8. 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現 ## 9. 什麼情境會需要需要找到 第 k 大/小元素 呢?又,你打算如何實作? ## 10. `list_for_each_safe` 和 `list_for_each` 的差異在哪?“safe” 在執行時期的影響為何? `list_for_each_safe` 額外多儲存一個safe,儲存當前的下下個位置,當如果當前的下個node被移除時,不會找不到當前的node要link哪個位置。 ## 11. for_each 風格的開發方式對程式開發者的影響為何? 可以增加程式可讀性 。 ## 12. 程式註解裡頭大量存在 @ 符號,這有何意義?你能否應用在後續的程式開發呢? ## 13 `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何? 確保每一個function的功能性及邏輯上正確,以減少Debug時,專案過大而不知道錯誤從何開始。 ## 14 `tests/` 目錄的 unit test 可如何持續精進和改善呢? ## MergeSort Implement 在實做mergesort時,因為在`list.h`已經有很多function or macro 可以直接使用,所以與其是說要做出一個mergesort,不如說很像是在了解linux kernel circular doubly linked list 的實做方式。 這裡是我的實做[code](https://github.com/yunchi0921/linux-list/tree/master/mergesort) 實作時參考[ldotrg](https://hackmd.io/s/SJ61s6g84?fbclid=IwAR13VneIAmylABJnftDUpFDALCfgMHh1z12qa4Qufb4KRsuJ6tyKoiLtV7w#Implementation-Merge-Sort)的共筆。 它提到使用[龜兔賽跑演算法](http://www.csie.ntnu.edu.tw/~u91029/Function.html#4)去判斷list的中間值,只要設定兔子跑得速度是烏龜的兩倍就可以在兔子跑完一圈的時候,烏龜正好在中間值,值得注意的是要如何判斷是奇數或是偶數? A:只要判斷說它離終點也就是head還有一步還是兩步的距離即可。 ```clike= while ((fast->next->next != head) && (fast->next != head)) ``` 其中有遇到一些困難是,不知道為什麼要使用&(address of),而不使用*(pointer),因為在unitTest上面測試都是使用&來傳遞參數,所以我就照著做,不過我使用pointer會造成segmentation fault。 >>後來發現不是不用pointer call,只是要多一步malloc的步驟,所以使用&(address of)可以直接由process配置,不確定是不是只有這個目的,還是有更進一步的行為。