3. vruntime 计算与权重:nice 值到权重的映射表、vruntime 计算公式、load weight 对调度的影响
CFS 调度器的核心思想是“让每个进程公平地获得 CPU 时间”。为了实现这一目标,CFS 引入了 虚拟运行时间(vruntime) 的概念。vruntime 并非真实的墙上时钟时间,而是经过进程权重加权后的“标准化”时间。权重越高的进程,其 vruntime 增长越慢,从而在调度周期中获得更多的实际 CPU 时间。
3.1 nice 值与权重映射表
在 Linux 中,用户通过 nice 值(范围 -20 到 19)来调整进程的优先级。CFS 调度器内部并不直接使用 nice 值,而是将其映射为一个 权重(weight)。权重决定了进程在调度周期中应占用的 CPU 时间比例。
内核维护了一个全局的映射数组 sched_prio_to_weight,将每个 nice 值映射到对应的权重。权重值的设计遵循了“每提升一个 nice 值,CPU 时间减少约 10%”的经验法则。
关键概念: nice 值越低(优先级越高),权重越大;nice 值越高(优先级越低),权重越小。权重直接决定了进程的“时间片”分配比例。
nice 值与权重映射表(部分)
| nice 值 | 权重 (weight) | 相对权重比例 | 说明 |
|---|---|---|---|
| -20 | 88761 | 约 100% | 最高优先级,权重最大 |
| -10 | 11051 | 约 12.4% | 相比 -20 降低约 87.6% |
| 0 | 1024 | 约 1.15% | 默认优先级,基准权重 |
| 10 | 127 | 约 0.14% | 相比 0 降低约 87.6% |
| 19 | 15 | 约 0.017% | 最低优先级,权重最小 |
调优提示: 在 cgroup 配置中,可以通过
cpu.weight 或 cpu.weight.nice 直接控制 cgroup 的权重,而无需修改进程的 nice 值。这为容器化环境提供了更灵活的 CPU 资源分配手段。
3.2 vruntime 计算公式
vruntime 的计算是 CFS 调度器的核心。其基本公式如下:
vruntime += (实际运行时间 * NICE_0_LOAD) / 进程权重
其中:
- 实际运行时间:进程在 CPU 上真实运行的时间(单位:纳秒)。
- NICE_0_LOAD:nice 值为 0 时的基准权重,即 1024。
- 进程权重:由进程的 nice 值通过映射表查得的权重。
从公式可以看出:
- 权重越大的进程(nice 值越低),分母越大,vruntime 增长越慢。
- 权重越小的进程(nice 值越高),分母越小,vruntime 增长越快。
CFS 调度器总是选择 vruntime 最小 的进程来运行,从而确保所有进程在虚拟时间轴上保持“公平”。
注意: 实际内核实现中,为了避免浮点运算,使用了
calc_delta_fair() 函数进行定点数运算。上述公式是简化后的逻辑模型。
vruntime 计算流程
- 进程被调度到 CPU 上运行。
- 时钟中断或调度点触发时,记录进程的 实际运行时间。
- 调用
update_curr()函数,根据公式更新当前进程的 vruntime。 - 将更新后的 vruntime 与红黑树中其他进程的 vruntime 进行比较。
- 如果当前进程的 vruntime 不再是红黑树中最小的,则触发调度,选择新的进程运行。
3.3 load weight 对调度的影响
在 CFS 调度器中,负载权重(load weight) 是一个更广义的概念。它不仅包括进程自身的权重,还考虑了进程所在的调度组(如 cgroup)的权重。负载权重直接影响调度器的以下行为:
- 调度周期计算:调度周期(
sched_latency)会根据运行队列的总负载权重进行缩放。总负载权重越大,调度周期可能越长。 - 时间片分配:每个进程在调度周期内获得的时间片与其权重成正比。公式为:
时间片 = 调度周期 * (进程权重 / 总负载权重)。 - 负载均衡:在多核系统中,负载均衡器会根据每个 CPU 运行队列的总负载权重,将进程从高负载 CPU 迁移到低负载 CPU。
核心影响: 负载权重是 CFS 调度器进行 CPU 资源分配和负载均衡的“货币”。权重越高,获得的 CPU 时间越多,也越不容易被迁移到其他 CPU。
负载权重对调度的影响示意图
3.4 权重与 vruntime 的交互示例
假设有两个进程 A 和 B,运行在同一个 CPU 上:
- 进程 A:nice = 0,权重 = 1024
- 进程 B:nice = -5,权重 = 3121(约是 A 的 3 倍)
如果两个进程都运行了 10ms 的实际时间:
- 进程 A 的 vruntime 增加:
10ms * 1024 / 1024 = 10ms - 进程 B 的 vruntime 增加:
10ms * 1024 / 3121 ≈ 3.28ms
这意味着,在相同的实际运行时间内,高权重进程 B 的 vruntime 增长更慢。CFS 调度器会优先选择 vruntime 更小的进程,因此进程 B 会获得更多的 CPU 时间。
实践建议: 在 cgroup 配置中,通过调整
cpu.weight 可以精细控制多个 cgroup 之间的 CPU 分配比例。例如,将两个 cgroup 的权重分别设置为 1024 和 2048,则后者将获得前者两倍的 CPU 时间。
3.5 本章小结
| 概念 | 核心公式/映射 | 对调度的影响 |
|---|---|---|
| nice 值 → 权重 | sched_prio_to_weight[nice+20] |
nice 值越低,权重越大,CPU 时间越多 |
| vruntime 计算 | vruntime += (实际时间 * 1024) / 权重 |
权重越大,vruntime 增长越慢,越容易被调度 |
| 负载权重 | 运行队列上所有进程权重之和 | 影响调度周期、时间片分配、负载均衡 |