Xenomai 实时系统系列(一):Linux 很快,但不一定准时

本章从一个电机控制任务出发,认识实时性,再比较两条路线:PREEMPT_RT 和 Xenomai 3 Cobalt。

1. 先讲一个会迟到的电机控制器

假设你写了一个电机控制程序,每隔 1 ms 读取传感器、计算误差,再输出控制量。代码逻辑正确,CPU 占用也不高,你甚至已经开始想庆功吃什么了。

结果电机偶尔抖一下。

排查后发现:大多数控制周期都很正常,某一轮却晚了十几毫秒。计算公式没错,但轮到结果出场时,现场已经不是刚才那个现场了。

控制系统需要答案正确,也需要答案及时到达。

控制周期、任务释放与截止时间

图 1:以截止时间等于周期的任务为例。等待、计算和输出,都要算进这一轮的时间预算。

这里先认清两个词:周期是任务多久来一次,截止时间是某次任务最晚什么时候完成。两者可以一样,也可以不一样。

超过截止时间的后果也有轻重。硬实时需求不允许错过规定的截止时间;软实时需求则可能容忍少量超时,只是质量会下降。电机超时是否会造成危险,要看控制策略和设备,不能只凭“电机”两个字就下结论。

本系列更关注需要严格时间预算的控制和采集任务。

2. 平均成绩好,不代表每次都准时

先看两组人为构造的延迟样本:A 组前五次很漂亮,第六次突然冒出大尖峰;B 组没有那么抢眼,但相当稳定。

构造样本中平均延迟与偶发尖峰的对比

图 2:这是概念示例,不是两种内核的测试结果。柱高经过示意处理,真实数值见标注。

只截取 A 组前五次,你会得到一张漂亮成绩单。把第六次也放进来,结论立刻变了。实际长时间测试中,少量尖峰还可能被大量正常样本“稀释”,让整体平均值仍然很好看。

因此,我们既看平均值,也看最大观测值、延迟分布和超时次数。如果任务预算只有 100 μs,那么“平均 30 μs”并不能替偶发的 2 ms 开脱。

实时系统关心的是:在约定的运行条件下,任务能不能守住截止时间。

3. Linux 为什么会偶尔迟到?

先替 Linux 说句公道话:没有 PREEMPT_RT,也不代表 Linux 完全不能抢占、没有实时调度策略。普通 Linux 同样支持 SCHED_FIFOSCHED_RR 等策略,抢占能力还取决于具体配置。

问题在于,高优先级不是一张“随时插队、立即执行”的通行证。

线程就绪时,CPU 可能还在处理硬中断或不可抢占的临界区;线程开始执行后,也可能因为锁竞争、缺页或 I/O 等待而无法及时完成。不同障碍发生在不同位置,不能全部塞进“调度器太慢”这个筐里。

事件响应、调度等待和端到端完成时间

图 3:任务开始运行,只说明它拿到了 CPU,不说明这一轮控制已经完成。

这张图也解释了一个常见误会:定时唤醒延迟是 5 μs,不等于整个控制回路只需要 5 μs。传感器传输、控制算法、驱动访问和执行器输出,都还有自己的时间成本。

怎样减少这些不受控的等待?接下来有两条路线。

4. PREEMPT_RT:先把 Linux 内部改得更容易抢占

PREEMPT_RT 的思路是:尽量把长时间不能打断的工作,变成能由调度器管理的执行上下文。这样高优先级任务就绪后,更容易及时抢占当前工作。Linux 官方原理说明

打个比方:原先窗口人员办一笔复杂业务,中途不能停;改造后,他能保存进度,先处理优先级更高的业务,再回来接着办。

PREEMPT_RT 如何减少实时任务等待

图 4:图中的宽度不表示性能比例;重点是原先较长的不可抢占工作被重新组织了。

其中有三项关键改造:

  • 中断线程化。许多设备中断处理转到可调度的 IRQ 线程中,能参与优先级安排;底层硬中断入口和部分特殊中断仍有例外。
  • 改变部分内核锁的行为。spinlock_t 等采用适合 RT 的实现,竞争时可阻塞,并支持优先级继承;raw_spinlock_t 等关键锁仍保持原有的不可抢占语义。
  • 减少不可抢占区。让更多内核执行路径有机会被高优先级任务打断,但不会消灭全部临界区。中断与执行上下文内核锁类型

“优先级继承”也可以用窗口来理解:低优先级任务拿着高优先级任务要用的锁,就暂时提高持锁者的优先级,让它尽快办完、交还钥匙。它减少不必要的插队干扰,不会让锁里的慢操作凭空变快

PREEMPT_RT 的好处,是继续沿用 Linux 的线程、系统调用和生态。不过,SCHED_FIFO、IRQ 优先级、内存锁定和驱动行为仍要配置与验证。改造了调度机制,不等于每个文件读写操作都自动拥有时间上界。

5. Xenomai Cobalt:增加一套实时协同机制

另一条路线是:让一套专门的实时核心处理关键任务,同时保留 Linux 的通用功能。

本章提到的“双内核 Xenomai”,特指 Xenomai 3 的 Cobalt 配置。这是软件架构,不是“买两颗 CPU,一颗装 Linux、一颗装 Xenomai”。Xenomai 官方安装说明

PREEMPT_RT 与 Xenomai 3 Cobalt 架构比较

图 5:左边改造同一个 Linux 内核;右边由 Cobalt 与 Linux 协同运行。连接表示架构关系,省略了具体系统调用细节。

在图示的 Dovetail 配置中:

  • Cobalt负责实时任务的调度和相关服务。
  • Linux继续负责通用线程、文件系统和普通网络等工作。
  • Dovetail提供中断管线和协同运行所需的机制,让带外(OOB)实时活动能优先于带内(In-band)Linux 活动执行。Dovetail 官方介绍

这里的“带内、带外”描述执行域,不是用户态、内核态的另一个叫法。Xenomai 用户态实时线程也能在带外执行。也不要把两个域直接等同于 CPU0 和 CPU1;绑核与 CPU 隔离是另外一项配置工作。

较早的 Xenomai 3 平台使用 I-pipe。阅读资料时,要先看版本和底层接口,别把 Dovetail 与 I-pipe 当成必须同时堆上的两层。

还有一个容易混淆的名字:Mercury。它是 Xenomai 3 的单内核配置,不增加 Cobalt 这样的实时协同内核,可以搭配 PREEMPT_RT。因此“Xenomai 和 PREEMPT_RT 完全互斥”这个说法不准确。Cobalt 与 Mercury 的区别

6. PREEMPT_RT 与 Cobalt,该怎么比较?

先比较工程模型,再在目标硬件上比较结果。不要先给它们贴上“一个只能软实时、一个保证硬实时”的标签。

对比维度 PREEMPT_RT Xenomai 3 Cobalt
改造思路 增强 Linux 自身的可抢占性 实时协同内核与 Linux 配合
谁调度关键任务 Linux 调度器 带外执行时由 Cobalt 调度
应用接口 主要沿用 Linux / POSIX 接口 使用 Xenomai 支持的实时接口;普通 Linux 调用要注意执行域
驱动与 I/O 沿用 Linux 驱动体系,但须验证 RT 兼容性和时延 严格实时设备路径通常需要 RTDM 或其他经验证的实时访问方式
工程代价 调整内核配置、优先级、亲和性及驱动行为 还要维护匹配的内核基础、用户库、实时驱动和跨域边界
适合优先评估的情况 已有 Linux 应用和驱动,希望较少改变编程模型 关键闭环可独立出来,且硬件与实时驱动已有支持

表中接口与架构差异依据上述官方文档;工程代价和评估顺序是基于这些差异的实践建议。

例如,一个已有完整 Linux 驱动的软件系统,可以先评估 PREEMPT_RT 是否满足截止时间及余量;一个已经有 Cobalt/RTDM 支持的运动控制平台,则可以重点评估这条实时路径。

最终判据还是任务本身:在约定硬件、负载和故障处理条件下,端到端完成时间能否满足预算?不能脱离平台,承诺“PREEMPT_RT 一定多少微秒,Xenomai 一定多少微秒”。硬件和固件也可能引入操作系统无法完全控制的延迟。Linux 实时硬件注意事项

7. 实时线程也不能随便“串门”

用了 Cobalt,实时循环里就能随意写文件、发普通网络包了吗?

还不行。Cobalt 实时线程调用普通 Linux 系统调用时,可能切回由 Linux 管理的执行域。程序还能继续工作,但这段路径的时间性质已经变了。Cobalt 线程模式说明

一个常见划分是:关键循环负责采样、计算和控制输出;日志、普通文件 I/O 和界面通信交给非实时线程。

实时控制循环与非实时服务线程的职责划分

图 6:缓冲区必须明确容量、满队列策略和访问开销。“换个线程”本身并不能解决无限等待。

这个习惯对 PREEMPT_RT 也有价值。严格实时循环应预先准备内存、避免运行中首次缺页,控制锁的持有时间,并审查每个可能阻塞的调用。

CPU 亲和性和隔离也可以帮助减少竞争,但它们不会自动消除内存总线、缓存、DMA、硬件和固件的共享干扰。给任务分了一个核,不等于给它包了一整台机器。

8. 动手:两种工具,先学会看同样的问题

下面是入门命令,参数已按官方手册核对,本章没有在目标实时设备上执行测试。图中的数字都是教学示例。

普通 Linux / PREEMPT_RT 可使用 rt-tests 中的 cyclictest

# 1 个测量线程,FIFO 优先级 80,周期 1000 μs,运行 60 秒
sudo cyclictest --mlockall --threads=1 --policy=fifo \
  --priority=80 --interval=1000 --duration=60 --quiet

它周期性唤醒线程,观察实际唤醒相对预定时刻的偏差。cyclictest 官方项目手册

Xenomai 3 Cobalt 则可使用自带的 latency

# 用户态测量任务,优先级 80,周期 1000 μs,运行 60 秒
# 安装前缀不同,请相应修改路径
sudo /usr/xenomai/bin/latency -t 0 -p 1000 -P 80 -T 60

这里小写 -p 是周期,单位 μs;大写 -P 才是优先级-t 0 测用户任务,-t 1 测内核任务,-t 2 测定时器 IRQ。本章选择 -t 0,因为要先观察用户态实时线程。latency 官方手册

两条命令采用相近参数方便理解,但工具实现和测量路径并不完全相同,不能直接把两个 Max 数字当成严格的内核横评。这里只做入门演示,尚未固定 CPU;正式比较还应统一 CPU 亲和性、周期、优先级、负载、测试时间,并核对测量起止点。

建议先记录空闲基线,再逐项加入 CPU、网络和磁盘负载,最后测试实际控制应用。每次同时保存环境与完整输出,关注最大值、分布及超时次数。

记住:Max 是这轮测试观察到的最大值,不是未来永远不会超过的保证。长测和压力测试能增加证据,仍不能替代完整路径分析与工程验证。

本章小结

实时性从截止时间出发:结果要正确,也要准时。PREEMPT_RT 通过内核抢占改造减少等待;Xenomai 3 Cobalt 则通过协同内核建立带外实时路径。两条路线都需要合适的应用写法、驱动和硬件配合。

下一章继续拆开右边那张图:Dovetail 如何分发中断,Cobalt 与 Linux 又怎样交接执行权?

posted @ 2026-09-18 18:23  大米饭1  阅读(14)  评论(0)    收藏  举报