CPU 排程演算法 (Scheduling Algorithms)
本系列文章內容參考自經典教材 Operating System Concepts, 10th Edition (Silberschatz, Galvin, Gagne)。本文對應章節:Section 5.3 Scheduling Algorithms。
前言:Gantt Chart(甘特圖)
在介紹各種排程演算法之前,先認識一個重要的視覺化工具:Gantt Chart(甘特圖)。Gantt Chart 是一種橫條圖(Bar Chart),用來呈現在特定排程下,各個 Process 佔用 CPU 的時間區間,並清楚標示每個切換的時間點。
本節的所有演算法範例都以 Gantt Chart 呈現,時間軸的單位為毫秒(milliseconds),為了讓概念聚焦,每個 Process 只考慮一次 CPU Burst。比較演算法時,統一使用平均等待時間(Average Waiting Time) 作為主要指標。
5.3.1 先到先服務 (First-Come, First-Served, FCFS)
FCFS 是最直覺的排程演算法:先到達 Ready Queue 的 Process 先獲得 CPU。實作上非常簡單,只需維護一個 FIFO 佇列:新 Process 加入佇列尾端,CPU 永遠從佇列頭部取出下一個 Process。
FCFS 的問題:Convoy Effect(護衛效應)
FCFS 的最大缺陷在於平均等待時間對 Process 到達順序非常敏感。考慮三個 Process,都在 t=0 同時到達:
| Process | CPU Burst |
|---|---|
| P1 | 24 ms |
| P2 | 3 ms |
| P3 | 3 ms |
若到達順序不同,平均等待時間的差異非常顯著,如下圖所示:
Example 1(到達順序 P1 → P2 → P3):P2 必須等 P1 的 24 ms 執行完畢才能開始,P3 更要等 27 ms。等待時間為 P1=0 ms、P2=24 ms、P3=27 ms,平均 17 ms。
Example 2(到達順序 P2 → P3 → P1):短 Process 先跑,P1 只等了 6 ms。等待時間為 P2=0 ms、P3=3 ms、P1=6 ms,平均只有 3 ms。
兩種順序下,平均等待時間相差將近六倍。這就是 Convoy Effect(護衛效應):大量短 Process 被一個長 Process 「護送」排在後面,被迫等待,導致 CPU 和 I/O 裝置的整體利用率下降。
在真實系統中,若一個 CPU-bound Process 佔住 CPU,所有 I/O-bound Process 只能排隊等待。等到 CPU-bound Process 終於完成並去做 I/O,所有 I/O-bound Process 快速跑完自己的短 CPU Burst 又回去做 I/O,結果 CPU 立刻閒置,等待 CPU-bound Process 再回來,這個惡性循環讓整體效率大幅下降。
FCFS 是非搶占式(Nonpreemptive) 排程:一旦 CPU 分配給某個 Process,它就一直佔有 CPU 直到主動放棄(終止或 I/O 等待)。這使得 FCFS 在互動式系統中不適用,因為無法保證每個 Process 定期得到 CPU 回應。
5.3.2 最短工作優先 (Shortest-Job-First, SJF)
SJF 的策略是:將 CPU 分配給下一個 CPU Burst 最短的 Process。若有兩個 Process 的 Burst 長度相同,以 FCFS 決定順序。SJF 排程能夠證明在所有排程演算法中,對於給定的 Process 集合,SJF 能夠達到最小的平均等待時間。
為什麼 SJF 最佳?
直覺理解:讓短 Process 先執行,能夠以較小的代價讓許多 Process 快速完成。長 Process 多等一段時間,但換來了大量短 Process 的等待時間大幅縮短,整體平均下來一定更優。
以四個 Process 為例(都在 t=0 到達):
| Process | CPU Burst |
|---|---|
| P1 | 6 ms |
| P2 | 8 ms |
| P3 | 7 ms |
| P4 | 3 ms |
SJF 按照 Burst 長度排序後執行(P4→P1→P3→P2),平均等待時間為 (0+3+9+16)/4 = 7 ms。若改用 FCFS 以 P1→P2→P3→P4 順序執行,平均等待時間為 10.25 ms。
SJF 的根本問題:無法預知 Burst 長度
SJF 在理論上最優,但有一個無法克服的實作問題:在 CPU Scheduling 層面,無法預先知道下一個 CPU Burst 的長度。CPU Burst 的長度只有在 Process 真正執行完畢後才能得知,根本無法在排程前就知道誰最短。
指數平均預測 (Exponential Average Prediction)
實務上的解決方案是:用過去的 CPU Burst 歷史來預測未來的 Burst 長度,稱為指數平均(Exponential Average)。預測公式為:
各符號的意義:
- :第 次 CPU Burst 的實際測量長度
- :第 次 CPU Burst 的預測值(前一次的猜測)
- :下一次 CPU Burst 的預測值(新的猜測)
- :控制「最近歷史」與「過去歷史」的相對權重,範圍為
下圖展示了當 、 時,預測值(橘線)如何追蹤實際 CPU Burst(藍色柱)的變化:
圖中藍色柱是每次的實際 CPU Burst(),橘色折線是對應的預測值()。可以觀察到:
- 前幾次 Burst 偏短(6, 4, 6, 4),預測值持續修正下調(10 → 8 → 6 → 5)
- 當連續出現長 Burst(13, 13, 13),預測值也相應上調(9 → 11 → 12)
- 預測值始終滯後於實際值,因為預測是基於過去的歷史資料
參數控制歷史與現狀的平衡:
| 值 | 效果 |
|---|---|
| ,完全忽略最近的 Burst,只看過去歷史 | |
| ,只看最近一次 Burst,完全忽略過去 | |
| 最近一次與過去歷史各佔一半(最常用) |
展開公式後可以看到,每個過去的 Burst 對預測值的貢獻都呈指數衰減:越久遠的 Burst 影響力越小。這正是「指數平均」名稱的來源。
搶占式 SJF:最短剩 餘時間優先 (Shortest-Remaining-Time-First, SRTF)
SJF 可以選擇實作為搶占式(Preemptive)或非搶占式(Nonpreemptive):
- 非搶占式 SJF:CPU 分配後,Process 一直執行到完成(或 I/O 等待)才重新排程
- 搶占式 SJF(SRTF):每當有新 Process 進入 Ready Queue,比較新 Process 的 Burst 長度與當前 Process 的剩餘時間,若新 Process 更短則搶占
下圖展示了四個 Process 在非搶占與搶占兩種模式下的 Gantt Chart:
上半部(Non-Preemptive SJF):所有 Process 在 t=0 到達,按 Burst 長度排序執行,平均等待時間為 7 ms。
下半部(SRTF):
- P1 在 t=0 開始執行,直到 t=1 有新 Process 到達
- P2 在 t=1 到達,(burst = 4 ms)短於 P1 目前剩餘時間(7 ms),因此搶占 P1。
- P3 在 t=2 到達,(burst = 9 ms)長於 P2 目前剩餘時間(4-1=3 ms),因此 P2 繼續執行。
- P4 在 t=3 到達,(burst = 5 ms),此時 P2 已執行了 3-1 = 2 ms,剩餘 4-2 = 2 ms,P4 較長,不搶占。P4 在 t=5 排在 P2 之後執行,等依此類推。
最終平均等待時間為 [(10-1) + (1-1) + (17-2) + (5-3)] / 4 = 6.5 ms。
如果在同樣的 Process 到達時間的情況下,Non-preemptive SJF 的平均等待時間會是 7.75 ms,可知 SRTF 的平均等待時間更小。