1. 调度器概述:Linux CFS调度器设计哲学、核心数据结构

1.1 CFS 设计哲学:公平不是时间片,而是权重比例

完全公平调度器(Completely Fair Scheduler, CFS)自 Linux 2.6.23 引入,取代了传统的 O(1) 调度器。其核心设计哲学可概括为:“在理想的多任务处理器上,每个任务应获得相等的 CPU 时间比例”。

CFS 摒弃了固定时间片(time slice)模型,转而采用 虚拟运行时间(vruntime) 作为调度决策的唯一依据。vruntime 越小,表示任务被“亏待”得越多,调度器就越倾向于选择它。

核心思想:CFS 不追求每个任务运行相同的时间长度,而是追求在任意时间窗口内,每个任务获得的 CPU 时间比例与其权重(nice 值)成正比。权重高的任务(低 nice 值)获得更多时间,权重低的任务(高 nice 值)获得更少时间。

1.2 核心数据结构:调度器的骨架

CFS 的调度决策依赖于三个核心数据结构,它们构成了调度器的“骨架”:rq(运行队列)、cfs_rq(CFS 运行队列)和 sched_entity(调度实体)。

数据结构 所属层级 核心作用 关键字段(示例)
rq 每个 CPU 一个 管理该 CPU 上所有调度类的任务(CFS、RT、DL) nr_running, cfs, rt, dl
cfs_rq 每个 CPU 一个(嵌套于 rq) 管理该 CPU 上所有 CFS 调度实体的红黑树 rb_root, min_vruntime, nr_running
sched_entity 每个任务一个(可嵌套) 表示一个可被调度的实体(任务或任务组) vruntime, load, on_rq, parent
关键理解:sched_entity 是 CFS 调度的基本单元。一个进程(task_struct)内嵌一个 sched_entity 实例。当启用 CPU 控制组(cgroup)时,一个任务组也可以作为一个 sched_entity 参与调度,实现组间公平。

1.3 数据结构关系:从 CPU 到任务

下图展示了 rq、cfs_rq 和 sched_entity 之间的层级关系与数据流向。

CFS 核心数据结构关系图 CFS 核心数据结构关系图 rq (per-CPU) nr_running: 5 cfs, rt, dl 子队列 cfs_rq min_vruntime: 1024 rb_root (红黑树) sched_entity vruntime: 2048 load: 1024 (nice=0) 包含 管理 vruntime: 512 vruntime: 1024 vruntime: 2048 数据结构关系说明: 1. 每个 CPU 拥有一个独立的 rq,rq 内嵌 cfs_rq、rt_rq、dl_rq。 2. cfs_rq 使用红黑树组织所有可运行的 sched_entity,按 vruntime 排序。 3. 红黑树最左节点(vruntime 最小)即为下一个被选中的任务。 4. sched_entity 可嵌套:任务组作为父实体,组内任务作为子实体。

1.4 vruntime:调度决策的“货币”

vruntime 是 CFS 调度决策的核心指标。其计算方式决定了调度的公平性:

// 核心计算公式(简化)
vruntime += (delta_exec * NICE_0_LOAD) / se->load.weight;

// 其中:
// delta_exec: 实际运行的物理时间
// NICE_0_LOAD: nice=0 时的标准权重(1024)
// se->load.weight: 当前调度实体的权重(由 nice 值决定)
重要推论:权重越高的任务(低 nice 值),其 load.weight 越大,vruntime 增长越慢,因此更容易被调度器选中,获得更多 CPU 时间。反之,权重越低(高 nice 值),vruntime 增长越快,被调度的机会越少。

1.5 调度实体权重与 nice 值映射

nice 值范围是 [-20, 19],映射到权重数组 sched_prio_to_weight。下表展示了部分映射关系:

nice 值 权重 (load.weight) 相对 nice=0 的 CPU 时间比例 典型场景
-20 88761 约 86.7 倍 实时音频处理
-10 11051 约 10.8 倍 交互式应用
0 1024 1 倍(基准) 普通后台任务
10 304 约 0.3 倍 批处理任务
19 15 约 0.015 倍 低优先级清理任务

1.6 调度周期与粒度控制

CFS 通过 sched_latency_ns 和 sched_min_granularity_ns 两个内核参数控制调度行为:

  • sched_latency_ns(默认 6ms):CFS 期望在一个调度周期内,所有可运行任务至少被调度一次。
  • sched_min_granularity_ns(默认 0.75ms):任务被调度后至少运行的时间,防止过于频繁的上下文切换。

当可运行任务数量增多时,每个任务获得的时间片 = sched_latency_ns / nr_running。如果该值小于 sched_min_granularity_ns,则使用 sched_min_granularity_ns * nr_running 作为实际调度周期。

性能调优启示:在跨 CPU 域迁移与负载均衡场景中,sched_latency_ns 和 sched_min_granularity_ns 直接影响任务在 CPU 之间的迁移频率和调度延迟。过小的粒度会导致频繁迁移,增加缓存失效开销;过大的粒度则可能导致负载不均。

1.7 本章小结

CFS 调度器通过 vruntime 和 红黑树 实现了 O(log n) 的调度复杂度,同时保证了公平性。核心数据结构 rq、cfs_rq、sched_entity 构成了调度器的骨架,为后续的负载均衡和跨 CPU 域迁移提供了基础。理解这些概念是调试调度器内核参数的前提。