4、实时内核补丁(PREEMPT_RT)

在嵌入式 Linux 系统中,标准的 Linux 内核虽然提供了多种抢占模型,但在面对微秒级或亚毫秒级的硬实时任务时,其调度延迟和中断响应时间往往无法满足要求。PREEMPT_RT(Real-Time Linux)补丁集通过一系列内核机制改造,将 Linux 内核转变为完全可抢占的实时操作系统。本章将系统介绍 PREEMPT_RT 补丁的原理、获取方式、打补丁流程、配置选项以及实时性验证方法。

核心概念: PREEMPT_RT 并非一个独立的内核,而是针对主线 Linux 内核的一组补丁(Patch Set),通过替换内核自旋锁为可抢占锁、中断线程化、高精度定时器改造等手段,大幅降低内核抢占延迟,使 Linux 具备硬实时能力。
PREEMPT_RT 补丁改造前后对比 标准 Linux 内核 用户空间任务 中断处理(不可抢占) 自旋锁(关抢占) 抢占延迟:1~10 ms PREEMPT_RT 内核 实时用户空间任务 中断线程化(可被抢占) 可抢占自旋锁(rt_mutex) 抢占延迟:5~100 µs

4.1 PREEMPT_RT 补丁介绍

PREEMPT_RT 补丁集由 Linux 基金会下的 Real-Time Linux(RTL)协作项目维护,其核心目标是将 Linux 内核改造为完全可抢占的实时操作系统。补丁主要从以下三个维度对内核进行深度改造:

改造维度 标准内核行为 PREEMPT_RT 改造后 实时性收益
中断线程化 中断处理程序在关闭本地中断的上下文中执行,不可被抢占 中断处理程序变为内核线程(IRQ Thread),可被高优先级实时任务抢占 消除中断导致的优先级反转
自旋锁可抢占 自旋锁持有期间禁止内核抢占,导致优先级反转 自旋锁替换为 rt_mutex 或可睡眠锁(sleepable spinlock) 锁持有时间不再阻塞高优先级任务
高精度定时器 定时器基于 jiffies,精度通常为 1~10 ms 使用 hrtimer 框架,精度达到纳秒级 支持微秒级定时调度
适用场景: PREEMPT_RT 适用于工业控制、机器人、航空航天、音频处理、医疗设备等对确定性延迟有严格要求的嵌入式系统。对于非实时或软实时场景(如桌面、服务器),标准内核的 PREEMPT 或 PREEMPT_VOLUNTARY 模型通常已足够。

4.2 补丁获取与打补丁

PREEMPT_RT 补丁以 .patch.xz 或 .patch.gz 格式发布,必须与对应版本的主线内核源码严格匹配。以下是获取与打补丁的标准流程。

4.2.1 补丁获取途径

  • 官方仓库: https://cdn.kernel.org/pub/linux/kernel/projects/rt/,按内核主版本号组织目录。
  • 版本匹配规则: 补丁文件名格式为 patch-5.10.x-rtX.patch.xz,其中 5.10.x 必须与主线内核小版本号完全一致。
  • 长期支持(LTS)版本: 推荐使用 LTS 内核(如 5.10、5.15、6.1)对应的 RT 补丁,社区维护周期长,稳定性高。

4.2.2 打补丁步骤

以下以 Linux 5.10.100 内核和对应的 PREEMPT_RT 补丁为例,展示完整的打补丁流程:

# 1. 下载主线内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xf linux-5.10.100.tar.xz
cd linux-5.10.100

# 2. 下载对应的 RT 补丁
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.10/patch-5.10.100-rt57.patch.xz

# 3. 解压补丁
xz -d patch-5.10.100-rt57.patch.xz

# 4. 应用补丁(dry-run 先检查冲突)
patch -p1 --dry-run < patch-5.10.100-rt57.patch

# 5. 确认无冲突后正式打补丁
patch -p1 < patch-5.10.100-rt57.patch

# 6. 验证补丁是否成功
grep "PREEMPT_RT" kernel/Kconfig.preempt
# 输出应包含 "config PREEMPT_RT" 相关选项
常见错误: 如果内核源码版本与补丁版本不匹配(例如内核是 5.10.100,补丁是 5.10.99-rt56),patch 命令会报告大量 hunk 失败。此时必须下载与内核完全匹配的补丁版本,或使用 git am 方式管理补丁。
PREEMPT_RT 补丁应用流程 下载主线内核源码 下载 RT 补丁 解压补丁文件 dry-run 检查 正式打补丁 验证补丁状态 配置实时抢占选项

4.3 配置实时抢占选项

打补丁成功后,内核源码中会新增 PREEMPT_RT 相关的 Kconfig 选项。通过 make menuconfig 进入内核配置界面,按以下路径启用实时抢占:

Kernel Features
  └> Preemption Model
      └> (X) Fully Preemptible Kernel (Real-Time)  [PREEMPT_RT]

选择 Fully Preemptible Kernel (Real-Time) 后,内核会自动启用以下关键实时特性:

配置选项 功能描述 推荐状态
CONFIG_PREEMPT_RT 完全可抢占内核主开关 启用
CONFIG_HIGH_RES_TIMERS 高精度定时器支持 启用(自动选中)
CONFIG_PREEMPT_RT_FULL 完整实时抢占特性集 启用
CONFIG_IRQ_FORCED_THREADING 强制中断线程化 启用
CONFIG_RT_GROUP_SCHED 实时任务组调度支持 按需启用
配置建议: 对于嵌入式实时系统,建议同时关闭 CONFIG_NO_HZ_FULL(全无滴答模式),因为该模式在实时场景下可能引入调度延迟抖动。使用 CONFIG_NO_HZ_IDLE 即可。

配置完成后,保存并退出,然后编译内核:

# 保存配置
make savedefconfig
cp defconfig .config

# 编译内核(以 ARM64 为例)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# 编译内核模块
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_install INSTALL_MOD_PATH=/path/to/rootfs

4.4 验证实时性

内核启动后,需要从系统状态、调度延迟和中断响应三个层面验证实时性是否生效。

4.4.1 系统状态检查

通过 /proc 文件系统和内核日志确认 PREEMPT_RT 已启用:

# 检查内核抢占模型
cat /proc/sys/kernel/sched_rt_runtime_us
# 输出应为 950000(表示实时任务可占用 95% CPU 时间)

# 检查内核日志
dmesg | grep -i "preempt"
# 应输出类似: "Linux version 5.10.100-rt57 ... PREEMPT_RT"

# 检查中断线程化
ps -eo pid,comm | grep irq
# 应看到类似 [irq/50-eth0] 的中断线程

4.4.2 调度延迟测试

使用 cyclictest(来自 rt-tests 工具集)测量内核调度延迟:

# 安装 rt-tests(以 Buildroot 或 Yocto 为例)
# 目标板上运行
cyclictest -t1 -p 99 -n -i 1000 -l 100000

# 输出示例:
# T: 0 (1234) P: 99 I: 1000 C: 100000 Min: 2 Act: 5 Avg: 4 Max: 18
指标 含义 典型值(PREEMPT_RT) 典型值(标准内核)
Min 最小延迟(µs) 1~3 2~10
Max 最大延迟(µs) 10~50 100~5000
Avg 平均延迟(µs) 3~10 10~100
关键判断: 如果 Max 延迟超过 100 µs 且系统负载较低,说明 PREEMPT_RT 配置可能未生效,或存在驱动未适配中断线程化。此时应检查 dmesg 中是否有 "IRQ handler type mismatch" 等警告。

4.4.3 中断响应延迟测试

使用 hackbench 或 stress-ng 施加系统负载,同时运行 cyclictest 观察延迟变化:

# 在另一个终端施加 CPU 和内存压力
stress-ng --cpu 4 --io 2 --vm 2 --vm-bytes 128M --timeout 60s

# 同时运行 cyclictest
cyclictest -t5 -p 99 -n -i 1000 -l 60000 -m

# 观察最大延迟是否仍在可接受范围内(通常 < 200 µs)
实时性验证流程 阶段一:系统状态 • 检查 /proc/sys/kernel/sched_rt_runtime_us • dmesg 确认 PREEMPT_RT 标志 • ps 查看中断线程化状态 • uname -a 查看内核版本 阶段二:延迟测试 • 运行 cyclictest 单线程 • 记录 Min/Max/Avg 延迟 • 施加 stress-ng 负载 • 观察最大延迟抖动 阶段三:功能验证 • 运行实时任务(如 PWM 控制) • 使用示波器测量 GPIO 抖动 • 验证任务周期确定性 • 记录最坏情况响应时间
注意事项: 实时性验证必须在目标硬件上进行,QEMU 等模拟器无法准确反映真实的中断延迟和缓存行为。此外,某些 SoC 的 GPU、VPU 或网卡驱动可能未完全适配 PREEMPT_RT,导致中断线程化失败,此时需要为这些设备添加 threadirqs 内核启动参数强制线程化。