# 台大 110 硬體 ###### tags: `NTU` `110` `硬體` 1. (a) C (or B) :::info 不知道它問的是 kernel 的 memory management subsystem 還是 heap allocator。不過 (b) 是 B,這題感覺應該要選 C ::: (b) B \(c\) A (d) A 2. DABC 3. 懶得寫 4. baseline 的程式碼: ```c= #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #define SIZE (1LL << 31) char data[SIZE]; int main() { int fd = open("big_file", O_WRONLY); write(fd, data, SIZE); return 0; } ``` 測試結果: ```bash= $ perf stat -e cycles ./test -F 63500 --repeat 5 1.780873839 seconds time elapsed 0.000000000 seconds user 0.718827000 seconds sys ``` (a) B :::info fsync 會強制把資料寫進 disk 裡,所以...整體時間理論上要比較慢 ```bash= $ perf stat -e cycles ./test -F 63500 --repeat 5 22.463485995 seconds time elapsed 0.000000000 seconds user 1.022562000 seconds sys ``` 一如預期 ::: (b) C ? 猜測:因為是 unbuffered I/O,所以在 user space 的時間基本上不會有太大的波動 不過我不知道要怎麼改 disk cache size (連該怎麼查都不知道),所以沒有實驗 \(c\) ? :::info 我不太懂 "line-at-a-time" 是什麼意思 ::: (d) B :::info 程式碼: ```c= #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #define SIZE (1LL << 31) int main() { int line = 128; char data[line]; for (int i = 0; i < line - 1; i++) data[i] = 'a'; data[line - 1] = '\0'; for (int i = 0; i < SIZE / line; i++) { puts(data); } return 0; } ``` 因為是 buffered I/O,留在 user space 的時間應該要比較長。 測試結果: ```bash= $ perf stat -e cycles ./test 1>./big_file -F 63500 --repeat 5 Performance counter stats for './test -F 63500 --repeat 5': 7,357,129,797 cycles 2.422631297 seconds time elapsed 0.444117000 seconds user 1.944515000 seconds sys ``` 一如預期 ::: (e) B :::info fflush() 會把 buffer 裡的資料全掃進 kernel,fsync 又會把資料再掃進 disk 裡,應該要更慢? 但 puts 是接到 std I/O,fsync 在這裡到底要幹嘛我也不清楚 ```bash= $ perf stat -e cycles ./test 1>./big_file -F 63500 --repeat Performance counter stats for './test -F 63500 --repeat': 1,847,542,983 cycles 526.039486192 seconds time elapsed 0.079181000 seconds user 1.411285000 seconds sys ``` 可以觀察到 user space 時間變短了些,在 kernel 的時間則是變長。 ::: 5. (a) 43 (b) B \(c\) B (d) 63 (e) ? :::info 逐行解說: 17: 針對 `SIGUSR1` 設 signal 處理函式 `sigHandling` 19: 針對 `SIGUSR2` 設 signal 處理函式 `sigHandling` 21: 針對 `SIGINT` 設 signal 處理函式 `sigHandling` 24: 將 `waitmask` 清空 25: 將 `SIGUSR1` 加進 `waitmask` 26: 將 `newmask` 清空 27: 將 `SIGINT` 加進 `newmask` 32: 將 `newmask` 加進 process 的 signal mask 在這一步之後,`newmask` 裡的所有信號(也就是 `SIGINT`)都會被擋下來 43: 將 process 停下來,並將目前的 signal mask 換成 `waitmask` 在這一步之後,process 會被 blocked,直到`waitmask` 裡(也就是 `SIGUSR1`)**以外**的信號(`SIGUSR2` or `SIGINT` 或其他會把 process terminated 掉的信號)發生。 51: 將 signal mask 換回原本的 ::: :::warning 關於 (e),signal 跟 sigaction 的差別在於:signal 會「重置」,所以信號被觸發之後,signal action 會被重置。但我不清楚 mask 是不是也會被重置。所以做了點實驗,答案是:不會 測試環境 ``` Linux desktop 5.11.0-44-generic #48~20.04.2-Ubuntu SMP Tue Dec 14 15:36:44 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux ``` 程式碼: ```c= #define _POSIX_C_SOURCE 1 #include <signal.h> #include <stdio.h> int hi = 0; void sighandler(int num) { hi = 1; } int main() { signal(SIGINT, sighandler); sigset_t newmask, oldmask; sigemptyset(&newmask); sigaddset(&newmask, SIGTSTP); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask)) { printf("error\n"); return -1; } while(1) { if (hi) { printf("hi\n"); hi = 0; } } } ``` 可以先按 Ctrl-Z,確定這個信號真的被擋住 然後再按 Ctrl-C,你會看到他在跟你打招呼 (ノ>ω<)ノ 再按 Ctrl-C,他不會理你-`д´-,代表信號處理函式真的被重置了。 再按 Ctrl-Z,程式還是沒停下來,代表Ctrl-Z還是被擋住,signal mask 沒有被重置 所以他會保留原本的 signal mask,也就是 `SIGINT` 但這裡沒這個選項?幹我也不知道 (╯°Д°)╯ ┻━┻ ,去問出題的。 ::: 6. (a) 20 (b) NO :::info 在地址有衝突時,增加 set-associativity 才有用。A,B,C 分別是起始於 0xA000, 0xA4B0, 0xA960,index 分別為 0, 4B, 96,沒有衝突。 ::: \(c\) 0 / 0 :::info 兩個 CPU 計算的資料區塊剛好錯開 ::: (d) 7 / 6 :::info - P0 第一次存取 => compulsory miss - P1 第一次存取 => coherence miss - P0 第二次存取 => coherence miss, false sharing - P1 第二次存取 => coherence miss, false sharing 總共 7 次 coherence miss,有 6 次 false sharing,應該吧。 ::: 7. 累了