# 2024q1 Homework4 (quiz3+4) contributed by < `BooleanII` > ## [第 3 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz3) ### 測驗 `1` 版本一使用 log2 操作,可獲得小於等於輸入值的 2 的冪次數值 a ,並以此數值進行 [Digit-by-digit calculation](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Digit-by-digit_calculation) 獲得平方根。 ```c int msb = (int) log2(N); int a = 1 << msb; ``` 而 a 其實可以透過第 2 週測驗的測驗 3 ,使用 ffs (find first bit) 找尋 N 第一個 set (設定為 1) 的位元,而版本 2 即為以找到 ffs 的方式進行實作。 待確認問題 1. 為何 log2 除了需要引入 math.h 檔外,在編譯時還需要連結 math library ? ### 測驗 `2` ### 測驗 `3` 由於向左或向右位移一個位元等同於將該數值乘或除以 2 ,故 2 的冪次可以透過將 1 左移進行實現。因此,以 2 為底的對數的整數項,可以透過找到該數的 ffs 獲得,版本一的實作輸入數值進行右移直到找該數值的 ffs 後停止。版本一的實作對於輸入數值小於等於 0 時,則輸出 -1 進行例外處理。 版本二為加快運算速度,以二元搜尋 (binary search) 的方式找出 ffs ,當數值大等於 2 的 16 次冪 (0x10000) 時,可以直接一口氣像右位移 16 個位元;而當數值大於等於 2 的 8 次冪 (0x100)時,則可以一口氣向右位移 8 個位元。 ```c static size_t ilog2(size_t i) { size_t result = 0; while (i >= 0x10000) { result += 16; i >>= 16; } while (i >= 0x100) { result += 8; i >>= 8; } while (i >= 0x10) { result += 4; i >>= 4; } while (i >= 2) { result += 1; i >>= 1; } return result; } ``` __builtin_clz 為 gcc 內建函式,可回傳無號整數 (unsigned int) 中自 MSB 開始到第一個 1 的 0 個數,以 32 bit 系統為例,當輸入為 2 時輸出為 31 、輸入為 16 時輸出為 27 。透過使用此函式可獲得版本三的實作,將 31 減去 __builtin_clz 結果即可得到 ffs 的位置。 ```c int ilog32(uint32_t v) { return (31 - __builtin_clz(v)); } ``` 故測 `3` 答案如下: 1. AAAA = 0x10000 2. BBBB = 0x100 3. CCCC = 0x10 4. DDDD = v #### 測驗 `3` 延伸問題 在 Linux 核心原始程式碼找出 log2 的相關程式碼 (或類似的形式),並解說應用案例 在 /usr/src 路徑下搜尋 log2 關鍵字獲得非常可觀的搜尋結果,為縮小範圍改以本次測驗的內容 ilog2 搜尋,並在結果中看到 hashtable.h 定義的 HASH_BITS 巨集 (macro) 有使用。 ```c #define HASH_SIZE(name) (ARRAY_SIZE(name)) #define HASH_BITS(name) ilog2(HASH_SIZE(name)) ``` 該巨集涉及到的 ARRAY_SIZE 巨集定義於 kernel.h 檔中: ```c /** * ARRAY_SIZE - get the number of elements in array @arr * @arr: array to be sized */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) ``` __must_be_array 巨集定義於 compiler.h 檔中: ```c /* &a[0] degrades to a pointer: a different type from an array */ #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) ``` BUILD_BUG_ON_ZERO 巨集定義於 build_bug.h 檔中: ```c #ifdef __CHECKER__ #define BUILD_BUG_ON_ZERO(e) (0) #else /* __CHECKER__ */ /* * Force a compilation error if condition is true, but also produce a * result (of value 0 and type int), so the expression can be used * e.g. in a structure initializer (or where-ever else comma expressions * aren't permitted). */ #define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); }))) #endif /* __CHECKER__ */ ``` __same_type 巨集定義於 compiler_types.h 檔中: ```c /* Are two types/vars the same type (ignoring qualifiers)? */ #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) ``` __same_type 巨集使用 gcc 提供的 built-in 函式 [__builtin_types_compatible_p](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html) 進行實作。 __builtin_types_compatible_p 用於比較兩個輸入參數的型態是否相同,相同則回傳 1 、不同則回傳 0 。需注意此巨集會忽略如 const 、 volatile 等 top level qualifier ,例如 int 與 const int 會被視為相同。 當 [BUILD_BUG_ON_ZERO](https://hackmd.io/@sysprog/c-bitfield#Linux-%E6%A0%B8%E5%BF%83-BUILD_BUG_ON_ZERO) 巨集輸入為非 0 時,程式碼會因為於 struct (結構體)中宣告 -1 的 bit field ,而在編譯階段發生錯誤;反之輸入為 0 時則則因 bit field 長度為 0 而輸出 0 。透過上述機制使 __must_be_array 輸入為 pointer (指標)時,會在編譯時期就發生錯誤,避免後續獲得錯誤的陣列長度計算結果。 > [Array-size macro that rejects pointers](https://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers) ### 測驗 `4` ### 測驗 `5` ## [第 4 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz4) ### 測驗 `1` ### 測驗 `2` ### 測驗 `3`