進程調度是作業系統核心中最頻繁執行、對效能影響最大的子系統。Linux 經歷了 O(n) 線性掃描 → O(1) 陣列佇列 → O(log N) CFS 紅黑樹 三代演進。本文深入剖析實時/普通進程調度策略、動態優先級計算、公平性實現、多核負載均衡、優先級繼承、中斷執行緒化等核心機制。
| 類別 | 調度策略 | 優先級範圍 | 特徵 |
|---|---|---|---|
| 實時進程 | SCHED_FIFO / SCHED_RR | 1-99 (最高) | 優先級固定、搶佔普通進程、無時間片(SCHED_FIFO)或固定時間片(SCHED_RR) |
| 普通進程 | SCHED_NORMAL (SCHED_OTHER) | 100-139 (nice -20~19) | 動態優先級、CFS 完全公平調度、時間片可變 |
| 批次/閒置 | SCHED_BATCH / SCHED_IDLE | 100-139 | 極低優先級、適合背景計算 |
sched_yield()、阻塞、或被更高優先級搶佔,否則獨佔 CPU。/proc/sys/kernel/sched_rt_runtime_us (預設 950ms) / sched_rt_period_us (1s),保留 5% CPU 給普通進程防死鎖。CAP_SYS_NICE(通常 root)才能 sched_setscheduler 設為實時。CFS 不直接用優先級決定誰跑,而是維護每個進程的 vruntime(虛擬運行時間):
vruntime += 實際運行時間 * (NICE_0_LOAD / 權重)
權重 = 1024 / (1.25 ^ nice) # nice -20 權重最高 ~88761,nice +19 最低 ~15
vruntime 越小 = 應該越早獲得 CPU。
TASK_RUNNING)掛在 cfs_rq->tasks_timeline 紅黑樹上,鍵值為 vruntime。
目標延遲 = sched_latency_ns (預設 6ms * log2(CPU數))
最小粒度 = sched_min_granularity_ns (預設 0.75ms)
時間片 = max(目標延遲 / 可執行進程數, 最小粒度)
進程數少 → 時間片大;進程數多 → 時間片縮小至最小粒度。
schedule():主動放棄 CPU(睡眠、等待鎖、sched_yield)。preempt_schedule():內核搶佔點(中斷返回、釋放鎖)。tick 周期性檢查:scheduler_tick() 更新 vruntime,判斷是否需要搶佔。雖 CFS 主打 vruntime,但 nice 值仍影響權重。早期 O(1) 調度器透過 睡眠時間 vs 運行時間 判斷互動性:
CFS 以權重取代動態優先級調整,但 sched_autogroup、sched_child_runs_first 等參數仍影響公平性感知。
| 觸發源 | 典型場景 | 調度點 |
|---|---|---|
| 主動阻塞 | sleep、wait、I/O 等待、exit | 系統調用內部呼叫 schedule() |
| 時間片耗盡 | CFS 判斷 vruntime 超過閾值 | scheduler_tick() / update_curr() |
| 高優先級就緒 | 實時進程喚醒、中斷處理釋放鎖喚醒進程 | 中斷返回路徑 preempt_schedule_irq() |
| 明確讓權 | sched_yield() | 系統調用直接呼叫 schedule() |
| 參數 | 預設 | 調優方向 |
|---|---|---|
sched_latency_ns | 6000000 | 降低 → 互動響應快,吞吐略降 |
sched_min_granularity_ns | 750000 | 降低 → 細粒度公平,切換開銷增 |
sched_wakeup_granularity_ns | 1000000 | 降低 → 喚醒更快搶佔 |
sched_rt_runtime_us | 950000 | 調高 → 實時佔用更多,普通進程可能飢餓 |
sched_autogroup_enabled | 1 | 0 → 關閉會話分組,適合伺服器 |
sched_migration_cost_ns | 500000 | 調高 → 減少跨核遷移,適合熱緩存工作負載 |