sched_fair 调度:负载权重、虚拟运行时间与最小虚拟时间
在 Linux 的 CFS(Completely Fair Scheduler,完全公平调度器) 中,负载权重(Load Weight)、虚拟运行时间(vruntime)、最小虚拟时间(min_vruntime) 是三个核心概念,共同决定了任务的调度优先级和 CPU 时间的公平分配。下面详细解析它们的含义、计算方式及相互关系。
1. 负载权重(Load Weight)
(1)作用
- 决定任务对 CPU 时间的“占有比例”,权重越高,获得的 CPU 时间越多。
- 由任务的
nice
值映射而来(nice
值越低,权重越高)。
(2)计算方式
- Linux 使用
sched_prio_to_weight
数组将nice
值(-20 到 19)映射为权重:static const int sched_prio_to_weight[40] = {/* -20 */ 88761, 71755, 56483, ..., 1024, 820, 655, 526, 423, 335, 272 };
nice=0
的权重为 1024(基准值)。nice=-20
(最高优先级)的权重为 88761,是nice=0
的 86.5 倍。nice=19
(最低优先级)的权重为 15,仅为nice=0
的 1.5%。
(3)对调度的影响
- 任务运行时间计算:
- 任务实际获得的 CPU 时间 = 调度周期 × (任务权重 / 所有任务权重之和)。
- 例如:若系统有两个任务,权重分别为 1024(
nice=0
)和 820(nice=1
),则nice=0
的任务获得1024 / (1024 + 820) ≈ 55.5%
的 CPU 时间。
2. 虚拟运行时间(vruntime)
(1)作用
- 量化任务的“公平性”,用于在红黑树中排序任务(选择
vruntime
最小的任务运行)。 - 任务运行时,
vruntime
会增长,增长速率与权重成反比(高权重任务增长更慢,低权重任务增长更快)。
(2)计算方式
- 公式:
vruntime += (实际运行时间 Δt) × (NICE_0_LOAD / 任务权重)
NICE_0_LOAD
是nice=0
的基准权重(1024)。- 示例:
nice=0
的任务运行 10ms:vruntime += 10 × (1024 / 1024) = 10
。nice=5
(权重=335)的任务运行 10ms:vruntime += 10 × (1024 / 335) ≈ 30.6
。
(3)调度决策
- CFS 维护一个按
vruntime
排序的红黑树,每次pick_next_task
选择vruntime
最小的任务执行。 - 公平性体现:
- 高权重任务
vruntime
增长慢,能更频繁地被调度。 - 低权重任务
vruntime
增长快,减少 CPU 占用。
- 高权重任务
3. 最小虚拟时间(min_vruntime)
(1)作用
- 跟踪 CFS 运行队列(
cfs_rq
)中所有任务的最小vruntime
,用于:- 新任务的初始化:新创建的或唤醒的任务的
vruntime
会被设置为min_vruntime
,避免“饿死”新任务。 - 跨 CPU 迁移:任务迁移时,
vruntime
会根据目标 CPU 的min_vruntime
调整,维持全局公平。
- 新任务的初始化:新创建的或唤醒的任务的
(2)更新规则
- 当任务被加入/移除运行队列时,
min_vruntime
会更新:min_vruntime = max(min_vruntime, 当前任务的 vruntime);
- 示例:
- 若队列中任务的最小
vruntime
是 1000,新任务的vruntime=1200
,则min_vruntime
仍为 1000。 - 若新任务的
vruntime=800
,则min_vruntime
更新为 800。
- 若队列中任务的最小
(3)对任务迁移的影响
- 当任务从一个 CPU 迁移到另一个 CPU 时,其
vruntime
会按目标 CPU 的min_vruntime
调整:vruntime_new = vruntime_old - min_vruntime_old + min_vruntime_new
- 防止任务因迁移获得不公平的优势或劣势。
4. 三者的协同工作流程
场景示例
-
任务 A(
nice=0
)和任务 B(nice=5
)启动:- 权重:A=1024,B=335。
- 初始
vruntime
均为 0(或min_vruntime
)。
-
调度周期 20ms:
- 任务 A 运行:
10ms
→vruntime += 10 × (1024/1024) = 10
。 - 任务 B 运行:
10ms
→vruntime += 10 × (1024/335) ≈ 30.6
。
- 任务 A 运行:
-
下一轮调度:
- 任务 A 的
vruntime=10
,任务 B 的vruntime=30.6
→ 选择vruntime
更小的 A 运行。
- 任务 A 的
-
长期效果:
- 任务 A 获得约 75% CPU 时间,任务 B 获得约 25%(符合权重比例)。
5. 关键数据结构
(1)task_struct
相关字段
struct task_struct {struct sched_entity se; // 调度实体// ...
};struct sched_entity {u64 vruntime; // 虚拟运行时间struct load_weight load; // 负载权重(含 weight 字段)// ...
};
(2)cfs_rq
运行队列
struct cfs_rq {u64 min_vruntime; // 当前队列的最小 vruntimestruct rb_root tasks_timeline; // 按 vruntime 排序的红黑树// ...
};
6. 总结
概念 | 作用 | 关键影响 |
---|---|---|
负载权重 | 决定任务获取 CPU 时间的比例 | nice 值 → 权重 → 时间分配 |
虚拟运行时间 | 量化任务的“公平性”并排序 | 红黑树中选择 vruntime 最小的任务 |
最小虚拟时间 | 维护全局公平性,处理新任务和迁移 | 防止新任务饿死,调整迁移任务的公平性 |
CFS 通过这三者的配合,实现了:
- 按权重分配 CPU 时间(高
nice
任务少占 CPU)。 - 动态公平调度(
vruntime
确保长期公平)。 - 多核一致性(
min_vruntime
处理跨 CPU 迁移)。