跳轉到

2007 - CFS 排班與 2.6.23(紅黑樹取代啟發式)#

案件摘要#

2007 年 10 月,Linux 2.6.23 發布,帶入 Ingo Molnar 的 CFS(Completely Fair Scheduler,完全公平排班器)—— 用紅黑樹取代 O(1) 排班器的啟發式互動偵測:

\[ \text{O(1)(啟發式:猜誰是互動行程)} \;\xrightarrow{\text{CFS}}\; \text{CFS(數學式:vruntime 最小者先跑)}. \]

理論純潔性戰勝啟發式——CFS 用「虛擬執行時間(vruntime)」把公平性變成數學問題。

前因 -- 為什麼會有這個案子#

  • O(1) 排班器的缺陷:O(1) 用複雜的啟發式判斷「哪些行程是互動式」(要給高優先度)——啟發式在桌面遊戲、影音編碼混跑時猜錯,互動卡頓——彌補的缺陷:啟發式不可靠。
  • Con Kolivas 的刺激:桌面玩家 Kolivas 的 RDSL/staircase 排班器(2006–07)在桌面互動上表現更好,但 Torvalds 不採用——Molnar 受刺激後一個週末寫出 CFS。
  • 理論基礎:理想公平排班(ideal fair scheduling)——若有無限細的時間粒度,每個可執行行程應獲得 1/n 的 CPU。CFS 用 vruntime 追蹤「誰領得少」。

線索與推理 -- CFS 的數學#

第一條線索:vruntime——公平的會計#

\[ \text{vruntime} += \Delta t \times \frac{NICE_0\text{-weight}}{\text{weight}(p)}. \]
  • 每個行程有 vruntime(虛擬執行時間);排班永遠選 vruntime 最小者——紅黑樹左下角 O(log n) 取得。
  • 彌補的缺陷:啟發式「猜互動」→ 數學式「誰領得最少就給誰」——公平性從猜測變成計算。

第二條線索:紅黑樹——O(log n) 的排班#

\[ \text{紅黑樹(self-balancing BST)}:\; \text{插入/刪除/最小值} = O(\log n). \]
  • 彌補的缺陷:O(1) 用 140 個佇列(優先度陣列)——粒度固定且僵硬;紅黑樹允許任意權重的動態排班。

第三條線索:群組排班(group scheduling)#

CFS 支援按使用者/群組分配:使用者 A 的 100 個行程與使用者 B 的 1 個行程,各得 50%—— - 彌補的缺陷:一個使用者開 100 個行程霸佔 CPU——公平性推廣到「群組」層級(後來與 cgroups 整合)。

C 範例:CFS 的核心資料結構#

/* kernel/sched_fair.c (2007) 風格 */
struct sched_entity {
    struct rb_node run_node;     /* 紅黑樹節點 */
    u64 vruntime;                /* 虛擬執行時間——公平的會計 */
    u64 sum_exec_runtime;        /* 實際執行時間 */
};

static struct sched_entity *
pick_next_entity(struct cfs_rq *cfs_rq) {
    /* 紅黑樹最左節點 = vruntime 最小 = 下一個跑 */
    struct rb_node *left = cfs_rq->rb_leftmost;
    return rb_entry(left, struct sched_entity, run_node);
}

Python 範例:CFS 的模擬#

from sortedcontainers import SortedList

class CFS:
    def __init__(self):
        self.tree = SortedList(key=lambda e: e["vruntime"])

    def enqueue(self, pid, weight):
        self.tree.add({"pid": pid, "weight": weight, "vruntime": 0})

    def schedule(self, dt=1.0):
        ent = self.tree[0]                      # vruntime 最小者先跑
        ent["vruntime"] += dt * 1024 / ent["weight"]
        self.tree.remove(ent); self.tree.add(ent)  # 重新插入平衡樹
        return ent["pid"]

cfs = CFS()
cfs.enqueue("nice0", 1024); cfs.enqueue("nice-5", 3121)
for _ in range(6):
    print(cfs.schedule())
# nice-5(權重高)被排班的次數多——公平按權重分配

Shell 範例:nice 權重的實證#

# nice 值影響 CFS 權重:nice -5 ≈ 3 倍 CPU 份額
$ nice -n -5 ffmpeg -i video.avi out.mp4 &   # 高權重:編碼加速
$ nice -n 19 updatedb &                       # 低權重:背景雜務讓路
$ top -b -n1 | head -8                        # 觀察 %CPU 份額

結案 -- 後果與影響#

  • 桌面互動的解決:CFS 讓「背景重負載 + 前景即時互動」共存——Linux 桌面可用性大躍進。
  • 理論的勝利:CFS 證明「數學公平」比「啟發式猜測」更可靠——後續 EEVDF(2024,6.6 排班器)進一步加入延遲敏感度。
  • Kolivas 的平行宇宙:他的 MuQSS(2016)持續服務桌面玩家社群——啟發式派與數學派的百年角力。
  • 歷史教訓:當啟發式開始猜錯,就該回到數學——CFS 是排班理論(公平共享)在實戰的勝利。

關鍵人物與文獻#

  • I. Molnar:CFS (2007, 2.6.23)。
  • C. Kolivas:RDSL/staircase (2006–07)、MuQSS (2016)。
  • C. A. Waldspurger:lottery/stride scheduling(1995,CFS 的理論先驅)。
  • 相關案件:2003-2.6核心革命.md、2008-cgroups與LXC.md。