iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
IT Operation

解構作業系統:30 天從 Process、Concurrency 到 Virtual Memory系列 第 5

Day 5|Context Switch:CPU 怎麼從 Process A 切換到 Process B?

  • 分享至 

  • xImage
  •  

上一篇提到,一個 Process 不只有程式碼。

當它正在 CPU 上執行時,還有一組很重要的 CPU Execution State,例如:

  • Program Counter
  • Stack Pointer
  • Registers
  • 其他與執行狀態相關的資訊

假設現在 CPU 正在執行 Process A:
CPU


Process A

但系統裡還有:
Process B
Process C
Process D
...

OS 不可能永遠只讓 A 使用 CPU。
如果現在要把 CPU 從 A 換給 B,A 執行到一半的狀態怎麼辦?
這就是今天的主角:
Context Switch


1. 為什麼需要 Context Switch?

假設我們只有一個 CPU Core。
在某一個瞬間,這個 Core 只能執行一個 instruction stream。
但我們平常使用電腦時,看起來可以同時:
播放音樂 + 開瀏覽器 + 使用 VS Code + 下載檔案 + 跑 Terminal

看起來好像全部都「同時在跑」。
其中一個重要原因,就是 OS 會讓 CPU 在不同可執行工作之間切換。
例如:

時間 ───────────────────────────►

Process A
████████

        Process B
        ████████

                Process C
                ████████

                        Process A
                        ████████

因為切換速度很快,所以從人的使用體驗來看,很多工作像是在同時進行。

當然,如果是 Multi-core CPU,不同 Core 本來就能真正同時執行不同的工作。
但即使有很多 Core,可以執行的 Process / Thread 數量通常仍然遠大於 CPU Core 數量。
所以 OS 還是需要在不同工作之間分配 CPU。
但是,怎麼讓 A 暫停之後,待會或等等可以從原本的位置繼續?


2. Context 到底是什麼?

讓一個執行實體在未來能正確繼續執行所需要保存的 CPU 狀態。
假設 Process A 正在做:
int c = a + b;
到了某個時間點,CPU 內部可能存在:

Program Counter = 0x401020
Stack Pointer   = 0x7fff....
Register R1     = 10
Register R2     = 20
Register R3     = ...
...

它們是:程式「現在執行到哪裡」的即時狀態。

如果 OS 直接讓 Process B 開始使用 CPU:
Process A
R1 = 10
R2 = 20
PC = A 的下一條指令

│ 不保存,直接換 B

Process B

R1 = B 的資料
R2 = B 的資料
PC = B 的指令

那 A 原本存在 CPU Registers 裡的資訊就會被覆蓋。
等 A 下次回來,
A:???我剛才跑到哪?
所以 Context Switch 的第一個核心就是:
在 CPU 被另一個執行實體使用之前,先保存目前必要的 execution context。


3. 一次 Context Switch 實際發生什麼?

假設:
Process A = Running
Process B = Ready

CPU 正在執行 A。

某個時刻,Kernel 決定接下來讓 B 執行。
process A

│ Running

CPU

│ ① 進入 Kernel

Kernel

│ ② 保存 A 的 Context

A 的 Kernel Management Data


│ ③ Scheduler 決定 B 執行

B 的 Kernel Management Data

│ ④ 恢復 B 的 Context
▼ CPU

│ ⑤ 返回 B 的執行流程
▼ Process B

① CPU 先進入 Kernel:Process A 不能自己任意決定:「好,我現在把 CPU 送給 Process B。」
CPU 排程與 Process 管理屬於 Kernel 的工作。

② 保存 A 的 Context:保存 A 未來恢復執行所需要的狀態,即使 CPU Registers 接下來被 B 使用,也不會讓 A 的必要狀態直接消失。

③ Scheduler 選擇下一個工作:接著 OS 的 Scheduler 會根據排程政策,從可以執行的工作中決定下一個要使用 CPU 的對象,例如:

  • Ready Queue:
    Process B
    Process C
    Process D
    所以Scheduler會抓Process B。

④ 恢復 B 的 Context:假設 B 以前執行過,那 Kernel 會有它之前保存下來的 execution context,例如:
B:
PC = 0x...
SP = 0x...
Registers = ...

Kernel 將必要的狀態恢復到 CPU。
這時 CPU 的執行環境就從:

A 的 Context 變成B 的 Context。

⑤ CPU 繼續執行 B:最後 CPU 從 B 對應的執行位置繼續,對 B 來說,它可能完全不會感覺到:我剛才被停了 10 ms,它看到的邏輯仍然:
Instruction 1
Instruction 2
Instruction 3
--- (沒看到的部分但中間其實被切走) ---
Instruction 4
Instruction 5
這就是 OS 能讓大量工作輪流使用 CPU,卻仍然維持各自執行狀態的重要基礎。


4. 那什麼時候會發生 Context Switch?

不是只有「時間到了」才會切換,常見原因可以分成幾種。

  • 情況一:Time Slice 用完
    假設 OS 使用 Preemptive Scheduling,Process A 已經執行一段時間。
    Timer Interrupt 發生後,Kernel 取得控制權,Scheduler 可能決定:
    執行夠久了

    讓其他 Ready 工作使用 CPU,於是:
    Process A:
    Running

    Ready

Process B:
Ready

Running

這時可能發生 Context Switch。

  • 情況二:目前工作需要等待

還記得 Day 2 的:read(fd, buffer, 100);如果需要的資料還沒有準備好,目前執行的工作可能必須等待。
此時:
Process A:
Running

│ 等待 I/O

Waiting

既然 A 現在不能繼續,讓 CPU 空等通常沒有意義。
Scheduler 就可以讓另一個 Ready 的工作執行:
A → Waiting

B → Running
這也可能造成 Context Switch。

  • 等等其他情況。

5.Cache 也可能受到影響

CPU 為了加速執行,本身還有很多 cache,例如:

CPU

├── Registers
├── Cache
└── TLB
當 CPU 長時間執行 Process A 時,Cache 裡可能已經有很多 A 常用的資料與指令,如果現在突然切到 Process B,B 使用的資料可能完全不同,那 B 一開始就可能遇到更多 cache miss,需要重新把自己的 working set 帶進 cache。

所以 Context Switch 的成本不只有「保存和恢復 Registers + 花幾個指令。」,還可能造成 Cache Locality 變差。


6.那是不是 Context Switch 越少越好?

如果完全不切:
Process A 那麼長
████████████████████████████████████████
那其他 Process 可能很久都拿不到 CPU。
→ 其他工作等待較久
→ Responsiveness / Fairness 可能變差

但如果切得太頻繁:
A B C A B C A B C A B C A B C...
又會花太多時間在切換本身。
→ Context Switch Overhead 增加
→ Cache / TLB 等副作用也可能增加

OS 還要決定:誰先跑?跑多久?什麼時候使用CPU?
這些就是後面的 Scheduling 問題。


今天的結論

CPU 要換人使用,但原本的工作之後還必須能從原來的位置繼續。
System Call
→ User Program 向 Kernel 請求服務

Interrupt
→ CPU 收到需要處理的事件

Context Switch
→ CPU 從一個 execution context 切換到另一個 execution context

對了,fork()是OS中很重要的角色,如果 fork() 要建立 Child Process,難道真的要把 Parent 的整份記憶體全部複製一次嗎?
下一篇:

Day 6|fork() 到底複製了什麼?


上一篇
Day 4|Process 其實不只是「正在執行的程式」
下一篇
Day 6|fork() 到底複製了什麼?
系列文
解構作業系統:30 天從 Process、Concurrency 到 Virtual Memory9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言