2. Linux调度器基础
2.1 CFS 完全公平调度器核心原理
CFS(Completely Fair Scheduler)是 Linux 内核默认的进程调度器,自 2.6.23 版本引入。其设计哲学是:为每个可运行进程分配 CPU 时间的“公平份额”,而非传统的时间片轮转。CFS 基于一个简单的数学模型——虚拟运行时间(vruntime),来模拟“理想多任务处理器”。
CFS 使用红黑树(Red-Black Tree)作为调度队列的数据结构,以 vruntime 为键值组织所有可运行的调度实体。红黑树保证了插入、删除和查找操作的时间复杂度均为 O(log N),适合大规模进程调度场景。
2.2 调度实体(sched_entity)与运行队列(rq)
调度实体是 CFS 调度的基本单位,由 struct sched_entity 表示。每个进程(task_struct)内嵌一个调度实体,用于参与 CFS 调度。运行队列(runqueue, rq)是每个 CPU 核心上的调度数据结构,管理该 CPU 上所有可运行的调度实体。
| 数据结构 | 内核定义 | 关键字段 | 作用 |
|---|---|---|---|
| 调度实体 | struct sched_entity |
vruntime, load.weight, on_rq, run_node |
参与 CFS 调度的基本单元,记录虚拟运行时间和权重 |
| CFS 运行队列 | struct cfs_rq |
rb_root, nr_running, min_vruntime, load |
管理每个 CPU 上 CFS 调度类的可运行实体 |
| 全局运行队列 | struct rq |
cfs, rt, dl, curr, idle |
每个 CPU 一个,包含所有调度类的运行队列 |
| nice 值 | 权重 (weight) | 相对 nice=0 的 CPU 占比 | 典型场景 |
|---|---|---|---|
| -20 | 88761 | ≈ 86.7 倍 | 实时性要求极高的内核线程 |
| -10 | 11051 | ≈ 10.8 倍 | 音频/视频处理进程 |
| -5 | 3355 | ≈ 3.3 倍 | 交互式桌面应用 |
| 0 | 1024 | 1.0 倍(基准) | 普通用户进程 |
| 5 | 335 | ≈ 0.33 倍 | 后台编译任务 |
| 10 | 110 | ≈ 0.11 倍 | 低优先级批处理任务 |
| 19 | 15 | ≈ 0.015 倍 | 空闲任务、SETI@home |
2.4 调度延迟与权重的关系
CFS 的调度延迟(sched_latency)是保证所有可运行进程在一个周期内至少获得一次 CPU 时间的目标延迟。默认值为 6ms,可通过内核参数 kernel.sched_latency_ns 调整。
每个进程在一个调度周期内获得的 CPU 时间由权重决定:
进程获得的 CPU 时间 = 调度周期 × (进程权重 / 所有进程权重之和)
通过 perf sched 工具可以观测调度延迟的实际表现:
# 记录调度事件
perf sched record -- sleep 5
# 分析调度延迟统计
perf sched latency
# 查看具体调度细节
perf sched map
perf sched timehist
2.5 本章小结
- CFS 核心原理:基于 vruntime 的红黑树调度,每次选择 vruntime 最小的进程运行。
- 调度实体:
struct sched_entity是 CFS 调度的基本单位,内嵌于进程描述符中。 - 运行队列:每个 CPU 一个
struct rq,包含 cfs_rq 管理所有 CFS 调度实体。 - nice 值映射:nice 值通过指数公式映射为权重,权重决定 vruntime 增长速率和 CPU 时间分配。
- 调度延迟:默认 6ms 的调度周期保证公平性,进程数过多时受最小粒度限制。