# 作業系統概論作業二
###### tags: `OS`
- 0716023
## Question
There are some potential issues with theprograms. Please identify at least two issues and describe the corresponding solutionsthat would fix the issues.
## Answer
### Problem I - 執行緒安全問題
這個程式並沒有保證執行緒安全。亦即,當多個 client 同時在修改值時,並不保證最後在共享記憶體中的值是其中一個 client 所寫的值。比如說 client A 和 B 同時寫入值為 $a$ 和 $b$ ,有可能最後的值非 $a$ 也非 $b$ 而是 $c$ 。
要改善這個問題,最直覺的解法是透過鎖來保證執行緒安全。然而不同的 client 是為不同的 process ,很難透過共用互斥鎖來達成執行緒安全。
最根本的原因出在這是 server/client 程式。一般在實作這樣的程式時,並不是透過共享記憶體,而是透過 I/O 和多執行緒來達成。
我們可以將程式改造如下: server 開一個 port 或其他方式,使得各 client 能夠與 server 有專有的聯絡通道。當 client 想改值時,將此值傳給 server 令 server 去改;當 client 想讀值時,傳 request 給 server 令其 response 該值。最後,在 server 端使用互斥鎖或讀寫鎖,就可以保證讀寫值時的執行緒安全。
### Problem II - 共享記憶體生命週期問題之一
理想的情況是,只有當 server 正在運行時, client 才能正常運行;當 server 結束時, client 不應該能夠寫值或讀值。然而,使用 `shmctl` 和 `SHM_RMID` 來釋放共享記憶體空間時,唯有在最後一個 attach 該空間的程序 detach 該空間時,[該空間才會被真正釋放 ](https://www.man7.org/linux/man-pages/man2/shmctl.2.html)。
```
IPC_RMID
Mark the segment to be destroyed. The segment will
actually be destroyed only after the last process detaches
it (i.e., when the shm_nattch member of the associated
structure shmid_ds is zero). The caller must be the owner
or creator of the segment, or be privileged. The buf
argument is ignored.
```
因此,當 server 結束時,既有正在運行的 client 依然能夠正常運作,且該共享記憶體空間並不會被釋放。
要解決此問題,作法如同 Problem I ,必須捨棄使用共享記憶體實作 server/client 程式的方式。
### Problem III - 共享記憶體生命週期問題之二
我在 server 端透過 `shmget` 取得記憶體空間時,加了參數 `IPC_EXCL` 。這是要確保真正獲得一塊新分配的記憶體空間,而不是獲得了某塊可能正在被其他程序使用的空間。我令 server 結束時,透過 `shmctl` 釋放記憶體。
```c
while (1) {
int cmd;
printf("\n");
printf("1: Show the value\n");
printf("2: Modify the value\n");
printf("3: Exit\n");
printf("Enter commands: ");
scanf("%d", &cmd);
if (cmd == 1)
printf("\033[1;32m[server] The value is %d\033[0m\n", ptr[0]);
else if (cmd == 2) {
printf("Input new value: ");
scanf("%d", &ptr[0]);
} else
break;
}
// detach from shared memory
shmdt(ptr);
// destroy the shared memory
shmctl(shmid, IPC_RMID, NULL);
```
若 server 因為某些原因不正常結束(例如 SIGINT),該記憶體空間將不會被釋放。此時若重新啟動 server , `shmget` 將無法獲得新的記憶體空間,導致 error 。且該未被釋放的共享記憶體空間將造成記憶體洩漏。
要解決此問題,可以在寫程式時實作 signal handler ,或是事後透過 `ipcs` `ipcrm` 等指令手動釋放記憶體空間。
### Problem IV - 錯誤輸入
若使用者在給予新值時輸入非數字(例如中文),可能導致程式崩壞(例如無窮迴圈等)。要解決此問題,應該在獲取輸入時使用 `fgets` 而非 `printf("%d")` ,然後檢查字串是否為數字,最後再透過 `atoi` 等方法轉成數字。