一、实时Linux概述
1.1 什么是实时系统
实时系统,说白了就是「在规定时间内必须完成任务」的系统。
我经常跟刚入行的工程师打个比方:你点了个外卖,系统说30分钟送到。普通系统可能35分钟送到,你骂两句也就完了。但实时系统如果35分钟送到,那可能生产线就停了,机器人撞了,甚至人受伤了。
实时系统分两种:
- 硬实时:截止时间错过 = 系统崩溃。比如汽车安全气囊,必须在碰撞后几毫秒内弹出。
- 软实时:偶尔超时还能接受,但性能会下降。比如视频播放,偶尔卡顿一下还能忍。
判断一个系统是不是实时系统,关键看三点:
- 确定性:任务的响应时间可预测,不是「尽量快」而是「保证在多快」
- 低延迟:从事件发生到系统响应的时间要短
- 抖动小:每次响应时间的波动范围要小
核心观点:实时 ≠ 快速。一个系统可以跑得慢,但只要每次都在规定时间内完成,它就是实时系统。反过来,一个系统跑得飞快,但有时候快有时候慢,那它就不是实时系统。
1.2 实时Linux的发展历程
Linux一开始并不是为实时设计的。它是个通用操作系统,追求的是「平均性能好」。但工业界需要实时能力,怎么办?
我记得2000年初的时候,工业控制领域基本被VxWorks、QNX这些商业实时操作系统垄断。Linux想进去?门都没有。原因很简单——Linux内核的关中断、自旋锁、大量不可抢占的临界区,这些都让实时性无从谈起。
但开源社区的力量是强大的。实时Linux的发展大致经历了三个阶段:
| 阶段 | 时间 | 方案 | 特点 |
|---|---|---|---|
| 第一阶段 | 1990s末-2000s初 | 双内核方案(RTLinux、Xenomai) | 在Linux下跑一个实时内核,实时任务走实时核,非实时任务走Linux核 |
| 第二阶段 | 2000s中-2010s | PREEMPT_RT补丁 | 直接改造Linux内核,让它本身具备实时能力 |
| 第三阶段 | 2010s末至今 | PREEMPT_RT合入主线 | 实时能力成为Linux内核的官方特性 |
这里我想多说一句双内核方案。我在2012年做过一个数控系统项目,用的就是Xenomai。当时为什么选它?因为PREEMPT_RT还不够成熟,抖动还在几百微秒级别。而Xenomai能做到几十微秒。但双内核方案的代价是——调试起来特别痛苦。你想想看,一个任务在实时核上跑,出了问题要查Linux核的日志,两个世界的数据要来回映射。嗯,那段时间我头发掉得厉害。
1.3 PREEMPT_RT补丁介绍
PREEMPT_RT补丁,全称是Real-Time Preemption Patch。它的目标很明确:让标准Linux内核变成硬实时系统。
怎么做到的?核心改造点有四个:
- 完全可抢占内核:把内核中几乎所有不可抢占的临界区都改成可抢占的。包括自旋锁改成互斥锁、中断线程化等。
- 中断线程化:把中断处理程序变成内核线程,让它们可以被调度器管理。这样高优先级的实时任务就可以抢占中断处理。
- 高精度定时器:用高精度定时器(HRT)替代传统的jiffies定时器,精度从毫秒级提升到纳秒级。
- 优先级继承:解决优先级反转问题。低优先级任务持有锁时,临时提升它的优先级,避免高优先级任务被无限阻塞。
我的经验:曾经有个项目,电机控制周期要求1ms,抖动不能超过50μs。我们用PREEMPT_RT跑在x86工控机上,实测抖动在20μs以内。但同样的代码放到ARM上,抖动就飙到了100μs以上。为什么?因为ARM的中断控制器和缓存架构跟x86不一样。所以记住:实时性能跟硬件平台强相关,别指望一套配置通吃所有平台。
使用PREEMPT_RT补丁其实很简单:
# 下载内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.tar.xz
# 下载对应版本的PREEMPT_RT补丁
wget https://cdn.kernel.org/pub/linux/kernel/projects/rt/5.15/patch-5.15-rt.tar.xz
# 打补丁
cd linux-5.15
xzcat ../patch-5.15-rt.tar.xz | patch -p1
# 配置内核,启用实时选项
make menuconfig
# 进入 General setup → Preemption Model
# 选择 Fully Preemptible Kernel (Real-Time)
# 编译安装
make -j4
make modules_install
make install
配置完成后,你可以通过查看 /proc/sys/kernel/sched_rt_runtime_us 来确认实时调度是否生效。
注意:PREEMPT_RT补丁不是万能的。它会让内核的吞吐量下降5%-15%,因为增加了调度开销。如果你的应用对吞吐量要求极高,但对实时性要求不高,那可能不需要打这个补丁。我在一个数据采集项目中就犯过这个错——打了补丁后实时性上去了,但数据吞吐量掉了20%,最后不得不重新评估需求。
1.4 工业控制对实时性的要求
工业控制领域对实时性的要求,可以用一句话概括:该什么时候完成,就必须什么时候完成。
不同场景的要求差别很大。我整理了一个表格:
| 应用场景 | 典型周期 | 抖动要求 | 实时等级 |
|---|---|---|---|
| PLC逻辑控制 | 10-100ms | ±1ms | 硬实时 |
| 运动控制(伺服) | 0.5-4ms | ±50μs | 硬实时 |
| 机器人控制 | 1-10ms | ±100μs | 硬实时 |
| 数据采集 | 1-100ms | ±500μs | 软实时 |
| HMI人机界面 | 50-200ms | ±10ms | 软实时 |
你可能会问:为什么运动控制要求这么高?
我举个例子。一个伺服电机以3000转/分钟运行,也就是每秒50转。如果控制周期是1ms,那么每个周期电机转了18度。如果抖动达到100μs,电机位置误差就是1.8度。对于高精度加工来说,这个误差可能直接导致工件报废。
工业控制对实时性的要求,具体体现在以下几个方面:
- 确定性调度:高优先级任务必须在指定时间内获得CPU
- 低中断延迟:从硬件中断触发到ISR开始执行的时间要短且稳定
- 低上下文切换开销:任务切换不能消耗太多CPU时间
- 资源隔离:实时任务和非实时任务不能互相干扰
- 可预测的I/O:外设访问的时间要可控
一句话总结:工业控制要的不是「快」,而是「稳」。一个系统哪怕跑得慢一点,只要每次都在规定时间内完成,它就是合格的实时系统。反过来,一个系统跑得飞快但偶尔掉链子,那在工业现场就是灾难。
好了,这一章的内容就到这里。实时Linux的核心概念、发展脉络、PREEMPT_RT补丁的原理,以及工业控制对实时性的具体要求,我都结合自己的项目经验讲了一遍。下一章我们会深入探讨实时内核的调度机制,看看Linux到底是怎么保证任务按时完成的。
个人建议:如果你是第一次接触实时Linux,不要急着打补丁。先在普通Linux上跑一个周期任务,用cyclictest工具测一下抖动。看看你的硬件平台在原生Linux下的实时表现。然后再打上PREEMPT_RT补丁,对比前后的差异。这样你才能真正理解这个补丁的价值。