# linux2021: fwfly > [題目描述](https://hackmd.io/@sysprog/linux2021-summer-quiz) > [作答 github](https://github.com/fwfly/2021_summer) ## 測驗 $\alpha$ 透過 gcc 展開 macro 會得到以下結果 ```cpp= static inline uint8_t rotl8(const uint8_t v, int c) { const int mask = (8) - (1); c &= mask; return (v << c) | LLL; }; ``` mask 是避免使用者輸入 > bits 數的數字,同時可以省掉一個 if 如果沒有 mask ,就需要用 if 寫成這樣 ```cpp // 假設是 8 bits; // 數字7 是因為如果位移 8 bits 就等於沒有做 rotate if (c > 7) c = 7 ``` ### LLL 跟 RRR ```cpp= LLL = (v >> (bits-c)) RRR = (v << (bits-c)) ``` 以 184 (隨機選的 8bits 數字)為例 184 的 bits 是 : 10111000 假設向左 rotate 2 個 bits (v << c) 會變成 111000 那最前面的 10 會移到最後面變成 000000 10 假設向左 rotate 4 個 bits 最前面的 1011 會移到最後面變成 0000 1011 假設向左 rotate 6 個 bits 最前面的 101110 會移到最後面變成 00 101110 透過觀察可以看到, overflow 的部分其實是等同於做反向的 shift 而這個 bits 數會是最大 bits 減去使用者輸入的數字 所以答案是 (v <反向> (bits-c)) #### 處理 c 等於 0 根據[文章](https://blog.regehr.org/archives/1063)表示,這樣的寫法需要去處理額外 0 的部分,因為當 rotate 為 0 時,bits 會全部 rotate 一輪,這樣會有額外運算.所以用個 if 去判斷是否為 0,不過這樣在 assembly 就會產生 branch #### no branch 解法 答案 ```cpp LLL = (v >> (-c&mask)) RRR = (v << (-c&mask)) ``` 要理解這樣的方式,需要先理解補數: [解讀計算機編碼-計算機為何如此編碼](https://hackmd.io/@sysprog/binary-representation?type=view#%E8%A8%88%E7%AE%97%E6%A9%9F%E7%82%BA%E4%BD%95%E5%A6%82%E6%AD%A4%E7%B7%A8%E7%A2%BC) ### reference * https://blog.regehr.org/archives/1063 ## 測驗 β ```cpp 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); } ``` uintptr_t : 任意數字 alignment : 要對齊的數字 假設要對齊的數字是 4 那輸入的數字跟相對應的結果就會如以下 ``` 1 -> 4 2 -> 4 3 -> 4 4 -> 4 5 -> 8 (需要用兩個 4 來做對齊) 6 -> 8 .. ... ``` 關於 memory alignment 可以參考以下文章 [memory alignment](http://opass.logdown.com/posts/743054-about-memory-alignment) ### MMM ```cpp (sz + mask) & ~mask; ``` ### 解說 參考來自 boost source code 1.68.0 : https://www.boost.org/doc/libs/1_68_0/boost/align/align_up.hpp 如果是 power of 2 , 假設 alignment 為 8 可以把 ```cpp (((sz + mask) / alignment) * alignment) ``` 看成 ```cpp // 因為 8 是 2 的 3 次方 (((sz + mask) >> 3) << 3) ``` 簡單來說就會是對後面 3 個 bits 直接清 0 如果是後面 3 個 bits 清成 0 則可以產生類似這樣的 mask (假設為 mask0) ``` 111111....1111000 ``` mask0 則會等於 alignment 減一 再取個 not ``` aliment -1 = ...00001000 - 1 = ...0000 0111 ``` 再取個 not ``` ~(aliment -1) = ~(...0000 0111) = ...1111 1000 ``` 這樣就可以完成新的 mask0 然後去做 alignment. ## 測驗 γ ```cpp #include <stdio.h> #include <unistd.h> int main(void) { for (int i = 0; i < NNN; i++) { fork(); printf("-"); } fflush(stdout); return 0; } ``` 先要理解 fork 跟 fflush 怎麼運作 ### NNN 透過測試的結果是 12 ### 解說 依照這篇 [stackoverflow](https://unix.stackexchange.com/questions/447898/why-does-a-program-with-fork-sometimes-print-its-output-multiple-times) 上面說的,兩個情形照成多印出來的狀況 1. printf 是先被 buffer 住的,等到碰到 `\n` 再印出 或 flush(清空) 2. fork 是會把 buffer 的狀態也一併作 copy, 所以如果在 fork 前 print, print 的內容也會一併的被 copy 不過以上的說法還需要實際驗證,但跟目前輸出的情形雷同 ``` To do : 輸出結果 ``` ### To do-實驗 ### reference * https://code.woboq.org/userspace/glibc/stdio-common/vfprintf-internal.c.html#1244 # 測驗 δ ### AAA ```cpp queue->last->next = node; ``` AAA 是在 con_push 裡面. 這個 function 是把新的值加到 queue 裡面, AAA 的部分則是把 node 加到 queue 的後面 ### BBB 跟 CCC BBB 跟 CCC 是在 con_pop 裡面 這個 function 主要是從 queue 中 pop 一個數字 BBB 是把值給 return_value CCC 則是做移動 first 的部分 ```cpp /* Queue not empty: retrieve data and rewire */ void *return_value = node->value; // BBB queue->first = new_header; // CCC ``` ## 測驗 ϵ ### XXX ```cpp x = x + 1; ``` iceil2 這個 function 主要是在找最高位的 bit 策略就是把高位的 bits 1 複製到低位的 bit 讓數字變成全部都是1 最後再 + 1 進行進位,來找到 ceil 所以以下的過程是在進行一個 bits 複製的動作 ``` x = x - 1; x = x | (x >> 1); x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >> 16); ``` 以 4097 為例 首先先減 1 變為 4096, 然後進行最前面第一個 bit 複製 先 shit 1 得到要複製的 bit ``` x = 4096 1 0000 0000 0000 0 1000 0000 0000 // x >> 1 ---------------- // use or 1 1000 0000 0000 ``` 因為已經有兩個 bits 了所以直接複製兩個 ``` 1 1000 0000 0000 0 0110 0000 0000 // x >> 2 ---------------- // use or 1 1110 0000 0000 ``` 現在有 4 個 bits 所以可以直接複製 4 個 ``` 1 1110 0000 0000 0 0001 1110 0000 // x >> 4 ---------------- // use or 1 1111 1110 0000 ``` 因為有 8 個 bits 為 1 了,所以可以一次複製 8 個 ``` 1 1111 1110 0000 0 0000 0001 1111 // x >> 8 ---------------- // use or 1 1111 1111 1111 ``` 16個的結果是一樣的,所以不變,最後再做 +1 來取得 ceil (天花板) ``` 01 1111 1111 1111 00 0000 0000 0001 // 1 ---------------- // use + 10 0000 0000 0000 ``` 所以就會得到 8192 ### YYY ```cpp *ip = mp->hs[i]; ``` 答案來自這個 [github repo](https://github.com/silentbicycle/mpool/blob/master/mpool.c#L240) 透過 gdb 可以知道 mp->hs[i] 會被換成 p 的 address 然後新的 address 的 value 會是原本的 address ```cpp Breakpoint 1, mpool_repool (mp=0x55555555a6b0, p=0x7ffff7ffb6c0, sz=60) at mpool.c:217 217 *ip = mp->hs[i]; //YYY (gdb) p mp->hs[i] $2 = (void *) 0x7ffff7fc8070 (gdb) n 218 assert(ip); (gdb) n 219 mp->hs[i] = ip; (gdb) n 220 } (gdb) p mp->hs[i] $3 = (void *) 0x7ffff7ffb6c0 //換成 0x7ffff7ffb6c0 (gdb) x 0x7ffff7ffb6c0 0x7ffff7ffb6c0: 0xf7fc8070 // 原本的 address ``` 經過幾輪的 repool 再透過 x 去 dump memory 可以發現前幾輪的 memory address 裡面的值是之前的 address ```cpp Breakpoint 1, mpool_repool (mp=0x55555555a6b0, p=0x7ffff7ffb700, sz=33) at mpool.c:217 217 *ip = mp->hs[i]; //YYY (gdb) p mp->hs[i] $4 = (void *) 0x7ffff7ffb6c0 (gdb) x 0x7ffff7ffb6c0 0x7ffff7ffb6c0: 0xf7fc8070 (gdb) n 218 assert(ip); (gdb) n 219 mp->hs[i] = ip; (gdb) n 220 } (gdb) p mp->hs[i] $5 = (void *) 0x7ffff7ffb700 (gdb) x 0x7ffff7ffb700 0x7ffff7ffb700: 0xf7ffb6c0 (gdb) x 0x7ffff7ffb6c0 0x7ffff7ffb6c0: 0xf7fc8070 ``` 但是再多做幾輪就會發現前幾次 address 中的直變成 9 (這邊我把程式的 p 從 7 改成 9) ```cpp Breakpoint 1, mpool_repool (mp=0x55555555a6b0, p=0x7ffff7ffbb00, sz=46) at mpool.c:217 217 *ip = mp->hs[i]; //YYY (gdb) p mp->hs[i] $6 = (void *) 0x7ffff7fc80c0 (gdb) x 0x7ffff7fc80c0 0x7ffff7fc80c0: 0xf7fc80d0 (gdb) n 218 assert(ip); (gdb) n 219 mp->hs[i] = ip; (gdb) n 220 } (gdb) x 0x7ffff7ffbb00 0x7ffff7ffbb00: 0xf7fc80c0 (gdb) x 0x7ffff7fc8070 0x7ffff7fc8070: 0x00000009 // 9 (gdb) x 0x7ffff7ffb6c0 0x7ffff7ffb6c0: 0x00000009 // 9 ``` ### Reference * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 ## 測驗 ζ LLL 跟 JJJ 是設定 poll 的 fd 跟要觸發的 events 以題目為例是只設定 POLLIN 然後從這段程式碼可以知道 pollsp[0] 是放 cl_fd ```cpp int from_client = polls[0].revents & POLLIN; if (from_client) move(cl_fd, target_fd, fds); else move(target_fd, cl_fd, fds); ``` 所以答案如下 ```cpp struct pollfd polls[2] = { [1] = {.fd = target_fd, .events = POLLIN}, // lll [0] = {.fd = cl_fd, .events = POLLIN}, // JJJ }; ```