2、Linux调度器概述:CFS调度器、实时调度器、调度类与调度策略简介
Linux内核调度器是操作系统的核心组件,负责在多个可运行的任务之间分配CPU时间,以最大化系统吞吐量、最小化响应延迟,并满足不同工作负载的优先级与实时性要求。在组调度(Group Scheduling)与CPU域迁移(Domain Migration)场景下,理解调度器的内部架构与策略是进行参数调优的前提。
本章将系统介绍Linux调度器的三大核心组件:CFS调度器(完全公平调度器)、实时调度器(RT调度器),以及它们所依托的调度类与调度策略体系。我们将通过结构图、对比表格和代码示例,帮助您建立清晰的调度器知识框架。
2.1 调度器整体架构
Linux内核采用模块化的调度类(Scheduling Class)架构,每个调度类负责管理一组具有相同调度策略的任务。调度器在每次进程切换时,按照优先级从高到低的顺序遍历调度类,选择下一个要运行的任务。
struct sched_class 结构体统一接口,使得不同调度算法可以共存于同一系统中。
2.2 调度类与调度策略
每个调度类支持一种或多种调度策略(Scheduling Policy)。调度策略决定了任务在所属调度类中的具体行为规则,例如时间片分配方式、优先级计算方法和抢占条件。
| 调度类 | 调度策略 | 策略标识 | 典型用途 |
|---|---|---|---|
| 停止类 | — | — | CPU热插拔、migration线程(不可被抢占) |
| 实时类 (RT) | 先进先出 | SCHED_FIFO |
硬实时任务,时间敏感型控制 |
| 实时类 (RT) | 轮转 | SCHED_RR |
软实时任务,同优先级时间片轮转 |
| CFS类 | 普通公平 | SCHED_NORMAL (SCHED_OTHER) |
普通用户进程、后台服务 |
| CFS类 | 批处理 | SCHED_BATCH |
非交互批处理任务,减少唤醒抢占 |
| CFS类 | 空闲 | SCHED_IDLE |
极低优先级后台任务 |
| 空闲类 | — | — | 每个CPU的空闲线程(pid=0) |
SCHED_NORMAL 策略的CFS任务受 cpu.shares 控制组权重影响。实时任务(SCHED_FIFO/SCHED_RR)则受 cpu.rt_runtime_us 和 cpu.rt_period_us 限制,防止实时任务饿死CFS任务。
2.3 CFS调度器:完全公平调度
CFS(Completely Fair Scheduler)是Linux内核自2.6.23版本以来的默认调度器,其设计哲学是“模拟一个理想的多任务处理器”——在理想情况下,每个任务应获得完全相等的CPU时间比例。
2.3.1 核心数据结构:红黑树
CFS使用红黑树(Red-Black Tree)来组织可运行队列(runqueue)中的任务。每个任务对应一个调度实体(struct sched_entity),其键值为虚拟运行时间(vruntime)。
- vruntime:任务的虚拟运行时间,计算公式为
vruntime += 实际运行时间 × (NICE_0_LOAD / 任务权重)。 - 权重(weight):由任务的nice值决定,nice值越低,权重越高,vruntime增长越慢,从而获得更多CPU时间。
- 选择逻辑:每次调度时,CFS选择红黑树中最左侧节点(即vruntime最小的任务)运行。
2.3.2 CFS关键参数
| 参数名 | 默认值 | 说明 | 调优影响 |
|---|---|---|---|
sched_latency_ns |
6,000,000 (6ms) | 目标调度延迟,CFS期望在此时间内让所有可运行任务至少运行一次 | 减小可降低交互延迟,增大可提升吞吐 |
sched_min_granularity_ns |
750,000 (0.75ms) | 任务最小运行时间片,防止频繁上下文切换 | 增大可减少切换开销,但可能增加延迟 |
sched_wakeup_granularity_ns |
1,000,000 (1ms) | 唤醒抢占粒度,控制唤醒任务能否抢占当前任务 | 增大可减少抢占次数,适合CPU密集型负载 |
sched_nr_migrate |
32 | 负载均衡时一次最多迁移的任务数 | 影响CPU域迁移的批量大小 |
2.4 实时调度器:RT调度器
实时调度器负责管理 SCHED_FIFO 和 SCHED_RR 策略的任务。与CFS不同,RT调度器使用静态优先级(范围1~99,数值越大优先级越高),且任务不会被低优先级的CFS任务抢占。
2.4.1 SCHED_FIFO 与 SCHED_RR 对比
| 特性 | SCHED_FIFO | SCHED_RR |
|---|---|---|
| 时间片 | 无时间片限制,运行直到主动让出CPU或阻塞 | 有固定时间片(默认100ms),用完后同优先级轮转 |
| 抢占条件 | 仅被更高优先级的FIFO/RR任务抢占 | 同优先级时间片耗尽,或被更高优先级任务抢占 |
| 适用场景 | 需要持续占用CPU的实时任务(如中断处理线程) | 多个同优先级实时任务需要公平分享CPU |
| 组调度支持 | 支持,通过 cpu.rt_runtime_us 限制组总RT时间 |
支持,与FIFO共享同一限制 |
cpu.rt_runtime_us 设置组级别的实时时间上限,或使用 kernel.sched_rt_runtime_us 全局限制(默认950ms/1s周期)。
2.5 调度类与组调用的交互
在组调度(cgroup v1 cpu 子系统或 v2 cpu 控制器)场景下,调度类的工作方式发生重要变化:
- CFS组调度:每个cgroup被抽象为一个
task_group,内部包含自己的CFS红黑树。CPU时间先在组之间按cpu.shares比例分配,再在组内任务之间按CFS规则分配。 - RT组调度:每个
task_group有独立的RT带宽限制(cpu.rt_runtime_us),组内所有实时任务共享该带宽。 - CPU域迁移:负载均衡器在遍历调度域时,会考虑每个cgroup的负载分布,将任务(或整个cgroup的负载)在CPU之间迁移。
2.6 调度策略选择与性能影响
在组调度与CPU域迁移调优场景中,选择合适的调度策略直接影响参数配置效果:
- 延迟敏感型负载(如数据库、Web服务器):使用
SCHED_NORMAL,配合较小的sched_latency_ns和sched_min_granularity_ns。 - CPU密集型批处理(如科学计算、视频编码):使用
SCHED_BATCH,减少唤醒抢占,提升缓存亲和性。 - 硬实时控制(如工业机器人、音频处理):使用
SCHED_FIFO,配合cgroup RT带宽限制防止系统锁死。 - 后台低优先级任务(如日志清理、备份):使用
SCHED_IDLE,仅在CPU空闲时运行。
/proc/sys/kernel/sched_* 参数和cgroup配置来优化这些行为。