1. 调度器概述:Linux 调度器发展史与设计哲学
进程调度是 Linux 内核最核心的子系统之一,它决定了哪个进程在何时获得 CPU 使用权。调度器的设计直接影响系统的吞吐量、响应延迟和公平性。本章从历史演进、核心目标与设计哲学三个维度,为后续的性能调优与延迟测量奠定基础。
1.1 Linux 调度器发展史
Linux 调度器经历了四次重大重构,每一次都针对特定负载场景做出了关键改进。下表总结了四个主要版本的对比:
| 调度器 | 内核版本 | 时间复杂度 | 核心数据结构 | 主要缺陷 |
|---|---|---|---|---|
| O(n) 调度器 | 2.4 及更早 | O(n) | 全局运行队列 + 循环链表 | 多核扩展性差,交互体验糟糕 |
| O(1) 调度器 | 2.6.0 ~ 2.6.22 | O(1) | 140 级优先级位图 + 活跃/过期数组 | 交互进程提升不足,公平性粗糙 |
| CFS (完全公平调度器) | 2.6.23 ~ 6.6 | O(log n) | 红黑树 (vruntime 为 key) | 延迟敏感型负载仍有抖动 |
| EEVDF (最早虚拟截止时间优先) | 6.6+ (可选) | O(log n) | 红黑树 + 截止时间队列 | 仍在演进,生态尚未完全成熟 |
1.1.1 O(n) 调度器:简单但低效
在 Linux 2.4 及更早版本中,调度器使用一个全局运行队列,所有可运行进程被链接在一个循环链表中。每次调度时,内核需要遍历整个链表来计算每个进程的“好度”(goodness),选择得分最高的进程运行。
1.1.2 O(1) 调度器:常数时间的突破
Linux 2.6.0 引入的 O(1) 调度器是调度史上的里程碑。它使用两个优先级数组(active 和 expired),每个优先级对应一个链表。调度器通过位图在常数时间内找到最高优先级的非空队列,实现了 O(1) 的选进程操作。
1.1.3 CFS:完全公平调度器
Linux 2.6.23 合并了 CFS(Completely Fair Scheduler),由 Ingo Molnár 设计。CFS 摒弃了固定时间片和优先级数组,转而使用红黑树按虚拟运行时间(vruntime)排序,实现了“理想多任务处理器”的近似。
// CFS 核心数据结构简化示意
struct cfs_rq {
struct rb_root_cached tasks_timeline; // 红黑树根
u64 min_vruntime; // 最小虚拟运行时间
unsigned int nr_running; // 可运行进程数
u64 exec_clock; // 执行时钟
};
// 调度实体
struct sched_entity {
struct rb_node run_node; // 红黑树节点
u64 vruntime; // 虚拟运行时间
u64 sum_exec_runtime; // 实际执行时间
struct load_weight load; // 权重
};
1.1.4 EEVDF:最早虚拟截止时间优先
EEVDF(Earliest Eligible Virtual Deadline First)是 Linux 6.6 开始引入的可选调度器,由 Peter Zijlstra 实现。它在 CFS 的 vruntime 基础上增加了“截止时间”维度,更适合延迟敏感型工作负载。
1.2 调度器核心目标
无论调度器如何演进,其核心目标始终围绕以下四个维度:
- 公平性 (Fairness): 每个进程应获得与其优先级/权重成比例的 CPU 时间。
- 吞吐量 (Throughput): 单位时间内完成的有用工作量最大化。
- 响应延迟 (Response Latency): 交互式进程从就绪到获得 CPU 的时间应尽可能短。
- 可扩展性 (Scalability): 随着 CPU 核心数和进程数增加,调度开销应保持可控。
| 目标 | 衡量指标 | 典型瓶颈 | 调优方向 |
|---|---|---|---|
| 公平性 | vruntime 偏差、CPU 时间分配比例 | 权重设置不合理、组调度配置不当 | 调整 nice 值、使用 cgroup cpu.shares |
| 吞吐量 | 上下文切换次数、CPU 利用率 | 过度抢占、TLB 抖动 | 调整 sched_min_granularity、sched_wakeup_granularity |
| 响应延迟 | 调度延迟 (sched_latency)、唤醒延迟 | CFS 的 vruntime 累积、负载均衡延迟 | 使用 EEVDF、调整 sched_latency_ns |
| 可扩展性 | 调度器开销 vs 核心数曲线 | 运行队列锁争用、调度域层级过深 | 优化调度域拓扑、使用 NO_HZ_FULL |
1.3 设计哲学:从“时间片”到“虚拟时间”
Linux 调度器的设计哲学经历了根本性转变:
- O(n)/O(1) 时代: 基于固定时间片和静态优先级,调度器决定“谁该运行多久”。
- CFS 时代: 基于虚拟运行时间,调度器回答“谁最需要运行”。公平性由数学保证,而非人为设定的时间片。
- EEVDF 时代: 在公平性基础上引入截止时间约束,为延迟敏感负载提供确定性保证。
1.4 本章小结
Linux 调度器从 O(n) 到 EEVDF 的演进,反映了对公平性、延迟和可扩展性不断深化的理解。作为性能调优工程师,需要掌握:
- 不同调度器版本的核心数据结构和时间复杂度。
- CFS 的 vruntime 机制是理解调度延迟的基础。
- EEVDF 为延迟敏感负载提供了新的调优维度。
- 调度器的设计哲学决定了调优策略的边界。