contributed by < xxiex123
>
linux2021
list_head
的結構如同 quiz1 所述,它是一個通用的 doubly-linked list 結構,只要將他當作struct 裏的一個 member ,那他就能幫助該 struct 建立一個雙向鏈接。其能達到這樣的功能主要靠歸功于 container_of
__extension__
用來防止使用 GNU extension 時可能發生的警告訊號。
將list_head作爲member 放到struct 裏,便擁有雙向鏈接特性。
這裏的成員 __element *next
其實沒必要存在,因爲 linked list 功能可直接用 list_head
完成。
從 DDerveialm 同學那邊看到,將 list_head list 成員放到該結構第一個成員的位置,可以使用 (list_ele_t *) list 去直接得到 list_ele_t 的位置,省下用 container_of
的麻煩和成本。因此修改後
而queue_t 結構定義如下
該例題並不需要用到 head 和tail,因此將其移除使結構更簡潔
在做了上述改動後,所有程式碼中的list_entry函數就都可以用(list_ele_t *)取代。
下面get middle 的方法
利用走一步比上走兩步的方法達到一種類似除2的效果,道理類似與100米賽跑,A選手速度為50m/s,B選手速度100m/s,所以B選手到終點時,A選手正好到中間的地方,也就是50米。
這邊list merge的部分,把左邊已排序好的list 和右邊已排序好的list 合并成一組已排序好的list。
上面的判別式應該是寫錯了
意為左邊list 如果為空,就把左邊list加到head裏,正確應該是:
左邊為空,則把右邊加到head,再return。
同理右邊空,把左邊加到head ,return。
下面 merge 不多説,把list 分成左右邊去處理,再合并,是一個遞迴演算法。
從quiz1 的 Non-recursive所述,遞迴函數大量使用stack的特性在效率上并不樂觀,且穩定性收到限制。
they’re really doing is using the stack as their own private array. This is much slower than using a real array, and could cause stack overflow on some systems
因此可以將merge sort 用iteration 方式實作。
考慮函式 func 接受一個 16 位元無號整數 N,並回傳小於或等於 N 的 power-of-2
我們觀察二進制 21 , 21=0000000000010101,其小於等於N的power of 2 是16,二進制為16=0000000000010000,可以發現其實答案只是左邊開始遇到的第一個1留著,其他為0。上述實作做法是吧第一個1後面全都變成1,即00010101->00011111,此時讓其+1,變成00100000,再往左位移,變成00010000,就是答案。
但考慮到第16bit為1的輸入,假設1000001000000000,變成左邊變1后111111111111111,加1變成0000000000000000,發生overflow,因此更好的做法是,1111111111111111 往右先位移,變成0111111111111111,再加1。
更:
從 linD026 的作業得知,N + 1
這個動作,因爲 1 是 int 形態,當 uint16 與 int 做運算時,會把較低位元的形態轉成高位元的形態再做運算,因此 N 會先轉成 int 形態再做 +1 的動作,所以并不會有 overflow 發生。
但儘管如此,上面所使用的方法還是一個較佳的做法,因爲假設我想做的是 uint32 形態的 rounddown_pow_of_2 ,那 overflow 就會發生。
由 linux 的 bitops.h
is_power_of_2 這個函數比較直觀,
x 假設是 2 的冪,那必定只有一個位元是 1,其他都是 0 ,而此數被 -1 的話,結果其實就是由這一個 1 (包含)開始向右做 reverse bits。
因此 x & (x - 1)必定為 0 。
而下面的 roundup 和 rounddown 則要追蹤到 fls 這個函數
而 1UL 代表 type 為 unsigned long 的 1。
由注解可以知道此函數尋找 most-significant bit 開始遇到的第一個 1。
由此函數,roundup 回傳 1UL << fls_long(n - 1)
,可以先思考,n 的 roundup pow of 2 其實就是原本的 fls + 1 的2的冪次,比方説 000100100100 fls 為 9 ,那其結果就是 2的10次方,也就是 001000000000。此結果可由 000000000001 << fls 來得到。而傳入 fls 的值為 n - 1 是因爲要避免值已經是2的冪的情況還去做 roundup 。
而 rounddown 則是回傳 1UL << (fls_long(n) - 1)
,可以想成是 roundup 的結果 >> 1 。這裏用到了 shift -1 的情況。
考慮到 n = 0 的時候,fls 會 return 0,因此變成 1UL << -1
,因此 n = 0 的 rounddown 也 = 0。
由維基百科 Slab allocation 的定義,由於 CPU 在初始化和拆除(destruction)核心的資料物件時,耗費了大量的時間導致效能降低,因此利用 slap allocation 的機制:
預先 allocated 一些常用到的特定形態和大小的資料物件在記憶體裏,當 kernel 要用到時可以不需要重新初始化記憶體,而直接到 slab 去拿一個空的(free)位置來用。
這個方法降低記憶體 fragmentation 的問題,因爲 slab 是由一塊實體上連續的記憶體空間所形成。
由 linux kernel organization 網站的 Chapter 8 Slab Allocator 也有提到
The slab allocator has three principle aims:
buddy system 在 linD026 同學作業中有詳細的説明,是一種 memory allocation 的方法,顯然這種方法效能被 fragmentation 所限制。
第三點指出,slab allocator 也讓 cpu 裏的 L1、L2 caches 使用率更佳。