--- tags: linux kernel --- # 2022q1 Homework3 (quiz3) contributed by <`zoanana990`> ## 測驗 `1` ## 測驗 `3` 考慮以下程式碼,能將輸入的 8 位元無號整數逐一位元反轉,如同 [LeetCode 190. Reverse Bits](https://leetcode.com/problems/reverse-bits/) 的描述。 ```c #include <stdint.h> uint8_t rev8(uint8_t x) { x = (x >> 4) | (x << 4); x = ((x & 0xCC) >> 2) | ((x && 0x33) << 2); x = ((x & 0xAA) >> 1) | ((x && 0x55) << 1); return x; } ``` 這題為[你所不知道的C語言:數值系統篇](https://hackmd.io/@sysprog/c-numerics)的簡化版,原版為 ``` new = num; new = ((new & 0xffff0000) >> 16) | ((new & 0x0000ffff) << 16); new = ((new & 0xff00ff00) >> 8) | ((new & 0x00ff00ff) << 8); new = ((new & 0xf0f0f0f0) >> 4) | ((new & 0x0f0f0f0f) << 4); new = ((new & 0xcccccccc) >> 2) | ((new & 0x33333333) << 2); new = ((new & 0xaaaaaaaa) >> 1) | ((new & 0x55555555) << 1); ``` 上面過程如下所示,以 `0x12345678` 為例: ```text bit 3 2 1 0 original number 1 2 3 4 5 6 7 8 0001 0010 0011 0100 0101 0110 0111 1000 1st switch (16 bits) 0101 0110 0111 1000_0001 0010 0011 0100 2nd switch (8 bits) 0111 1000_0101 0110 0011 0100_0001 0010 3rd switch (4 bits) 1000_0111 0110_0101 0100_0011 0010_0001 4th switch (2 bits) 0010 1101 1001 0101 0001 1100 1000 0100 5th switch (1 bit) 0001 1110 0110 1010 0010 1100 0100 1000 result 1 e 6 a 2 c 4 8 ``` 由上可知,程式碼會是對半調換,將程式碼簡化為 `uint8_t` , 原理相同 ::: info 問題: - [x] 補完程式碼 - [x] 解釋上述程式碼運作原理 - [ ] 在 Linux 核心原始程式碼找出類似技巧的實作並解說其應用場景 ::: --- ## 測驗 `5` 考慮以下程式碼,能將輸入的 8 位元無號整數逐一位元反轉,如同 [LeetCode 190. Reverse Bits](https://leetcode.com/problems/reverse-bits/) 的描述。 ```c #include <stdint.h> uint8_t rev8(uint8_t x) { x = (x >> 4) | (x << 4); x = ((x & 0xCC) >> 2) | ((x && 0x33) << 2); x = ((x & 0xAA) >> 1) | ((x && 0x55) << 1); return x; } ``` 這題為[你所不知道的C語言:數值系統篇](https://hackmd.io/@sysprog/c-numerics)的簡化版,原版為 ``` new = num; new = ((new & 0xffff0000) >> 16) | ((new & 0x0000ffff) << 16); new = ((new & 0xff00ff00) >> 8) | ((new & 0x00ff00ff) << 8); new = ((new & 0xf0f0f0f0) >> 4) | ((new & 0x0f0f0f0f) << 4); new = ((new & 0xcccccccc) >> 2) | ((new & 0x33333333) << 2); new = ((new & 0xaaaaaaaa) >> 1) | ((new & 0x55555555) << 1); ``` 上面過程如下所示,以 `0x12345678` 為例: ```text bit 3 2 1 0 original number 1 2 3 4 5 6 7 8 0001 0010 0011 0100 0101 0110 0111 1000 1st switch (16 bits) 0101 0110 0111 1000_0001 0010 0011 0100 2nd switch (8 bits) 0111 1000_0101 0110 0011 0100_0001 0010 3rd switch (4 bits) 1000_0111 0110_0101 0100_0011 0010_0001 4th switch (2 bits) 0010 1101 1001 0101 0001 1100 1000 0100 5th switch (1 bit) 0001 1110 0110 1010 0010 1100 0100 1000 result 1 e 6 a 2 c 4 8 ``` 由上可知,程式碼會是對半調換,將程式碼簡化為 `uint8_t` ::: info 問題: - [x] 補完程式碼 - [x] 解釋上述程式碼運作原理 - [ ] 在 Linux 核心原始程式碼找出類似技巧的實作並解說其應用場景 ::: --- ## 測驗 `9` ```c /* maximum alignment needed for any type on this platform, rounded up to a power of two */ #define MAX_ALIGNMENT 16 /* Given a size, round up to the next multiple of sizeof(void *) */ #define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ (((x) + MAX_ALIGNMENT - MMM) & ~(NNN)) ``` :::info - [x] 補完程式碼 - [x] 解釋上述程式碼運作原理,並撰寫出對應 ROUND_DOWN 巨集 - [x] 在 Linux 核心找出類似的巨集和程式碼,說明 round-up/down 的應用場合 ::: - 解釋這個程式碼的原理 - 首先以本程式為例,先羅列出我們想要什麼 - `16` 是我們想要統一的尺寸,也就是不管我們輸入什麼我們都必須回傳16,因此最好的方法是將 `16` 的那個位元組保留下來,其他歸零 ``` 16: 1 0 0 0 0 int: 0 0 1 0 0 char: 0 0 0 0 1 15: 0 1 1 1 1 -------------------------------- int+15: 1 0 0 1 1 char+15: 1 0 0 0 0 &(~15): 1 0 0 0 0 -------------------------------- Output: 1 0 0 0 0 ``` - `ROUND_UP` 程式碼 ```c /* maximum alignment needed for any type on this platform, rounded up to a power of two */ #define MAX_ALIGNMENT 16 /* Given a size, round up to the next multiple of sizeof(void *) */ #define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ (((x) + MAX_ALIGNMENT - 1) & (~(MAX_ALIGNMENT - 1))) ``` - `ROUND_DOWN` 實作 ```c #define ROUND_DOWN_TO_ALIGNMENT_SIZE(x) \ (((x) ) & (~(MAX_ALIGNMENT - 1))) ``` - 這裡程式碼不需要先把原本的 `type` 相加,直接消除即可 - `linux` 中的 `round_up/down` 函式,位於 `linux/include/linux/math.h` ```c #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) ``` - 測試程式碼 ```c int main(){ int x = 20; int y = MAX_ALIGNMENT; printf("x = %d, y = %d, round_up = %d, round_down = %d, \ ROUND_UP_TO_ALIGNMENT_SIZE = %d, \ ROUND_DOWN_TO_ALIGNMENT_SIZE = %d\n", \ x, y, round_up(x, y), round_down(x, y), \ ROUND_UP_TO_ALIGNMENT_SIZE(x), \ ROUND_DOWN_TO_ALIGNMENT_SIZE(x)); return 0; } ``` - 結果 ```c x = 20, y = 16, round_up = 32, round_down = 16, ROUND_UP_TO_ALIGNMENT_SIZE = 32, ROUND_DOWN_TO_ALIGNMENT_SIZE = 16 ``` - 這個函式的應用在 `stack` 存放訊號時,如以下程式碼 ```c static unsigned long align_sigframe(unsigned long sp) { #ifdef CONFIG_X86_32 /* * Align the stack pointer according to the i386 ABI, * i.e. so that on function entry ((sp + 4) & 15) == 0. */ sp = ((sp + 4) & -FRAME_ALIGNMENT) - 4; #else /* !CONFIG_X86_32 */ sp = round_down(sp, FRAME_ALIGNMENT) - 8; #endif } ``` --- ## 測驗 `10` ```c #define DIVIDE_ROUND_CLOSEST(x, divisor) \ ({ \ typeof(x) __x = x; \ typeof(divisor) __d = divisor; \ (((typeof(x)) -1) > 0 || ((typeof(divisor)) -1) > 0 || \ (((__x) > 0) == ((__d) > 0))) \ ? ((RRR) / (__d)) \ : ((SSS) / (__d)); \ }) ``` :::info - [ ] 補完程式碼 - [ ] 解釋上述程式碼運作原理 - [ ] 在 Linux 核心找出類似的巨集和程式碼,說明 `div round-up/closest` 的應用場合 ::: --- ## 測驗 `11`