Linux CFS 调度器深度实战:从 vruntime 红黑树到 NUMA 感知的多核负载均衡
一、调度器演进与设计哲学
Linux 内核的进程调度器经历了从简单的 O(n) 调度器到 O(1) 调度器,再到 Completely Fair Scheduler (CFS) 的演进。CFS 自 2.6.23 版本引入内核主线以来,彻底改变了 Linux 对 CPU 时间分配的基本假设——它不维护传统的 runqueue 和时间片概念,而是基于"虚拟运行时间"(virtual runtime, vruntime)实现"完全公平"的调度理念。
CFS 由 Ingo Molnár 提出,其核心思想简洁而深刻:如果一个调度器能在一台拥有 N 个 CPU 核心的机器上,让每个可运行任务获得 1/N 的 CPU 时间,那么这个调度器是"完全公平"的。为了逼近这个理想模型,CFS 引入了以下关键抽象:
- virtual runtime (vruntime):每个调度实体维护一个单调递增的虚拟时间戳,记录它在虚拟时钟下的累计运行时间
- 红黑树 (rbtree):所有可运行任务按 vruntime 排序在红黑树上,最左侧节点即 vruntime 最小的任务,也就是"最亏欠" CPU 的任务
- min_vruntime:每个 cgroup 和 CPU runqueue 维护一个最小 vruntime 基准,用于新任务的初始化和负载均衡时的偏移校正
二、调度实体与 vruntime 计算
CFS 的核心数据结构是 sched_entity,它是 task_struct 的内嵌字段。每个调度实体包含以下关键字段:
struct sched_entity {
struct load_weight load; // 调度权重
struct rb_node run_node; // 红黑树节点
u64 exec_start; // 本次开始执行时间
u64 sum_exec_runtime; // 累计实际运行时间
u64 vruntime; // 虚拟运行时间
u64 prev_sum_exec_runtime; // 上次切换时累计时间
// ...
};
vruntime 的计算公式直接体现了"公平"的权重分配思想:
delta_exec = now - entity->exec_start; // 实际执行时间段
delta_exec_weighted = delta_exec * (NICE_0_LOAD / entity->load.weight);
entity->vruntime = delta_exec_weighted;
其中关键因子 NICE_0_LOAD / entity-

发表评论 取消回复