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 计算流程

  1. 进程被调度到 CPU 上运行。
  2. 时钟中断或调度点触发时,记录进程的 实际运行时间。
  3. 调用 update_curr() 函数,根据公式更新当前进程的 vruntime。
  4. 将更新后的 vruntime 与红黑树中其他进程的 vruntime 进行比较。
  5. 如果当前进程的 vruntime 不再是红黑树中最小的,则触发调度,选择新的进程运行。

3.3 load weight 对调度的影响

在 CFS 调度器中,负载权重(load weight) 是一个更广义的概念。它不仅包括进程自身的权重,还考虑了进程所在的调度组(如 cgroup)的权重。负载权重直接影响调度器的以下行为:

  • 调度周期计算:调度周期(sched_latency)会根据运行队列的总负载权重进行缩放。总负载权重越大,调度周期可能越长。
  • 时间片分配:每个进程在调度周期内获得的时间片与其权重成正比。公式为:时间片 = 调度周期 * (进程权重 / 总负载权重)。
  • 负载均衡:在多核系统中,负载均衡器会根据每个 CPU 运行队列的总负载权重,将进程从高负载 CPU 迁移到低负载 CPU。
核心影响: 负载权重是 CFS 调度器进行 CPU 资源分配和负载均衡的“货币”。权重越高,获得的 CPU 时间越多,也越不容易被迁移到其他 CPU。

负载权重对调度的影响示意图

负载权重对 CFS 调度的影响 负载权重对 CFS 调度的影响 进程权重 (nice 值映射) 总负载权重 (进程 + cgroup) 调度决策 (时间片/均衡) 调度周期 总负载越大,周期越长 sched_latency 缩放 时间片分配 时间片 = 周期 × (权重/总负载) 权重越高,时间片越大 负载均衡 按总负载迁移进程 高负载 → 低负载 CPU 负载权重对调度的影响总结 • 进程权重:由 nice 值决定,是负载权重的“原子单位”。 • 总负载权重:运行队列上所有进程(包括 cgroup 组)的权重之和。 • 调度影响:总负载权重越大,调度周期越长,单个进程的时间片可能被压缩。 • 均衡影响:负载均衡器以总负载权重为基准,确保各 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 增长越慢,越容易被调度
负载权重 运行队列上所有进程权重之和 影响调度周期、时间片分配、负载均衡