VM 記憶體管理與保護 (VM for Memory Management & Protection)

Overview Table

主題 核心機制 帶來的好處 書頁
每行程獨立位址空間 OS 為每個 process 維護一份獨立的 page table 行程間記憶體天然隔離;多個 VP 可映射到同一 PP 實現共享 p.847
簡化 linking 每個 process 的記憶體映像格式一致(code 固定起於 0x400000) linker 產出的執行檔與實體位址無關 p.847-848
簡化 loading loader 只建立 PTE 指向目的檔位置、標記 invalid,不複製資料 資料由 VM 系統首次參照時 demand paging 載入 p.848
簡化 sharing 不同 process 的 VP 映射到同一個實體頁 kernel code、C 標準函式庫(如 printf)只需一份 p.848
簡化 memory allocation malloc 要求 k 頁時,配置 k 個連續虛擬頁→ 映射到任意分散的實體頁 免除尋找連續實體記憶體的負擔 p.848
記憶體保護 PTE 加上 SUP / READ / WRITE 權限位元,每次位址轉譯時由硬體檢查 頁層級 (page-level) 存取控制;違規 → SIGSEGV p.848-849

9.4 VM as a Tool for Memory Management (p.847-848)

VM 的價值不只在「用 DRAM 快取較大的虛擬位址空間」。歷史上如 DEC PDP-11/70 的虛擬位址空間甚至小於實體記憶體,VM 依然有用——因為它大幅簡化記憶體管理並提供自然的保護機制。關鍵轉折:先前假設整個系統只有一張 page table;實際上 OS 為每個行程提供一份獨立的 page table,也就是一個獨立的虛擬位址空間

        Virtual address spaces          Address        Physical memory
                                       translation
             0 ┌─────────┐                              0 ┌─────────┐
               │  VP 1   │──────────────────────┐        │         │
 Process i:    │  VP 2   │────────┐             └───────▶│  PP 2   │
           N-1 └─────────┘        │                      │         │
                                  └─────────────────────▶│  PP 7   │◀── shared
             0 ┌─────────┐        ┌─────────────────────▶│  page   │
               │  VP 1   │────────┘                      │         │
 Process j:    │  VP 2   │──────────────────────────────▶│  PP 10  │
           N-1 └─────────┘                          M-1  └─────────┘

(Figure 9.9:OS 為每個 process 維護獨立的 page table;不同 process 的 VP 可映射到相同的實體頁)

記憶錨點

「一個 process = 一張 page table = 一個虛擬位址空間」。隔離是預設(映射到不相交的實體頁),共享是例外(刻意映射到同一實體頁)。

簡化 Linking (p.847-848)

獨立位址空間讓每個 process 使用相同的基本記憶體映像格式,與 code/data 實際落在實體記憶體何處無關(參見第 8 章 Fig 8.13 的 Linux process 記憶體佈局)。

例外:上述 0x400000 是教科書中未啟用 ASLR 的傳統 Linux 佈局。現代系統常以 ASLR(位址空間配置隨機化)與 PIE 隨機化各區段起點,細節見 03-Machine-Level-Programs/06-Buffer-Overflow-and-Pointer-Safety

簡化 Loading (p.848)

載入執行檔 / 共享目的檔的 .text.data 時,Linux loader 的動作是:

 loader 載入流程(不搬資料!)
 ┌────────────────────────────────────────────────────────┐
 │ 1. 為 code/data segment 配置虛擬頁 (allocate VPs)       │
 │ 2. 將這些頁標記為 invalid(未快取)                      │
 │ 3. 讓 PTE 指向目的檔中對應的位置                         │
 └────────────────────────────────────────────────────────┘
                     │  (loader 到此結束,零複製)
                     ▼
 首次參照該頁(CPU 取指令或指令存取記憶體)
                     │
                     ▼
        page fault → VM 系統自動、on demand 換入該頁

簡化 Sharing (p.848)

獨立位址空間給 OS 一個一致的機制管理 user process 之間、以及 user process 與 OS 之間的共享:

情境 Page table 安排
私有的 code、data、heap、stack(預設) 各 process 的 VP 映射到不相交 (disjoint) 的實體頁
共享 kernel code、C 標準函式庫(如 printf) 不同 process 的適當 VP 映射到同一組實體頁,全系統只保留一份副本

簡化 Memory Allocation (p.848)

當程式要求額外 heap 空間(例如呼叫 malloc):


9.5 VM as a Tool for Memory Protection (p.848-849)

現代系統必須讓 OS 能控制對記憶體系統的存取。一個 user process 不應該能夠:

獨立位址空間已天然隔離各 process 的私有記憶體;而位址轉譯機制可自然延伸出更細緻的存取控制:因為位址轉譯硬體在 CPU 每次產生位址時都會讀取 PTE,只要在 PTE 中加入額外的權限位元 (permission bits),即可以「頁」為單位控制存取。

              Page tables with permission bits
              SUP  READ  WRITE   Address          Physical memory
            ┌─────┬─────┬─────┬─────────┐          ┌─────────┐ PP 0
      VP 0: │ No  │ Yes │ No  │  PP 6   │─┐        ├─────────┤
Proc  VP 1: │ No  │ Yes │ Yes │  PP 4   │─┼──┐  ┌─▶│  PP 2   │
  i   VP 2: │ Yes │ Yes │ Yes │  PP 2   │─┼──┼──┘  ├─────────┤
            └─────┴─────┴─────┴─────────┘ │  └────▶│  PP 4   │
                                          │        ├─────────┤
            ┌─────┬─────┬─────┬─────────┐ └───────▶│  PP 6   │◀─┐
      VP 0: │ No  │ Yes │ No  │  PP 9   │────┐     ├─────────┤  │
Proc  VP 1: │ Yes │ Yes │ Yes │  PP 6   │────┼─────┼─────────┼──┘
  j   VP 2: │ No  │ Yes │ Yes │  PP 11  │─┐  └────▶│  PP 9   │
            └─────┴─────┴─────┴─────────┘ │        ├─────────┤
                                          └───────▶│  PP 11  │
                                                   └─────────┘

(Figure 9.10:每個 PTE 增加三個權限位元,實現 page-level 保護)

三個權限位元的語意

位元 意義 檢查規則
SUP 是否須在 kernel (supervisor) mode 才能存取 kernel mode 可存取任何頁;user mode 只能存取 SUP = No 的頁
READ 讀取權限 控制對該頁的讀取
WRITE 寫入權限 控制對該頁的寫入

以 Figure 9.10 為例:process i 在 user mode 執行時——

違規時的處理流程

 指令違反權限位元
        │
        ▼
 CPU 觸發 general protection fault(一種 fault 類例外)
        │
        ▼
 控制權轉移到 kernel 中的 exception handler
        │
        ▼
 kernel 對違規的 process 送出 SIGSEGV
        │
        ▼
 Linux shell 回報:"segmentation fault"
保護檢查是「零額外成本」搭載在位址轉譯上的

因為 MMU 硬體本來就要在每次記憶體存取時讀 PTE 做轉譯,權限檢查只是順路多看幾個位元——不需要額外的記憶體存取,也不需軟體介入(除非真的違規才觸發例外)。

常見混淆:protection fault vs. page fault

兩者都由 MMU 讀 PTE 時發現、都以例外形式進入 kernel,但語意完全不同:page fault(valid bit = 0,頁不在 DRAM)由 kernel 換入頁後重新執行該指令,對程式透明;permission 違規(general protection fault)則導致 SIGSEGV,預設終止程式。前者見 09-Virtual-Memory/01-Address-Spaces-and-VM-Caching

Aside:計數 page faults (p.847)

Linux 的 getrusage 函式可監看 process 的 page fault 次數與許多其他資源使用資訊。


Exam/Test Patterns

情境 / 關鍵字 答案
「VM 空間比實體記憶體小還有用嗎?」 有(如 DEC PDP-11/70):VM 的價值還包括簡化記憶體管理保護,不只 caching (p.847)
「每個 process 有幾張 page table?」 每個 process 一張獨立的 page table = 一個獨立虛擬位址空間 (p.847)
「兩個 process 如何共享 libc / kernel code?」 各自的 VP 映射到同一個實體頁(單一副本)(p.848)
「64 位元 Linux code segment 起始位址?」 虛擬位址 0x400000;data 在其後、stack 在最高處向下長 (p.847)
「loader 載入 .text/.data 時複製了多少資料?」 ;只設定 PTE 指向目的檔並標 invalid,首次參照時才 demand paging 換入 (p.848)
「把連續虛擬頁映射到檔案任意位置」叫什麼? Memory mapping;應用層 API 為 mmap (p.848)
malloc 要 k 頁,OS 需要找連續實體頁嗎?」 不需要;k 個連續虛擬頁可映射到散佈各處的 k 個實體頁 (p.848)
「PTE 的三個權限位元?」 SUP(須 kernel mode)、READWRITE (p.849)
「user mode 能存取 SUP=Yes 的頁嗎?」 不能;kernel mode 才可存取任何頁,user mode 只能存取 SUP=No 的頁 (p.849)
「違反權限位元會發生什麼?」 CPU 觸發 general protection fault → kernel handler → 送 SIGSEGV → shell 顯示 "segmentation fault" (p.849)
「如何查看 process 的 page fault 次數?」 Linux getrusage 函式 (p.847 Aside)
給定權限位元表,問某 process 在 user mode 可否讀/寫某 VP 先看 SUP(Yes 則 user mode 一律拒絕),再看 READ / WRITE 各自的 Yes/No