第316篇 Linux PREEMPT_RT实时补丁详解

发布时间:2026/9/1 7:54:38
第316篇 Linux PREEMPT_RT实时补丁详解 上篇聊了FreeRTOS等RTOS在MCU上的应用。但很多机器人场景中计算密集的任务感知、规划需要跑在Linux上。普通Linux的实时性不够好——最大延迟可能达到毫秒级别。PREEMPT_RT补丁把Linux变成了一个接近RTOS的实时操作系统。为什么Linux不够实时普通Linux内核中有很多不可抢占的区域。最大的问题是中断处理——中断处理期间内核是关抢占的如果一个中断处理程序执行时间长比如网络驱动处理大量数据包其他高优先级的实时线程就被阻塞了。另一个问题是spin_lock。内核中大量使用spin_lock来保护共享数据结构。持有spin_lock期间内核禁止抢占。如果锁的持有时间长实时线程的调度延迟就大。还有内核线程的问题。一些内核操作比如内存回收、文件系统同步在内核线程中执行这些线程的优先级和调度策略不受用户控制。PREEMPT_RT做了什么PREEMPT_RT补丁对Linux内核做了几个关键修改大幅提高了实时性能。中断线程化。把几乎所有的中断处理程序都转换成了内核线程。中断触发时只做一个最小化的应答ack硬件实际的处理工作在线程上下文中执行。这样中断处理就变成了一个普通的内核线程可以被高优先级的实时线程抢占。每个中断线程可以独立设置优先级和调度策略。比如网络中断线程可以设低优先级不影响控制任务而定时器中断线程设高优先级。Spin_lock变成可抢占的Mutex。原来持有spin_lock时不能抢占现在变成了可以睡眠的锁rt_mutex。高优先级的实时线程不会因为低优先级线程持有锁而被长时间阻塞。当实时线程等待rt_mutex时持锁线程的优先级会被临时提升优先级继承确保它尽快释放锁。实测表明PREEMPT_RT内核的锁操作比普通内核慢2到5倍但在大多数机器人应用中这个开销可以接受。高精度定时器。PREEMPT_RT使用高精度定时器hrtimer替代了原来的低精度定时器定时分辨率从毫秒级提升到了纳秒级。这对需要精确定时的控制任务非常重要——比如1kHz的控制循环如果用低精度定时器周期可能在0.9ms到1.1ms之间波动用hrtimer可以控制在1ms正负几微秒。线程化的softirq。softirq比如网络收包的NAPI、块设备IO完成也被线程化了可以在实时线程的调度框架下管理。这意味着你可以给网络收包线程设一个较低的优先级让它不会干扰控制任务。内核中还有一些区域保持不可抢占。比如某些体系结构相关的代码上下文切换的核心部分、一些底层的内存管理操作。这些区域通常非常短几百纳秒对实时性能的影响有限。实时性能的量化PREEMPT_RT的效果可以用cyclictest来量化。在一台普通的x86机器上普通Linux最大延迟通常在几百微秒到几毫秒之间取决于系统负载。高负载时可能超过10ms。PREEMPT_RT Linux最大延迟通常在几十微秒以内。即使在较高负载下也能保持在100微秒以下。在ARM嵌入式平台上比如i.MX6或JetsonPREEMPT_RT的表现类似。最大延迟从普通Linux的几毫秒降低到几十微秒。# cyclictest的使用示例 # 创建3个实时线程优先级80周期1ms运行60秒 sudo cyclictest -l100000000 -m -S -p80 -i1000 -t1 -a0 -D60 # 输出: T:0 (12345) P:80 I:1000 C:60000 Min:1 Act:2 Avg:2 Max:8 # Max:8 表示最大延迟是8微秒怎么编译和使用PREEMPT_RT编译PREEMPT_RT内核的步骤。下载Linux内核源码和对应版本的PREEMPT_RT补丁。给内核打补丁patch -p1 patch-xxx-rt.patch。配置内核make menuconfig在Fully Preemptible Kernel选项中选择Fully Preemptible。编译安装内核。重启后用uname -r确认内核版本带有rt标记。使用PREEMPT_RT时应用程序需要设置实时调度策略。用sched_setscheduler()系统调用或者chrt命令# 把进程设置为SCHED_FIFO策略优先级50 sudo chrt -f 50 ./my_realtime_app # 或者用SCHED_RR时间片轮转 sudo chrt -r 50 ./my_realtime_appSCHED_FIFO和SCHED_RR是两种实时调度策略。SCHED_FIFO没有时间片——高优先级任务一直运行直到主动让出CPU或者被更高优先级任务抢占。SCHED_RR有时间片——同优先级的实时任务轮流执行。面试要点PREEMPT_RT的核心修改。面试官问PREEMPT_RT做了什么你要能说出中断线程化和spin_lock转Mutex这两个最关键的修改。这两个修改解决了Linux中最大的两个不可抢占区域。PREEMPT_RT的局限性。虽然PREEMPT_RT大幅改善了实时性能但它仍然不是硬实时的。内核中还有一些小的不可抢占区域比如某些架构相关的代码最大延迟不能保证为零。如果需要真正的硬实时保证微秒级的确定性还是需要用RTOS。PREEMPT_RT适合软实时和准硬实时的场景。CPU隔离。除了PREEMPT_RTLinux还提供了CPU隔离功能isolcpus或nohz_full启动参数可以把某些CPU核心从通用调度中隔离出来专门给实时任务使用。这进一步减少了实时任务被干扰的可能性。在机器人中通常把一个核心专门分配给控制任务其他核心跑感知和规划。配合irqbalance的排除列表确保中断也不会分配到隔离的核心上。内核配置的影响。PREEMPT_RT的效果还跟其他内核配置有关。CONFIG_NO_HZ动态时钟滴答可以减少不必要的定时器中断。CONFIG_HIGH_RES_TIMERS启用高精度定时器。CONFIG_CPU_FREQ设为performance模式避免频率切换带来的延迟。这些配置组合起来能让PREEMPT_RT的效果最大化。内存锁定。实时程序的内存应该被锁定在物理内存中mlockall防止被交换到磁盘。页面错误page fault会导致不可预测的延迟——如果一个页面不在物理内存中内核需要从磁盘读取这个时间可能是几毫秒甚至更长。在启动时调用mlockall(MCL_CURRENT | MCL_FUTURE)可以避免这个问题。PREEMPT_RT的版本兼容性。PREEMPT_RT补丁需要跟特定的Linux内核版本匹配。不是每个内核版本都有对应的RT补丁。选择内核版本时要确认有对应的RT补丁可用。目前主线Linux5.15已经合并了部分PREEMPT_RT的代码未来可能会完全集成到主线中。在那之前还是需要单独打补丁。给你的建议如果你在做机器人软件开发PREEMPT_RT是必须了解的技术。建议在一台x86电脑或者树莓派上编译一个PREEMPT_RT内核跑cyclictest看看效果。然后写一个简单的实时程序体验SCHED_FIFO和SCHED_RR的区别。在项目中如果你的控制循环跑在Linux上一定要用PREEMPT_RT。普通Linux的延迟波动会严重影响控制质量。配合CPU隔离和实时调度策略Linux上的控制循环可以达到几十微秒级的抖动。部署PREEMPT_RT时容易忽略的几个点。BIOS中关闭C-states和P-states——CPU的深度睡眠状态和频率切换都会引入延迟。关闭Turbo Boost——频率的动态变化导致执行时间不确定。设置正确的GRUB启动参数isolcpus2,3 nohz_full2,3 rcu_nocbs2,3隔离2号和3号核心。用systemd设置实时任务的CPU亲和性CPUAffinity2。这些配置组合起来能让你的实时任务获得最佳的确定性。还有一个建议用trace-cmd和kernelshark来分析PREEMPT_RT内核的调度行为。这两个工具可以可视化内核的调度事件帮你找到延迟的来源。如果发现某个内核函数频繁导致长延迟可能需要打补丁或者换一种实现方式。上一篇第315篇 RTOS与FreeRTOS下一篇预告第317篇 实时调度策略详解