# linux2021: OscarShiang
## 測驗 $\alpha-1$
> 舉出 Linux 核心原始程式碼裡頭 bit rotation 的案例並說明
首先在 Linux source code [`include/linux/bitops.h`](https://elixir.bootlin.com/linux/latest/source/include/linux/bitops.h#L86) 可以看到關於 bitops 的實作
這邊以 8 bit 的實作舉例:
```c
static inline __u8 rol8(__u8 word, unsigned int shift)
{
return (word << (shift & 7)) | (word >> ((-shift) & 7));
}
static inline __u8 ror8(__u8 word, unsigned int shift)
{
return (word >> (shift & 7)) | (word << ((-shift) & 7));
}
```
而在 [`/tools/testing/selftests/bpf/progs/test_jhash.h`](https://elixir.bootlin.com/linux/latest/source/tools/testing/selftests/bpf/progs/test_jhash.h#L14) 的 `__jhash_mix()` 可以看到使用 `rol32` 將 `a`, `b`, `c` 三組數字進行混合,進而減少算出的 hash 值碰撞的機率。
```c
#define __jhash_mix(a, b, c) \
{ \
a -= c; a ^= rol32(c, 4); c += b; \
b -= a; b ^= rol32(a, 6); a += c; \
c -= b; c ^= rol32(b, 8); b += a; \
a -= c; a ^= rol32(c, 16); c += b; \
b -= a; b ^= rol32(a, 19); a += c; \
c -= b; c ^= rol32(b, 4); b += a; \
}
```
## $\alpha-2$
> x86_64 指令集具備 rotr 和 rotl 指令,上述 C 程式碼經過編譯器最佳化 (例如使用 gcc) 後,能否運用到這二個指令呢?
我認為有機會。但是應該只會發生在使用操作 32 位元或是 64 位元的資料時,因為指令集並不支援小於 32 位元以下數值的 rotate 指令。
:::warning
不能只有「認為有機會」,證明!
:notes: jserv
:::
我在 Intel-based Mac 上面使用 gcc 搭配 `-O1` 編譯以下的程式碼:
```c
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[]) {
uint32_t a = 0x1;
uint32_t b;
scanf("%d\n", &b);
a = (a >> b) | (a << (32 - b));
printf("%d\n", a);
return 0;
}
```
:::info
這邊不將 `b` 設定為常數是因為 gcc 會直接計算出最後 a 的數值而不會產生 rotate 的指令
:::
經過反組譯可以發現在 `0x100003f63` 的位址有 `rorl` 指令的存在:
```
test`main:
test[0x100003f40] <+0>: pushq %rbp
test[0x100003f41] <+1>: movq %rsp, %rbp
test[0x100003f44] <+4>: pushq %rbx
test[0x100003f45] <+5>: pushq %rax
test[0x100003f46] <+6>: leaq 0x5b(%rip), %rbx ; "%d\n"
test[0x100003f4d] <+13>: leaq -0xc(%rbp), %rsi
test[0x100003f51] <+17>: movq %rbx, %rdi
test[0x100003f54] <+20>: xorl %eax, %eax
test[0x100003f56] <+22>: callq 0x100003f7e ; symbol stub for: scanf
test[0x100003f5b] <+27>: movb -0xc(%rbp), %cl
test[0x100003f5e] <+30>: movl $0x1, %esi
test[0x100003f63] <+35>: rorl %cl, %esi <= rotate intruction
test[0x100003f65] <+37>: movq %rbx, %rdi
test[0x100003f68] <+40>: xorl %eax, %eax
test[0x100003f6a] <+42>: callq 0x100003f78 ; symbol stub for: printf
test[0x100003f6f] <+47>: xorl %eax, %eax
test[0x100003f71] <+49>: addq $0x8, %rsp
test[0x100003f75] <+53>: popq %rbx
test[0x100003f76] <+54>: popq %rbp
test[0x100003f77] <+55>: retq
```
## $\beta-1$
> 說明上述程式碼的運作原理
這段程式碼共可以分為兩個部分
:::warning
power of 2 的翻譯是「2 的冪」,而非「冪次」
:notes: jserv
:::
- 給定 `alignment` 的數值**不是** 2 的冪
如果不是 2 的冪,只要數值換成 `alignment` 的倍數即可,所以當 `sz` 是 `alignment` 的倍數時, `(sz + mask) / alignment` == `(sz + mask) / alignment` 所以得到的即是本身;但是如果 `sz` 不是 `alignment` 的倍數時,就會算成大於 `sz` 且最接近的 `alignment` 的倍數。
- 給定 `alignment` 的數值是 2 的冪
這邊的做法其實與上述的作法類似,但是因為 2 的冪可以利用 mask 直接去除 `sz % alignment` 的部分,因此寫法就會變成
```c
(sz + mask) & (~mask)
```
## $\gamma$
> 解釋上述程式碼輸出 `-` 字元數量的原理
因為 for 迴圈所使用的變數是區域變數,所以對於每個新 `fork()` 產生的執行緒而言,他們都需要印出 `NNN` 個 `-`,而在執行過程中總共會產生出的執行緒共是 $2^{NNN}$ 個
因此若假設迴圈總共執行 $n$ 次的話, `-` 的個數就會是 $2^n \times n$
而題目給定的 `49152` 根據上述的推論可以轉換成下列的形式
$$
49152 = 2 ^ {12} \times 12
$$
所以可知 `NNN` 即是 `12`
## $\zeta-1$
> 解釋上述程式碼運作原理
這段程式碼的作用就是以 proxy 作為兩個伺服器的中介人並進行資料互傳。
因此 `proxy()` 在做的就是利用 `poll` 監聽 `target_fd` 與 `cl_fd` 兩者,當其中一方要傳送資料的時候,就使用 splice 進行對傳。
###### tags: `linux2021`