---
# System prepended metadata

title: 作業系統概論作業二
tags: [OS]

---

# 作業系統概論作業二
###### 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` 等方法轉成數字。
