---
# System prepended metadata

title: 作業系統相關問題
tags: [作業系統, 後端面試]

---

## 什麼是作業系統？

**作業系統（Operating System, OS）** 是管理電腦硬體與軟體資源的系統軟體。它提供一個讓應用程式和使用者可以與硬體進行互動的基礎。作業系統的主要職責包括管理電腦的硬體資源（如 CPU、記憶體、硬碟），提供使用者介面，以及協調和管理應用程式的執行。

### 主要功能：

1. **資源管理**：管理系統資源，包括處理器、記憶體、硬碟、輸入輸出裝置等。
2. **檔案系統管理**：管理資料存儲，提供檔案的創建、刪除、讀寫等操作。
3. **進程管理**：管理應用程式的執行，包括進程創建、執行、暫停及終止。
4. **使用者介面**：提供圖形化或命令行的使用者操作界面。

### 常見作業系統比較表

| 作業系統      | 特點                           | 優點                                   | 缺點                                  |
|-------------|------------------------------|--------------------------------------|-------------------------------------|
| Windows     | 廣泛使用於個人電腦和商業環境中      | 使用者友好，軟體支援豐富                   | 易受病毒攻擊，系統穩定性可能不如其他作業系統    |
| macOS       | 蘋果公司開發，用於 Mac 電腦     | 優異的使用者體驗，穩定性高                  | 軟體兼容性較低，硬體限制嚴格                  |
| Linux       | 開源作業系統，廣泛應用於伺服器和桌面系統 | 高度可自訂和開源，安全性好                | 使用者友好性略遜於 Windows 和 macOS，入門門檻較高 |
| Unix        | 主要用於伺服器和工作站         | 穩定性和安全性高，多用戶支援                | 使用者界面不如其他作業系統親民，學習曲線陡峭    |

### 常見面試問題及解答

1. **問題**：作業系統的主要功能是什麼？
   - **解答**：作業系統的主要功能包括資源管理、檔案系統管理、進程管理、提供使用者介面等。

2. **問題**：解釋作業系統中的多任務處理。
   - **解答**：多任務處理是指作業系統能夠同時執行多個任務（或稱進程）的能力。這通過時間分片技術實現，即將 CPU 時間分配給不同的任務。

3. **問題**：什麼是虛擬記憶體？
   - **解答**：虛擬記憶體是一種記憶體管理技術，它使應用程式認為它有更多的可用記憶體而實際上是使用硬碟空間來擴展物理記憶體。

4. **問題**：解釋內核模式和使用者模式的區別。
   - **解答**：在作業系統中，內核模式（也稱為監督模式）是一種訪問權限模式，允許執行所有硬體指令。使用者模式限制了程式

## progress vs process vs threads
我將首先簡要介紹程式、進程（行程）和執行緒的概念，然後通過一個生活中的實際範例來說明它們之間的關係。

### 程式（Program）
- **定義**：程式是一組指令的集合，它描述了如何執行特定的任務或解決特定問題。
- **特點**：程式是靜態的，儲存在硬碟上，不佔用系統資源。
- **舉例**：一本食譜，描述了製作一道菜所需的步驟和材料。

### 進程（Process）
- **定義**：進程是程式的一次執行實例。當程式被啟動時，它變成了一個進程，擁有自己的記憶體空間和執行環境。進程是系統資源分配和排程的基本單位。
- **特點**：每個進程有自己的地址空間和系統資源。
- **舉例**：根據食譜在廚房製作一道菜，使用各種廚具和食材。

### 執行緒（Thread）
- **定義**：執行緒是進程中的一個單獨的控制序列。一個進程可以有多個執行緒，它們共享進程的資源，但可以並行執行不同的任務。
- **特點**：執行緒是多核心CPU上並行執行的基本單位。
- **舉例**：在製作菜餚的同時，同時煮湯和烤麵包。

### 比較表

| 概念      | 程式                      | 進程                      | 執行緒                        |
|---------|-------------------------|-------------------------|---------------------------|
| 定義      | 指令集合的靜態表示               | 執行中程式的動態實例            | 進程中的單一執行序列                |
| 記憶體   | 儲存在硬碟上                  | 擁有獨立的地址空間和資源          | 共享所屬進程的地址空間和資源         |
| 資源需求  | 不消耗資源                  | 消耗記憶體和CPU                  | 與進程相比資源消耗更少               |
| 執行     | 需要被操作系統載入內存並執行      | 由操作系統管理                   | 可以並行執行，提高效率              |

### 常見面試問題及解答

1. **問題**：進程和線程之間的主要區別是什麼？
   - **解答**：進程有自己獨立的地址空間和系統資源，而線程是進程中的執行單位，共享所屬進程的地址空間和資源。線程的創建和上下文切換成本比進程低。

2. **問題**：為什麼我們需要線程？
   - **解答**：線程允許在同一進程內並行執行多個任務，這可以提高應用程序的響應性和資源利用率，特別是在多核心處理器上。

3. **問題**：一個程式和一個進程之間有什麼區別？
   - **解答**：程式是一組存儲在硬碟上的指令，而進程是程式的執行實例，運行在操作系統的控制下，佔用記憶體和其他系統資源。進程表示程式的動態執行過程。
## Muti-thread vs Muti-process
### 多執行緒（Multi-threading）
- **定義**：多執行緒是指在單一進程中同時運行多個執行緒。這些執行緒共享進程的記憶體和資源，但可以並行執行不同的任務。
- **優點**：共享記憶體和資源，創建和管理成本低，切換執行緒效率高。
- **缺點**：共享記憶體可能導致資料競爭和同步問題，一個執行緒的錯誤可能影響整個進程。

### 多進程（Multi-processing）
- **定義**：多進程是指同時運行多個獨立的進程，每個進程擁有自己的記憶體和執行環境。
- **優點**：進程間相互獨立，一個進程的崩潰不會影響其他進程，安全性和穩定性較高。
- **缺點**：資源消耗較高，進程間通信複雜，創建和切換成本高。

在選擇多執行緒或多進程時，需要根據應用的需求和特性來決定。如果需要資源共享和高效的執行緒管理，則選擇多執行緒可能更好；如果需要更高的穩定性和隔離性，則多進程可能是更合適的選擇。
## User Mode vs Kernel Mode

在操作系統中，user mode（用戶模式）和 kernel mode（核心模式）是兩種不同的執行狀態，它們各自具有不同的權限和功能。

### User Mode（用戶模式）
- **定義**：用戶模式是操作系統中的一種受限制的執行狀態。在這種模式下，應用程式無法直接訪問硬件資源和核心記憶體。
- **特點**：
  - 受限的執行權限。
  - 防止對操作系統核心和硬件的直接操作。
  - 安全性較高，因為應用程式錯誤不會直接影響到系統的穩定性。
- **程式範例**：大多數日常使用的應用程式，如網頁瀏覽器、文檔編輯器等，都在用戶模式下運行。

### Kernel Mode（核心模式）
- **定義**：核心模式是操作系統中具有最高權限的執行狀態。在這種模式下，操作系統可以直接訪問所有硬件資源和系統記憶體。
- **特點**：
  - 完全的執行權限。
  - 能夠直接訪問硬件和所有記憶體。
  - 高風險，因為一個錯誤可能導致整個系統崩潰。
- **程式範例**：操作系統的核心組件，如裝置驅動程式、核心系統程式等，都在核心模式下運行。

### 比較表

| 特性          | User Mode（用戶模式）                  | Kernel Mode（核心模式）                  |
|--------------|-----------------------------------|------------------------------------|
| 訪問權限       | 受限，無法直接訪問硬件和核心記憶體       | 完全訪問，可以直接操作硬件和所有記憶體       |
| 安全性         | 較高，應用程式錯誤不會直接影響操作系統核心 | 較低，錯誤可能導致系統崩潰             |
| 常見應用       | 網頁瀏覽器、文檔編輯器等應用程式      | 操作系統核心、裝置驅動程式等           |
| 風險和影響     | 相對較小的影響範圍                     | 錯誤可能導致整個系統不穩定或崩潰          |

## Race Condition（競爭條件）

### 定義
競爭條件發生在多個進程或線程同時訪問共享資源並試圖對其進行修改時。由於這些進程或線程的執行順序無法預測，它們對共享資源的操作可能會交織在一起，導致不可預知的結果或數據不一致。

### 程式範例（Python）

假設有兩個線程同時更新共享數據（如銀行賬戶餘額）。沒有適當的同步機制，就可能出現競爭條件。

```python
import threading

# 共享資源
account_balance = 100

def add_money(amount):
    global account_balance
    account_balance += amount

def withdraw_money(amount):
    global account_balance
    account_balance -= amount

# 創建線程
thread1 = threading.Thread(target=add_money, args=(50,))
thread2 = threading.Thread(target=withdraw_money, args=(70,))

# 啟動線程
thread1.start()
thread2.start()

# 等待線程結束
thread1.join()
thread2.join()

# 輸出最終餘額
print(f"Final account balance: {account_balance}")
```

在這個例子中，沒有控制對 `account_balance` 的訪問，所以最終的餘額取決於線程的執行順序，這就是競爭條件。解決這個問題的一種方式是使用互斥鎖來同步對共享資源的訪問。

### 解決方法
1. **使用互斥鎖（Mutex）**：通過鎖定機制來確保同一時間只有一個進程或線程可以訪問共享資源。
2. **原子操作**：實施不可分割的操作，確保在操作進行期間不會被其他進程中斷。
3. **序列化訪問**：安排進程或線程依序訪問共享資源。

### 互斥鎖範例
以下是 Python 中的互斥鎖（Mutex，這裡是 `threading.Lock`）來解決競爭條件問題的範例。在這個例子中，我們使用鎖來確保在修改共享資源（如銀行賬戶餘額）時，一次只有一個線程可以進行操作。

```python
import threading

# 共享資源
account_balance = 100
# 創建互斥鎖
lock = threading.Lock()

def add_money(amount):
    global account_balance
    with lock:  # 使用互斥鎖
        account_balance += amount

def withdraw_money(amount):
    global account_balance
    with lock:  # 使用互斥鎖
        account_balance -= amount

# 創建線程
thread1 = threading.Thread(target=add_money, args=(50,))
thread2 = threading.Thread(target=withdraw_money, args=(70,))

# 啟動線程
thread1.start()
thread2.start()

# 等待線程結束
thread1.join()
thread2.join()

# 輸出最終餘額
print(f"Final account balance: {account_balance}")
```

在這個修改後的範例中，我們對 `add_money` 和 `withdraw_money` 函數中涉及到修改 `account_balance` 的部分加上了鎖。當一個線程進入這部分代碼時，它會先獲取鎖，這樣其他線程就無法同時進入這個區塊，直到鎖被釋放。這確保了在修改 `account_balance` 時不會發生競爭條件，從而保證了數據的一致性和正確性。


## Deadlock
Deadlock 是一種多個進程因為競爭資源而造成的僵局，如果沒有外力干預，這些進程都無法繼續執行。
要產生 Deadlock，必須滿足以下四個條件：
1. 互斥條件：進程對所分配的資源進行排他性控制，一次只能有一個進程使用。
2. 佔有並等待條件：進程持有部分資源並等待其他進程已佔有的資源。
3. 不可剝奪條件：進程不能被強制放棄已獲得的資源，只能自願釋放。
4. 循環等待條件：一組進程形成循環等待的關係，每個進程都在等待下一個進程釋放資源。

解決 Deadlock 的方法有以下幾種：

1. 預防或避免 Deadlock：通過破壞上述四個條件中的一個或多個，確保系統永遠不會進入 Deadlock 狀態。例如，規定進程只有在一次獲得所有需要的資源後才能執行，或者使用銀行家演算法來判斷資源分配是否安全。
2. 檢測和恢復 Deadlock：允許系統進入 Deadlock 狀態，然後通過一些演算法來檢測是否有 Deadlock 發生，如果有，則採取一些措施來恢復，例如終止一個或多個進程，或者回收一些資源。
3. 忽略 Deadlock：這是一種不負責任的方法，就是假裝 Deadlock 從來不會發生，或者讓使用者自己處理。這種方法在一些不太可能發生 Deadlock 的系統中使用，例如 UNIX 和 Windows。


### 程式範例
下面是一個使用Python 來寫一個類似的程式範例，模擬 Deadlock 的情況。請看以下的程式碼：

```python
import threading
import time

# 定義兩個鎖
lock1 = threading.Lock()
lock2 = threading.Lock()

# 定義第一個進程的函數
def process1():
    # 嘗試獲得 lock1
    lock1.acquire()
    print("Process 1 acquired lock 1")
    time.sleep(1)

    # 嘗試獲得 lock2
    print("Process 1 waiting for lock 2")
    lock2.acquire()
    print("Process 1 acquired lock 2")

    # 釋放 lock1 和 lock2
    lock1.release()
    lock2.release()

    print("Process 1 finished")

# 定義第二個進程的函數
def process2():
    # 嘗試獲得 lock2
    lock2.acquire()
    print("Process 2 acquired lock 2")
    time.sleep(1)

    # 嘗試獲得 lock1
    print("Process 2 waiting for lock 1")
    lock1.acquire()
    print("Process 2 acquired lock 1")

    # 釋放 lock1 和 lock2
    lock1.release()
    lock2.release()

    print("Process 2 finished")

# 創建兩個線程
t1 = threading.Thread(target=process1)
t2 = threading.Thread(target=process2)

# 開始執行兩個線程
t1.start()
t2.start()

# 等待兩個線程結束
t1.join()
t2.join()
```

執行這個程式，會得到以下的輸出：

```
Process 1 acquired lock 1
Process 2 acquired lock 2
Process 1 waiting for lock 2
Process 2 waiting for lock 1
```

可以看到，兩個進程都在等待對方釋放資源，形成了循環等待的狀況，因此產生了 Deadlock。

### 避免Deadlock
當然可以，我會嘗試用 Python 來寫一個程式範例，展示如何避免或解決 deadlock 的情況。請看以下的程式碼：

```python
import threading
import time

# 定義兩個鎖
lock1 = threading.Lock()
lock2 = threading.Lock()

# 定義第一個進程的函數
def process1():
    # 嘗試獲得 lock1
    lock1.acquire()
    print("Process 1 acquired lock 1")
    time.sleep(1)

    # 嘗試獲得 lock2
    print("Process 1 waiting for lock 2")
    # 使用 try/finally 語句來確保 lock1 會被釋放
    try:
        # 使用 timeout 參數來避免無限等待
        if lock2.acquire(timeout=3):
            print("Process 1 acquired lock 2")
            # 釋放 lock2
            lock2.release()
        else:
            print("Process 1 failed to acquire lock 2")
    finally:
        # 釋放 lock1
        lock1.release()

    print("Process 1 finished")

# 定義第二個進程的函數
def process2():
    # 嘗試獲得 lock2
    lock2.acquire()
    print("Process 2 acquired lock 2")
    time.sleep(1)

    # 嘗試獲得 lock1
    print("Process 2 waiting for lock 1")
    # 使用 try/finally 語句來確保 lock2 會被釋放
    try:
        # 使用 timeout 參數來避免無限等待
        if lock1.acquire(timeout=3):
            print("Process 2 acquired lock 1")
            # 釋放 lock1
            lock1.release()
        else:
            print("Process 2 failed to acquire lock 1")
    finally:
        # 釋放 lock2
        lock2.release()

    print("Process 2 finished")

# 創建兩個線程
t1 = threading.Thread(target=process1)
t2 = threading.Thread(target=process2)

# 開始執行兩個線程
t1.start()
t2.start()

# 等待兩個線程結束
t1.join()
t2.join()
```

執行這個程式，會得到以下的輸出：

```
Process 1 acquired lock 1
Process 2 acquired lock 2
Process 1 waiting for lock 2
Process 2 waiting for lock 1
Process 1 failed to acquire lock 2
Process 2 failed to acquire lock 1
Process 1 finished
Process 2 finished
```

可以看到，兩個進程都在等待對方釋放資源，但是因為使用了 timeout 和 try/finally 的技巧，避免了無限等待和資源泄露的問題，因此沒有產生 deadlock。

這個程式範例是參考了[這個網站](^4^)的 Java 版本，並且做了一些適合 Python 的修改。如果您想了解更多關於 Python 中的 deadlock，您可以參考[這個網站](^1^)或[這個網站](^2^)。

希望這個程式範例

來源: 與 Bing 的交談， 2023/12/28
(1) Java 实例 – 死锁及解决方法 | 菜鸟教程. https://www.runoob.com/java/thread-deadlock.html.
(2) Day-25 Deadlock - iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天. https://ithelp.ithome.com.tw/articles/10278641.
(3) 第二十八天 死結(Deadlock) - iT 邦幫忙::一起幫忙解決難題，拯救 IT 人的一天. https://ithelp.ithome.com.tw/articles/10206625.