3. PREEMPT_RT补丁详解

PREEMPT_RT 补丁是 Linux 内核实现硬实时能力的关键技术。它通过一系列精细化的内核改造,将标准 Linux 内核从“尽力而为”的软实时系统,转变为可预测、低延迟的硬实时操作系统。本章将深入剖析 PREEMPT_RT 的原理、关键内核配置选项,并手把手演示如何为内核打上补丁并编译。

3.1 PREEMPT_RT 原理

标准 Linux 内核在中断上下文、软中断以及自旋锁持有区域中,抢占是被禁止的。这意味着即使一个高优先级的实时任务就绪,也必须等待这些不可抢占区域执行完毕,从而产生不可预测的延迟抖动(Jitter)。PREEMPT_RT 的核心思想是:将内核中几乎所有不可抢占的区域都变为可抢占,从而将最大延迟降低到微秒级。

核心目标:将内核的最坏情况延迟(Worst-Case Latency)从毫秒级降低到几十微秒甚至更低,使 Linux 能够满足工业控制、航空航天、机器人等领域的硬实时要求。

PREEMPT_RT 主要通过以下三大机制实现这一目标:

  1. 中断线程化(Interrupt Threads):将原本在关闭中断的硬中断上下文中执行的中断处理程序,改为运行在内核线程中。这些线程可以被实时调度器管理和抢占,从而避免高优先级任务被长中断处理程序阻塞。
  2. 自旋锁可抢占化(Sleeping Spinlocks / RT-Mutexes):将内核中大部分自旋锁(spinlock)替换为支持睡眠的互斥锁(rt_mutex)。当一个任务试图获取已被占用的锁时,它会被阻塞并让出 CPU,而不是忙等待。这消除了优先级反转和死锁的根源,并允许高优先级任务立即抢占低优先级任务。
  3. RCU 抢占化(Preemptible RCU):将读-拷贝-更新(RCU)机制改造为可抢占版本,使得在读端临界区内也可以发生抢占,进一步缩小了不可抢占区域。
关键概念:优先级反转(Priority Inversion)是实时系统的头号敌人。PREEMPT_RT 通过优先级继承协议(Priority Inheritance Protocol)与可睡眠锁的结合,从根本上解决了这一问题。
PREEMPT_RT 核心机制架构 标准 Linux 内核 中断上下文(不可抢占) 自旋锁(忙等待) 软中断(不可抢占) RCU 读端(不可抢占) PREEMPT_RT 补丁 PREEMPT_RT 内核 中断线程化(可被抢占) 可睡眠锁(rt_mutex) 软中断线程化(可被抢占) 可抢占 RCU 三大核心改造效果 1. 中断线程化:将硬中断处理程序变为内核线程,受实时调度器管理。 2. 自旋锁可抢占化:用 rt_mutex 替代 spinlock,锁等待时让出 CPU。 3. RCU 抢占化:在读端临界区内允许抢占,进一步缩小不可抢占窗口。 结果:内核几乎完全可抢占,最坏情况延迟降低 1-2 个数量级。

3.2 内核配置选项详解

在配置 PREEMPT_RT 内核时,需要正确设置一系列内核配置选项。这些选项决定了内核的实时行为。下表列出了最关键的配置项及其含义:

配置选项 推荐值 说明
CONFIG_PREEMPT_RT y 启用完全可抢占内核(Fully Preemptible Kernel),这是 PREEMPT_RT 的核心开关。
CONFIG_HZ_1000 y 设置内核时钟频率为 1000 Hz。更高的频率提供更细粒度的调度时间片,降低调度延迟。
CONFIG_HIGH_RES_TIMERS y 启用高精度定时器,提供纳秒级定时精度,是实时应用的基础。
CONFIG_NO_HZ_FULL y 自适应滴答(Adaptive Ticks)模式,在 CPU 空闲或运行单个任务时停止周期性时钟中断,减少干扰。
CONFIG_IRQ_FORCED_THREADING y 强制所有中断处理程序线程化,这是 PREEMPT_RT 的默认行为。
CONFIG_PREEMPT_RCU y 启用可抢占 RCU,允许在读端临界区内发生抢占。
CONFIG_CPUSETS y 支持 CPU 集合,用于将实时任务绑定到特定 CPU 核心,隔离干扰。
CONFIG_RT_GROUP_SCHED y 启用实时组调度,允许对实时任务组进行带宽限制和隔离。
配置陷阱:不要同时启用 CONFIG_PREEMPT_RT 和 CONFIG_PREEMPT(低延迟桌面)或 CONFIG_PREEMPT_VOLUNTARY(自愿抢占)。它们互斥,且 CONFIG_PREEMPT_RT 是唯一提供硬实时保证的选项。
PREEMPT_RT 内核配置决策树 选择抢占模型 CONFIG_PREEMPT_RT CONFIG_HZ_1000 CONFIG_HIGH_RES_TIMERS CONFIG_NO_HZ_FULL

3.3 为内核打上 PREEMPT_RT 补丁并编译

以下步骤演示了如何为 Linux 内核打上 PREEMPT_RT 补丁并进行编译。假设您使用的是 Ubuntu/Debian 系统,内核版本为 5.10.x。

步骤 1:准备环境与下载源码

# 安装编译工具和依赖
sudo apt-get update
sudo apt-get install build-essential libncurses-dev bison flex libssl-dev libelf-dev

# 下载标准内核源码
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

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

步骤 2:应用补丁

# 应用 PREEMPT_RT 补丁
patch -p1 < patch-5.10.100-rt60.patch

# 验证补丁是否成功应用(无 .rej 文件生成)
find . -name "*.rej"

步骤 3:配置内核

# 使用当前系统的配置作为基础
cp /boot/config-$(uname -r) .config

# 进入菜单配置界面
make menuconfig

# 在菜单中导航并设置:
# General setup --->
#   Preemption Model (Fully Preemptible Kernel (RT))  ---> 选择此项
# Processor type and features --->
#   Timer frequency (1000 Hz)  ---> 选择此项
#   High Resolution Timer Support ---> 启用
# Kernel Features --->
#   NO_HZ_FULL ---> 启用

步骤 4:编译与安装

# 编译内核(使用多核加速,-j 参数根据 CPU 核心数调整)
make -j$(nproc)

# 编译内核模块
make modules_install

# 安装内核
make install

# 更新引导加载器
update-grub

步骤 5:验证实时内核

# 重启并选择新内核启动
sudo reboot

# 登录后检查内核版本和抢占模型
uname -a
# 输出应包含 "PREEMPT RT"

# 检查 /proc/sys/kernel/sched_rt_runtime_us
cat /proc/sys/kernel/sched_rt_runtime_us
# 默认值 -1 表示无限运行时间,可根据需要调整
编译优化提示:对于嵌入式系统,建议使用 make localmodconfig 生成最小化配置,只编译当前硬件所需的模块,可显著减少内核体积和编译时间。
PREEMPT_RT 补丁应用与编译流程 1. 下载源码 内核 + RT 补丁 2. 应用补丁 patch -p1 3. 配置内核 make menuconfig 4. 编译 make -jN 5. 安装与重启 make install && reboot 关键检查点与常见问题 1. 补丁版本必须与内核版本严格匹配,否则应用失败。 2. 配置时务必选择 "Fully Preemptible Kernel (RT)",否则补丁无效。 3. 编译前建议运行 make olddefconfig 自动接受默认值。 4. 如果遇到模块签名问题,可在配置中禁用 CONFIG_MODULE_SIG。 5. 首次启动建议保留旧内核,以便回退。

3.4 验证实时性能

编译并启动 PREEMPT_RT 内核后,可以使用 cyclictest 工具快速验证实时性能。以下命令运行一个基本的延迟测试:

# 安装 rt-tests
sudo apt-get install rt-tests

# 运行 cyclictest,使用 1 个线程,间隔 1000 微秒,运行 10000 次
sudo cyclictest -t1 -p99 -i1000 -l10000 -m

# 输出示例(重点关注 Max 列):
# T: 0 (12345) P: 99 I: 1000 C: 10000 Min: 2 Act: 3 Avg: 4 Max: 18

在 PREEMPT_RT 内核上,Max 值通常应低于 50 微秒(取决于硬件平台)。如果 Max 值超过 100 微秒,则需要进一步进行系统调优,例如隔离 CPU、调整中断亲和性、禁用电源管理特性等。

性能基准:在 x86_64 平台上,一个调优良好的 PREEMPT_RT 系统,其最大延迟通常可以控制在 10-30 微秒以内。在 ARM 嵌入式平台上,这一数值通常在 50-150 微秒之间。