---
# System prepended metadata

title: 作業系統 2025_謝仁偉

---

# 作業系統 2025_謝仁偉
## Chapter 1: Introduction
- **作業系統**（Operating System）：介於使用者與電腦硬體之間的程式，用於：
  - 執行使用者程式
  - 讓解決問題更容易
  - 使系統好用
  - 有效率地運用硬體
- 電腦系統分四部分，由低到高為：
  - **硬體**（Hardware）：CPU、記憶體、I/O設備
  - **作業系統**（Operating system）
  - **應用程式**（Application programs）：文字編輯器、瀏覽器、資料庫、遊戲
  - **使用者**（Users）：人、機器、其他電腦
![image](https://hackmd.io/_uploads/HkFrhkJ2gl.png)

- OS沒有絕對的定義，一般有兩個職責：
  - **資源分配者**：管理所有資源，並在競爭時公平分配
  - **控制程式**：防止錯誤與不當使用。

- 開機或重啟時，會載入開機程式（Bootstrap Program），這程式會初始化系統、載入作業系統核心（Kernel）、開始執行。
- CPUs和裝置控制器透過匯流排來共享記憶體

- 裝置完成任務會中斷（Interrupt）CPU，讓 OS 處理下一步。中斷機制可暫停目前程式，轉而執行中斷服務程式（ISR）
- trap/exception是軟體引發的中斷（錯誤、請求）
- 作業系統是由中斷驅動，不會一直主動檢查所有事件，而是靠「中斷」來推動工作
- OS會保存CPU狀態（暫存器、程式計數器），判斷中斷的類型（polling、vector），做出對應的處置
==（會考）==
![capture_temp](https://hackmd.io/_uploads/SJT-BaJ2xx.jpg)
- **同步IO**：CPU 必須等待裝置完成
- **非同步IO**：I/O開始後先返回使用者程式，I/O會被放到等待佇列中，等待系統呼叫，使用者不能直接下I/O指令
- OS透過裝置表（Device-status table）來更新I/O條目，紀錄類別、地址、狀態

- **主記憶體**：隨機、揮發
- **二級儲存區**：非揮發
- **SSD**（Solid-state disks）：比硬碟快、非揮發
- **快取**（Caching）：將資訊複製到更快的儲存系統中，主記憶體可以被視為二級儲存的快取
- ![capture_temp](https://hackmd.io/_uploads/r1tiHA1nle.jpg)

- **多處理器（Multiprocessor）系統**：
  - 有多個 CPU
  - 優點：==（會考）==
		1. Increased throughput（提高吞吐量）
		2. Economy of scale（規模經濟效益）
		3. Increased reliability / fault tolerance（提高可靠性）
  - 又稱作平行系統（parallel systems）、緊密耦合系統（tightly-coupled systems）
  - **Graceful Degradation**：在部分元件發生故障時，仍能維持有限的服務，而不是完全停止運作 ==（會考）==
  - **Fault Tolerant**：在故障發生時仍能正常運作 ==（會考）==
  
- 多處理器系統分為：
  - **非對稱**（Asymmetric）：每個處理器負責特定任務
  - **對稱**（Symmetric）：所有 CPU 可執行所有任務

- 電腦系統組成的定義 ==（會考）==：
  - **CPU（中央處理器）**：執行指令的硬體 
  ==（The hardware that executes instructions）==
  - **Processor（處理器）**：一個包含一（多）個 CPU 的實體晶片 
  ==（physical chip that contains one or more CPUs.）==
  - **Core（核心）**：CPU 的基本運算單元 
  ==（The basic computation unit of the CPU）==
  - **Multicore（多核心）**：在同一個 CPU 上包含多個運算核心 
  ==（Including multiple computing cores on the same CPU.）==
  - **Multiprocessor（多處理器）**：包含多個處理器的系統 
  ==（Including multiple processors）==

- **叢集系統**（Clustered System）：
  - 多台電腦（節點）透過儲存區域網路（san，storage-area network）共享儲存資源
  - 可用於高可用性（High Availability）或高效能運算（HPC）
  - 熱備援（Hot-standby）模式可在主伺服器故障時自動接手
  - **非對稱叢集**（Asymmetric Clustering）：其中一台機器處於熱備援（hot standby）模式
  - **對稱叢集**（Symmetric Clustering）：多個節點同時執行應用程式，並互相監控狀態

- **多程式設計**（Multiprogramming/Batch system）：讓 CPU 交替執行多個工作，避免閒置

- **分時系統**（Time Sharing/multitasking）：
  - CPU 以極高頻率在不同工作之間切換，讓使用者能夠同時操作多個程式
  - 若有多個工作要執行，由CPU排成決定順序
  - 若行程無法全部放入主記憶體，透過交換（swapping） 將部分程式暫時移出再載入
  - 虛擬記憶體（virtual memory） 允許執行不完全載入至記憶體的程式

- OS 由「中斷」驅動：
  - **硬體中斷**：裝置發出的信號
  - **軟體中斷**（Trap / Exception）：軟體錯誤（如除以零）、要求 OS 服務、無限迴圈、程式互相干擾等問題

- **雙模式**（Dual-mode）：
  - 讓 OS 保護自己與其他系統元件
  - 兩種模式為**使用者模式**（User Mode）、**核心模式**（Kernel Mode）
  - 模式位元由硬體提供，用來判斷當前執行權限，核心模式下才能執行特權指令
  - **系統呼叫**（System Call）會從使用者模式切換到核心模式
  - 某些 CPU 支援多重模式，例如虛擬機管理模式（VMM Mode）

- User Mode 切換至 Kernel Mode
   ![image](https://hackmd.io/_uploads/rkz04VjTlg.png =100%x)

- **行程**（Process）：執行中的程式
  - 程式是靜態的、行程是動態的
  - 結束時需釋放資源
  - 單執行緒（Single-threaded）行程只有一個指令計數器（program counter），多執行緒（Multi-threaded）行程有多個指令計數器

- 在多處理器環境下，必須確保每份資料一致（cache 
coherency）

- **輸入輸出子系統**（I/O Subsystem）：
  - I/O記憶體管理，包含 ==（會考）==：
    - **緩衝（Buffering）**：在傳輸資料時暫存資料
    - **快取（Caching）**：將部分資料儲存在更快的儲存區中以提高效能
    - **周邊設備同步操作（Spooling）**：用硬碟當作中介緩衝區，讓 I/O（輸入輸出）與 CPU 能同時運作
    ==（讓一個task的輸出和另一task的輸入同時進行）==
    	- 把 I/O 任務先暫存在硬碟或磁碟檔案中，等裝置有空再執行，讓慢速裝置（例如印表機、磁帶機）能與高速的 CPU 同步運作
  - 通用裝置介面（Device Driver Interface）
  - 特定硬體的驅動程式

- 保護與安全：
  - **保護**（Protection）：控制行程或使用者對資源的存取權限
  - **安全**（Security）：防禦內部與外部攻擊，如病毒、DDoS、身份竊取。
  - 系統透過User ID與group ID來控制存取權
  - 權限提升（Privilege Escalation）允許程式臨時獲得更高權限
   
- Virtualization ==（會考）==：
  - **虛擬化**（Virtualization）：一台實體電腦分成多個獨立環境，使主機 OS（Host）同時運行多個客體 OS（Guest）
  - **模擬**（Emulation）：用於模擬不同的架構（如 PowerPC → x86），但速度慢
  - **VMM**（Virtual Machine Manager）：提供虛擬化服務
  ![image](https://hackmd.io/_uploads/B1EZzLspee.png =100%x)

- **分散式系統**（Distributed Systems）：
  - 由多台實體電腦透過網路（TCP/IP）連接而成，常見形式有：
  - Local Area Network (LAN，區域網)：房間、校園
  - Wide Area Network（WAN，廣域網）：城市間、國家間
  - Metropolitan Area Network（MAN，都會網）：同個城市內的建築
  - Personal Area Network （PAN，個人網）：如藍牙

- **核心資料結構**（Kernel Data Structures）：
  - OS 內部使用各種資料結構：
  - 單向、雙向、循環鏈結串列
  - 二元搜尋樹（BST）與平衡樹（如紅黑樹）
  - 雜湊表（Hash Map）：用雜湊函數快速查找 [key:value]
  - 位元圖（Bitmap）：用 0/1 表示多個項目的狀態
  ![image](https://hackmd.io/_uploads/SycrSIjplg.png =100%x)
  ![image](https://hackmd.io/_uploads/Sy7UrIjalx.png =100%x)
  ![image](https://hackmd.io/_uploads/H1xwrLi6gx.png =100%x)

- OS 運算環境：
  - **傳統運算**（Traditional）：獨立桌機或工作站，但大多與網路連接
  - **行動運算**（Mobile）：手機與平板，具 GPS、感測器等
  - **用戶端-伺服器**（Client-Server）：伺服器提供資料或檔案服務
    - **計算伺服器**（Compute Server）：提供運算服務，如資料庫查詢
    - **檔案伺服器**（File Server）：提供檔案存取介面以儲存與擷取檔案
  - **對等式網路**（P2P）：節點彼此都是客戶端也是伺服器，如 Skype
  - **雲端運算**（Cloud Computing）：
    - **IaaS**（基礎建設即服務，Infrastructure as a Service）：提供硬體資源（伺服器、儲存）
    - **PaaS**（台即服務，Platform as a Service）：提供應用平台（資料庫服務）
    - **SaaS**（軟體即服務，Software as a Service ）：提供應用軟體（Google Docs）
    - **公有雲**（Public Cloud）：任何人都可付費使用
    - **私有雲**（Private Cloud）：公司內部自行運行
    - **混合雲**（Hybrid Cloud）：兩者結合
  - **即時與嵌入式系統**（Real-Time Embedded）：
    - 具有時間限制（如汽車控制器、醫療裝置）
    - 必須在固定時間內完成任務

## Chapter 2： Operating-System Structures
- 作業系統提供的服務：
  - **使用者介面**（UI）：提供圖形介面（GUI）、命令列介面（CLI）或觸控介面（Touch-Screen Interface）
  - **程式執行（Program Execution）**：載入程式到記憶體並執行
  - **I/O 操作（I/O Operations）**：控制與管理輸入/輸出裝置
  - **檔案系統操作（File-System Manipulation）**：讀寫檔案、建立與刪除、存取控制
  - **通訊（Communication）**：行程之間或遠端系統之間交換資訊（訊息傳遞、共用記憶體）
  - **錯誤偵測（Error Detection）**：偵測並回報硬體或軟體錯誤
  - **資源分配（Resource Allocation）**：為行程分配 CPU、記憶體、裝置、檔案等資源
  - **紀錄（Accounting）**：追蹤資源使用量，用於評估效能
  - **保護與安全（Protection and Security）**：
    - **保護（Protection）**：確保所有對系統資源的存取都受到控制
    - **安全（Security）**：防止外部使用者或惡意程式入侵系統
  ![image](https://hackmd.io/_uploads/Hkka-tspxe.png =100%x)

- **命令列介面**（CLI/command interpreter）：使用者輸入文字命令，例如 Linux shell 或 Windows cmd
- **圖形介面**（GUI）：使用視窗、滑鼠、圖示，如 macOS、Windows

- **系統呼叫**（System Calls）：
  - 應用程式與作業系統之間的程式設計介面
  - 通常以高階語言（C 或 C++）撰寫
  - 多數程式不會直接呼叫系統呼叫，而是透過**高階應用程式介面（API，Application Programming Interface）** 間接存取
  - 每個系統呼叫都有一個對應的編號，系統呼叫介面（System-call interface）會以這些編號作為索引建立表格
  ![image](https://hackmd.io/_uploads/ry0QBFjTgg.png =100%x)

- 三種系統呼叫常見參數傳遞方式 ==（會考）==：
  1. 暫存器傳遞：直接在 CPU 暫存器中傳參數，若參數過多，暫存器數量可能不足
    ==（將參數存在 CPU register中傳送）==
  2. 記憶體區塊傳遞：將參數存在記憶體中的一個表格或區塊，再把該區塊的位址存入暫存器
    - Linux 混合使用這兩種，五個以內的參數放暫存器，超過五個就用區塊
    ==（將參數存在 memory 的 block，傳 block 的 address）==
  3. 堆疊傳遞：程式把參數壓入堆疊，OS 再從堆疊取出，不限制參數的數量與長度
    ==（程式把參數push進堆疊，再由 OS POP）==
		
- 系統呼叫的種類 ==（會考，例如舉五個例子）==：
  - **Process Control（行程控制）**：
    - create process, terminate process（建立、終止行程）
    - load, execute（載入與執行）
    - get process attributes, set process attributes（取得與設定屬性）
    - wait for time, wait event, signal event（等待事件或時間）
    - allocate and free memory（分配與釋放記憶體）
    - Dump memory if error（錯誤傾印）
    - Debugger for determining bugs, single step execution（除錯）
    - Locks for managing access to shared data between processes（使用鎖以控制共享資料存取）

  - **File Management（檔案管理）**：
    - create file, delete, open, close file（建立、刪除、開啟、關閉檔案）
    - read, write, reposition（讀取、寫入、重新定位檔案指標）
    - get and set file attributes（取得或設定檔案屬性）

  - **Device Management（裝置管理）**：
    - request device, release device（請求、釋放裝置）
    - read, write, reposition（讀寫裝置資料）
    - get device attributes, set device attributes（設定裝置屬性）
    - logically attach or detach devices（邏輯上掛載或卸除裝置）
    - 
  - **Information Maintenance（資訊維護）**：
    - get time or date, set time or date, get system data, set system data（取得或設定時間、日期、系統資訊）
    - get and set process, file, or device attributes（修改行程、檔案或裝置屬性）

  - **Communications（通訊）**：
    - create, delete communication connection（建立與刪除通訊連線）
    - send, receive messages（若使用訊息傳遞模式（message passing），可依主機或行程名稱傳送與接收訊息）
    - Shared-memory model create and gain access to memory regions（共享記憶體模式下可建立並存取記憶區域）
    
  - **Protection（保護）**：
    - Control access to resources（控制資源存取）
    - Get and set permissions（取得與設定權限）
    - Allow and deny user access（允許或拒絕使用者存取）


- **系統程式**（System Programs）：提供使用者開發與執行的便利環境
  - **檔案管理**：建立、刪除、複製、列印、清單管理
  - **狀態資訊**：查詢時間、記憶體用量、使用者數量，或效能、日誌、除錯資料
  - **檔案修改**：文字編輯器、搜尋與轉換指令。
  - **程式語言支援**：編譯器、組譯器、除錯器。
  - **程式載入與執行**：載入器、連結器、除錯系統。
  - **通訊工具**：訊息傳遞、網頁瀏覽、郵件、遠端登入、檔案傳輸。
  - **背景服務**：
    - 開機時啟動，提供排程、錯誤紀錄、列印等功能。
    - 在使用者模式運作，稱為 daemon（守護行程） 或 subsystem（子系統）。
  - **應用程式**：使用者自行執行的程式，不屬於作業系統本身

- OS 設計沒有單一最佳解，需先定義目標與規格，兩類目標：
  - **使用者目標**：好用、易學、可靠、安全、快速
  - **系統目標**：易設計、易維護、具彈性、可靠、無錯且高效

- 設計原則 ==（會考）==
  - **Policy**：**What** will be done?
  - **Mechanism**： **How** to do it?
- 使用高階語言更容易**移植（port）**但稍慢。
- **模擬**可讓 OS 跑在非原生硬體上。

- 作業系統結構：
  - **MS-DOS**：
    - 沒有模組劃分
    - 功能層交錯、界線不明
    - 為節省空間，犧牲了模組化與安全性
    ![image](https://hackmd.io/_uploads/HJcofosTll.png =100%x)


  - **UNIX**：
    - 分為系統程式與核心兩層
    - 核心提供檔案系統、排程、記憶體管理等
    ![image](https://hackmd.io/_uploads/H1DRfso6xl.png =100%x)

  - **Layered Approach（分層設計）**：
    - 系統分成多層，由底層硬體至最高層使用者介面
    - 每層僅能使用下層的服務
    - 優點：模組化高、容易維護
    ![image](https://hackmd.io/_uploads/BJ_W7oipgx.png =100%x)

  - **Microkernel（微核心）**：
    - 將非核心功能移至使用者空間
    - 核心僅保留基本的記憶體、行程、通訊功能
    - 優點：可靠、安全、可移植
    - 缺點：效能稍低

  - **Modules（模組化核心）**：
    - 現代 OS（如 Linux、Solaris）採可載入核心模組設計
    - 物件導向概念，每個模組可獨立載入、卸載、互通
    - 比分層結構更靈活
    
  - **Hybrid Systems（混合式系統）**：
    - 多數現代 OS 都是混合設計
    - Linux / Solaris：單核心（monolithic）＋模組化
    - Windows：主要為單核心，但含微核心元素
    - macOS X：Mach 微核心 + BSD + 動態核心模組 +（Kernel Extensions） + Aqua 介面

- 除錯與效能調整：
  - 除錯是找出並修復錯誤的過程
  - OS 會記錄錯誤日誌（log files）
  - **應用程式錯誤**可生成**核心傾印檔**（core dump）
  - **系統錯誤**可生成**崩潰傾印**（crash dump）
  - 效能調整（Performance Tuning）可藉由觀察追蹤紀錄與統計樣本進行

- 系統開機（System Boot）：
  - 儲存在韌體（ROM）中的開機載入器（Bootstrap Loader）會定位核心、載入記憶體並啟動
  - 常見開機載入器：GRUB，可選不同核心與版本


## Ch3 Processes

### Process
- 正在執行中的程式（job 與 process 幾乎可互換），活動狀態由兩項內容表示：
  - **程式計數器**（program counter）：記錄下一行要執行的指令位址
  - **處理器暫存器**（processor’s registers）：儲存目前運算狀態
- 一個行程的記憶體配置依位址 **由低到高** 可分四個部分（**會考**）：
  - **程式碼區**（text section）：儲存可執行**程式碼** ==（code）==
  - **資料區**（data section）：儲存**全域變數** ==（global variables）==
  - **堆積區**（heap）：儲存執行期間**動態配置**的記憶體（例如 `malloc()` 或 `new`）==（dynamic memory allocation）==
  - **堆疊**（stack）：函式呼叫**暫存資料**（函式參數、返回位址、區域變數）==（temporary data）==
  ![image](https://hackmd.io/_uploads/S1K-GJxRlx.png =100%x)
- **Program** 是被動實體，**Process** 是主動實體

### 行程狀態（Process State）==（會考）==：
- **new**（建立中）：正在被建立
- **running**（執行中）：CPU 正在執行指令
- **waiting**（等待中）：等待某事件發生
- **ready**（就緒）：等待被分配到 CPU
- **terminated**（結束）：已完成執行
![image](https://hackmd.io/_uploads/HJgp7Je0xx.png =100%x)
- new → ready：被接納（admitted）
- ready → running：被排程（ scheduler dispatch）
- running → waiting：因 I/O 或事件等待（I/O or event wait）
- waiting → ready：I/O 或事件完成（I/O or event completion）
- running → ready：因中斷（interrupt）
- running → terminated：執行結束（exit）

### 行程控制區塊（Process Control Block, PCB）
- 紀錄行程相關資訊，又稱任務控制區塊（task control block）：
- Process state（行程狀態）：例如 running、waiting 等
- Program counter（程式計數器）：記錄下一個要執行的指令位址
- CPU registers（CPU 暫存器）：儲存行程在 CPU 上的暫存資料
- CPU scheduling information：排程資訊，例如優先權、排程佇列指標
- Memory-management information：記憶體分配資訊
- Accounting information：紀錄 CPU 使用量、執行時間、時間限制等。
- I/O status information：輸入輸出裝置使用情況、開啟的檔案清單等

### Process Scheduling
- 目標：最大化 CPU 使用率
- **行程排程器**（Process scheduler）：從所有可執行的行程中選擇下一個要分配給 CPU 的行程
- 系統維護多個 scheduling queues（排程佇列）：
  - Ready queue：準備好執行、等待被分配 CPU 的行程
  - Wait queue：正在等待某事件（例如 I/O 完成）的行程
  ![image](https://hackmd.io/_uploads/HJev9klRlx.png =100%x)

### 排程器(Schedulers）（==會考==）
- **短期排程器**（**Short-term Scheduler** 或 **CPU Scheduler**）：
  ==（決定哪個process被放入CPU）==
  - **負責選擇下一個要執行的行程並分配 CPU**
  - 必須非常快速
- **長期排程器**（**Long-term Scheduler** 或 **Job Scheduler**）：
  ==（決定哪個process被放入ready queue）==
  -  **負責決定哪些行程要被放入 ready queue**
  -  被觸發的頻率較低（秒或分鐘級）。
  -  控制系統中的多程式程度（degree of multiprogramming）
- **中期排程器**（**Medium-term Scheduler**）：
  ==（當負載過高時，將 process 從 memory 移到 disk ，需要時再移回來）==
	- 當系統的多程式程度過高時，可以用來降低負載
  - 暫時從記憶體中移除某些行程，存放到磁碟，等需要時再載回記憶體繼續執行，稱為交換（Swapping）。
  ![image](https://hackmd.io/_uploads/HktNhyl0xl.png =100%x)

- 行程可以分為兩種類型：
	- **I/O 密集行程**（I/O-bound process）：花較多時間在輸入輸出操作（I/O），CPU 執行時間短但頻繁
	- **CPU 密集行程**（CPU-bound process）：花較多時間做運算（computation），CPU 執行時間長但次數少。

### 上下文切換（Context Switch）
- 當 CPU 從一個行程切換到另一個行程時，需要：
  - 儲存舊行程的狀態
  - 載入新行程的已儲存狀態
- 上下文被儲存在行程控制區塊（PCB）中

### 行程建立（Process Creation）
- **父行程**（Parent Process）可以建立**子行程**（Child Processes），子行程也可以再建立其他行程，形成行程樹（Tree of Processes）
- 每個行程都有行程識別碼（Process Identifier, PID）
- A Tree of Processes in Linux：
  ![image](https://hackmd.io/_uploads/B11ZyllRgx.png =100%x)
- 新行程的記憶體位址空間有兩種可能：
	- 子行程是父行程的完全複製（duplicate）
  - 子行程載入一個新的程式來執行
- **fork()**：用來建立新行程
- **exec()**：通常在 fork() 之後使用，以新程式取代原行程的記憶體內容
  ![image](https://hackmd.io/_uploads/rysfeglRxx.png =100%x)

### 行程終止
- 行程執行完最後一條語句後，會呼叫 exit() 系統呼叫請 OS 刪除自己
	- 狀態資料透過 wait() 回傳給父行程
- 父行程可使用 abort() 系統呼叫強制終止子行程
- 父行程可使用 wait() 等待子行程終止，並取得其結束狀態與行程識別碼（PID）
- 連鎖終止（Cascading Termination）：某些OS中，若父行程結束，所有子孫行程都會被終止
- **殭屍行程**（Zombie Process）（==會考==）
	- 行程已經結束，但父行程尚未呼叫 wait()的子行程
	- 父行程呼叫 wait() 時被釋放
- **孤兒行程**（Orphan Process）（==會考==）
  - 父行程在未呼叫 wait() 前就終止的子行程
  - OS會將其父行程改為 init 行程，init 行程會定期呼叫 wait() 回收它們

### 行程間通訊（Interprocess Communication）
- 在一個系統內，不同行程可以是：
	- 獨立的（independent）
	- **合作的**（cooperating）
- 合作行程（Cooperating Process）
	- 原因包括（==會考==）：
		- **資訊共享**（Information sharing）
		- **計算加速**（Computation speedup）
		- **模組化**（Modularity）
  - 透過行程間通訊（Interprocess Communication, IPC） 來互動，有兩種主要模型（==會考==）：
		- 共享記憶體（Shared memory）
		- 訊息傳遞（Message passing）
  ![image](https://hackmd.io/_uploads/HyGANleRlx.png =100%x)
- 生產者、消費者問題（Producer-Consumer Problem）
	- 生產者（Producer）行程負責產生資料
	- 消費者（Consumer）行程負責取用資料
- 依照緩衝區（buffer）的設定可分為：
	- 無界緩衝區（unbounded buffer）：緩衝區容量理論上無限大
	- 有界緩衝區（bounded buffer）：緩衝區大小固定，以循環陣列（circular array）方式實作

### 訊息傳遞（Message Passing）
- 一種行程用來通訊與同步行為（synchronize their actions）的機制
- IPC（行程間通訊）機制提供兩種操作：
	- 傳送訊息：send(message) 
	- 接收訊息：receive(message)
- 當行程 P 和 Q 想要通訊時，它們需要：
	- 建立一個通訊連結（communication link）
	- 透過 send/receive 交換訊息
- 直接通訊（Direct Communication）：行程必須明確指定對方的名稱
	- send(P, message)：傳訊息給行程 P
	- receive(Q, message)：從行程 Q 接收訊息
- 間接通訊（Indirect Communication）：訊息透過郵箱（mailbox）或埠口來收發
	- 每個 mailbox 有唯一的 ID
	- 只有共享同一個 mailbox 的行程才能互相通訊

- 同步機制（Synchronization） ==（會考）==
	- **阻塞式**（Blocking）為 **同步（Synchronous）** 傳輸：
		- 阻塞式傳送（Blocking send）：傳送方會被卡住，直到訊息被接收為止
		- Blocking receive（阻塞式接收）：接收方會被卡住，直到有訊息可用為止
	- **非阻塞式**（Non-blocking）為 **非同步（Asynchronous）** 傳輸：
		- 非阻塞式傳送（Non-blocking send）：傳送方送出訊息後立即繼續執行。
		- 非阻塞式接收（Non-blocking receive）：接收方會立刻收到：
			- 一則有效訊息（valid message）
			- 空訊息（null message）

- **通訊端**（Sockets）
	- 一個通訊的端點（endpoint），一個連線包含一對 socket
	- IP 位址：哪一台電腦
	- Port（埠號）：哪個應用程式
	- IP 位址 127.0.0.1（loopback） 代表本機系統
	- Java 中有三種類型的 socket：
		- **Connection-oriented (TCP)**：有連線導向的通訊（可靠、有順序）
		- **Connectionless (UDP)**：無連線導向的通訊（快速但不保證送達）
- 遠端程序呼叫（Remote Procedure Calls，RPC）
	- Stubs（存根）：用戶端的代理程式，代表伺服端上的實際程序
	- 資料表示透過 External Data Representation (XDL) 格式處理
		- 包含 Big-endian（高位元組在前） 與 Little-endian（低位元組在前）
	- 遠端通訊比本地通訊更容易發生錯誤，因此通常設計兩種傳遞策略 ==（會考）==：
		- **exactly once**：每個 RPC 只執行一次
		- **at most once**：每個 RPC 最多執行一次（可能丟失）

## Ch4 Threads & Concurrency
### 執行緒（threads）
- 程序內的執行單元，不同任務可以由不同的執行緒分別執行
- 大多數現代應用程式都是多執行緒（multithreaded）
- 優點 ==（會考）==：
	- **回應性**（Responsiveness）：當程式的一部分被阻塞時，其他執行緒仍可繼續執行
	- **資源共享**（Resource Sharing）：多個執行緒共享同一程序的資源，比起使用共享記憶體或訊息傳遞更容易
	- **經濟性**（Economy）：建立與切換執行緒比建立與切換程序便宜得多
	- **可擴展性**（Scalability）：程序能在多處理器架構上同時利用多核心運算

### 多核心、多處理器（Multicore, Multiprocessor）
- **平行系統（Parallel system）**：能同時執行多個任務
	- 資料平行（Data parallelism）：分配相同資料到多個核心上執行，每個核心執行相同的操作
	- 任務平行（Task parallelism）：分配不同的執行緒到不同核心上，每個執行緒執行不同的任務
- **並行系統（Concurrent system）**：任務之間交錯執行，非同時進行
- **晶片多執行緒**（Chip Multithreading, CMT）
	- 每條硬體執行緒都有自己的架構狀態，因此可被視為一個邏輯 CPU（Logical CPU）
	- 例如CPU有4核心 x 2 硬體執行緒 = 8 個邏輯 CPU
	![image](https://hackmd.io/_uploads/rJ2GsqlAxx.png =100%x)
 
	- 並行 vs. 平行 ==（會考）==
		- 並行（Concurrent）：多個任務在單核心交錯執行
		- 平行（Parallel）：多個任務於不同核心同時執行
	![image](https://hackmd.io/_uploads/rkPLi9lRxx.png =100%x)

- Amdahl’s Law ==（會考）==
	- 計算在增加核心數量時，應用程式性能可提升的上限
	- S：序列化比例（serial portion）
	- N：處理核心數量（number of processing cores）
	![image](https://hackmd.io/_uploads/ryiYnqgAle.png)

### 多執行緒模型（Multithreading Models）
- 執行緒支援可以出現在使用者層級（User level）或核心層級（Kernel level）
	- 使用者層級的稱為使用者執行緒（User Threads）
	- 核心層級的稱為核心執行緒（Kernel Threads）
- 使用者執行緒與核心執行緒之間有三種對應模型 ==（會考）==：
	- 多對一（Many-to-One Model）
	- 一對一（One-to-One Model）
	- 多對多（Many-to-Many Model）
	- 雙層（Two-level Model）
- **多對一（Many-to-One Model）**
	- **許多**使用者層級執行緒會對應到**單一**核心執行緒
	- 若其中一個執行緒被阻塞（blocking），則所有執行緒都會被阻塞
	- 多個執行緒無法同時平行執行
	- 目前只有極少系統使用此模型
	![image](https://hackmd.io/_uploads/BJhTpigAxx.png =100%x)
- **一對一（One-to-One Model）**
	- **每個**使用者層級執行緒都對應到**一個**核心層級執行緒
	- 建立一個使用者執行緒時，也會自動建立對應的核心執行緒
	- 與多對一模型相比，可支援更多的並行（concurrency）
	- 但若核心執行緒數量太多，會對系統效能造成負擔
	- 範例：Windows、Linux、Solaris 9 之後版本
	![image](https://hackmd.io/_uploads/HJDtAigCxx.png =100%x)
- **多對多（Many-to-Many Model）**
	- **多個**使用者層級執行緒對應到**少於或等於**數量的核心層級執行緒
	- 開發者可以自由建立所需的使用者執行緒數量，核心執行緒則可在多處理器上平行（parallel）執行
	- 當某個執行緒執行阻塞系統呼叫時，核心可以改排程其他執行緒執行
	- 較少採用：實作困難、現今核心執行緒數量
	![image](https://hackmd.io/_uploads/rymNlnlCeg.png =100%x)

### 執行緒函式庫（Thread Libraries）
- 提供建立與管理執行緒的 API，有兩種實作方式：
	- 完全在使用者空間實作，不依賴核心支援
	- 核心層級函式庫，由作業系統直接支援
- 主要有三種執行緒函式庫：
	- POSIX Pthreads（可為使用者層或核心層函式庫）
	- Windows Threads（核心層函式庫）
	- Java Threads（以主機系統上的執行緒函式庫實作）

- Pthreads（POSIX 執行緒）
	- 可以是使用者層級或核心層級的實作
	- 是規範（specification），不是實作（implementation）
	- 常見於 UNIX 系統：如 Solaris、Linux、macOS

- Windows Threads
	- WaitForMultipleObjects() 函式用於等待多個執行緒完成，需傳入四個參數：
		- 要等待的物件數量
		- 指向物件陣列的指標
		- 一個旗標（flag），用來指示是否要等待所有物件都完成才繼續
		- 超時時間（timeout），可以設定為具體時間或 INFINITE

- Java Threads
	- 由 JVM（Java Virtual Machine） 管理
	- 透過底層作業系統的執行緒模型實作
  - 兩種建立方式：
		- 繼承 Thread 類別（class） 並覆寫其中的 run() 方法
		- 實作 Runnable 介面

### 隱式多執行緒（Implicit Threading）
- 顯式多執行緒過多會使程式正確性更難維護
- 開始由編譯器與執行期函式庫自動建立與管理執行緒
- 常見三種方法：
	- Thread Pools
	- OpenMP
	- Grand Central Dispatch（GCD）
- Thread Pools（執行緒池）
	- 建立一組固定數量的執行緒，等待被分派任務
	- 優點：
		- 更快速
		- 限制執行緒數量，避免系統負載過重。
		- 將「要執行的任務」與「建立執行緒的細節」分離，讓排程策略更彈性
- OpenMP（開放式多處理）
	- 編譯器指令（compiler directives） 和 API，支援 C、C++、Fortran
	- 用於在共享記憶體環境（shared-memory environment）中實現平行化程式
	- 定義平行區塊，這些區塊可以同時在多核心上執行
- Grand Central Dispatch（GCD）
  - Apple 為 macOS 與 iOS 開發的技術
  - serial queue（序列佇列）：
		- 任務以 FIFO（先進先出）順序執行，一次只跑一個
		- 每個行程都有一個 main queue（主佇列）
  - concurrent queue（並行佇列）：
		- 仍然是 FIFO，但允許多個任務同時執行

### 執行緒區域儲存（Thread-Local Storage, TLS） ==（會考）==
  - 允許每個執行緒擁有自己的資料副本
	- 與一般區域變數不同：區域變數僅在函式呼叫期間有效，**TLS 在整個執行緒生命週期內都可見**
	- **相較於靜態變數（static），每條執行緒都有獨立副本**

## Ch5： CPU Scheduling
### 多重程序（Multiprogramming）
- 目標是讓系統中隨時都有某個程序在執行，以達到最大化 CPU 使用率
- 程序由一連串的 **CPU–I/O 週期（CPU–I/O Burst Cycle）** 組成，每個周期包括：
	- CPU burst（CPU 密集計算階段）
	- I/O burst（等待輸入輸出階段）

### CPU Scheduler
- 短期排程器（Short-term scheduler）：從就緒佇列（ready queue）中挑選一個程序分配給 CPU 執行
- 排程決策可能發生在以下四種情況：
	1. 程序從執行狀態（running）→ 等待狀態（waiting）
	2. 程序從執行狀態（running）→ 就緒狀態（ready）
	3. 程序從等待狀態（waiting）→ 就緒狀態（ready）
	4. 程序終止（terminate）
- **非搶先式（nonpreemptive）或合作式（cooperative）排程** ：排程只在情況 1 與 4 發生
- **搶先式（preemptive）**：排程在所有情況發生
	- 當多個程序共用資料時，搶先式排程可能導致競爭條件（race condition）
	- 若程序在修改關鍵核心資料時被搶先（例如 I/O 佇列），而核心或驅動程式同時讀寫該資料，將造成錯誤
	- 因為中斷（interrupt）可隨時發生，且核心不能忽略它，
	所以被中斷影響的程式區段必須受到保護

### 派遣器（Dispatcher）
- 負責把 CPU 控制權交給短期排程器選出的程序
- 派遣延遲（Dispatch latency）：派遣器從停止一個程序到開始另一個程序所需的時間

### Scheduling
- Scheduling Criteria（排程評估準則）==（會考）==
	- CPU 利用率（CPU utilization）：讓 CPU 儘量保持忙碌
	- 吞吐量（Throughput）：每單位時間內完成的程序數
	- **周轉時間**（Turnaround time）：完成一個程序所花的總時間（程序到達 => 完成）
	- **等待時間**（Waiting time）：程序在就緒佇列中等待的時間（程序進入Ready queue => 得到CPU開始執行的**總時間**）
	- **回應時間**（Response time）：從送出請求到第一次回應出現的時間（程序到達 => 得到CPU開始執行）

### First- Come, First-Served (FCFS) Scheduling（先來先服務排程） ==（會考）==
![image](https://hackmd.io/_uploads/B1Ta10x0gl.png =100%x)
![image](https://hackmd.io/_uploads/HJVgZ0xRle.png=100%x)
- 車隊效應（Convoy Effect）：當短任務排在長任務後面時，所有短任務都必須等待。

### Shortest-Job-First (SJF) Scheduling（最短工作優先） ==（會考）==
- 選擇最短的程序先執行
- 最佳（optimal）演算法，可得到最小平均等待時間
- 難點：系統無法預知下一次 CPU burst 的長度
![image](https://hackmd.io/_uploads/S1Ph-Al0xl.png=100%x)

### Shortest Remaining Time First（最短剩餘時間優先） ==（會考）==
- 可搶先版本的SJF
- 利用先前 CPU 執行期的長度進行指數平均法（exponential averaging），估計下一次 CPU 執行期的長度：
	1. 𝑡ₙ = 第 n 次實際的 CPU 執行期長度
	2. τₙ₊₁ = 下一次預測的 CPU 執行期長度
	3. α，滿足 0 ≤ α ≤ 1
	4. 定義：τₙ₊₁ = α·𝑡ₙ + (1 − α)·τₙ
- 當 α = 0 時：最近的觀測值（tₙ）不影響結果
- 當 α = 1 時：只考慮最近一次的 CPU burst
![image](https://hackmd.io/_uploads/S14uF0xClx.png)

### Priority Scheduling ==（會考）==
- 每個程序會被分配一個優先權值（priority number），數字越小、優先權越高
- 分為可搶先（Preemptive）、不可搶先（Nonpreemptive）
- 問題：Starvation（飢餓）：低優先權程序可能永遠等不到執行機會。
- 解法：Aging（老化）：隨著等待時間增加，逐漸提高程序的優先權
![image](https://hackmd.io/_uploads/ryo7qCeCex.png)

### Round Robin (RR) ==（會考）==
- 每個程序都獲得一小段 CPU 時間（稱為 time quantum q），當時間到達時，該程序會被搶先（preempted）並移到就緒佇列尾端
- 效能分析：
	- 若 q 很大：變成 FIFO（先來先服務）
	- 若 q 太小：上下文切換開銷太高
![image](https://hackmd.io/_uploads/BycYR0gReg.png)
- 通常 RR 的平均週轉時間比 SJF 高，但回應時間（response time）較佳
- q 應該比上下文切換時間大
![image](https://hackmd.io/_uploads/H1N111W0le.png)

### Multilevel Queue（多層佇列排程）
- 就緒佇列（ready queue） 被分成多個獨立的佇列，例如：
	- foreground（前景）：互動型程序（interactive），使用RR
	- backgound（背景）：批次程序（batch），使用FCFS
![image](https://hackmd.io/_uploads/B1dYJybCxl.png)
- Multilevel Feedback Queue（多層回饋佇列）：一個程序可以在不同佇列間移動，可藉此實現老化（aging）機制

### Thread Scheduling
- 使用者層執行緒：
	- PCS（Process-Contention Scope）：CPU 的競爭發生在同一個程序的執行緒之間
- 核心層執行緒：
	- SCS（System-Contention Scope）：系統中所有執行緒共同競爭 CPU

### Multiple-Processor Scheduling
- 同質處理器（homogeneous processors）：每顆 CPU 的能力相同
- 多處理器排程有兩種方式：
	1. 非對稱多處理（Asymmetric multiprocessing）：
		- 只有一顆 CPU 負責系統資料存取與排程
		- 其他 CPU 只執行工作，減少共享資料的衝突
	2. 對稱多處理（Symmetric multiprocessing, SMP）：
		- 每顆 CPU 都能自行排程
		- 所有 CPU 共用一個就緒佇列，或各自有私有佇列
		- 目前最常見
- Processor affinity（處理器親和性）：程序傾向在同一顆 CPU 上持續執行
	- Soft affinity（軟性親和性）：儘量讓程序留在同一顆 CPU，但在負載平衡時仍可移動
	- Hard affinity（硬性親和性）：程序明確指定可運行的 CPU 子集合
- NUMA（Non-Uniform Memory Access）：雖然系統中的所有 CPU 可共用一個記憶體空間，但每個 CPU 對本地記憶體的存取速度更快，若要存取其他 CPU 的記憶體，速度會較慢
- Load Balancing（負載平衡）：讓工作平均分配，確保所有 CPU 保持忙碌
- Push migration：由特定任務定期檢查各處理器的負載，若發現不平衡，就**推送**工作到閒置的處理器
- Pull migration：閒置的處理器主動**拉取**工作，從忙碌的 CPU 移轉部分任務過來

### CPU Scheduling
- Soft real-time systems（軟即時系統）：不保證關鍵任務會準時被排程
- Hard real-time systems（硬即時系統）：任務必須在截止時間前完成

###  Rate Monotonic (RM) Scheduling（速率單調排程）
- 優先權依據任務週期的倒數決定：
	- 週期短：優先權高
	- 週期長：優先權低
![image](https://hackmd.io/_uploads/rkHvV1b0gl.png=100%x)

### Earliest Deadline First Scheduling（最早截止時間優先）
- 優先權依照「截止時間」分配：
	- 截止時間越早：優先權越高
	- 截止時間越晚：優先權越低
![image](https://hackmd.io/_uploads/HyagrkWRxx.png)

## Ch6: Synchronization Tools

- Race Condition：多個行程共同讀寫一個資料，會因為交錯執行而產生問題（e.g. i++會被拆成三個指令）
![capture_temp](https://hackmd.io/_uploads/r1Jqyn5WWe.jpg)
- Critical section（臨界區）：
    - 每個行程中需要共享資料的區域
    - 一次只能有一個行程進入
    - entry section：嘗試排隊進入臨界區
    - critical section：讀寫共享資料，一次只有一個行程進入
    - exit section：離開臨界區要做的處理
    - remainder：不需要共享資料的程式碼區段
    ![capture_temp](https://hackmd.io/_uploads/ByCFL2qZbl.jpg)
- 臨界區問題的解法需要滿足以下三個條件：
    - Mutual Exclusion（互斥）：某個行程在臨界區中，其他行程都不能進入自己的臨界區
    - Progress（進度性）：決定下一個行程能進臨界區的決定不能無限拖延
    - Bounded Waiting（有限等待）：每個請求的等待次數有上限
- 作業系統核心分為兩種：
    - Preemptive(可搶先)：行程在核心模式執行時，仍然允許被搶先
    - Non-Preeptive(不可搶先)：進入核心模式後行程會持續執行，直到離開核心模式（早期使用）

### Peterson’s Solution （==會考==）
- 針對兩個行程競爭的寫法，假設讀寫是原子操作
- 行程共用：
    - turn：輪到哪一個行程可以進入臨界區
    - flag[2]：表示該行程是否準備進入臨界區（true） 
![capture_temp](https://hackmd.io/_uploads/SyuJT3cbWx.jpg)
- 互斥、進度、有限等待皆成立

### Synchronization Hardware
 - 利用上鎖來保護臨界區
 - Atomic：不可中斷的操作
 ![capture_temp](https://hackmd.io/_uploads/S1zHJp9-bl.jpg)

### test_and_set Instruction （==會考==）
- ![capture_temp](https://hackmd.io/_uploads/Syz13tC--l.jpg)
1.指令以原子方式執行
2.回傳參數target原本的值
3.把參數target的新值設為True
- 用法：lock是共享變數
    ![capture_temp](https://hackmd.io/_uploads/HyZnhtR-Ze.jpg)
### compare_and_swap Instruction：
1.指令以原子方式執行
2.回傳參數value原本的值
3.只有當value等於expected，value才會改成new_value
- 用法：lock是共享變數，初始為0
    ![capture_temp](https://hackmd.io/_uploads/HkEpaFA-Zg.jpg)
### Bounded-Waiting Mutual Exclusion：
- 考慮n個process互斥且有限等待的解法，像是在排隊等號碼牌（待補充）
    ![capture_temp](https://hackmd.io/_uploads/ByaTRYCZ-x.jpg)
### Mutex Locks（互斥鎖）：
- 互斥鎖是用來保護臨界區的一種方式
- 先呼叫acquire()取得鎖，結束後再用release()釋放鎖，兩個函式為原子操作
- 缺點：需要busy waiting，因此也被稱為spinlock（自旋鎖）
    ![capture_temp](https://hackmd.io/_uploads/SJm_l9AZ-x.jpg)

### Semaphore（號誌）
- 同步工具，比mutex lock更進階
- S：一個整數變數，同時允許多少個資源被使用
- 使用wait()和signal()兩個原子操作來做存取
![capture_temp](https://hackmd.io/_uploads/SkiEo90-We.jpg)
- Counting semaphore：S的整數值沒有限制範圍
- Binary semaphore：S的整數值為0或1，功能與mutex lock相同
- 事件順序控制範例：
![capture_temp](https://hackmd.io/_uploads/SJ0RWoCW-l.jpg)
- 必須保證沒有兩個process在同一個Semaphore執行wait()或signal()，這導致變成一個新的critical section problem
- Implementation with No Busy Waiting：
    - block：將呼叫這個操作的peocess放到waiting queue中
    - wakeup：從wating queue中拿出一個process，把它放會到ready queue
![capture_temp](https://hackmd.io/_uploads/By4PBoRbZe.jpg)
- Deadlock（死結）：兩個程序無限互相等待對方
![capture_temp](https://hackmd.io/_uploads/SJ_4VyNzbe.jpg)
- Starvation（飢餓）：indefinite blocking（無限期阻塞），某個程序永遠不會被喚醒，永遠無法進入臨界區
- Priority Inversion（優先順序反轉）：
    - 當低優先序程序擁有高優先序所需的lock，會導致高優先程序被延遲
    - 解決方法：使用Priority-Inhertance protocol(優先繼承協定)，讓低優先序暫時提升優先權

### Problems with Semaphores（==會考==）
- 錯誤使用semaphores可能導致是時間錯誤，只在特定執行順序下發生，不一定可以重現
- e.g. signal(mutex)...wait(mutex)：會導致多個程序同時進入臨界區（破壞互斥）
- e.g. wait(mutex)...wait(mutex)：第二次wait()會導致程序永久堵塞，因為semaphore永遠不可用
- e.g. 漏掉wait(mutex)或signal(mutex)，可能造成mutual exclusion（互斥失效）或permanent block（永久阻塞）

### Monitors（監控器）
- 高階同步機制，提供方便且安全的方式讓多個程序共享資源
- 一種抽象資料型別（abstract data type），內部變數只能被Monitor中的程序存取
- 任一時間只允許一個程序在Monitor內執行
![capture_temp](https://hackmd.io/_uploads/rJTTllVGWe.jpg)
![capture_temp](https://hackmd.io/_uploads/rybz7bxNMWg.jpg)

### Condition Variables
- condition x,y;
- 在一個條件變數上允許兩種操作：
    - x.wait()：呼叫這個操作的process會被暫停，一直等到有人對x呼叫x.signal()
    - x.signal()：喚醒其中一個先前呼叫x.wait()而睡著的process，如果不存在則沒有效果
- 可以想成排隊的概念，x,y=某個隊伍，程序是在等待得人
![capture_temp](https://hackmd.io/_uploads/ryLEHgEz-x.jpg)
- 如果 process P 呼叫 x.signal()，而 process Q 目前在 x.wait() 中被暫停，會有兩種情況發生：
- Signal and wait：P 呼叫 signal 後，自己等待，直到 Q 離開 monitor 或 Q 去等其他條件
- Signal and continue：P 呼叫 signal 後，自己繼續執行，Q 要等到 P 離開 monitor 後才會拿到機會進入
![capture_temp](https://hackmd.io/_uploads/rk0RdgEfWl.jpg)
- process 在進入 monitor 前必須先執行 wait(mutex)，離開 monitor 時必須執行 signal(mutex)
- signal-and-wait-scheme：
    - 發出 signal 的 process 可以使用 next 這個 semaphore 來暫停自已
    - 變數 next_count 用來記錄目前有多少 process 被暫停在 next 上
- 對 monitor 中的每個 procedure F 改成下圖，可以保證 monitor 內的互斥（==會考排列、填空或改錯==）
- Variables：
	- semaphore mutex;  // (initially  = 1)
	- semaphore next;   // (initially  = 0)
	- int next_count = 0;
![capture_temp](https://hackmd.io/_uploads/rkvLObVGZg.jpg)
- x.wait 和 x.signal 的實做：
![capture_temp](https://hackmd.io/_uploads/BkYqFZEfZe.jpg)
![capture_temp](https://hackmd.io/_uploads/SyLiFZEzbg.jpg)
- 用 FCFS 來喚醒條件變數上排隊的 process通常不夠好，因此會用有優先權的方式 x.wait(c) 的寫法， c 是 priority number（優先權數字），數字越小代表優先權越高
- Single Resource Allocation（單一資源配置）：
    - 使用優先權數字來把單一資源分給多個競爭的程序，數字代表某個 process 打算使用這個資源的最長時間
    - R 是型別 ResourceAllocator 的一個實例
    ![capture_temp](https://hackmd.io/_uploads/rycm6-EMbg.jpg)
- 用 monitor 來配置單一資源：
![capture_temp](https://hackmd.io/_uploads/ryejpZ4fWg.jpg)

## Ch8 Deadlocks
- 每個行程使用資源的步驟都是：request（請求）、use（使用）、release（釋放）
- DeadLcok Characterization：死結發生的條件是以下四個條件同時（simultaneously ==考單字==）成立（==會考簡答，6分寫標題，12分加說明==）
    1. **Mutual exclusion（互斥）**：某些資源一次只能給一個process用，e.g.印表機
    2. **Hold and wait（持有且等待）**：程序已經拿到至少一個資源，但還需要等別人的資源
    3. **No preemption（不可搶佔）**：資源只能等持有持有的程序完成才能釋放
    4. **Circular wait（循環等待）**：P0等p1的資源、P1等p2的資源......Pn等p0的資源，形成閉環
- 例如以下的 Mutex Locks 會發生死結：
![capture_temp](https://hackmd.io/_uploads/B1zr7QEGWx.jpg)
- Resource-Allocation Graph：死結可以用系統配置圖來描述
	- 頂點 (Vertices)：
		- $P$：所有行程的集合 $\{P_1, P_2, ...\}$
		- $R$：所有資源類型的集合 $\{R_1, R_2, ...\}$
	- 邊 (Edges)：
		- 請求邊 (Request Edge)：$P_i \rightarrow R_j$  （行程 P 請求資源 R）
		- 分配邊 (Assignment Edge)：$R_j \rightarrow P_i$  （資源 R 已分配給行程 P）
	![image](https://hackmd.io/_uploads/ByiYiIUzZx.png)
	- 資源分配圖範例：
	![image](https://hackmd.io/_uploads/HkY1hU8fZx.png)
	- 有死結的資源分配圖：
	![image](https://hackmd.io/_uploads/BkzW2UUGWe.png)
	- 有循環但無死結的圖
	![image](https://hackmd.io/_uploads/SkkrhIIGWg.png)
- **死結判斷方式**：
	- 如果圖中沒有循環 -> 沒有死結
	- 如果途中有循環：
		- 若每種資源只有一個實例 -> 必有死結
		- 若每種資源有多個實例 -> 可能有死結
- 處理死結的方法：
	- 做預防和避免，確保系統永遠不進入死結
	- 允許死結發生再做修復
	- 無視問題，假裝死結不存在，大多作業系統採用的辦法（包含Windows 和 Linux(UNIX)）
- **預防死結**：
	- **互斥（Mutual Exclusion）**：對於不可共用的資源（如印表機）必須保持互斥，但可共用資源（如唯讀檔案）則不需要
	- **持有並等待（Hold and Wait）**：
		- 規定行程在執行前必須一次申請完所有資源，或申請資源時手上不能有其他資源
		- 缺點：資源利用率低，且可能導致飢餓（Starvation）
	- **不可搶佔（No Preemption）**：如果申請不到新資源，就必須釋放手上所有資源，之後再全部重新申請
	- **循環等待（Circular Wait）**：將所有資源編號，強制行程只能依照編號順序（例如由小到大）申請資源
- **避免死結**：
	- 需要行程預先提供資訊（例如最多需要多少資源）
	- 演算法會動態檢查資源分配狀態，確保永遠不會出現循環等待
- 安全狀態（Safe State）：（==會考==）
	- 如果系統能找到一個安全序列 (Safe Sequence) $<P_1, P_2, ..., P_n>$，讓所有行程都能順利執行完畢，系統就是處於安全狀態
	- 就算 $P_i$ 要的資源現在不夠，只要等前面的 $P_j$ 用完歸還，$P_i$ 最終還是能拿到資源
- **死結判斷方式**：
	- 安全狀態 -> 沒有死結
	- 不安全狀態 -> 可能有死結
	- 避免死結演算法：確保系統永遠保持在安全區
![image](https://hackmd.io/_uploads/BkFuedUfWl.png)
- **宣告邊（Claim Edge）**：
	- 虛線，表示行程未來可能會請求該資源
	- 當請求發生時，虛線變實線
	- 只有當把請求邊變成分配邊不會導致圖中出現循環時，才允許分配
![image](https://hackmd.io/_uploads/rkoP-dUG-e.png)
![image](https://hackmd.io/_uploads/S1Eu-OUGZx.png)

### Banker’s Algorithm （==會考==）
- 適用於多重資源實例
- 行程需預先宣告最大需求（Maximum use）
- 行程請求資源時若不安全則需等待
- Banker’s Algorithm 資料結構：
	- Available: 目前系統還剩多少資源
	- Max: 每個行程最多需要多少資源
	- Allocation: 目前已經給每個行程多少資源
	- Need: 每個行程還需要多少資源才能完成（$Need = Max - Allocation$）
	![image](https://hackmd.io/_uploads/ryOST38fZl.png)

#### Safety Algorithm
- 安全性演算法：檢查當前狀態是否安全，用來找找安全序列，步驟如下：
	1. 設定工作向量 Work：一開始 Work = Available (目前的閒置資源)
	2. 設定 Finish 向量：所有 Process 的 Finish[i] = false (還沒做完)
	3. 尋找可執行的 Process：
		- 從頭掃描，找到一個 Process $P_i$ 滿足：
			1. Finish[i] == false (還沒做完)
			2. Need[i] <= Work (它缺的資源，我們現在夠給它)
	4. 模擬執行與回收：
		- 如果找到了這樣的 $P_i$，假設它執行完畢，釋放它手上的資源：$$Work = Work + Allocation[i]$$
		- 標記 Finish[i] = true
		- 回到步驟 3，繼續找下一個
	5. 判斷結果：
		- 如果所有 Process 都能被標記為 true，則系統處於 Safe State
		- 找到的順序 (例如 $<P_1, P_3, P_4, P_2, P_0>$) 即為 Safe Sequence 2
![image](https://hackmd.io/_uploads/Hku0a3LGZx.png)

#### Resource-Request Algorithm
- 資源請求演算法
	- 當行程 $P_i$ 提出請求時：
		1. 檢查請求是否超過它宣告的最大值（Request[i] <= Need[i]）
		2. 檢查系統有沒有足夠資源 (Request[i] <= Available)
		3. 假裝分配給它，更新狀態：
			- Available = Available - Request[i]
			- Allocation[i] = Allocation[i] + Request[i]
			- Need[i] = Need[i] - Request[i]
		4. 執行安全性演算法：
			- 如果是安全的，就給資源給 $P_i$ （Granted）
			- 不安全，收回分配的資源，讓 $P_i$ 繼續等待
		![image](https://hackmd.io/_uploads/H175k6IGZl.png)

#### Banker’s Algorithm範例（==會考==）
![image](https://hackmd.io/_uploads/H1NC1aLzWl.png)
![image](https://hackmd.io/_uploads/BJACy6LfWe.png)
![image](https://hackmd.io/_uploads/BkRylTUG-e.png)

### Deadlock Detection
- 若是允許系統進入死結狀態，就需要偵測演算法和恢復機制
- 單一實例系統：
	- 維護一張等待圖（wait-for graph）：
		- 圖中的節點（Nodes）代表行程（Processes）
		- $P_i \rightarrow P_j$ 表示行程 $P_i$ 正在等待行程 $P_j$（釋放資源）
	- 定期執行演算法來搜尋圖中是否存在循環（cycle），如果圖中存在循環，就代表存在死結
	- 偵測圖中循環的演算法所需的運算量級為 $n^2$，其中 $n$ 是圖中頂點（即行程）的數量
![image](https://hackmd.io/_uploads/HkC9fTUfZe.png)
- 多重實例系統：
	- Available（可用資源向量）：一個長度為 $m$ 的向量，表示每種資源類型目前還剩下多少可用
	- Allocation（已分配矩陣）：一個 $n \times m$ 的矩陣，定義了目前已經分配給每個行程的各類資源數量
	- Request（請求矩陣）：
		- 一個 $n \times m$ 的矩陣，表示每個行程目前正在請求的資源
		- 如果 $Request[i][j] = k$，代表行程 $P_i$ 正在請求 $k$ 個 $R_j$ 類型的資源實例

#### Detection Algorithm（==會考==）
- $n$：行程 (Process) 的數量
- $m$：資源種類 (Resource types) 的數量
- Available：目前可用的資源向量
- Allocation：每個行程目前已持有的資源矩陣
- Request：每個行程目前正在請求的資源矩陣
- 執行步驟：
	1. 初始化：
		- 設定暫存向量 $Work$ (代表當前可用的模擬資源量) 初始化為 $Available$
		- 設定向量 $Finish$ (標記行程是否能完成) 的初始值：
			- 若 Allocation[i] $\neq 0$ (該行程持有資源)，則設 Finish[i] = fals
			- 若 Allocation[i] $== 0$ (該行程沒佔用任何資源)，則設 Finish[i] = true
	2. 尋找可執行行程：
		- 尋找一個索引 $i$ (行程 $P_i$) 同時滿足以下兩個條件：
			1. Finish[i] == false (尚未標記為完成)
			2. Request[i] <= Work (該行程目前的請求量 $\le$ 系統目前的可用量)
		- 如果找不到這樣的 $i$，跳到步驟 4。
	3. 回收資源：
		- 假設該行程 $P_i$ 獲得資源並執行完畢，將其資源歸還給系統：
			- Work = Work + Allocation[i]
			- Finish[i] = true
		- 回到步驟 2 繼續檢查下一個行程
	4. 判斷死結：
		- 檢查 $Finish$ 向量
		- 如果存在某個 $i$ 使得 Finish[i] == false，則代表系統處於死結狀態
		- 所有 Finish[i] == false 的行程 $P_i$ 都是死結的一部分。 
![image](https://hackmd.io/_uploads/SJAfaC8zZg.png)
![image](https://hackmd.io/_uploads/BJQm60Izbl.png)
![image](https://hackmd.io/_uploads/By2r6AIzZg.png)
![image](https://hackmd.io/_uploads/ryz8pAIMbl.png)
- 如果太常偵測死結，系統開銷會太大，如果太久沒偵測的話，影響的範圍會越來越大

### Recovery from Deadlock（從死結恢復）
- 終止行程：
	- 方法一：殺掉所有死結行程
	- 方法二：一次殺一個，殺到死結解開為止
	- 選擇程序標準：優先權、已執行時間、已用資源等
- 從死結恢復需要考慮以下三個問題：
	1. 選擇受害者（Selecting a victim）：成本最小化
	2. 回滾（Rollback）：把行程退回到安全狀態再重啟
	3. 飢餓（Starvation）：要確保同一個行程不會倒楣一直被選中當受害者

## CH9 Main Memory
- 記憶體單元只會看到一連串的位址與讀取請求（addresses + read requests），或者是位址加上數據與寫入請求（address + data and write requests）
- 存取暫存器只需要一個 CPU 時脈週期
- 存取主記憶體可能需要多個週期，這會導致 CPU 停頓（stall）
- 快取（Cache） 位於主記憶體和 CPU 暫存器之間
- 一對基底（base、起點）和限制（limit、長度）暫存器定義了邏輯位址空間，限制每個程式的存取範圍
![image](https://hackmd.io/_uploads/r1-HK-ufZx.png)
![image](https://hackmd.io/_uploads/BJZqFZdGWl.png)

### Address Binding（位址綁定）
- 程式以二進制形式存在磁碟上，執行時將其放入記憶體，若沒有支援，則載入到位址 0000
- 位址在程式生命週期的不同階段有不同的表示方式：
	- 原始碼位址（Source code）：通常是符號（例如變數 count）
	- 編譯碼位址（Compiled code）：綁定到可重定位（relocatable）位址
	- 連結器或載入器（Linker/Loader）：將可重定位位址綁定到絕對位址
	- 每個綁定過程都是將一個位址空間映射到另一個位址空間
- 指令和數據綁定到記憶體位址可以在三個不同階段發生：
	- 編譯時期（Compile time）：
		- 如果在編譯時就已經知道記憶體位置，就可以產生絕對代碼（absolute code）
		- 如果位置變了就需要重新編譯
	- 載入時期（Load time）：
		- 如果編譯時不知道行程將駐留在記憶體的何處，編譯器必須產生可重定位代碼（relocatable code）
		- 最終的綁定會延遲到載入時期
		- 如果起始位址改變，只需要重新載入使用者程式碼
	- 執行時期（Execution time）：
		- 如果行程在執行過程中可以從一個記憶體區段移動到另一個區段，則綁定會延遲到執行時期
		- 需要硬體支援位址映射

- 使用者程式的多步驟處理： （==考填空==）
	- 編譯時期（Compile time）：原始程式（Source program） -> 編譯器（Compiler） -> 目的檔（Object file）
	- 載入時期（Load time）：目的檔 + 其他目的檔 -> 連結器（Linker） -> 執行檔（Executable file）
	- 執行時期（Execution time/Run time）：執行檔 + 動態連結函式庫（Dynamically linked libraries） -> 載入器（Loader） -> 記憶體中的程式
	![image](https://hackmd.io/_uploads/BJPkSfuM-l.png)
- 「邏輯位址空間」綁定到單獨的「實體位址空間」的概念，是正確記憶體管理的核心
	- 邏輯位址（Logical address）：虛擬位址（virtual address），由 CPU 產生
	- 實體位址（Physical address）：記憶體單元看到的位址
	- 編譯、載入時期：邏輯位址和實體位址相同
	- 執行時期：邏輯位址和實體位址不同
	- 邏輯位址空間：程式產生的一組所有邏輯位址
	- 實體位址空間：對應於這些邏輯位址的一組所有實體位址

### Memory-Management Unit (MMU)
- 記憶體管理單元：在執行時期將虛擬位址映射到實體位址的硬體裝置
- 簡單方案：在使用者行程產生位址並送往記憶體時，將重定位暫存器（Relocation register）的值加到每一個位址上
	- 基底暫存器（Base register）在此稱作重定位暫存器
	- 使用者程式處理的是邏輯位址，不會看到真實的實體位址

### Dynamic Relocation Using a Relocation Register
- 常式（Routine）直到被呼叫時才載入
- 未使用的常式永遠不會被載入，有更好的記憶體空間利用率
- 所有常式都以可重定位的載入格式保存在磁碟上
- 當需要大量程式碼來處理不常發生的情況時特別有用
![image](https://hackmd.io/_uploads/rJ-uLBuGbe.png)

### Dynamic Linking and Shared Libraries
- 動態連結函式庫（Dynamically linked libraries，DLLs）：系統函式庫，在程式執行時才連結到使用者程式
- **靜態連結（Static linking）**：系統函式庫和程式碼由載入器合併成一個二進位程式映像
- **動態連結（Dynamic linking）**：連結被推遲到執行時期
	- 通常用於系統函式庫，例如標準 C 語言函式庫
	- 這些函式庫可以在多個行程之間共享，因此主記憶體中只需有一個 DLL 的實例，DLLs 也被稱為共享函式庫（shared libraries）
	- DLLs 可以擴展到函式庫更新（例如錯誤修復），函式庫可以被新版本取代，所有引用該函式庫的程式將自動使用新版本

### Swapping
- 一個行程可以暫時從記憶體被置換（swapped）到備用儲存裝置（backing store），然後再帶回記憶體繼續執行，使得行程的總實體記憶體空間可以超過實際的實體記憶體
- 備用儲存裝置（Backing store）：快速的磁碟，夠大以容納所有使用者的記憶體映像副本，必須提供對這些映像的直接存取
- Roll out, roll in：用於優先級排程演算法的置換變體，低優先級行程被換出，以便載入並執行高優先級行程
- 置換時間的主要部分是傳輸時間，總傳輸時間與置換的記憶體量成正比
- 系統維護一個就緒佇列（ready queue），包含那些記憶體映像在磁碟上的就緒行程
- 現代作業系統通常不再使用標準置換，因為所需時間太長
![image](https://hackmd.io/_uploads/H163F8_G-l.png)
- 如果下一個要上 CPU 的行程不在記憶體中，需要換出（swap out）一個行程並換入（swap in）目標行程
- 上下文交換（Context switch）時間會變得非常長，e.g. 100MB 的行程置換到傳輸率為 50MB/sec 的硬碟需要4秒
- 置換不能用於待處理的 I/O，或者總是透過核心（kernel）的緩衝區進行傳輸，然後再從核心複製到行程的位址空間，這稱為雙重緩衝（double buffering），會增加負擔
-
### Contiguous Allocation（連續配置）
- 主記憶體通常分為兩個分區（partitions）：
	- 常駐作業系統（Resident OS），通常放在低位址記憶體，包含中斷向量
	- 使用者行程則放在高位址記憶體
	- 每個行程包含在記憶體的一個單一連續區段中
- 使用重定位暫存器來保護使用者行程彼此不受影響，也不受作業系統程式碼和資料改變的影響
	- 基底暫存器（Base register）：包含最小實體位址的值
	- 限制暫存器（Limit register）：包含邏輯位址的範圍（每個邏輯位址必須小於限制暫存器）
	- MMU 動態映射邏輯位址
	- 這允許核心程式碼是暫時的 (transient) 且核心大小可以改變
![image](https://hackmd.io/_uploads/rJSh0UuMZl.png)

### Multiple-partition Allocation（多重分區配置）
- **可變分區大小**（Variable-partition sizes）：根據特定行程的需求調整大小，用於提高效率
- **孔洞**（Hole）：一塊可用的記憶體區塊，各種大小的孔洞散布在記憶體各處
- 當一個行程到達時，系統會從一個夠大的孔洞中配置記憶體給它
- 當行程離開時，它會釋放其分區，相鄰的空閒分區會被合併
- 作業系統維護：
	1. 已配置的分區（allocated partitions）
	2. 空閒的分區（free partitions）
![image](https://hackmd.io/_uploads/HkRKz85MWx.png)
- 如何動態分配儲存位址：
	- 首次配適（First-fit）：配置「第一個」夠大的孔洞
	- 最佳配適（Best-fit）：配置「最小」但夠大的孔洞，必須搜尋整個清單，除非清單依大小排序
		- 剩餘孔洞最小
	- 最差配適（Worst-fit）：配置「最大」的孔洞，必須搜尋整個清單
		- 剩餘孔洞最大
	- First-fit 和 Best-fit > Worst-fit

### Fragmentation（碎片化）（==會考==）
- **外部碎片（External Fragmentation）**：總記憶體空間足以滿足請求，但它不是連續的
- **內部碎片（Internal Fragmentation）**：配置的記憶體可能比請求的記憶體稍大，但多餘部分未被使用
- 緊縮（Compaction）：重新排列記憶體內容，將所有空閒記憶體集中成一個大區塊，來減少外部碎片

### 分段（Segmentation）
- 支援使用者觀點的記憶體管理機制
- 一個程式是區段（segments）的集合
- 一個區段是一個邏輯單元，例如：主程式、程序、函數、方法、物件、區域變數、全域變數、堆疊、陣列
![image](https://hackmd.io/_uploads/BJJ32Ucfbx.png)
![image](https://hackmd.io/_uploads/rJ333UqMWl.png)
- 邏輯位址：由二元組（two tuple）組成，<區段編號（segment-number）, 位移量（offset）>
- **區段表（Segment table）**：映射二維的實體位址，每個表項目有：
	- **基底（base）**：包含區段在記憶體中開始的實體位址
	- **限制（limit）**：指定區段的長度
- **區段表基底暫存器（Segment-table base register，STBR）**：指向區段表在記憶體中的位置
- **區段表長度暫存器（Segment-table length register，STLR）**：指示程式使用的區段數量
	- 如果區段編號 s < STLR，則 s 是合法的
- 保護（Protection）：每個區段表項目紀錄
	- 驗證位元 (validation bit)：0 代表非法區段
	- 讀／寫／執行權限 （read/write/execute privileges）
![image](https://hackmd.io/_uploads/S1oRywczWx.png)

### Paging（分頁）
- 行程的實體位址空間可以是不連續的，只要有可用的實體記憶體，就可以配置給行程
	- 優點：避免了外部碎片、避免了記憶體區塊大小不一的問題
	- 缺點：仍然會有內部碎片
- 頁框（frames）：實體記憶體分割成固定大小的區塊
- 分頁（pages）：邏輯記憶體分割成同樣大小的區塊
- 要執行大小為 N 個分頁的程式，需要找到 N 個空閒頁框並載入程式
- 分頁表（page table）：將邏輯位址轉換為實體位址
- 位址轉換機制：
	- **分頁編號**（Page number, p)：用作分頁表的索引，分頁表包含每個分頁在實體記憶體中的基底位址
	- **分頁位移**（Page offset, d）：與基底位址結合，定義送往記憶體單元的實體記憶體位址
![image](https://hackmd.io/_uploads/Bk6UUwqMWe.png)
![image](https://hackmd.io/_uploads/r1QFLv5Gbx.png)
![image](https://hackmd.io/_uploads/ryv6IwqfZe.png)
![image](https://hackmd.io/_uploads/ryHiiP5Gbx.png)
![image](https://hackmd.io/_uploads/BJO6iPcz-g.png)
![image](https://hackmd.io/_uploads/B1Xb2v5zZe.png)
- 分頁表保存在主記憶體中
- **分頁表基底暫存器（Page-table base register，PTBR）** 指向分頁表
- **分頁表長度暫存器（Page-table length register，PTLR）** 指示分頁表的大小
- 每一次資料／指令存取需要兩次記憶體存取，查分頁表+拿資料／指令
- **轉譯後備緩衝區（TLBs, translation look-aside buffers）** ：或稱關聯式記憶體（associative memory），使用特殊的快速查找硬體快取來解決兩次記憶體存取的問題
- 固定（Wired down）：重要資訊會被固定在TLB上
- 位址空間識別碼（ASIDs）：識別每個行程位址，避免拿錯
![image](https://hackmd.io/_uploads/rJzkCvqzZg.png)

### 有效存取時間（EAT） （==會考==）
- 命中率（Hit ratio）$\alpha$：在關聯式暫存器（TLB）中找到分頁編號的機率
- 有效存取時間（Effective Access Time）：$\alpha$ $\times$ 存取時間 + (1 - $\alpha$) $\times$ 2倍存取時間
- 考慮 $\alpha = 80\%$，記憶體存取時間為 10ns：
	- $EAT = 0.80 \times 10 + 0.20 \times 20 = 12ns$ 2
- 考慮 $\alpha = 99\%$
	- $EAT = 0.99 \times 10 + 0.01 \times 20 = 10.1ns$

### 記憶體保護（Memory Protection）
- 透過保護位元 (protection bit) 來指定為唯讀（read-only）或讀寫（read-write）
- **有效-無效位元（Valid-invalid bit）** 附在分頁表的每個項目上：
	- Valid（有效）：表示該分頁在行程的邏輯位址空間內，是合法的分頁
	- Invalid（無效）：表示該分頁不在行程的邏輯位址空間內
	- 或使用分頁表長度暫存器（page-table length register，PTLR）
![image](https://hackmd.io/_uploads/H1Zmhtcz-l.png)

### 共享分頁（Shared Pages）
- **共享代碼**（Shared code）：
	- 一份唯讀（read-only）或重入（reentrant） 程式碼在行程間共享
	- 類似於多個執行緒共享同一個行程空間
- **私有代碼與數據**（Private code and data）：
	- 每個行程保存一份獨立的代碼和數據副本
	- 私有代碼和數據的分頁可以出現在邏輯位址空間的任何地方
![image](https://hackmd.io/_uploads/Hy6z155zZl.png)

### 分頁表（Page Table）（==會考==）
- 直接建立分頁表需要巨大的主記憶體連續空間
- 解法：使用階層式分頁（Hierarchical Paging）或雜湊分頁表（Hashed Page Tables）或反向分頁表（Inverted Page Tables）

#### 階層式分頁（Hierarchical Paging）
- 將邏輯位址空間拆解成多個分頁表，例如兩層分頁表
- 兩層分頁表（two-level page table）：
	- 外部分頁表（Outer page table）：只有一個，項目指向內層分頁表
	- 分頁表的分頁（Page of page table）：內層表格，項目指向真正的記憶體 Frame
![image](https://hackmd.io/_uploads/rJoYfqcGWg.png)
- 一個邏輯位址（在 32-bit 機器，1K 分頁大小）被分為：
	- 一個 22 bits 的分頁編號，分為：
		- 一個 12-bit 的分頁編號（$p_1$）
		- 一個 10-bit 的分頁位移（$p_2$）
	- 一個 10 bits 的分頁位移
- **前向映射分頁表**（forward-mapped page table）：
	- $p_1$（12 bits）：外部分頁表的索引
	- $p_2$（10 bits）：內部分頁表的位移
	- d（10 bits）：分頁位移
![image](https://hackmd.io/_uploads/ryjMNqqzWx.png)
![image](https://hackmd.io/_uploads/BJB74qqfbx.png)
- 兩層分頁表對於 64 bit 不太夠用，須拆成三層，但會導致效率變差
![image](https://hackmd.io/_uploads/rkEK495Gbe.png)

#### 雜湊分頁表（Hashed Page Tables）
- 虛擬分頁編號透過雜湊（hushed）進入一個分頁表
- 該分頁表包含一個元素鏈結串列（linked list），雜湊到同一位置的元素串在一起
- 每個元素包含：(1) 虛擬分頁編號、(2) 映射的頁框值、(3) 指向下一個元素的指標
- 虛擬分頁編號在鏈結中比對、尋找匹配，如果找到則提取對應的實體頁框
- 64-bit 的變體是叢集分頁表（clustered page tables）：
- 類似雜湊表，但每個項目指向多個分頁（例如 16 個），而非 1 個
- 對於稀疏（sparse）位址空間特別有用
![image](https://hackmd.io/_uploads/ryVtIccGWe.png)

#### 反向分頁表（Inverted Page Tables）
- 追蹤所有實體分頁，記憶體的每個實體分頁只有一個項目
- 項目包含：存放在該實體位置的虛擬位址，以及擁有該分頁的行程資訊（PID）
- 優點：減少儲存分頁表所需的記憶體
- 缺點：當分頁引用發生時，搜尋表格的時間增加
![image](https://hackmd.io/_uploads/ryCBucqfWg.png)

## Ch10 Virtual Memory
- 虛擬記憶體（Virtual Memory）：
	- 將「使用者邏輯記憶體」與「實體記憶體」分開，只有部分程式需要存在記憶體中才能執行
	- 「邏輯位址空間」可以比「實體位址空間」大得多
	- 允許位址空間被數個行程共享
	- 允許更有效率的行程建立
	- 更多程式可以同時並行、載入或置換行程所需的 I/O 更少
- 虛擬記憶體可以透過請求分頁（Demand paging）、請求分段（Demand segmentation）實作
![image](https://hackmd.io/_uploads/B1ldHjcGWg.png)

### 虛擬位址空間（Virtual address space）
- 行程如何儲存在記憶體中的邏輯視圖
- 邏輯位址空間設計為堆疊從最大邏輯位址開始並「向下」增長，而堆積則「向上」增長
- 兩者之間未使用的位址空間是洞（hole）
- 允許稀疏（sparse）的位址空間，留下的洞可用於增長、動態連結函式庫（DLLs）等
![image](https://hackmd.io/_uploads/ryrJ0o5fZe.png)
![image](https://hackmd.io/_uploads/H1rLCj9GWg.png)

### 請求分頁（Demand Paging）
- 在載入時將整個行程帶入記憶體，或者只有當需要時才將分頁帶入記憶體
- 減少 I/O、減少記憶體需求、更快的回應、更多使用者
- 懶惰置換器（Lazy swapper）：除非分頁被需要，否則絕不將分頁置換入記憶體
- Pager：處理分頁的置換器 
![image](https://hackmd.io/_uploads/BJfn13cG-x.png)

### 有效-無效位元（Valid-Invalid Bit）
- 每個分頁表項目都有一個有效-無效位元，V 代表在記憶體中，i 代表不在記憶體中
- 最初，所有項目的位元都設為 i
- MMU 位址轉換期間，如果分頁表項目中的位元是 i 會觸發分頁錯誤（Page Fault）
![image](https://hackmd.io/_uploads/HkXCIhcM-x.png)

### 分頁錯誤（Page Fault）
- 如果有個參考指向一個分頁，第一次參考到該分頁時會觸發作業系統的 trap：
	1. 檢查是否為無效的參考，是的話終止，否則開始交換分頁
	2. 找到一個空閒的頁框，透過排程磁碟操作，將分頁置換到頁框中
	3. 設定位元為 v
	4. 重新啟動指令
![image](https://hackmd.io/_uploads/BkvhvncMZx.png)
- 純請求分頁（Pure demand paging）：記憶體中沒有任何分頁，接著每個其他分頁都在第一次存取時發生 page fault
- 請求分頁的效能（==有可能考排序==）
![image](https://hackmd.io/_uploads/rJmyc29f-l.png)
- 服務時間分成三個部分：
	- 服務分頁錯誤中斷：很快
	- 讀取分頁：花很多時間
	- 重新啟動行程：很快
- 分頁錯誤率（Page Fault Rate）$0 \le p \le 1$
	- 如果 $p = 0$，沒有分頁錯誤
	- 如果 $p = 1$，每次參考都發生錯誤
- 有效存取時間（EAT）：$EAT = (1 - p) \times \text{記憶體存取時間} + p \times (\text{分頁錯誤負擔} + \text{換出時間} + \text{換入時間})$
![image](https://hackmd.io/_uploads/SJ_Hi3cGWl.png)

### Copy-on-Write
- 寫入時複製（Copy-on-Write, COW）：
	- 允許父行程和子行程最初在記憶體中共享相同的分頁
	- 如果任一行程寫入共享分頁，則會建立該共享分頁的副本
![image](https://hackmd.io/_uploads/rJGG2h9G-l.png)
![image](https://hackmd.io/_uploads/H13fn2cGZx.png)

### 頁面置換（Page Replacement）
- 透過頁面置換來防止記憶體過度分配
- 使用修改位元（modify/dirty bit）來減少頁面傳輸的負擔，只有被修改過的頁面才需要寫回磁碟
- Process 1 需要載入分頁 B，但hysical Memory 已經滿了：
![image](https://hackmd.io/_uploads/ByIB66cfbg.png)
- 基本頁面置換：
	1. 在磁碟上找到所需分頁的位置
	2. 找到一個空閒頁框：
		- 如果有空閒頁框，就使用它
		- 如果沒有空閒頁框，使用頁面置換演算法選擇一個受害者頁框（victim frame），受害者是髒 （dirty）的話就寫回磁碟
	3. 將所需分頁帶入空閒頁框；更新分頁表和頁框表
	4. 透過重新啟動造成 trap 的指令來繼續行程
![image](https://hackmd.io/_uploads/Hk0ZyC5Mbg.png)
- 頁框配置演算法（Frame-allocation algorithm）決定：
	- 給每個行程多少個頁框？
	- 要置換哪些頁框？
- 頁面置換演算法 (Page-replacement algorithm)：
	- 希望在第一次存取和重新存取時都有最低的分頁錯誤率
- 後續的範例都使用此參考字串：7,0,1,2,0,3,0,4,2,3,0,3,0,3,2,1,2,0,1,7,0,1,3,0,3,2,1,2,0,1,7,0,1

### 演算法
#### FIFO Algorithm （==會考==）
- 先進先出（First-In-First-Out，FIFO）演算法：最早來的最先被移除
	- Belady's Anomaly（貝拉迪異常）：增加更多頁框反而導致更多分頁錯誤 （==會考==）
![image](https://hackmd.io/_uploads/BJfQZAczWg.png)

#### Optimal Algorithm （==會考==）
- 最佳化演算法（Optimal Algorithm）：置換「在最長時間內不會被使用」的分頁
	- 缺點：無法實作，只能作為評估其他演算法的標準

#### LRU Algorithm （==會考==）
- 最近最少使用演算法（Least Recently Used Algorithm）：置換「最長一段時間沒有被使用」的分頁
	- 主流演算法，效果比 FIFO 好
![image](https://hackmd.io/_uploads/ryS570qfZl.png)
- 實作方式：
	- 計數器實作：每個分頁項目有個計數器，需置換時，搜尋表格找最小值（較慢）
	![image](https://hackmd.io/_uploads/SyUVECqMWx.png)
	- 堆疊實作：用雙向鏈結串列保存分頁編號堆疊，分頁被參考時移到頂端（較快，不需要搜尋，但麻煩）
	- LRU 和 OPT 是 **Stack Algorithm**：當分配給行程 $n$ 個頁框 (frames) 時，記憶體中的頁面集合，永遠是分配 $n+1$ 個頁框時的子集，不會發生 Belady's Anomaly （==會考==）

##### LRU 近似演算法
- 附加參考位元演算法（Additional-Reference-Bits Algorithm）：
	- 每個分頁關聯一個位元，初始為 0，被參考時設為 1
	- 定期將位元右移到歷史紀錄中
- 二次機會演算法（Second-Chance Algorithm）：又稱時鐘演算法（clock algorithm）
	- FIFO 加上硬體提供的參考位元
	- 如果要置換的頁面：
		- 參考位元 = 0：置換它
		- 參考位元 = 1：設為 0，檢查下一個分頁，給它第二次機會
![image](https://hackmd.io/_uploads/HyvQHA9GZl.png)
- Enhanced Second-Chance Algorithm：增強型二次機會演算法，同時使用參考位元（reference bit）和修改位元（modify bit）來改進
考慮有序對（reference, modify）：
	- (0, 0)：最近沒用過也沒修改 -> 最佳置換對象
	- (0, 1)：沒用過但修改過 -> 須寫回磁碟，次佳
	- (1, 0)：最近用過但乾淨 -> 可能很快又要用
	- (1, 1)：最近用過且修改過 -> 最不該置換

#### Counting Algorithms
- 計數演算法：以下兩個算法效果普通且少用
	- 最不常使用（Least Frequently Used，LFU）演算法：置換計數最小的分頁
	- 最常使用（Most Frequently Used，MF）演算法：置換計數最大的分頁

#### Page-Buffering Algorithms
- 頁面緩衝演算法：始終保留一個空閒頁框池（pool of free frames）
- 當發生 page fault，像以前一樣選擇受害者
- 但在寫出受害者之前，先將所需分頁讀入空閒池中的頁框，藉此提升速度

### 頁框的配置（Allocation of Frames）
- 每個行程都需要最少數量的頁框，最大數量為系統中的總頁框數
- 兩種主要的配置方案：
	- 固定配置（fixed allocation）
	- 優先權配置（priority allocation）

#### 固定配置（fixed allocation）
- 均等配置（Equal allocation）：
	- e.g. 如果有 100 個頁框和 5 個行程，每個行程分給 20 個頁框
	- 保留一些作為空閒頁框緩衝池
- 比例配置（Proportional allocation）：
	- 依照行程的大小來動態配置
	- 公式：$a_i = (s_i / S) \times m$
		- $s_i$ = 行程 $i$ 的大小
		- $S$ = 所有行程總大小
		- $m$ = 總頁框數
	- e.g. 總共 62 頁框，P1 = 10、P2 = 127
	- P1 分到 $10/137 \times 62 \approx 4$
	- P2 分到 $127/137 \times 62 \approx 57$

#### 優先權配置（priority allocation）
- 使用比例配置方案，但依照優先權而非行程大小
- 如果行程 $P_i$ 產生分頁錯誤：
	- 選擇置換它自己的一個頁框
	- 或者選擇置換一個優先權較低的行程的頁框

### 全域 vs. 區域置換（Global vs. Local Replacement）
- 全域置換（Global replacement）：
	- 行程從所有頁框的集合中選擇一個置換頁框
	- 一個行程可以奪走另一個行程的頁框
	- 行程執行時間可能變異很大，但吞吐量較高，所以較常見
- 區域置換（Local replacement）：
	- 每個行程只從它自己分配到的頁框集中選擇
	- 每個行程的效能較一致
	- 但可能導致記憶體利用率不足

### 非均勻記憶體存取（Non-Uniform Memory Access）
- 非均勻記憶體存取（NUMA）：記憶體存取速度會變動
- 記憶體配置在「靠近」執行該執行緒的 CPU 處效能最佳

### 抖動（Thrashing） （==會考==）
- **抖動（Thrashing）**：行程忙於換入換出分頁，而非執行工作
	- 如果一個行程沒有「足夠」的分頁，分頁錯誤率會非常高
	- Page fault 取得分頁 => 置換現有的頁框 => 很快又需要被置換掉的頁框
	- 導致低 CPU 利用率
	- 作業系統以為 CPU 沒事做，加入另一個行程到系統中
	 （==會考==）![image](https://hackmd.io/_uploads/rJM_jsoG-l.png)
- 請求分頁（Demand Paging）： （==會考==）
	- 區域性模型（Locality model）：當行程執行時，它會從一個區域移動到另一個區域
	- 執行中的程式通常由數個不同的區域組成，它們可能會重疊
	- 區域大小總和 > 總記憶體大小 => Thrashing

### Working-Set（工作集）
- $\Delta \equiv$ 工作集視窗 (working-set window) $\equiv$ 固定數目的分頁參考（例如 10,000 個指令）
- $WSS_i$ (行程 $P_i$ 的工作集大小) = 在最近 $\Delta$ 時間內參考到的分頁總數
	- 如果 $\Delta$ 太小，無法包含整個區域性
	- 如果 $\Delta$ 太大，會包含數個區域性
	- 如果 $\Delta = \infty$，將包含整個程式
- $D = \Sigma WSS_i \equiv$ 對頁框的總需求
	- 如果 $D > m$（總需求 > 總頁框數），則發生 Thrashing
		- 暫停或換出其中一個行程
	
#### 追蹤工作集 （==會考==）
- 範例：$\Delta = 10,000$
- 計時器每 5,000 個時間單位中斷一次
- 為每個分頁在記憶體中保留 2 個位元
- 每當計時器中斷時，複製並將所有參考位元設為 0
- 如果記憶體中的任一位元 = 1 -> 該分頁在工作集中
- 改進：使用 10 個位元並每 1,000 個時間單位中斷一次

#### 分頁錯誤頻率（Page-Fault Frequency，PFF）
- 比工作集模型更直接控制 Thrashing 的方法
- 建立「可接受的」分頁錯誤頻率（PFF）率，並使用區域置換策略
	- 如果實際頻率太低（沒什麼錯誤），代表分給它的格子太多了 => 行程失去頁框
	- 如果實際頻率太高（一直錯誤），代表格子不夠用 -> 行程獲得頁框
	![image](https://hackmd.io/_uploads/Sk1bAjofZl.png)

#### 工作集與分頁錯誤率（Working Sets and Page Fault Rates）
- 行程的工作集與其分頁錯誤率之間有直接關係
- 隨時間改變出現工作集、波峰和波谷 （==會考 working set==）
![image](https://hackmd.io/_uploads/r1gFAssfbx.png)

### 記憶體映射檔案（Memory-Mapped Files）
- 記憶體映射檔案 I/O 允許檔案 I/O 被視為一般的記憶體存取，透過將磁碟區塊映射到記憶體分頁
- 將記憶體映射檔案技術用於所有 I/O
- 有些 OS 使用記憶體映射檔案來處理標準 I/O
![image](https://hackmd.io/_uploads/rkbky3jfWg.png)
![image](https://hackmd.io/_uploads/rJelk2iGWx.png)

### 夥伴系統（Buddy System） （==會考==）
- 從由實體連續分頁組成的固定大小區段中配置記憶體
- 使用 2 的次方配置器來配置記憶體（無條件進位到下一個最高的 2 的次方）
- 當需要的配置比可用的還小時，目前的區塊會分裂成兩個 夥伴 (buddies) （下一個較低的 2 的次方）
- 繼續分裂直到有合適大小的區塊可用
- e.g. 假設有 256KB 的區塊可用，核心請求 21KB
	- 分裂成 $A_L$ 和 $A_R$，各 128KB
	- 其中一個進一步分裂成 $B_L$ 和 $B_R$，各 64KB
	- 其中一個進一步分裂成 $C_L$ 和 $C_R$，各 32KB ，其中一個用來滿足請求
- 優點：可以快速將未使用的區塊合併（coalesce）X成較大的區塊
- 缺點：碎片化
![image](https://hackmd.io/_uploads/r1ix03izWl.png)

### Slab 配置器（Slab Allocator）（==會考==）
- Slab：一或多個實體連續的分頁（==會考==）
- Cache：一或多個 Slabs 組成（==會考==）
	- 每個唯一的核心資料結構都有一個單獨的 Cache
	- 每個 Cache 填滿了物件（objects）：該資料結構的實例（==會考==）
	- 當 Cache 建立時，填滿了標記為空閒（free）的物件
	- 當結構被儲存時，物件標記為已使用（used）
- 如果 Slab 中的已用物件滿了，下一個物件從空閒 Slab 配
- 如果沒有空閒 Slabs，則配置新的 Slab
- 優點：沒有碎片化（剛好符合物件大小）、記憶體請求滿足速度快
![image](https://hackmd.io/_uploads/SJUIyTif-l.png)

### 其他考量
#### 預先分頁（Prepaging）
- 用來減少行程啟動時發生的大量分頁錯誤
- 在參考之前，預先將行程需要的所有或部分分頁載入
- 但如果預先載入的分頁沒被使用，I/O 和記憶體就浪費了

#### 分頁大小（Page Size）
- 碎片化（Fragmentation）：頁面越大，最後一頁浪費的空間（內部碎片）越多
- 分頁表大小（Page table size）： 頁面越小，頁數越多，分頁表越大（浪費記憶體）

#### TLB 覆蓋範圍（TLB Reach）（==會考==）
- TLB Reach：透過 TLB 可以存取的記憶體總量
- TLB Reach = (TLB Size) $\times$ (Page Size)
- 理想情況下，每個行程的工作集（Working set）都儲存在 TLB 中
- 增加分頁大小：可能導致碎片化增加，因為並非所有應用程式都需要大分頁
- 提供多種分頁大小：允許需要大分頁的應用程式有機會使用它們，而不會增加碎片化

#### I/O 互鎖（I/O Interlock）
- I/O 互鎖（I/O Interlock）：分頁有時必須被鎖定（locked）在記憶體中
	- 用於從裝置複製檔案的分頁必須被鎖定，防止被頁面置換演算法選中並驅逐
	- 釘選（Pinning）分頁以將其鎖定在記憶體中











































