# 台大 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. 累了