Lazy loaded imageOS06 调度

type
status
date
slug
summary
tags
category
icon
password

调度(Scheduling)

调度是操作系统的核心功能之一,负责在多个竞争任务之间分配有限的 CPU 资源。随着计算场景从单用户单任务演变为多用户、多线程、多核乃至分布式环境,调度策略也日益复杂,需兼顾公平性吞吐量响应延迟资源利用率。本章系统梳理经典调度算法、现代调度器设计及前沿研究进展。

对调度的研究:从简化假设到现实复杂性

早期调度理论基于若干简化假设
  • 一个用户只运行一个程序;
  • 一个程序只有一个线程;
  • 程序彼此独立。
这些假设使“公平性”等指标易于定义(如对程序公平 vs 对用户公平)。然而,现实系统需应对:
  • 硬件多样性:单核、多核、多 CPU、GPU 集群;
  • 应用类型差异:桌面交互、Web 服务、大数据处理、大语言模型(LLM)推理;
  • 任务行为特征:典型程序呈现 CPU Burst 模式——短时间(通常 < 8ms)密集使用 CPU,随后长时间 I/O 阻塞。
调度器需识别任务类型并动态调整策略,以优化整体性能。

调度的评价指标

指标
定义
优化目标
周转时间(Turnaround Time)
任务从提交到完成的总时间
最小化平均/中位数
Makespan
一批任务全部完成所需时间
最小化
响应时间(Response Time)
任务从提交到首次执行的时间
最小化(尤其交互式应用)
吞吐量(Throughput)
单位时间内完成的任务数
最大化
公平性(Fairness)
资源在用户/任务间的合理分配
避免饥饿,保障服务质量
吞吐量优化常通过减少上下文切换开销、高效利用缓存实现;公平性需权衡用户 vs 任务视角。

经典调度算法

FCFS(First-Come, First-Served)

  • 机制:按到达顺序执行,运行至阻塞或结束。
  • 问题
    • 队头阻塞(HOL Blocking):长任务阻塞后续短任务;
    • 护航效应(Convoy Effect):I/O 密集型任务被 CPU 密集型任务拖慢。
  • 适用性:简单但性能差,仅适用于批处理场景。

RR(Round Robin)

  • 机制:每个任务分配固定时间片(quantum,通常 10–100ms),时间片结束时抢占并移至队尾。
  • 特性
    • 最大等待时间 ≤ (n-1) × q(n 为任务数);
    • 时间片 q 需远大于上下文切换开销(否则吞吐量下降);
    • q 过小 → 频繁切换、缓存失效;q 过大 → 退化为 FCFS。
  • 优缺点
    • ✅ 公平性好,无饥饿;
    • ❌ 长任务性能差,缓存命中率低于 FCFS。

严格优先级调度(Strict Priority Scheduling)

  • 机制:始终运行最高优先级任务;同优先级任务 RR 调度。
  • 问题
    • 低优先级任务饥饿
    • 优先级反转(Priority Inversion):高优先级任务等待低优先级任务持有的锁(如火星探路者号故障)。
  • 改进
    • 动态提升长期未运行任务的优先级;
    • 为低优先级队列预留 CPU 时间。

已知任务长度的调度策略

SJF(Shortest Job First)

  • 机制:所有任务就绪时,按执行时间升序调度。
  • 优点:最小化平均周转时间。
  • 缺点:需预知任务长度;长任务可能饥饿。

STCF / SRTF(Shortest Time to Completion First)

  • 机制:SJF 的抢占版本,新任务若剩余时间更短则立即抢占。
  • 特性
    • 性能 ≥ SJF;
    • 对短任务极度友好,但长任务可能永久饥饿;
    • 需准确预测任务长度(可通过历史 CPU Burst 拟合,如拉格朗日插值)。
预测方法:利用任务过往的 CPU Burst 长度预测未来行为。

比例份额调度(Proportional Share Scheduling)

彩票调度(Lottery Scheduling)

  • 机制
    • 每个任务分配若干“彩票”;
    • 调度时随机抽取一张彩票,对应任务获得 CPU;
    • 短任务可分配更多彩票以提升响应性。
  • 优点
    • 无饥饿(每个任务至少一张彩票);
    • 开销小,无需维护复杂状态;
    • 支持彩票货币(用户发行子彩票)、转让通胀
  • 缺点:随机性导致短期不公平。

步长调度(Stride Scheduling)

  • 机制
    • 步长 = 大常数 / 彩票数;
    • 每任务维护“行程”(pass),执行后增加步长;
    • 调度器选择行程最小的任务。
  • 优点:绝对公平,无随机波动。
  • 缺点:新任务行程初始化困难。

MLFQ(Multi-Level Feedback Queue)

MLFQ 利用任务历史行为动态调整优先级,平衡响应性与吞吐量。

核心设计

  • 多级队列:高优先级队列时间片短(如 1ms),低优先级队列时间片长(如 100ms);
  • 初始策略:新任务放入最高优先级队列;
  • 降级规则:若任务用完时间片仍未阻塞,则移入低一级队列;
  • 升级规则:若任务在时间片内主动放弃 CPU(如 I/O),则保留在当前或升入更高队列。

改进与挑战

问题
解决方案
饥饿
定期将所有任务重置到最高优先级队列
愚弄调度器(Gaming)
任务通过频繁虚假 I/O 提升优先级
交互性识别
假设“短 Burst + 长睡眠” = 交互式任务,赋予高优先级

最终规则

  1. 优先运行高优先级队列任务;
  1. 新任务进入最高优先级队列;
  1. 用完时间配额则降级;
  1. 每隔周期 S,所有任务重置到最高优先级。
MLFQ 是 Unix/Linux 早期调度器的基础。

多处理器调度

多核环境下,调度需兼顾负载均衡缓存亲和性(Cache Affinity)。

调度模型

模型
机制
优缺点
SQMS(单队列)
所有 CPU 共享一个就绪队列
✅ 负载均衡好<br>❌ 队列锁竞争、缓存失效严重
MQMS(多队列)
每 CPU 独立队列
✅ 无锁、缓存亲和性高<br>❌ 负载不均

负载均衡技术

  • 工作迁移(Migration):定期将任务从繁忙 CPU 迁移到空闲 CPU;
  • 工作窃取(Work Stealing):空闲 CPU 主动从其他队列“窃取”任务。
Linux 实践:O(1) 和 CFS 使用多队列;BFS(Brain Fuck Scheduler)使用单队列。

多处理中的自旋锁优化

基本自旋锁

性能问题与优化

  • 问题test_and_set 是写操作,导致缓存行在核心间“乒乓”传递。
  • 优化:Test-Test-and-Set
    • 减少不必要的原子写,降低缓存一致性开销。

适用场景

  • 等待时间极短(< 上下文切换开销);
  • 多核系统中屏障同步。

Gang Scheduling

  • 思想:协作线程组(如并行任务)应同时被调度到多个 CPU 上。
  • 优势
    • 减少自旋等待时间;
    • 提升并行效率。
  • 现代实践:OS 调度单位是线程,但可通过 CPU 亲和性(affinity)绑定线程组。

实时调度(Real-Time Scheduling)

目标:为任务响应时间提供可预测上界

硬实时 vs 软实时

类型
要求
示例
硬实时
必须在截止时间前完成,否则系统失效
自动驾驶、航天控制
软实时
尽量在截止时间前完成,允许偶尔失败
视频流、语音通话

经典算法

算法
机制
适用场景
可调度条件
EDF(Earliest Deadline First)
动态优先级:截止时间最早者优先
通用实时任务
CPU 利用率 ≤ 100%
RMS(Rate-Monotonic Scheduling)
静态优先级:周期越短,优先级越高
周期性任务
CPU 利用率 ≤ 69%
DMS(Deadline-Monotonic Scheduling)
静态优先级:截止时间越短,优先级越高
周期 ≠ 截止时间的任务
比 RMS 更灵活
EDF 最优但开销大;RMS/DMS 简单但保守。

饥饿(Starvation)与解决方案

饥饿定义

线程长期无法获得 CPU 资源,但系统仍有进展(区别于死锁)。

各算法的饥饿风险

算法
饥饿风险
原因
FCFS
非抢占,但长任务阻塞短任务
RR
时间片轮转保证公平
严格优先级
低优先级任务被永久忽略
SRTF/MLFQ
长任务被短任务持续抢占

解决思路

  • 老化(Aging):随等待时间增加,动态提升优先级;
  • 定期重置:如 MLFQ 的周期性重置;
  • 资源预留:为低优先级任务保留最小 CPU 份额。
根本目标:在效率与公平间取得平衡,优先保障 I/O 密集型和交互式任务。

调度策略的演变历程

1. 1960s–1970s:分时系统与优先级

  • 目标:大型机上混合 CPU 密集型(科学计算)与交互式任务(终端);
  • 方法:交互任务高优先级,后台任务低优先级。

2. 1980s:个人计算机与公平性

  • 需求:防止单任务垄断 CPU(如渲染阻塞用户操作);
  • 代表:Unix nice 值(-20 到 19),用户自愿降低优先级。

3. 1990s–2000s:互联网与可预测性

  • 挑战:服务器整合、突发流量;
  • 重点:延迟 SLO(如 P95 < 100ms)、资源隔离。

Linux 调度器演进

O(1) 调度器(Linux 2.6)

核心设计

  • 140 级优先级
    • 0–99:实时任务(不可 nice);
    • 100–139:用户任务(nice -20 → 100,nice +19 → 139)。
  • 双队列(Active/Expired):
    • Active:当前可运行任务;
    • Expired:时间片耗尽任务;
    • Active 空时原子交换两队列。
  • 位图优化:140 位位图快速定位最高优先级非空队列。

动态优先级调整

  • I/O 密集型任务:频繁睡眠 → 提升优先级;
  • CPU 密集型任务:长时间运行 → 降低优先级;
  • 防饥饿:Expired 队列中长期未运行任务优先级提升。

时间片分配

  • 公式:时间片 = BASE + (140 - priority) × GRANULARITY
  • 示例:优先级 100 → 200ms,139 → 10ms。
优势:O(1) 复杂度,实时任务绝对优先;缺陷:启发式规则复杂,公平性不足。

CFS(Completely Fair Scheduler,Linux 2.6.23+)

核心思想

模拟“理想多任务 CPU”:所有任务同时运行,各得 1/n CPU 时间。

关键机制

  • 虚拟运行时(vruntime):
    • vruntime += 实际运行时间 × 1024 / 权重
    • 权重由 nice 值决定:权重 = 1024 / (1.25)^nice
  • 红黑树:以 vruntime 为键,O(log N) 定位最小 vruntime 任务。
  • 时间片动态计算
    • 时间片 = max(目标延迟 / 任务数, 最小粒度)
    • 默认:目标延迟 = 20ms,最小粒度 = 1ms。

交互性优化

  • 睡眠补偿:睡眠期间 vruntime 不增长,唤醒后优先调度;
  • 新任务优待:初始 vruntime 设为当前最小值。

对比 O(1)

维度
CFS
O(1)
公平性
严格按 vruntime,无饥饿
依赖启发式,可能饿死
复杂度
O(log N)
O(1)
交互性
自动补偿睡眠任务
依赖 sleep_avg 和 IC
CFS 通过数学化机制,在公平性、吞吐量、响应性间取得平衡。

死锁(Deadlock)

死锁 vs 饥饿

  • 饥饿:部分线程无进展,但系统整体有进展;
  • 死锁所有相关线程互相等待,系统停滞。

死锁四条件(Coffman 条件)

  1. 互斥(Mutual Exclusion):资源一次仅一任务使用;
  1. 持有并等待(Hold and Wait):持有资源同时等待其他资源;
  1. 非抢占(No Preemption):资源只能自愿释放;
  1. 循环等待(Circular Wait):等待关系成环。
破坏任一条件即可避免死锁。

哲学家进餐问题

  • 场景:N 哲学家,N 筷子,需同时获取左右筷子;
  • 死锁条件:所有哲学家同时拿起左筷子;
  • 解决方案
    • 资源排序(如先拿编号小的筷子);
    • 限制同时进餐人数 ≤ N-1。

死锁处理策略

1. 预防(Prevention)

  • 程序员责任:编写无死锁代码;
  • 方法
    • 资源排序:所有线程按全局偏序(如地址)请求锁;
    • 一次性分配:启动时申请所有资源。

2. 避免(Avoidance)

  • OS 责任:在分配前检查是否进入不安全状态
  • 银行家算法
    • 模拟分配后,检查是否存在安全序列(所有任务可完成);
    • 安全则分配,否则阻塞。

3. 检测与恢复

  • 定期检测:构建资源分配图,检测环;
  • 恢复:终止任务、回滚状态、抢占资源。

4. 忽略

  • 现实策略:内核避免死锁,用户态死锁由应用处理(如重启)。
根本解决:减少资源共享、虚拟化资源、要求一次性申请。

现代计算机系统的调度研究

关键概念

术语
说明
Tail-at-Scale
长尾延迟(如 P99)主导用户体验
Tail Latency SLO
服务等级目标,如“99% 请求 < 100ms”
Work Conservation
有任务时 CPU 不空闲
Connection Delegation
连接分配策略:<br>• Partitioned:固定 CPU,局部性好但负载不均<br>• Floating:动态分配,负载均衡但开销大

前沿调度系统

1. ZygOS:微秒级低尾延迟

  • 问题:数据中心 RPC 的尾延迟难以满足 SLO;
  • 方案
    • 单队列 + 工作窃取:全局负载均衡,避免多队列局部过载;
    • 数据平面优化:无共享架构,减少锁竞争;
    • 精简内核:降低上下文切换开销。
  • 效果:尾延迟降低至 SLO 的 10 倍以内,吞吐量提升 1.5 倍。
洞见:单队列在尾延迟敏感场景优于多队列。

2. Shinjuku:动态时间片 RR

  • 核心:根据任务优先级动态调整时间片;
  • 应用:亚毫秒级 RPC 服务;
  • 效果:尾延迟比传统 RR 降低 40%。

3. Tiresias:分布式深度学习调度

  • 挑战
    • 任务执行时间不可预测;
    • GPU 资源需同时分配 Worker 和 PS;
    • 强制合并放置导致资源浪费。
  • 方案
    • 二维调度(2DAS):同时考虑 GPU 数量(空间)和剩余时间(时间);
    • 智能放置:基于 RDMA 分析,动态决定是否合并;
    • 离散化 LAS:避免高成本抢占。
  • 效果:平均作业完成时间(JCT)降低 5.5 倍。

4. DRF(Dominant Resource Fairness)

  • 问题:多资源(CPU、内存、GPU)公平分配;
  • 核心
    • 主导资源:用户占用比例最高的资源;
    • 主导份额:在主导资源上的占用比例;
    • 分配原则:最大最小公平化主导份额。
  • 示例
    • 总资源:9 CPU, 18 GB;
    • 用户 A(1 CPU, 4 GB)→ 主导资源 = 内存;
    • 用户 B(3 CPU, 1 GB)→ 主导资源 = CPU;
    • DRF 分配:A 得 2 CPU+8 GB(主导份额 66%),B 得 6 CPU+2 GB(主导份额 66%)。
  • 优势:满足策略无关性(strategy-proof)、帕累托最优。

5. FairRide:公平缓存共享

  • 问题:传统缓存(LRU/LFU)不公平,易被欺骗;
  • 方案
    • 最大最小公平:初始均分缓存;
    • 概率阻塞:共享文件访问按 p = 1/(n+1) 阻塞(n 为共享用户数);
  • 保证
    • 隔离性:性能 ≥ 静态分配;
    • 策略无关性:无法通过作弊获益。
  • 折中:放弃严格帕累托效率,实现“近最优”。

新兴场景调度

Jolteon:Serverless Workflow 调度

  • 目标:在成本与延迟间权衡;
  • 建模
    • 白盒:计算耗时 ∝ 1/资源量;
    • 黑盒:无先验,动态学习;
  • 优化:给定成本最小化延迟,或给定延迟最小化成本。

DistServe / LoongServe:大语言模型调度

  • 挑战
    • Prefill(计算密集)与 Decode(内存密集)阶段特性不同;
    • 长上下文导致显存与计算超线性增长;
    • 静态并行策略不适应负载变化。
  • 方案
    • 解耦 Prefill/Decode:分别 batching;
    • 弹性并行度:动态调整 GPU 数量,减少通信开销;
    • KV Cache 优化:降低显存碎片与传输开销。

总结与对比

系统/算法
核心贡献
适用场景
性能优势
ZygOS
单队列 + 工作窃取
微秒级 RPC
尾延迟降低至 SLO 10x
Shinjuku
动态时间片 RR
实时任务
尾延迟降低 40%
Tiresias
二维调度 + 智能放置
分布式 DL
JCT 降低 5.5x
DRF
多资源主导份额公平
异构集群
公平性优于 CEEI
FairRide
概率阻塞机制
共享缓存
命中率近最优
未来方向:结合 ML 预测任务特征、探索量子调度、面向 Serverless/LLM 的专用调度器。
Prev
OS05 并发与同步
Next
关于建站
Loading...
Catalog
Article List
SunVapor的小站
计算机系统导论
操作系统
文档