3. PREEMPT_RT补丁详解
PREEMPT_RT 补丁是 Linux 内核实现硬实时能力的关键技术。它通过一系列精细化的内核改造,将标准 Linux 内核从“尽力而为”的软实时系统,转变为可预测、低延迟的硬实时操作系统。本章将深入剖析 PREEMPT_RT 的原理、关键内核配置选项,并手把手演示如何为内核打上补丁并编译。
3.1 PREEMPT_RT 原理
标准 Linux 内核在中断上下文、软中断以及自旋锁持有区域中,抢占是被禁止的。这意味着即使一个高优先级的实时任务就绪,也必须等待这些不可抢占区域执行完毕,从而产生不可预测的延迟抖动(Jitter)。PREEMPT_RT 的核心思想是:将内核中几乎所有不可抢占的区域都变为可抢占,从而将最大延迟降低到微秒级。
核心目标:将内核的最坏情况延迟(Worst-Case Latency)从毫秒级降低到几十微秒甚至更低,使 Linux 能够满足工业控制、航空航天、机器人等领域的硬实时要求。
PREEMPT_RT 主要通过以下三大机制实现这一目标:
- 中断线程化(Interrupt Threads):将原本在关闭中断的硬中断上下文中执行的中断处理程序,改为运行在内核线程中。这些线程可以被实时调度器管理和抢占,从而避免高优先级任务被长中断处理程序阻塞。
- 自旋锁可抢占化(Sleeping Spinlocks / RT-Mutexes):将内核中大部分自旋锁(spinlock)替换为支持睡眠的互斥锁(rt_mutex)。当一个任务试图获取已被占用的锁时,它会被阻塞并让出 CPU,而不是忙等待。这消除了优先级反转和死锁的根源,并允许高优先级任务立即抢占低优先级任务。
- RCU 抢占化(Preemptible RCU):将读-拷贝-更新(RCU)机制改造为可抢占版本,使得在读端临界区内也可以发生抢占,进一步缩小了不可抢占区域。
关键概念:优先级反转(Priority Inversion)是实时系统的头号敌人。PREEMPT_RT 通过优先级继承协议(Priority Inheritance Protocol)与可睡眠锁的结合,从根本上解决了这一问题。
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 是唯一提供硬实时保证的选项。
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 生成最小化配置,只编译当前硬件所需的模块,可显著减少内核体积和编译时间。
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 微秒之间。