目錄
- 為什麼需要 CPU 排程?
- 執行緒為何是排程單位
- 並發 vs 並行
- 硬體核心與執行緒數量
- 搶佔式 vs 協作式排程
- 時間片與排程器
- 常見排程演算法
- 情境切換(Context Switch)
- 實務觀察與調校
- 常見問題
- 總結
為什麼需要 CPU 排程?
一台機器的 CPU 核心數有限,但同時要執行的執行緒往往遠多於核心數。CPU 排程(CPU Scheduling) 就是作業系統決定「哪條執行緒、在哪個核心、執行多久」的機制。
可執行的執行緒(就緒佇列): T1 T2 T3 T4 T5 …
CPU 核心: Core0 Core1
↑ ↑
排程器決定:誰上、上多久、何時換下
目標:在有限核心上,讓眾多執行緒看起來像「同時」在跑,同時兼顧公平、回應速度與吞吐量。
執行緒為何是排程單位
現代作業系統中,CPU 排程的基本單位是執行緒(kernel thread),而非行程:
- 行程是資源擁有單位(位址空間、檔案…)
- 執行緒是執行 / 排程單位(有自己的 stack、PC、暫存器)
排程器從「就緒(Ready)」的執行緒中挑一條放上 CPU。
Linux 的特例:Linux 排程器排的是 "task"(
task_struct),它不嚴格區分行程與執行緒——同一行程的多條執行緒只是「共享資源的 task」。概念上仍是「以執行流為單位排程」。使用者層執行緒不在此列:協程 / goroutine / 纖程是在使用者空間被排到核心執行緒上,OS 不直接排它們(見 執行緒模型筆記)。
並發 vs 並行
兩個最常被搞混的詞:
| 概念 | 定義 | 關鍵 |
|---|---|---|
| 並發(Concurrency) | 多個任務在同一段時間內推進,可能靠快速切換交錯執行 | 結構:能同時「處理」多件事 |
| 並行(Parallelism) | 多個任務在同一時刻真的同時執行 | 需要多核心,真的同時跑 |
並發(單核): T1▮ T2▮ T1▮ T2▮ T1▮ ← 快速切換,看起來同時,實際輪流
並行(雙核): Core0: T1▮▮▮▮▮
Core1: T2▮▮▮▮▮ ← 真的同一時刻一起跑
- 單核心也能並發(靠排程切換),但無法並行
- 並行一定是並發的一種,但並發不一定並行
名言(Rob Pike):並發是「同時處理很多事」的能力,並行是「同時執行很多事」。 並發是結構設計,並行是執行表現。
硬體核心與執行緒數量
能真正並行的執行緒數量,上限就是 CPU 能同時執行的硬體執行緒數。要算清楚,得先分清楚「核心」的兩種意思。
實體核心 vs 邏輯核心(虛擬核心)
| 名詞 | 是什麼 |
|---|---|
| 實體核心(Physical Core) | 晶片上真正的運算單元 |
| 邏輯核心(Logical Core / 虛擬核心) | 靠 Hyper-Threading(Intel)/ SMT(AMD) 技術,讓一個實體核心對 OS 假裝成兩個,共用同一套執行單元 |
4 實體核心 + Hyper-Threading(每核 2 執行緒)
= 8 邏輯核心
→ 作業系統看到的是「8 顆 CPU」,排程也以邏輯核心為對象
Hyper-Threading / SMT 的真相
一個實體核心開兩個邏輯核心,不等於兩倍效能。兩條硬體執行緒共用同一核心的執行單元、快取,只是趁某條在等記憶體時讓另一條補上空檔。
- 實際增益常見約 1.2~1.3 倍(視工作負載),不是 2 倍
- CPU 全速計算(無等待)時,超執行緒幫助有限
- 所以「8 邏輯核心」的並行實力 < 「8 個真實體核心」
「虛擬核心」的兩個意思
別混淆——這個詞有兩種常見用法:
- 桌機 / 伺服器 CPU:通常指 Hyper-Threading 的邏輯核心
- 雲端 / 虛擬機:常指 vCPU——Hypervisor 分配給 VM 的核心(一個 vCPU 多半對應一條實體機的邏輯核心,但會被多台 VM 共享、超賣)
怎麼查有幾核
# Linux
nproc # 邏輯核心數(OS 可用的)
lscpu # 看 Socket / Core / Thread per core 詳情
# macOS
sysctl -n hw.physicalcpu # 實體核心
sysctl -n hw.logicalcpu # 邏輯核心
執行緒數該設多少?
執行緒數的設定以邏輯核心數為基準,再依工作型態調整:
| 工作型態 | 建議執行緒數 | 原因 |
|---|---|---|
| CPU 密集(運算為主,少等待) | ≈ 邏輯核心數(或 +1) | 再多也沒核心可跑,只增加切換 |
| I/O 密集(大量等網路 / 磁碟) | 遠多於核心數 | 多數執行緒在等,可超開填滿等待空檔 |
常用的經驗公式(Brian Goetz):
最佳執行緒數 ≈ 邏輯核心數 × (1 + 等待時間 / 計算時間)
純計算(等待≈0)→ ≈ 核心數
等待是計算的 3 倍 → ≈ 核心數 × 4
關鍵直覺:CPU 密集靠「核心數」設上限(多了只是徒增情境切換);I/O 密集才值得超開,因為大部分執行緒都卡在等待、沒佔用核心。這也呼應了執行緒模型筆記裡 M:N 模型為何適合海量 I/O。
搶佔式 vs 協作式排程
決定「執行緒何時讓出 CPU」的兩種策略:
搶佔式(Preemptive)
OS 可以強制中斷正在執行的執行緒(時間片用完、或更高優先權者就緒),收回 CPU 交給別人。
- 現代通用 OS(Linux、Windows、macOS)都是搶佔式
- 優點:單一執行緒無法霸佔 CPU,公平、回應快
- 代價:需要時鐘中斷與情境切換
協作式(Cooperative / Non-preemptive)
執行緒主動讓出才會切換(如執行到 yield、等 I/O)。OS 不強制收回。
- 早期系統、部分協程排程採用
- 優點:切換點明確、開銷小
- 缺點:一條不讓出(無窮迴圈)就會卡死全部
搶佔式:時間到 → OS 強制換人(不管你願不願意)
協作式:你跑完 / 主動 yield → 才換人(不讓就霸佔)
協程(coroutine)多是協作式:在明確的 await / yield 點切換。
時間片與排程器
時間片(Time Slice / Quantum)
搶佔式系統給每條執行緒一段最長連續執行時間(時間片,通常數毫秒)。用完就被搶佔、回到就緒佇列排隊。
- 時間片太長 → 回應變慢(別人等太久)
- 時間片太短 → 情境切換太頻繁,開銷吃掉效能
優先權(Priority)
排程器通常依優先權決定先後;高優先權執行緒先取得 CPU。需處理飢餓(starvation)——低優先權者長期搶不到,常用「老化(aging)」逐步提升等待者優先權。
現代排程器舉例
- Linux CFS(Completely Fair Scheduler):以「虛擬執行時間」追求公平,讓每條 task 取得近似等比的 CPU 時間(較新核心引入 EEVDF 排程器)。
常見排程演算法
| 演算法 | 規則 | 特點 |
|---|---|---|
| FCFS(先到先服務) | 依到達順序 | 簡單;長任務會拖住後面(護航效應) |
| SJF(最短工作優先) | 先跑預估最短的 | 平均等待短;需預估、可能飢餓 |
| Round Robin(輪詢) | 每人一個時間片輪流 | 公平、回應好;切換開銷 |
| Priority(優先權) | 高優先權先 | 重要任務優先;需防飢餓 |
| Multilevel Feedback Queue | 多層佇列 + 動態調整優先權 | 兼顧互動與批次,通用 OS 常用 |
通用桌面 / 伺服器 OS 多採類似「多層回饋佇列 / 公平排程」的混合策略,而非單一純演算法。
情境切換(Context Switch)
當 CPU 從一條執行緒切到另一條時,必須保存舊的、載入新的執行狀態,這個動作叫情境切換。
切換時保存 / 還原什麼
- 暫存器、程式計數器(PC)、堆疊指標
- 若跨行程切換:還要切換位址空間(page table)、刷新 TLB
Thread A 執行中
│ 時間片用完 / 等 I/O / 被搶佔
▼
保存 A 的狀態 → 排程器選 B → 載入 B 的狀態 → B 執行
└──────── 這段純開銷,沒做有用工作 ────────┘
成本
- 直接成本:保存 / 載入狀態的 CPU 週期
- 間接成本:快取(cache)與 TLB 失效——新執行緒的資料不在快取裡,要重新載入,往往比直接成本更傷
- 同行程內執行緒切換 比跨行程切換便宜(不必換位址空間 / 刷 TLB)
情境切換是「純開銷」:這段時間 CPU 沒在做應用的有用工作。過多執行緒 / 過度切換會讓 context switch 吃掉效能——這也是「執行緒不是越多越好」的根本原因。
實務觀察與調校
觀察情境切換
vmstat 1 # cs 欄 = 每秒 context switches;in = 中斷數
pidstat -w 1 # 每個行程的自願/非自願切換次數
- 自願切換(voluntary):主動等 I/O / 鎖造成
- 非自願切換(involuntary):時間片用完被搶佔,數值過高可能代表 CPU 競爭激烈或執行緒過多
調校方向
- 執行緒數量設定貼近核心數(CPU 密集約 = 核心數;I/O 密集可更多),避免過度切換
- 減少鎖競爭(降低自願切換與等待)
- 善用執行緒池,避免頻繁建立 / 銷毀執行緒
監控指標細節見 作業系統效能監控指標。
常見問題
問題 1:並發和並行差在哪?
並發是「同一段時間內處理多件事」(單核靠切換也行);並行是「同一時刻真的同時執行」(需多核)。並發是結構,並行是執行表現。
問題 2:單核 CPU 能多執行緒嗎?
能。單核透過快速切換達成並發,多條執行緒交錯推進,但無法並行(同一時刻只有一條真的在跑)。
問題 3:搶佔式和協作式哪個好?
通用 OS 用搶佔式,避免單一執行緒霸佔、回應更穩。協作式切換開銷小但風險高(一條不讓出就卡死全部),多見於協程排程。
問題 4:為什麼執行緒不是越多越好?
執行緒超過核心數太多時,情境切換與鎖競爭的開銷會抵銷並發收益,甚至更慢。CPU 密集型尤其明顯。
問題 5:context switch 數值多少算高?
沒有絕對值,要看基準與趨勢。重點看非自願切換是否異常攀升、是否伴隨 CPU 高使用率與效能下降,再對照執行緒數調整。
問題 6:實體核心和邏輯核心(虛擬核心)差在哪?執行緒數要看哪個?
實體核心是真正的運算單元;邏輯核心是 Hyper-Threading / SMT 讓一個實體核心對 OS「假裝」成的兩個,共用執行單元(增益約 1.2~1.3 倍,非 2 倍)。OS 排程與執行緒數設定都以邏輯核心數為基準(nproc / sysctl hw.logicalcpu),再依 CPU 密集(≈ 核心數)或 I/O 密集(可超開)調整。
總結
核心要點
- CPU 排程決定哪條執行緒、在哪核心、跑多久;執行緒是排程的基本單位
- 並發=同段時間處理多事(單核可);並行=同刻真的同時(需多核)
- 現代 OS 用搶佔式 + 時間片,避免霸佔;協程多是協作式
- 演算法:FCFS / SJF / RR / Priority / 多層回饋佇列,通用 OS 採混合公平策略
- 情境切換保存/載入狀態,是純開銷;快取 / TLB 失效是隱性成本
- 執行緒數應貼近核心數,過度切換反而拖慢
快速參考
| 詞 | 一句話 |
|---|---|
| 並發 | 同段時間處理多事(結構) |
| 並行 | 同刻同時執行(需多核) |
| 搶佔式 | OS 強制收回 CPU |
| 協作式 | 執行緒主動讓出 |
| 時間片 | 一次最長連續執行時間 |
| 情境切換 | 換執行緒時存/載狀態的開銷 |
建立日期:2026-06-18