# linux2021: RoyWFHuang ###### tags: `linux_class` > [2021 年暑期「Linux 核心」課程](https://hackmd.io/@sysprog/linux2021-summer/) > [2021 年暑期「Linux 核心」課程先決測驗題](https://hackmd.io/@sysprog/linux2021-summer-quiz) ## 題目 α ```clike= #include <stdint.h> #define __DECLARE_ROTATE(bits, type) \ static inline type rotl##bits(const type v, int c) \ { \ const int mask = (bits) - (1); \ c &= mask; \ \ return (v << c) | (LLL); \ } \ \ static inline type rotr##bits(const type v, int c) \ { \ const int mask = (bits) - (1); \ c &= mask; \ \ return (v >> c) | (RRR); \ } #define DECLARE_ROTATE(bits) __DECLARE_ROTATE(bits, uint##bits##_t) ``` ### 其中一種可能答案: LLL = v >> ((-c) & mask) RRR = v << ((-c) & mask) ### 討論: 此題看到後, 原本第一個想法是 LLL = v >> (bits - c) LLL = v << (bits - c) 但後來查閱 linux kerenl 內部 [/include/linux/bitops.h](https://elixir.bootlin.com/linux/latest/source/include/linux/bitops.h)似乎可以直接用 bitwise 直接解決 bits - c 其實和 c 互為補數關係 也就是在 n (0 ~ n-1) 進位循環系統中```(n - c ) + c) = n === 0``` 所以意義上 bits - c = -c 但 -c 必須要在 n 進位系統中, 所以用 mask將之限制在 n 中 所以就得出了 (-c & mask) 的結論 ### 延伸問題: #### 1.舉出 Linux 核心原始程式碼裡頭 bit rotation 的案例並說明 此問題我直接聯想到密碼學裡面 value 和 key 運算後, 進行旋轉然後在和 key運算的方式, 透過[bootlin](https://elixir.bootlin.com/linux/latest/source) 反查 [ror](https://elixir.bootlin.com/linux/latest/C/ident/ror64) 的使用位置得到 sha512演算法中會使用到. 也才讓我回想起, 以前學 sha演算法的時候, 他的 rotate 計算. #### 2.x86_64 指令集具備 rotr 和 rotl 指令,上述 C 程式碼經過編譯器最佳化 (例如使用 gcc) 後,能否運用到這二個指令呢? ```clike= // main program int main() { uint8_t rtol = rand()%256; rtol = rotl8(rtol, 4); printf("%x\n", rtol); return 0; } ``` 在 [compiler explorer](https://godbolt.org/) 中, 把此程式放入, 並使用 -Os的最佳化, 出現下面的程式碼 ```asm .LC0: .string "%x\n" main: push rax call rand mov ecx, 256 mov edi, OFFSET FLAT:.LC0 cdq idiv ecx xor eax, eax rol dl, 4 movzx esi, dl call printf xor eax, eax pop rdx ret ``` 改用 -O3 的最佳化, 出現下面的程式碼 ```asm .LC0: .string "%x\n" main: sub rsp, 8 call rand mov edi, OFFSET FLAT:.LC0 rol al, 4 movzx esi, al xor eax, eax call printf xor eax, eax add rsp, 8 ret ``` 似乎都會直接使用到 rol 的 x86指令集 ## 題目β ```clike= #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 MMM; } return (((sz + mask) / alignment) * alignment); } ``` ### 答案 MMM = (((sz) + (mask)) & ~(mask)) ### 討論 MMM = (sz & (~mask)) + ((((sz ^ mask) & mask) + 1) & (mask + 1) ^ (mask + 1)) 看完 [/include/uapi/linux/const.h](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/const.h#L32)才發現, 自己把事情想的太複雜了. ```clike= (((x) + (mask)) & ~(mask)) ``` 很簡單的作法, 只有剛好對齊的時候, 是不用多一個page去存, 所以只要把現在的 address + mask, 只要不是剛好對齊, 都會進位 然後再把尾數用 ~(mask)去除, 就可以得到答案了. ### 延伸問題 #### 1.說明上述程式碼的運作原理 同上述, 只有剛好對齊的時候, 是不用多一個page去存, 所以只要把現在的 address + mask, 只要不是剛好對齊, 都會進位 然後再把尾數用 ~(mask)去除, 就可以得到答案了. #### 2.在 Linux 核心原始程式碼找出類似 align_up 的程式碼,並舉例說明其用法 [/include/uapi/linux/const.h](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/const.h#L32) 直覺想到的是, 記憶體配置的時候, 在計算alignment配置量, 比方說, ==malloc(9)==, 在以32bit 處理器架構下, 一次fetch的量為32bit, 所以 malloc(9) 就要多配置一個, 甚至 strcut 各個element的管理, 都會用到對齊的方式, 方便加速存取資料. 參考 kernel 中, 有相當多地方使用到[align](https://elixir.bootlin.com/linux/latest/C/ident/ALIGN), 包括檔案系統, 記憶體管理, 各家晶片廠提供的 [driver/net] 1. [fs/ext4](https://elixir.bootlin.com/linux/latest/source/fs/ext4/file.c#L343) 中有使用到, 當資料寫回 disk中, 依照 disk blk進行 alignment 寫入, 目的是加速資料的寫入, 若沒有對齊, 則變成要分兩次寫入 disk, 增加io的時間, 也造成讀取上, 需要分別讀取兩次才能組成完整資料的時間. 2. [/mm/memblock.c](https://elixir.bootlin.com/linux/latest/source/mm/memblock.c#L1943)初始化記憶體. ## 題目γ ```clike= #include <stdio.h> #include <unistd.h> int main(void) { for (int i = 0; i < NNN; i++) { fork(); printf("-"); } fflush(stdout); return 0; } ``` ### 答案 NNN = 12 ### 討論 這題真的是純粹把程式丟下去跑, 第一次跑10 出來的次數剛好是 10240, 所以大膽的猜測應該是 2^NNN *10 + M = 49152, 而這個最接近的數字就是 12. 實際原理如下 [fork 連 io_buffer中的資料都會 copy](https://coolshell.cn/articles/7965.html) fork 會把 buff 中的 data也給 copy出來, 所以在最後 fflush的時候, 就會把 child process的 io buffer的資料給印出, ### 延伸問題 #### 解釋上述程式碼輸出 - 字元數量的原理 ## 題目 δ ### 答案 AAA = queue->last->next = node; BBB = node->value; CCC = queue->first = new_header; ### 討論 (TODO...) ### 延伸問題 (TODO...) #### 1.解釋上述程式碼運作原理並指出實作缺失 #### 2.以 lock-free 程式設計 改寫上述程式碼 ## 題目 ϵ ### 答案 (TODO...) XXX = x + 1 YYY = *ip = mp->hs[i]; ### 討論 (TODO...) 這題其實我沒作答, 直接放棄 [ref](https://github.com/silentbicycle/mpool/blob/master/mpool.c) ### 延伸問題 (TODO...) #### 1.解釋上述程式碼運作原理 #### 2.提出效能和功能的改進策略,並予以實作 ## 題目 δ ### 答案 III = .fd = fds[1], .events = POLLOUT JJJ = .fd = fds[0], .events = POLLIN ### 討論 (TODO...) ### 延伸問題 (TODO...) #### 1.解釋上述程式碼運作原理 #### 2.以 epoll 系統呼叫改寫程式碼,並設計實驗來驗證 proxy 程式碼的效率 ## 作業一 ch.5 ch6 ftrace