linux2021: tony2037
github: https://github.com/tony2037/LinuxKernel2021
測驗
bits
必須為 2 的指數, 也就是 2, 4, 8 …
- 考慮
bits
=8, mask=7=0b111, 目的是產生一個上限遮罩, 也就是說:
c &= mask
: 當 c = 1 時: c & mask = 1; 會等價於 c = 9 = 0b1001 & 0b0111 = 1, 因為總共為 8 個 bits, 所以 circular-shift x bits = circular-shift x+8 bits
- (v << c) 算出了 shift 之後的高位, 接下來我們要考慮怎麼找到 shift 後的低位
- 考慮一個 8 bits 的位元組 left circular shift 3 個 bits.:
- xxxooooo: x 代表 shift 後會成為低位的 3 個 bits, o 代表 shift 後成為高位的 5 個 bits
- 我們得出要把這個位元組向左 shift 5 個 bits 來得到 00000xxx
- c = 0b011; -c = 0b1111101; -c & mask = 0b101 = 5
include/linux/bitops.h
轉向看 linux kernel 是怎麼寫的.
看到 include/linux/bitops.h
的
測驗
align_up
給定 uintptr_t sz
回傳對齊 size_t alignment
, 也就是可以被size_t alignment
整除的, 回傳值 (uintptr_t
, 以下稱作 ret
)
並且滿足下列條件
- ret % alignment = 0
- sz ret
考慮以下狀況:
alignment
= 7, 令 sz = 7 * k + r (0 r 7)
mask
= 6 = alignment
- 1
- (7 * k + r + 6) / 7 * 7
- = ( k + (r+6) / 7)* 7
- 因為 (0 r 7)
- 所以
- 若 r > 0 : 7 * k + 7 = 7 (k+1)
- 若 r = 0 : 7 * k
再考慮以下狀況
alignment
= 4 = 0b100; mask
= 3 = 0b11
- (sz & ~mask) + (((sz & mask) + mask) & (mask + 1)) 代表的含意
- (sz & ~mask) = sz - (sz % alignment)
- 接下來考慮 (sz & mask) 可能有兩種狀況
- = 0: 不再進位, (0 + mask) & alignment = 0
-
0: 進位, + alignment = & alignment = & (mask + 1); (* + mask) & alignment = 1 * alignment
這就帶到了 測驗 β - 2: 甚麼時候會用到 bit rotation
測驗 β - 2
首先看到 crypto/sha3_generic.c
測驗 γ
根據 CS:APP 第十章, 以及 man page. FORK(2)
The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent.
也就是說 child proccess 跟 parent proccess refer 到同一個 file descriptor table entry
但是 buffer 卻不是相同的, 也就是說, 每個 proccess write 到自己的 buffer
對於這題來說, 每個 proccess 會寫 "-" * N 到 buffer
並且因為 fflush(stdout) 指向同一個 fd.
於是就會寫 個 "-" 到 stdout
於是 f(12)=49152
更進一步, 我們可以透過 strace 來觀察
execve("./main", ["./main"], 0x7ffe0e14e190 /* 21 vars */) = 0
... 讀 cache
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14238
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
brk(NULL) = 0x55e31a2a6000
brk(0x55e31a2c7000) = 0x55e31a2c7000
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14261
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14270
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14282
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14295
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14305
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14315
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=14238, si_uid=1000, si_status=0, si_utime=0, si_stime=1} ---
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14324
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14537
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=14315, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14815
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14821
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f911e113810) = 14826
write(1, "------------", 12) = 12
exit_group(0) = ?
+++ exited with 0 +++
49152
可以看到單個 proccess 寫了 12 個 "-" 到 stdout, write(1, "------------", 12)
測驗 δ
為了理解這段程式邏輯, 首先先看 queue 裡面的 element, 也就是 node 的定義
這是一個 singly-linked list, value 的部分為一個 pointer of void 指向一段記憶體位置, 也是 data 儲存的地方.
這樣設計的好處是可以把 data 存儲在 node 以外的記憶體位置, 增加分配的自由度.
看到這個 structure 我們只能知道有兩個 pointer of node_t 跟兩把對應的 mutex, 實際上怎麼運行的需要看到之後的邏輯
我覺得這段比較有意思的是: 會在 queue 裡面先放一個 dummy node.
所有如果只有 dummy node 的時候就是空的狀態
要把一個 node push 進一個 queue, 首先要先 acquire last
這把鎖, 可想而知, 當我們要 pop 的時候就是要 acqiuire first
這把鎖.
這樣做的目的是保證 push, pop 不會競爭.
我在 Linux DESKTOP-S362NF0 5.4.72-microsoft-standard-WSL2 #1 SMP Wed Oct 28 23:40:43 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
環境下使用
會發現在連結時期有 undefined reference
錯誤. 原因是因為找不到 libpthread.
於是修改 Makefile 如下
透過靜態聯結直接指定 libpthread archive 檔案解決上述問題.
測驗 ϵ - 1