从打补丁到一行配置:PREEMPT_RT 主线化之后,实时内核到底该怎么配?

从打补丁到一行配置:PREEMPT_RT 主线化之后,实时内核到底该怎么配?

摘要:Linux 6.12(2024-11-17,LTS)把 PREEMPT_RT 收进主线,二十年的 out-of-tree 补丁史就此收尾。但"能选了"不等于"会配了"------真正的差异不在 make menuconfig 少打几条命令,而在 Kconfig 语义、架构覆盖面、以及随后的 preemption 模型重构。本文从 PREEMPT 概念讲起,给出 RT 相关配置项全解,对齐到 7.2 时代的现状,并提供可直接复用的配置、验证与调优清单。


一、先厘清概念,再给结论

1.1 PREEMPT 到底是什么

**抢占(preemption)**说的是:一个正在运行的任务,能不能被更高优先级的任务中途赶下 CPU。真正的麻烦在于------这个任务此刻是不是在内核态

PREEMPT_NONE 模型下,任务一旦通过系统调用陷入内核,就一路跑到返回用户态为止。这期间哪怕实时任务被唤醒,也只能干等,而这个"等"的上限不可控。抢占模型就是为解决它而生的:

模型 内核态代码何时可被抢占 延迟 典型场景
PREEMPT_NONE 仅在返回用户态等少数几个点 最高 服务器、HPC、数据库
PREEMPT_VOLUNTARY 内核代码里显式埋好的让出点(cond_resched() 桌面发行版
PREEMPT 除临界区外,几乎处处可抢 低延迟桌面、软实时

【配图位 1:PREEMPT_NONE 与 PREEMPT 的调度延迟时序对比图】

内核靠每个任务身上的 preempt_count 计数来判断"此刻能不能抢":

  • 计数 > 0 → 不可抢占
  • spin_lock()preempt_disable()、处于中断上下文 → 计数加一
  • 高优先级任务被唤醒时只是置上 TIF_NEED_RESCHED 标志,真正的切换发生在检查点preempt_enable() 时、中断返回内核态时、异常返回用户态时

所以 PREEMPT 并不是"处处可抢"------抢占请求要是落在临界区里,只能等 preempt_count 归零才生效。这段等待正是延迟尾巴的主要来源之一。

【配图位 2:preempt_count 决定的抢占边界图】

顺着这个思路,三个配置项其实是三个递进的问题:

PREEMPT 管的是"内核态能不能抢",

PREEMPT_LAZY 管的是"抢得急不急",

PREEMPT_RT 管的是"临界区算不算"。

  • PREEMPT 让内核态可抢,代价是吞吐下降------持有锁的任务被抢占,别人只能自旋干等
  • PREEMPT_LAZY(6.13 引入)把普通任务的抢占推迟到 tick 边界,缓解上述代价
  • PREEMPT_RT 再进一步:把 spinlock_t 换成可睡眠的 rt_mutex,连临界区都变成可抢占的

完整的抢占模型对照表见 6.3 节。另外注意两个容易混的符号:CONFIG_PREEMPT 是抢占模型菜单项,CONFIG_PREEMPTION 是"该内核支持抢占"的内部符号,后者不要手动改。

1.1.1 一个高频混淆:内核态 ≠ 临界区

读表 1.1 时最容易卡住的就是"临界区"三个字。"内核态"和"临界区"根本不是一个维度上的概念,混为一谈会把后面所有配置项都理解偏:

  • 内核态说的是 CPU 特权级------能不能执行特权指令、能不能访问内核地址空间,由硬件决定。
  • 临界区说的是互斥------这段访问共享数据的代码不允许被并发执行,由锁决定。

两者正交,组合出四种情况:

位置 能否被抢占 取决于
用户态(任何配置) 时钟中断,与 CONFIG_PREEMPT 无关
内核态 · 非临界区 PREEMPT 下能 CONFIG_PREEMPT
内核态 · 临界区 不能(RT 除外) preempt_count
用户态 · 临界区 (锁拦不住) 这是坑,挡不住

【配图位:内核态 × 临界区 四象限图,标注各象限抢占行为】

为什么这俩总被搅在一起? Linux 语境下"能不能抢占"恰好要同时看两个维度,于是早年"内核里不能抢占"的说法传久了,让人默认"内核态 = 临界区"。三个纠偏:

  1. 用户态抢占一直存在,跟 CONFIG_PREEMPT 无关。 时钟中断一来,只要 TIF_NEED_RESCHED 被置上,返回用户态时就切换。PREEMPT_NONE 并不是"不能抢占",而是"内核态不给额外抢占点"。
  2. CONFIG_PREEMPT 只管一种情况:进程陷在内核态、又没在临界区里时,要不要允许抢占。
  3. 内核态临界区之所以抢不动,不是因为它在内核态,而是因为 preempt_count > 0 ------spin_lock() 会同时给你"互斥"和"禁止抢占"两件事;而用户态自旋锁只能给互斥,给不了禁抢占

一个直观类比:

内核态 ------ 能不能用公章(特权级,硬件说了算)

临界区 ------ 会议室是不是被你占着(互斥范围,锁说了算)

在走廊走(内核态非临界区)可以被叫走;在会议室开会(临界区)你锁了门------但用户态程序的会议室门只是个牌子,调度器照样能把你拎出来,门口排队自旋的人就白等了。

这一点恰好补完了后文 §7 的 PostgreSQL 案例:7.0 移除 PREEMPT_NONE 后,持有用户态自旋锁的进程也可能被抢占,其余核上自旋等待的线程跟着空转,吞吐掉到约 51%。理解"用户态临界区挡不住抢占",才能理解这个根因。

1.2 一图看懂:6.12 前后的差异

维度 6.12 之前(补丁时代) 6.12 及之后(主线时代)
获取方式 下载与内核版本一一对应的 -rtNN 补丁,patch -p1 打入 主线源码自带,无需任何外部补丁
配置项 打了补丁后才出现(早期叫 CONFIG_PREEMPT_RT_FULL,后期统一为 CONFIG_PREEMPT_RT CONFIG_PREEMPT_RT,依赖 EXPERT + ARCH_SUPPORTS_RT
在 Kconfig 中位置 Preemption Model 的四选一成员 6.12 仍是四选一;6.13 起被移出 choice,成为独立开关
版本约束 补丁版本必须与内核版本完全一致,差一个小版本就冲突 任意主线/LTS 版本,随取随用
架构覆盖 x86 为主,arm64 时好时坏 x86(32/64)、arm64、riscv(6.12)→ loongarch(6.13)→ arm32(7.1)
与 PREEMPT_DYNAMIC 互斥(depends on ... && !PREEMPT_DYNAMIC 6.13 起可共存,但启动参数只保留 preempt=full
长期维护 自己维护补丁与 rebase 随内核主线演进,LTS 6.12 支持至 2028-12-31

一句话:以前是"给内核做手术",现在是"打开一个开关";而变化真正的分水岭是 6.13------PREEMPT_RT 从"一种抢占模型"变成了"一个正交的开关"。


二、补丁时代:那些年我们踩过的坑

典型流程(以 5.10 为例):

bash 复制代码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patch-5.10.100-rt64.patch.xz
tar -xf linux-5.10.100.tar.xz && cd linux-5.10.100
xzcat ../patch-5.10.100-rt64.patch.xz | patch -p1

make menuconfig
# General setup → Preemption Model → Fully Preemptible Kernel (Real-Time)

这条路上有五个老兵都熟悉的雷:

  1. 版本锁死patch-5.10.100-rt64 只对 5.10.100 有效,换成 5.10.101 就可能满屏 Hunk FAILED
  2. 只覆盖少数 LTS:官方只为少数长期支持版本发布补丁,用非 LTS 内核基本无解。
  3. 与厂商 BSP 打架:树莓派、NXP、全志等厂商内核已被改过,再叠一层 RT 补丁,冲突概率陡增。
  4. 滞后:新内核发布后要等 RT 团队 rebase,往往晚数周甚至一个版本。
  5. 技术债无法回流 :RT 补丁里那些为了规避主线缺陷的 hack(最典型的是 printk 相关处理)长期躲在主线之外,无法被社区检验------这也正是合入拖了二十年的最后一个卡点。

2024 年 9 月,Thomas Gleixner 提交了那封著名的 pull request:

Enable PREEMPT_RT on supported architectures: After twenty years of development we finally reached the point to enable PREEMPT_RT support in the mainline kernel. All prerequisites are merged, so enable it on the supported architectures ARM64, RISCV and X86(32/64-bit).

对应改动只有三行------三个架构的 Kconfig 各加一句 select ARCH_SUPPORTS_RT。压垮骆驼的最后一块拼图,是 printk 的重写(nbcon)。


三、主线时代:从 6.12 起的标准流程

bash 复制代码
# 以 6.12 LTS 为例,也可以直接用当前主线 7.x
git clone --depth 1 -b v6.12 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux

cp /boot/config-$(uname -r) .config && make olddefconfig

# 关键:CONFIG_PREEMPT_RT 依赖 EXPERT,默认不可见
scripts/config --enable CONFIG_EXPERT
scripts/config --enable CONFIG_PREEMPT_RT
make olddefconfig

make -j$(nproc)
sudo make modules_install && sudo make install

验证:

bash 复制代码
uname -v                                # 应含 "SMP PREEMPT_RT"
zcat /proc/config.gz | grep PREEMPT_RT  # CONFIG_PREEMPT_RT=y
cat /sys/kernel/realtime                # 1(部分内核/发行版提供该接口)

坑位提醒 :如果在 Preemption Model 里看不到 "Fully Preemptible Kernel (Real-Time)",先检查两件事------CONFIG_EXPERT 是否打开、目标架构是否 select ARCH_SUPPORTS_RT。PowerPC 至今仍未合入支持。

不想自己编译的话,主流发行版也都有现成 RT 内核:Ubuntu Pro 的 linux-realtime、Debian 的 linux-image-rt-amd64、RHEL 的 kernel-rt、Yocto 的 linux-yocto-rt


四、真正的差异:Kconfig 语义变了

这才是本文想讲的重点。光说"不用打补丁了"太浅,6.12 和 6.13 之间还有一次语义重构。

版本 6.12:四选一里的第四项

刚合入时,PREEMPT_RT 被塞进了原本的 Preemption Model choice 里:

kconfig 复制代码
choice
    prompt "Preemption Model"
    config PREEMPT_NONE ...
    config PREEMPT_VOLUNTARY ...
    config PREEMPT ...
    config PREEMPT_RT
        bool "Fully Preemptible Kernel (Real-Time)"
        depends on EXPERT && ARCH_SUPPORTS_RT
endchoice

config PREEMPT_DYNAMIC
    depends on HAVE_PREEMPT_DYNAMIC && !PREEMPT_RT   # 与 RT 互斥

也就是说,RT 被当成了"第四种抢占模型",与 PREEMPT_DYNAMIC 水火不容。

版本 6.13:RT 被移出 choice(commit 35772d627b55)

Peter Zijlstra 在 2024 年 10 月提的这组补丁(2024-11-05 合入,落在 6.13 周期)把 RT 拿了出来:

kconfig 复制代码
choice
    config PREEMPT_NONE ...
    config PREEMPT_VOLUNTARY ...
    config PREEMPT ...
    config PREEMPT_LAZY ...          # 6.13 新增
endchoice                             # ← endchoice 提前

config PREEMPT_RT
    bool "Fully Preemptible Kernel (Real-Time)"
    depends on EXPERT && ARCH_SUPPORTS_RT

config PREEMPT_DYNAMIC
    depends on HAVE_PREEMPT_DYNAMIC   # 不再 !PREEMPT_RT

commit message 里那句话很值得记住:

Strictly speaking PREEMPT_RT is not a change in how preemption works, but rather it makes a ton more code preemptible.

严格来说,PREEMPT_RT 不是"另一种抢占模型",而是"让更多代码变得可抢占"。 它和 NONE/VOLUNTARY/FULL 不在同一个维度上,硬塞进四选一本身是概念错误。

配套地,调度器代码里把 none/voluntary 两个分支用 #ifndef CONFIG_PREEMPT_RT 包了起来------RT 内核上即使传了 preempt=none 也不会生效,只认 full

这次重构的直接收益是:RT 内核也能开 PREEMPT_DYNAMIC,为后续统一铺路。


五、2025---2026:主线化之后的连锁反应

PREEMPT_RT 进主线不是终点,而是把一系列重构的闸门拉开了。按时间线看:

版本 时间 事件
6.12 (LTS) 2024-11-17 PREEMPT_RT 主线化,支持 x86(32/64)、arm64、riscv
6.13 2025-01-19 引入 CONFIG_PREEMPT_LAZY;RT 移出 choice;loongarch 支持 RT
6.18 (LTS) 2025-11-30 持续打磨;softirq 大锁在 RT 上可去除(CONFIG_PREEMPT_RT_NEEDS_BH_LOCK 作为回退开关)
7.0 2026-04-12 主流架构(x86、arm64、loongarch、powerpc、riscv、s390)移除 PREEMPT_NONEPREEMPT_VOLUNTARY,只留 full 与 lazy
7.1 2026-06-14 32 位 ARM 支持 PREEMPT_RT
7.2 2026-08-16 当前稳定线(截至 2026-09 为 7.2.5),7.3-rc 已开

两个值得单独说的点:

(1)PREEMPT_LAZY 是冲着 RT 的开销来的。 它把 SCHED_NORMAL 任务的抢占请求推迟到 tick 边界(平均延迟 TICK_NSEC/2),但对 RR/FIFO/DEADLINE 类仍是完全抢占。目标是让"完全可抢占"的开销接近自愿抢占,从而最终干掉 cond_resched() 满天飞的局面。

(2)7.0 那次"删选项"是有代价的。 2026 年 4 月,AWS 工程师报告 PostgreSQL 在 7.0-rc 上吞吐掉到基线的约 51%,根因正是 PREEMPT_NONE 消失后,持有用户态自旋锁的进程可能被抢占,导致其余进程忙等。Peter Zijlstra 拒绝 revert,给出的解法是新合入的 RSEQ time-slice extension。这场争论说明:抢占模型的统一不只是内核内部的事,用户态的锁设计也被牵连进来了。


六、RT 相关配置项全解:开什么、关什么、别动什么

前面讲了"怎么选",这一节讲"选完之后还要配什么"。很多人以为勾上 CONFIG_PREEMPT_RT 就完事了,实际差得远。

6.1 五组最容易搞混的对照

对照 区别在哪
PREEMPT vs PREEMPT_RT 前者让非临界区 的内核代码可抢;后者进一步把临界区本身 变成可抢占的睡眠锁(spinlock_t → rt_mutex)。RT 是 PREEMPT 的超集,开 RT 会自动 select PREEMPT
PREEMPT_LAZY vs PREEMPT_VOLUNTARY 都为降抢占开销。VOLUNTARY 靠代码里显式埋 cond_resched();LAZY 由调度器把 SCHED_NORMAL 的抢占请求推迟到 tick 边界,对 RT 类仍是完全抢占。7.0 后主流架构上 VOLUNTARY 已被移除
NO_HZ_IDLE vs NO_HZ_FULL 前者(旧称 NO_HZ)只停空闲 CPU 的周期性 tick;后者在 CPU 上只有一个可运行任务时也停 tick,是实时隔离核的关键
RCU_NOCB_CPU vs RCU_BOOST 前者把 RCU 回调卸载到专门的 kthread(配合 rcu_nocbs=),让隔离核不再被打扰;后者提升"被抢占的 RCU 读侧临界区持有者"的优先级,缩短其持锁时间。作用在不同环节,实时系统通常两个都要
PREEMPT_DYNAMIC vs PREEMPT_RT 前者是运行时 维度(启动时用 preempt= 选模型);后者是编译期 维度(扩展可抢占范围)。6.13 起两者可共存,但 RT 内核上 preempt=none/voluntary 已被编译期剔除,只认 full

冷知识:看老资料时会遇到 CONFIG_PREEMPT_LL 这个名字。2019 年 Gleixner 引入 CONFIG_PREEMPT_RT 那次,为了把 CONFIG_PREEMPT 腾出来做内部符号,曾把菜单项 "Preemptible Kernel (Low-Latency Desktop)" 改名成 PREEMPT_LL;后来 PREEMPT_LAZY 引入时又重组回现在的结构。

6.2 核心开关(必开)

选项 说明
CONFIG_EXPERT RT 的"门"。不打开它,menuconfig 里根本看不到 RT 选项
CONFIG_ARCH_SUPPORTS_RT 架构能力位,由架构自己的 Kconfig select,用户改不了。7.2 时 x86、arm64、arm32、riscv、loongarch 已置位,powerpc 仍未合入
CONFIG_PREEMPT_RT 总开关,依赖 EXPERT && ARCH_SUPPORTS_RT
CONFIG_IRQ_FORCED_THREADING 强制中断线程化。个别必须留在硬中断上下文的中断用 IRQF_NO_THREAD 标记(时钟源、perf、级联中断控制器等)
CONFIG_PREEMPTIONCONFIG_PREEMPT_COUNTCONFIG_UNINLINE_SPIN_UNLOCK 内部符号,由 RT 自动 select,不要手动干预

6.3 抢占模型(非 RT 场景下的四选一)

模型 抢占时机 延迟 吞吐 典型场景 7.2 现状
PREEMPT_NONE 仅在返回用户态等显式点 最高 最好 数据库 / HPC 仅剩不支持抢占的架构
PREEMPT_VOLUNTARY 额外的显式让出点 桌面 仅剩不支持 LAZY 的架构
PREEMPT(full) 除临界区外处处可抢 略降 低延迟桌面、软实时 主流架构保留
PREEMPT_LAZY NORMAL 类推迟到 tick 边界,RT 类完全抢占 低(对 RT 类) 接近 VOLUNTARY 通用推荐 主流架构默认推荐
PREEMPT_RT 几乎处处可抢,含临界区 最低 再降几个百分点 硬实时 独立开关,与上表正交

注意最后一行:PREEMPT_RT 已经不在这个"四选一"里了------这正是第四节讲的 6.13 语义重构的结果。7.0 之后主流架构上实际是 full / lazy 二选一,RT 另行叠加。

6.4 时钟与定时器

选项 建议 说明
CONFIG_HIGH_RES_TIMERS 必开 微秒级定时的前提,没有它一切都免谈
CONFIG_HZ_1000 推荐 tick 频率提到 1000 Hz。配合 nohz_full 时对隔离核影响变小,但对 housekeeping 核仍有意义
CONFIG_NO_HZ_FULL 推荐 配合启动参数 nohz_full= 使用,见下节

一个容易忽略的点:hrtimer 默认仍在硬中断上下文执行 ,只有以 HRTIMER_MODE_SOFT 初始化的才走 softirq。所以定时器回调写长了照样会咬人。

6.5 RCU 三兄弟

复制代码
CONFIG_PREEMPT_RCU=y        # RCU 读侧临界区可抢占(RT 下自动生效)
CONFIG_RCU_NOCB_CPU=y       # 配合启动参数 rcu_nocbs=2-3
CONFIG_RCU_BOOST=y          # 提升被抢占的 RCU 读侧持有者优先级
CONFIG_RCU_BOOST_PRIO=1     # 提升到的优先级(默认 1,通常够用)
CONFIG_RCU_BOOST_DELAY=500  # 提升前的等待毫秒数

RCU_BOOST 解决的是一个很隐蔽的场景:低优先级任务在 RCU 读侧临界区里被抢占,导致高优先级任务等 grace period 干等。它靠优先级继承把前者临时提上去。

6.6 建议关闭或固定的选项

选项 建议 原因
CONFIG_CPU_FREQ 关,或固定 performance governor 变频切换瞬间引入延迟
CONFIG_CPU_IDLE / CONFIG_INTEL_IDLE 保留但限制深睡,BIOS 层关深 C-State 深 C-State 唤醒可达毫秒级
CONFIG_SUSPEND / CONFIG_HIBERNATION 实时设备用不上,纯增路径
CONFIG_SWAP 换页是延迟炸弹
CONFIG_TRANSPARENT_HUGEPAGE 关,或设为 madvise khugepaged 与大页合并制造延迟尖峰
CONFIG_KSM 页面扫描带来周期性抖动
CONFIG_SCHED_AUTOGROUP 面向桌面交互,干扰确定性
CONFIG_RT_GROUP_SCHED 实时场景通常关 启用后 cgroup 层的 RT 带宽限制会覆盖全局 sched_rt_runtime_us
CONFIG_LOCKDEP / CONFIG_PROVE_LOCKING 仅调试期开 开销大且改变时序,开着它测出来的延迟不作数
CONFIG_DEBUG_ATOMIC_SLEEPCONFIG_DEBUG_RT_MUTEXES 仅调试期开 同上

6.7 只在测量期开启

复制代码
CONFIG_FTRACE / CONFIG_FUNCTION_TRACER      # 追踪基础设施
CONFIG_IRQSOFF_TRACER / CONFIG_PREEMPT_TRACER   # irqsoff、preemptoff、preemptirqsoff
CONFIG_OSNOISE_TRACER / CONFIG_TIMERLAT_TRACER  # 对应 rtla osnoise / rtla timerlat

测完记得关掉------tracer 自身有观测开销,留着跑生产等于给自己挖坑。

6.8 RT 下语义会发生变化的机制(避坑要点)

这部分来自 kernel.org 官方的 real-time 文档,是驱动开发者最容易踩的:

  • spinlock_t / rwlock_t 在 RT 上不再关中断 (中断已线程化),蜕变为可睡眠的 rt_mutex 并带优先级继承。只有 raw_spinlock_t 保持"关抢占 / 关中断"的传统语义,用于调度器、底层中断处理等极少数路径。
  • 中断 :全部强制线程化,例外是带 IRQF_NO_THREADIRQF_PERCPUIRQF_ONESHOT 的中断。线程化后可用 chrt 调整 irq/N-* 的优先级。
  • softirq :在线程上下文执行且可被抢占 。因此不能再靠 local_bh_disable() 保护 per-CPU 数据,要改用 local_lock_nested_bh()
  • per-CPU 数据 :改用 local_lock_t(RT 上实现为 per-CPU 的 spinlock_t);确实需要显式关抢占的极端场合用 preempt_disable_nested()
  • 控制台 :RT 下 printk 输出由专门线程处理,好处是原子上下文可以安全打印,代价是内核崩溃时若切不到打印线程,最后那几条消息就丢了(panic 路径有例外)。这正是 nbcon 重写成为主线化最后一块拼图的原因------第二节那句"卡了二十年"不是修辞。

七、开了 RT 不等于实时:必做的调优

CONFIG_PREEMPT_RT=y 只保证"内核里该可抢的地方都可抢",剩下的活在你手上。内核侧的选项上一节已经列全,这里只讲内核之外的部分。

启动参数(把 CPU 2、3 划给实时任务)

复制代码
isolcpus=domain,managed_irq,2-3 nohz_full=2-3 rcu_nocbs=2-3 irqaffinity=0-1

运行时

bash 复制代码
echo never > /sys/kernel/mm/transparent_hugepage/enabled   # THP 会制造延迟尖峰
echo -1 > /proc/sys/kernel/sched_rt_runtime_us            # 关闭 RT 带宽节流(慎用)
ps -eLo pid,rtprio,comm | grep irq                        # 查看中断线程优先级,默认 50
chrt -f -p 90 <irq_thread_pid>                            # 关键中断优先级提到实时任务之上

应用层

  • mlockall(MCL_CURRENT | MCL_FUTURE)------缺页是实时任务的头号杀手
  • sched_setscheduler(SCHED_FIFO) + 合理优先级
  • 用户态互斥量设 PTHREAD_PRIO_INHERIT,内核的 PI 管不到用户态锁
  • 绑核:pthread_setaffinity_np 或 cpuset cgroup
  • BIOS 层:关深度 C-State、关 Turbo、视情况关 SMT;能用 hwlatdetect 先探一下 SMI

测量工具

bash 复制代码
cyclictest -m -Sp99 -i1000 -d0 -h800        # rt-tests,最经典
rtla timerlat top                            # tools/tracing/rtla,定位 timer 路径延迟
rtla osnoise top                             # 定位操作系统噪声
hwlatdetect                                  # 检测 SMI 等硬件层延迟
trace-cmd record -e ...                      # 配合 wakeup_rt / osnoise tracer

调优到位后,x86 平台上 cyclictest 的 Max 通常落在几十微秒量级;ARM 平台视 SoC 与驱动质量,常见在 50--150 µs。这些是经验值,必须用自己硬件的实测数据说话,不能直接写进论文。


八、方案光谱:PREEMPT_RT 该摆在什么位置

结合异构与虚拟化的视角,把常见实时方案横着排一遍:

方案 类型 典型最坏延迟 优势 代价
PREEMPT_RT 单一 Linux 内核 数十 µs 完整 Linux 生态、主线维护、零补丁 延迟取决于全内核与驱动质量
Xenomai 4 / EVL co-kernel(Dovetail) 约 10 µs 级 与 Linux 内核解耦,延迟更低更稳 仍需 out-of-tree 补丁,API 自成体系
Jailhouse 静态分区 hypervisor 由裸机 RTOS 决定 Linux 与 RTOS 硬件级隔离,cell 之间互不干扰 静态分区、资源独占,跨 cell 通信靠 IVSHMEM
RTOS(Zephyr / FreeRTOS) 独立实时内核 个位数 µs 延迟由构造保证 无 Linux 软件生态

一个有意思的推论:RT 主线化放大了双核方案的维护成本劣势。Xenomai 的 Dovetail 补丁如今和当年的 RT 补丁一样,需要跟着内核版本 rebase;而 PREEMPT_RT 已经不需要了。所以对多数新项目来说,选型顺序建议是:

  1. 先试 PREEMPT_RT,测出来的 Max 能满足 deadline 就用它;
  2. 满足不了,再考虑 co-kernel(Xenomai/EVL);
  3. 如果还要功能安全隔离或"Linux 崩了控制回路照跑"级别的确定性,那才是 Jailhouse 这类静态分区方案的战场------它卖的不是延迟数字,是隔离性。

九、给不同读者的建议

  • 工程落地:直接上 6.12 LTS 或 6.18 LTS,别追最新主线。LTS 支持到 2028 年底,够一个产品周期了。
  • 教学 :这件事本身就是操作系统课的绝佳案例------一个 out-of-tree 项目如何用二十年时间、以"每次回流一小块"的方式进入主线,期间还顺手改造了 printk、RCU、中断线程化。讲"开源协作"比讲概念有说服力得多。
  • 科研/论文:写实时性对比实验时,务必注明内核版本、preemption 模型、cyclictest 参数、隔离核配置、BIOS 设置,否则数据不可复现。7.0 之后抢占模型只剩两种,实验基线也要跟着更新。

参考资料

  • Thomas Gleixner, GIT PULL sched/rt for v6.12-rc1(2024-09)
  • Peter Zijlstra, sched: Enable PREEMPT_DYNAMIC for PREEMPT_RT(commit 35772d627b55)
  • Peter Zijlstra, sched: Further restrict the preemption modes(commit 7dadeaa6e851,2026-01)
  • KernelNewbies: Linux 6.12 / Linux 7.0
  • LWN: The first half of the 7.0 merge window
  • kernel.org Releases(版本与 LTS 状态查询)
  • 内核文档:How realtime kernels differ(锁、中断、softirq、per-CPU 在 RT 下的语义变化)
  • 内核文档:Porting an architecture to support PREEMPT_RT(架构移植要求,第六节 6.2 与 6.8 的依据)
  • 内核文档:Real-time Linux(theory of operation)、Scheduler、Deadline scheduling

本文基于 2026 年 9 月的主线状态撰写(当前稳定版 7.2.5)。内核演进很快,引用前请以 kernel.org 为准。

相关推荐
杨云龙UP1 小时前
MySQL Host is blocked because of many connection errors 导致 JDBC 连接失败排查与解决
linux·运维·网络·数据库·sql·mysql·登录失败
企鹅的蚂蚁2 小时前
LubanCat RK3588 实时 Linux 开发(九):从 U-Boot 到 initramfs 的串口启动故障分析
linux·rk3588·u-boot·串口调试·initramfs·linux启动
kkkkkkkkkk_Z2 小时前
学嵌入式和Linux应用编程|学习日记:UDP协议与Wireshark抓包学习笔记
linux·笔记·学习
嵌入式小能手2 小时前
飞凌嵌入式ElfBoard-Shell编程应用实例-提取字符并设置rtc时间
linux
刃神太酷啦3 小时前
Redis 核心进阶:哨兵、集群、缓存问题与分布式锁详解----《Hello Redis!》(6)
linux·c语言·数据库·c++·redis·分布式·缓存
大卡片3 小时前
LCD相关知识
linux
闲云野鹤在人间3 小时前
Docker入门|第3章 镜像详解
linux·网络·docker·容器·centos·云计算·php
小卿噢3 小时前
malloc 成功 ≠ 你有内存:一次把 OOM 从头测到尾
linux·后端
团子股股东峥哥3 小时前
day38-RHEL-管理存储堆栈
linux·运维·服务器