## 測驗α ### α - 1 解釋上述程式碼運作原理 ## 測驗β ### β - 1 解釋程式碼的運作行為 ```c #include <stdint.h> static inline uintptr_t align_up(uintptr_t sz, size_t alignment) { uintptr_t mask = alignment - 1; if ((alignment & mask) == 0) { /* power of two? */ return (sz + mask ) & (~mask); } return (((sz + mask) / alignment) * alignment); } ``` 程式的目的是針對給定的 alignment 數值,輸出大於等於 alignment 的記憶體對齊地址。但當 alignment 為二的冪次時可以透過數字的特性來作 二的冪次的特性為 - least significant bit 必定為 0 - 除了 least significant bit 外只會有一個位元為1 如下: ```c 0000 0010 //2 0000 0100 //4 0000 1000 //8 0001 0000 //16 0000 0000 ... ... ``` 接著可以透過可以透過 sz + mask 來判斷是否 sz 是否需要向上對齊。 若 mask 對應的位元任一數為 1 則代表 sz 許要向上取 alignment 的倍數,反之則不需要。 相加之後需要透過與 ~mask 做 && 來清除多餘的位元。 舉例來說假設輸入的 sz = 17, alignment = 4 。 預期輸出的數值為 20 。 17 的二位元表示為 ```c 0001 0001 ``` 4 的二位元表示則為 ```c 0000 0100 ``` mask ```c 0000 0011 ``` 17 + mask ```c 0001 0001 +0000 0011 =0001 0100 ``` (17 + mask) & ~mask ```c 0001 0100 &1111 1100 =0001 0100 ``` 可以看到結果如同預期。 ### β - 2 在 Linux 核心原始程式碼找出類似 align_up 的程式碼,並舉例說明其用法 從 Linux 的 [`const.h`](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/const.h#L32) 可以看到 `__ALIGN_KERNEL(x, a)` 這個 macro 定義另一個 macro `__ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)` 而 `__ALIGN_KERNEL_MASK` 本身定義的函式則為 `(((x) + (mask)) & ~(mask))`。 相關的用法可以在 [`ioam6_parser.c`](https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/net/ioam6_parser.c#L344) 的 `static int check_ioam6_data(__u8 **p, struct ioam6_trace_hdr *ioam6h, const struct ioam_config cnf)` 中看到透過 `__ALIGN_KERNEL` 將 len 以 4 向上對齊。 ```c ... ... if (ioam6h->type.bit22) { len = cnf.sc_data ? strlen(cnf.sc_data) : 0; aligned = cnf.sc_data ? __ALIGN_KERNEL(len, 4) : 0; ... ... ``` ## 測驗γ - 1