3. 调度类体系结构:stop_sched_class、dl_sched_class、rt_sched_class、fair_sched_class、idle_sched_class 的层次与优先级

Linux 内核的调度器并非一个单一的调度算法,而是采用了一种 可扩展的调度类(Scheduling Class) 架构。这种设计允许内核同时支持多种调度策略,每种策略由对应的调度类实现。调度类按照严格的优先级顺序组织成一个链表,内核在每次选择下一个运行的任务时,会从最高优先级的调度类开始遍历,直到找到一个有就绪任务的调度类为止。

这种层次化的架构是 Linux 调度器灵活性和实时性的基石。本章将深入剖析五个核心调度类的层次结构、优先级关系以及各自的职责。

3.1 调度类层次结构总览

Linux 内核定义了五个调度类,按照优先级从高到低排列如下:

  1. stop_sched_class:停止调度类(最高优先级)
  2. dl_sched_class:Deadline 调度类
  3. rt_sched_class:实时调度类
  4. fair_sched_class:完全公平调度类(CFS)
  5. idle_sched_class:空闲调度类(最低优先级)

内核通过 struct sched_class 结构体来抽象每个调度类,该结构体包含了一系列函数指针,如 enqueue_task、dequeue_task、pick_next_task 等。所有调度类的实例通过 next 指针链接成一个单向链表。

核心机制: 调度器在选择下一个任务时,从 stop_sched_class 开始,依次调用每个调度类的 pick_next_task 方法。如果某个调度类返回了一个任务,则直接运行该任务,不再检查更低优先级的调度类。只有当所有高优先级调度类都没有就绪任务时,才会选择 idle_sched_class 中的空闲任务。

下面的 SVG 图展示了这五个调度类的层次结构与优先级关系:

Linux 调度类层次结构与优先级 stop_sched_class 优先级最高,用于停止 CPU dl_sched_class Deadline 调度类,严格保证截止时间 rt_sched_class 实时调度类(SCHED_FIFO / SCHED_RR) fair_sched_class 完全公平调度类(CFS) idle_sched_class 空闲调度类,运行 idle 任务 优先级说明 1. 从上到下优先级 依次降低 2. 高优先级调度类 有就绪任务时, 低优先级类不会 被选中 3. 每个调度类内部 有自己的调度 策略和算法 4. 调度类通过 struct sched_class 的 next 指针 链接成链表

3.2 各调度类详细解析

3.2.1 stop_sched_class(停止调度类)

优先级:最高(优先级 0)

职责:用于执行一些必须立即完成且不能被其他任务抢占的临界操作,例如 stop_machine 机制、CPU 热插拔、内核补丁(live patching)等。该调度类中的任务一旦被选中,将独占 CPU,直到任务主动完成。

  • 特点:不可抢占,不可睡眠,执行时间极短。
  • 典型场景:系统关机、CPU 上线/下线、ftrace 的某些跟踪点。
  • 实现:通常只有一个 per-CPU 的停止任务。
注意:普通用户进程永远不会被分配到 stop_sched_class。该调度类仅供内核内部使用,滥用会导致系统锁死或严重性能问题。

3.2.2 dl_sched_class(Deadline 调度类)

优先级:第二高

职责:实现 SCHED_DEADLINE 策略,基于 最早截止时间优先(Earliest Deadline First, EDF) 算法,结合 常数带宽服务器(Constant Bandwidth Server, CBS) 机制,为实时任务提供确定性的调度保证。

每个 SCHED_DEADLINE 任务需要指定三个参数:

参数 含义 说明
runtime 运行时间 每个周期内任务需要执行的最大时间
deadline 截止时间 任务必须在该时间点之前完成
period 周期 任务释放的周期

调度规则:内核始终选择 deadline 最早的任务来运行。CBS 机制确保每个任务不会消耗超过其声明的 runtime,从而保护其他实时任务。

性能调优提示:SCHED_DEADLINE 适用于对延迟有严格要求的硬实时场景,如音视频处理、工业控制。但参数设置不当(如 runtime 总和超过 period)会导致系统过载,调度器可能无法保证所有截止时间。

3.2.3 rt_sched_class(实时调度类)

优先级:第三高

职责:实现传统的 POSIX 实时调度策略:SCHED_FIFO 和 SCHED_RR。实时任务的优先级范围为 0(最高)到 99(最低)。

策略 行为 适用场景
SCHED_FIFO 先进先出,无时间片限制。高优先级任务会立即抢占低优先级任务。同一优先级的任务按队列顺序执行,直到主动让出 CPU 或阻塞。 需要极低延迟且执行时间可预测的实时任务
SCHED_RR 时间片轮转。与 SCHED_FIFO 类似,但同一优先级的任务会按时间片轮流执行。 需要公平性的软实时任务
关键区别:SCHED_FIFO 任务如果不主动让出 CPU(例如通过 sched_yield 或阻塞),可以无限期运行,这可能导致低优先级任务(包括 CFS 任务)饿死。SCHED_RR 通过时间片机制避免了同一优先级内的饿死问题,但无法解决不同优先级之间的饿死。

3.2.4 fair_sched_class(完全公平调度类)

优先级:第四高

职责:实现 完全公平调度器(Completely Fair Scheduler, CFS),这是 Linux 默认的调度类,用于普通进程(SCHED_NORMAL / SCHED_BATCH / SCHED_IDLE)。

CFS 的核心思想是模拟一个“理想的多任务处理器”,为每个任务分配一个 虚拟运行时间(vruntime)。调度器总是选择 vruntime 最小的任务来运行,从而保证所有任务获得公平的 CPU 时间份额。

  • nice 值:影响任务的权重,范围 -20 到 +19。nice 值越低,权重越高,获得的 CPU 时间越多。
  • 调度粒度:CFS 使用 targeted_latency 和 min_granularity 来控制调度延迟和切换开销。
  • 组调度:支持将任务分组,在组之间进行公平分配。
性能调优提示:对于交互式应用(如桌面、Web 服务器),可以适当减小 targeted_latency(通过 /proc/sys/kernel/sched_latency_ns)来降低调度延迟。对于批处理任务,可以增大 min_granularity 来减少上下文切换开销。

3.2.5 idle_sched_class(空闲调度类)

优先级:最低

职责:当所有其他调度类都没有就绪任务时,内核选择 idle_sched_class 中的 per-CPU 空闲任务(idle task) 来运行。空闲任务通常执行 halt 或 mwait 指令,使 CPU 进入低功耗状态,直到下一个中断到来。

  • 特点:优先级最低,永远不会被主动调度,只有在没有其他任务可运行时才会被选中。
  • 统计:空闲任务的运行时间通常被用作 CPU 空闲率的指标。
  • 实现:每个 CPU 都有一个独立的空闲任务,其 task_struct 在系统初始化时创建。

3.3 调度类优先级与任务选择流程

下面的 SVG 流程图展示了内核在 pick_next_task 函数中如何遍历调度类链表并选择下一个任务:

pick_next_task 调度类遍历流程 开始选择任务 检查 stop_sched_class 有就绪任务? 是 → 运行该任务 检查 dl_sched_class 有就绪任务? 是 → 运行该任务 检查 rt_sched_class 有就绪任务? 是 → 运行该任务 检查 fair_sched_class 有就绪任务? 流程说明 1. 从最高优先级 调度类开始检查 2. 如果当前调度类 有就绪任务, 直接返回该任务 3. 如果没有,则 继续检查下一个 调度类 4. 如果所有调度类 都没有就绪任务, 最终选择 idle 调度类的空闲任务

3.4 调度类优先级对比总结

下表总结了五个调度类的优先级、调度策略、典型用途和关键特性:

调度类 优先级 调度策略 典型用途 关键特性
stop_sched_class 最高 内核内部专用 CPU 热插拔、stop_machine 不可抢占,独占 CPU
dl_sched_class 2 SCHED_DEADLINE (EDF + CBS) 硬实时任务、音视频处理 截止时间保证,带宽隔离
rt_sched_class 3 SCHED_FIFO / SCHED_RR 软实时任务、工业控制 静态优先级,可抢占 CFS
fair_sched_class 4 CFS (SCHED_NORMAL / BATCH / IDLE) 普通用户进程、服务器负载 公平分配,基于 vruntime
idle_sched_class 最低 空闲任务 CPU 空闲时运行 最低优先级,低功耗

3.5 调度类与调度策略的映射关系

用户态通过 sched_setscheduler 系统调用为进程设置调度策略,内核将这些策略映射到对应的调度类:

  • SCHED_NORMAL (SCHED_OTHER) → fair_sched_class
  • SCHED_BATCH → fair_sched_class(批处理风格,减少唤醒抢占)
  • SCHED_IDLE → fair_sched_class(极低优先级,仅在 CPU 空闲时运行)
  • SCHED_FIFO → rt_sched_class
  • SCHED_RR → rt_sched_class
  • SCHED_DEADLINE → dl_sched_class
重要结论:调度类的层次结构确保了实时任务(SCHED_FIFO/RR/DEADLINE)能够优先于普通任务(SCHED_NORMAL)获得 CPU 资源。这种设计使得 Linux 能够在同一个系统中同时运行实时应用和通用负载,而实时应用不会因为普通进程的干扰而错过截止时间。