当前位置:首页>Linux>[linux 调度器第九讲] 实时调度:RT 与 Deadline 两种调度器

[linux 调度器第九讲] 实时调度:RT 与 Deadline 两种调度器

  • 2026-09-09 11:21:32
[linux 调度器第九讲] 实时调度:RT 与 Deadline 两种调度器

实时调度:RT 与 Deadline

Linux 调度子系统技术文档系列 · 第 9 篇

一次 RT 进程引发的生产事故

2019 年,某工业自动化公司部署了一套基于 Linux 的实时数据采集系统。为了降低传感器数据处理的延迟,工程师将所有采集进程设置为 SCHED_FIFO 最高优先级。上线第一天,系统表现完美——数据延迟从 5 毫秒降低到 50 微秒。第三天凌晨,整个产线突然卡死。监控画面凝固,报警系统静音,产线上的机械臂停在半空中。运维人员赶到现场后发现:一个 SCHED_FIFO 的日志线程进入了无限循环,由于它的优先级高于所有普通进程(包括 sshd、systemd-journald),系统变成了"砖头"——除了硬复位,没有任何恢复手段。

这不是虚构的故事,而是 RT throttling 机制诞生的真实诱因之一。Linux 的实时调度是一把双刃剑:它能给关键任务确定性的响应时间,也能在配置不当时让整个系统陷入瘫痪。

什么是实时调度?有哪些类型?

实时调度的核心诉求不是"快",而是"确定"。普通进程追求公平——每个进程最终都能分到 CPU;实时进程追求承诺——我必须在 2 毫秒内响应,这个承诺必须兑现。

Linux 内核提供了三种实时调度策略,每种策略对应不同的使用场景:

调度策略
调度算法
适用场景
时间片
SCHED_FIFO
静态优先级 + 抢占
需要连续执行直到完成的任务
无
SCHED_RR
静态优先级 + 时间片轮转
多个同优先级任务共享 CPU
默认 100ms
SCHED_DEADLINE
EDF(最早截止时间优先)
有明确周期和截止时间约束的周期性任务
由 runtime 参数决定

这三种策略共享一个关键特征:它们的优先级(1-99)始终高于普通进程的 nice 值(-20 到 19)。当 RT 进程就绪时,它会强制抢占当前运行的普通进程,这种抢占没有任何延迟。

核心数据结构:RT 与 Deadline 的骨架

理解实时调度的第一步,是看清它在内核中的数据组织方式。RT 进程和 Deadline 进程分别通过 sched_rt_entity 和 sched_dl_entity 两个结构体嵌入到 task_struct 中,每个进程只属于其中一个调度类。

sched_rt_entity:RT 进程的身份证

run_list 是 RT 调度的关键——每个优先级(1-99)对应一个独立的 FIFO 链表。调度器从最高优先级开始扫描,找到第一个非空链表,取出队首任务执行。这种设计保证了 O(1) 的调度决策复杂度。

RT 优先级位图:快速查找的加速器

如果每次调度都要遍历 99 个优先级链表,效率会非常低。Linux 使用了一个巧妙的优化:优先级位图(priority bitmap)。

位图的每一位对应一个优先级。当某个优先级的队列从空变为非空时,对应位被置 1;当队列从非空变为空时,对应位被清零。调度器只需要用 find_first_bit() 指令(在 x86 上是单条 BSF 汇编指令)就能瞬间找到最高就绪优先级。这就像一本带索引的字典,你不需要从头翻起,直接跳到有内容的那一页。

sched_dl_entity:Deadline 的三参数模型

Deadline 调度器的数据结构更加复杂,因为它需要跟踪任务的执行时间、周期和截止时间。

三参数模型是 Deadline 调度的核心设计。WCET(最坏执行时间)、周期和相对截止时间,这三个参数完整描述了一个周期性任务的时序特征。调度器根据这些参数计算带宽需求 dl_runtime / dl_period,并通过准入测试(Admission Test)判断系统是否有足够的剩余带宽接纳新任务。如果所有任务的带宽总和超过 CPU 容量,新任务会被拒绝。

执行路径:RT 带宽控制的限流机制

回到文章开头的生产事故。SCHED_FIFO 进程进入死循环后,由于它没有主动让出 CPU 的时机(FIFO 策略没有时间片),也没有更高优先级的进程来抢占它,CPU 被 100% 占用。普通进程——包括负责网络通信的 sshd、负责日志的 journald、甚至负责终端的 getty——全部被饿死。

这是 RT 调度器的固有风险。为了解决这个问题,Linux 在 2.6.38 内核引入了 RT 带宽控制(RT Bandwidth Control)机制。

95% 规则:为什么 RT 不能独占 CPU

内核定义了两个关键参数:

这两个参数的含义是:在任意 1 秒的滑动窗口内,RT 进程最多只能运行 0.95 秒,剩余 0.05 秒强制留给普通进程。95% 这个数字并非随意选择——它基于一个经验法则:如果 RT 进程需要超过 95% 的 CPU,说明系统的实时负载设计本身就存在问题,应该重新评估架构,而不是用实时优先级来掩盖设计缺陷。

RT throttle 触发的完整路径

RT throttle 的检查嵌入在 RT 进程的运行时更新路径中,每次时钟中断都会执行:

当 rt_throttled 被置 1 后,RT 进程会被强制从运行队列中移除,CPU 会切换到普通进程。限流状态会持续到下一个周期(默认 1 秒)开始时自动解除。

这个机制带来了一个有趣的现象:如果你在音频处理中使用了 SCHED_FIFO,却发现音频偶尔卡顿,很可能不是你的代码有问题,而是 RT throttling 在起作用。一个经典的排查命令是:

# 查看当前 RT 带宽配置
cat/proc/sys/kernel/sched_rt_period_us #1000000
cat/proc/sys/kernel/sched_rt_runtime_us # 950000 
# 临时关闭限流(不推荐用于生产环境)
echo-1>/proc/sys/kernel/sched_rt_runtime_us

chrt 命令:用户空间与内核的桥梁

chrt 命令是用户空间设置实时优先级的标准工具,它通过 sched_setscheduler() 系统调用与内核交互:

Deadline 调度器的准入测试

Deadline 调度器比 RT 调度更"聪明"——它不会让系统超载。在任务创建时,内核会执行准入测试(Admission Test),确保新任务不会导致已有任务错过截止时间。

准入测试是 Deadline 调度器的安全阀。它保证了一个数学定理:如果所有任务的带宽总和不超过 CPU 容量,且每个任务的 deadline >= runtime,那么所有任务都不会错过截止时间。这是 EDF 算法的最优性保证。

为什么引入 Deadline 调度器?

RT 调度基于静态优先级,它有一个根本性缺陷:优先级继承(Priority Inheritance)问题。假设系统中有两个 RT 任务:任务 A 优先级 90,每 100ms 执行 5ms;任务 B 优先级 80,每 50ms 执行 3ms。由于 A 的优先级更高,它总是抢占 B。但 A 的截止时间可能比 B 更宽松——A 可能每 10 秒才需要响应一次,而 B 每 50ms 就要响应一次。静态优先级无法表达"紧迫程度",它只能表达"重要程度"。

Deadline 调度器用 EDF(Earliest Deadline First)算法解决了这个问题。每个任务的优先级不是预先设定的,而是动态计算的——截止时间最早的任务获得最高优先级。这种策略在数学上被证明是最优的:如果一个任务集合可以被任何算法调度成功,EDF 一定也能调度成功。

引入 Deadline 调度器的第二个动机是带宽隔离。在 RT 调度中,一个失控的高优先级任务会饿死所有低优先级任务。而在 Deadline 调度中,每个任务的运行时间被严格限制在 dl_runtime 内。即使一个任务耗尽了它的运行时,它只会自己被限流(dl_throttled),不会影响其他 Deadline 任务的执行。这就像给每个任务分配了一个独立的"时间预算账户",花完了就等下个周期,不拖欠别人的额度。

总结

实时调度是 Linux 对时间敏感任务的基础设施。RT 调度类(SCHED_FIFO/SCHED_RR)用静态优先级和抢占机制保证确定性响应,但需要 RT throttling 来防止系统被饿死。Deadline 调度器(SCHED_DEADLINE)用三参数模型和 EDF 算法提供更精确的时间保证,并通过准入测试确保系统不会超载。理解这些机制的 trade-off——确定性与灵活性、优先级与截止时间、抢占与限流——是在生产环境中正确使用实时调度的前提。

一个常见误区:认为设置实时优先级就能让程序"变快"。实时调度保证的是"按时",而不是"更快"。如果你的音频程序延迟高,首先要检查的是程序本身的逻辑,而不是盲目提升优先级。

思考题:

  1. 如果你有一个音视频处理程序,同时使用了 SCHED_FIFO 和 SCHED_DEADLINE 两种策略,哪种策略更适合音频缓冲区的填充任务?为什么?
  2. RT throttling 机制在某些场景下会导致"周期性卡顿",你能设计一种方案来检测这种卡顿吗?
  3. Deadline 调度器的准入测试拒绝了你的任务创建请求(返回 -EBUSY),但你确认 CPU 还有很多空闲时间,这可能是什么原因?如何排查?

本系列文章基于 Linux 6.19.13 内核源码采用 CC BY-NC-SA 4.0 协议,转载请注明出处

最新文章

随机文章