行程與上下文切換 (Processes & Context Switches)

Overview Table

主題 核心概念 書頁
行程定義 Process = 執行中程式的實例 (an instance of a program in execution),在 context 中執行 p.768
兩大抽象 (1) 獨立的邏輯控制流 → 獨占 CPU 的錯覺;(2) 私有位址空間 → 獨占記憶體的錯覺 p.768
邏輯控制流 單步除錯所見的 PC 值序列;多行程的邏輯流在時間上交錯 (interleaved) p.768-769
並行流 (concurrent flow) 兩流在時間上重疊即為 concurrent,與核心數無關 p.769
平行流 (parallel flow) concurrent flows 的真子集:同時跑在不同 core / 機器上 p.770
私有位址空間 n-bit 位址 → 2n 個位址;他行程一般不可讀寫;佈局結構統一(code 段起於 0x400000) p.770-771
User / Kernel mode 控制暫存器中的 mode bit;user mode 禁止特權指令與存取 kernel 區;唯一升權途徑是例外 p.770-772
/proc 檔案系統 讓 user mode 行程以文字檔階層讀取 kernel 資料結構;2.6 版另有 /sys p.771-772
上下文切換 建立在低階例外機制之上的高階 ECF;由 scheduler 決策 (scheduling);三步驟:存→還→轉 p.772-773

8.2 行程 (Processes) — 抽象的本質 (p.768)

例外 (exceptions) 是 OS kernel 得以提供 process 概念的基本建構元件。行程是計算機科學中最深刻、最成功的想法之一。它給程式三重錯覺:

經典定義:process 是「執行中程式的一個實例」。每個程式都在某個行程的 context(上下文) 中執行。Context = 程式正確執行所需的狀態,包括:

每當使用者在 shell 輸入可執行檔名稱,shell 就建立一個新行程,並在其 context 中執行該可執行檔;應用程式自己也能建立新行程來執行自身或其他程式的程式碼。

行程提供給應用程式的兩個關鍵抽象:

  1. 獨立的邏輯控制流 (independent logical control flow) → 獨占 processor 的錯覺
  2. 私有位址空間 (private address space) → 獨占記憶體系統的錯覺
Tip

本節刻意不深入 OS 如何「實作」行程,而聚焦行程「提供給應用程式什麼抽象」——這是 CSAPP 一貫的 programmer's perspective。


8.2.1 邏輯控制流 (Logical Control Flow) (p.768-769)

用除錯器單步執行程式時,觀察到的一連串 PC 值——全部落在自己程式的可執行檔或動態連結的 shared objects 中——這個 PC 值序列就是邏輯控制流 (logical flow)

時間  Process A    Process B    Process C     ← Figure 8.12
 |       |
 |       |  (A 執行一段, 被 preempt)
 |                    |
 |                    |  (B 執行到結束)
 |                                 |
 |                                 |  (C 執行一段)
 |       |
 |       |  (A 恢復, 執行到結束)
 |                                 |
 v                                 |  (C 執行到結束)
        單一實體控制流被切分成三條邏輯流,彼此交錯 (interleaved)

8.2.2 並行流 (Concurrent Flows) (p.769-770)

邏輯流有多種形式:exception handlers、processes、signal handlers、threads、Java processes 都是邏輯流。

並行 (concurrent) 的精確定義:流 X 與 Y 互為 concurrent 若且唯若

X 在 Y 開始之後、Y 結束之前開始;或 Y 在 X 開始之後、X 結束之前開始

亦即兩流的執行在時間上重疊 (overlap in time)。以 Figure 8.12 為例:A 與 B 並行、A 與 C 並行;B 與 C 並行(B 的最後一條指令在 C 的第一條指令之前執行完)。

術語 定義
Concurrency(並行) 多條流在時間上重疊執行的一般現象
Multitasking(多工) 行程之間輪流執行;又稱 time slicing
Time slice(時間片) 行程執行其邏輯流一部分的一段時間(Fig 8.12 中 A 的流含 2 個 time slices)
Parallel flows(平行流) concurrent flows 的真子集 (proper subset):同時在不同 core 或不同機器上執行,稱為 parallel execution
Important

Concurrent 與硬體無關:只要兩流在時間上重疊,即使跑在同一顆單核 processor 上也是 concurrent。Parallel 才要求不同 core / 機器。判斷 concurrency 只看「時間區間是否重疊」,不看是否真的同時執行。

判斷法(Practice Problem 8.1 型)

給定各行程 (start, end) 時間,兩兩檢查時間區間是否相交。例:A(1,3)、B(2,5)、C(4,6) → AB 重疊 (Y)、AC 不重疊 (N,A 在 3 結束、C 在 4 開始)、BC 重疊 (Y)。


8.2.3 私有位址空間 (Private Address Space) (p.770-771)

n-bit 位址的機器上,address space2n 個可能位址的集合:0,1,,2n1。行程給每個程式自己私有的位址空間——「私有」意指空間中某位址對應的記憶體位元組,一般而言不能被其他行程讀寫。

各行程位址空間內容不同,但組織結構相同。x86-64 Linux 行程的位址空間佈局(Figure 8.13):

 2^48-1 ┌────────────────────────────────┐
        │   Kernel virtual memory        │ ← user code 不可見
        │   (code, data, heap, stack)    │   (memory invisible to user code)
        ├────────────────────────────────┤
        │   User stack                   │ ← %rsp (stack pointer)
        │   (run time 時建立, 向下長)     │
        ├──────────── ↓ ─────────────────┤
        │                                │
        ├──────────── ─ ─────────────────┤
        │   Memory-mapped region         │
        │   for shared libraries         │
        ├──────────── ─ ─────────────────┤
        │                                │
        ├──────────── ↑ ─────────────────┤ ← brk
        │   Run-time heap                │
        │   (由 malloc 建立, 向上長)      │
        ├────────────────────────────────┤
        │   Read/write segment           │ ┐
        │   (.data, .bss)                │ │ 從可執行檔
        ├────────────────────────────────┤ │ 載入
        │   Read-only code segment       │ │ (loaded from the
        │   (.init, .text, .rodata)      │ ┘  executable file)
0x400000├────────────────────────────────┤
        │          (未使用)               │
      0 └────────────────────────────────┘
一般化敘述的例外

「私有位址空間不能被其他行程讀寫」是一般情況 (in general)——存在例外:例如透過共享記憶體映射機制可讓多行程共享實體頁面,見 09-Virtual-Memory/04-Memory-Mapping;而 /proc/<pid>/maps 也能讓別的行程「觀察」到行程的記憶體段配置 (p.772)。私有性的實作機制(page table、保護位元)見 09-Virtual-Memory/02-VM-Management-and-Protection


8.2.4 使用者模式與核心模式 (User and Kernel Modes) (p.770-772)

要讓 process 抽象「密不透風 (airtight)」,processor 必須提供機制限制應用程式可執行的指令可存取的位址空間範圍。實作方式:某個控制暫存器中的 mode bit

Kernel mode(又稱 supervisor mode) User mode
mode bit 已設定 (set) 未設定 (not set)
可執行指令 指令集中的任何指令 禁止特權指令 (privileged instructions)
可存取記憶體 系統中任何記憶體位置 不可直接參考 kernel 區的 code/data
違規後果 fatal protection fault

User mode 被禁止的特權操作例:halt processor改變 mode bit發起 I/O 操作。User 程式必須改經 system call interface 間接存取 kernel 的 code 與 data。

模式轉換的唯一途徑是例外 (exception):

        user mode                          kernel mode
   ┌─────────────────┐   interrupt/fault/  ┌─────────────────┐
   │ application code│──trapping syscall──▶│ exception       │
   │ (初始即為 user)  │                     │ handler 執行     │
   │                 │◀───handler return───│ (in kernel mode)│
   └─────────────────┘   (改回 user mode)   └─────────────────┘

/proc 檔案系統 (p.771-772):Linux 的巧妙機制,把許多 kernel 資料結構的內容匯出成一個文字檔階層,讓 user mode 行程可以讀取。例:


8.2.5 上下文切換 (Context Switches) (p.772-773)

OS kernel 用一種較高階的 ECF 形式——context switch——實作 multitasking;它建立在 08-Exceptional-Control-Flow/01-Exceptions 所述的低階例外機制之上

Context(上下文):kernel 為每個行程維護的、重啟一個被 preempt 的行程所需的狀態,包含:

Scheduling(排程):kernel 在行程執行的某些時點,可決定 preempt 當前行程、重啟先前被 preempt 的行程;此決策由 kernel 中的 scheduler 程式碼處理。kernel 選定新行程 = 「scheduled 該行程」。之後以 context switch 機制轉移控制:

Context switch 三步驟:
  (1) 保存 (save)    當前行程的 context
  (2) 還原 (restore) 某個先前被 preempt 行程已保存的 context
  (3) 轉移 (pass)    控制權給這個新還原的行程

觸發時機(兩大類):

觸發來源 情境
System call 期間 syscall 阻塞 (block) 等待事件:如 read 需磁碟存取、sleep 明確要求睡眠 → kernel 令當前行程 sleep,切換到別的行程。即使 syscall 不阻塞,kernel 也可以決定切換而非直接返回
Interrupt 例如週期性 timer interrupt(典型每 1 ms 或 10 ms 一次)→ kernel 可判定當前行程「跑得夠久了」而切換

Figure 8.14 — context switch 的剖析(process A 執行 read):

時間          Process A                Process B
 |    ── user code (A) ──────
 |          read (trap)
 |    ── kernel code ────────┐  trap handler 發起磁碟 DMA、  ┐
 |                           │  設定磁碟完成後發 interrupt    │ context
 |                           └──────▶ ── user code (B) ──   ┘ switch
 |                                         |
 |    disk interrupt (資料已到記憶體)        |
 |                            ┌── kernel code ──────────    ┐ context
 |    ◀───────────────────────┘  (B 跑夠久了, 切回 A)        ┘ switch
 |    ── user code (A) ──────
 v      從 read 返回, 繼續執行 read 之後的下一條指令

流程細節 (p.772-773):

  1. A 在 user mode 執行,呼叫 read trap 進 kernel;trap handler 向磁碟控制器請求 DMA 傳輸,並安排磁碟在傳輸完成後中斷 processor
  2. 磁碟取資料需時很久(數十毫秒等級),kernel 不空等,執行 A→B 的 context switch
  3. 切換前,kernel 是在 user mode 代表 A 執行;切換前半段,kernel 在 kernel mode 代表 A 執行;某個時點起改為(仍在 kernel mode)代表 B 執行;切換完成後,在 user mode 代表 B 執行
  4. B 在 user mode 執行一段時間,直到磁碟發出 interrupt(資料已從磁碟送達記憶體);kernel 判定 B 跑夠久了,執行 B→A 的 context switch,把 A 的控制權還到 read 之後的那一條指令,A 繼續執行直到下一個例外
Important

沒有獨立的 kernel process:kernel 永遠是「代表某個行程 (on behalf of some process)」執行指令,切換過程中只是「代表的對象」從 A 變成 B。這是常考觀念。

Warning

Figure 8.13 原書圖中把 stack pointer 標為 %esp(第 3 版沿用的 32-bit 記號);在 x86-64 下實際的 stack pointer 暫存器是 %rsp,見 03-Machine-Level-Programs/04-Procedures-and-the-Stack。另外圖頂標示 2481,反映 x86-64 實務上只用 48-bit 虛擬位址(而非完整 264),詳見 09-Virtual-Memory/01-Address-Spaces-and-VM-Caching


Exam/Test Patterns

情境 / 關鍵字 答案
「process 的經典定義?」 An instance of a program in execution;在某個 context 中執行
「process 提供哪兩個關鍵抽象?」 (1) 獨立的邏輯控制流(獨占 CPU 錯覺);(2) 私有位址空間(獨占記憶體錯覺)
「logical flow 是什麼?」 程式執行時觀察到的 PC 值序列
給 start/end 時間,問哪些行程 concurrent 檢查時間區間是否重疊;A(1,3) B(2,5) C(4,6) → AB: Y、AC: N、BC: Y(Practice 8.1)
「單核 CPU 上兩個交錯執行的行程是 concurrent 嗎?」 。concurrency 與核心數無關;只有 parallel 才要求不同 core/機器
「parallel 與 concurrent 的關係?」 parallel flows 是 concurrent flows 的真子集
「multitasking / time slicing?」 行程輪流執行;每段執行期間 = time slice
「n-bit 機器的 address space 大小?」 2n 個位址:02n1
「x86-64 Linux 的 code segment 起始位址?」 0x400000
「user mode 執行特權指令或碰 kernel 記憶體會怎樣?」 fatal protection fault
「user mode → kernel mode 的唯一途徑?」 例外:interrupt、fault、或 trapping system call
「user 程式如何讀 kernel 資料結構?」 /proc 檔案系統(文字檔階層);/proc/cpuinfo/proc/<pid>/maps;2.6+ 另有 /sys
「context switch 三步驟?」 (1) save 當前行程 context (2) restore 先前被 preempt 行程的 context (3) pass control 給它
「context 包含什麼?」 通用/浮點暫存器、PC、user stack、status registers、kernel stack、page tableprocess tablefile table
「什麼時候發生 context switch?」 syscall 阻塞(read 等磁碟、sleep)、syscall 不阻塞時 kernel 也可決定切換、timer interrupt(每 1/10 ms)
「排程決策由誰做?」 kernel 中的 scheduler;選定新行程 = scheduled
「切換期間 kernel 是獨立行程嗎?」 ,kernel 一律「代表某行程」執行;切換中途改為代表新行程