嵌入式 Linux 实时性优化入门:PREEMPT_RT 到底解决了什么问题?¶
适合人群: 有 Linux 基础、想入门实时嵌入式开发的工程师
先从一个问题说起¶
假设你在做一个工业机械臂控制系统,要求每 1ms 精确执行一次控制循环。你用普通的 Linux 跑了一段时间,发现大多数时候都没问题,但偶尔会出现 20ms、甚至 50ms 的"卡顿"——机械臂抖了一下。
这不是你的代码有 bug,而是普通 Linux 内核本身的设计决定了它无法保证实时性。
今天这篇文章,就来聊清楚:什么是实时性、普通内核为什么做不到、PREEMPT_RT 如何解决这个问题,以及你怎么快速上手验证。
一、什么是"实时性"?¶
很多人第一次听到"实时",以为是"很快"。其实不对。
实时性的核心不是快,而是可预测。
一个实时系统的要求是:在规定时间内,一定能完成任务。
| 普通系统 | 实时系统 | |
|---|---|---|
| 关注指标 | 平均响应时间 | 最坏响应时间(WCET) |
| 典型场景 | 网页服务器 | 电机控制、音频采集 |
| 偶尔延迟 | 可以接受 | 不可接受 |
实时系统还分软实时和硬实时。工业控制、汽车 ECU 这类场景要求硬实时——错过 deadline 就是故障。
二、普通 Linux 为什么无法保证实时性?¶
普通 Linux 的调度器设计目标是整体吞吐量最大化,不是最坏延迟最小化。主要有三个"拦路虎":
① 内核不可抢占区域
普通内核里有大量使用自旋锁(spinlock)保护的临界区。当一个低优先级任务持有自旋锁时,高优先级任务只能等着——哪怕你把任务优先级设再高也没用。这段等待时间完全不可控。
② 中断会阻塞所有任务
外设中断(比如网卡、USB)触发时,当前 CPU 会停下来执行中断服务程序(ISR)。ISR 执行期间,任何任务都被阻塞,包括你的"高优先级实时任务"。
③ 内核线程优先级混乱
普通内核里的 softirq、workqueue 等机制会在低优先级内核线程中处理大量工作,调度顺序难以精确控制。
这三个问题加在一起,导致普通 Linux 的最坏调度延迟可能高达几十毫秒,而且无法预测。
三、PREEMPT_RT 做了什么?¶
PREEMPT_RT 是一组内核补丁(现在已逐步合入主线),核心思路是:让内核的几乎每一个地方都可以被高优先级任务抢占。
它主要做了三件事:
① 把自旋锁换成可睡眠的互斥锁
原本持有自旋锁时 CPU 会忙等(spin),现在换成 rt_mutex,持有锁的任务可以被挂起,让更高优先级的任务先跑。这是最关键的改动。
② 中断线程化(Threaded IRQ)
把绝大多数中断处理程序变成普通的内核线程(irqthread),这样它们就有了优先级,实时任务可以抢占中断处理线程。
③ 全面可抢占内核
通过更细粒度的锁和调度点,内核态代码的大部分路径都变得可抢占,大大缩短了高优先级任务被阻塞的时间窗口。
效果是什么?在打了 PREEMPT_RT 补丁的内核上,经过合理配置,最坏调度延迟可以控制在几十到几百微秒,而且是可预测的。
四、动手验证:用 cyclictest 测延迟¶
光说不练没意义,下面带你跑一个最经典的实时延迟测试工具:cyclictest。
Step 1:安装工具
Step 2:跑基准测试(普通内核)
参数说明:
-l 100000:循环 10 万次-p 90:任务优先级设为 90(实时优先级)-i 200:每 200 微秒唤醒一次-h 400:统计延迟直方图
Step 3:观察输出
你会看到类似这样的输出:
关注 Max 这一列——这是最坏延迟。普通内核上,这个数字可能飙到几百甚至上千微秒,而且每次跑结果不一样。
Step 4:换 RT 内核后对比
如果你的系统装了 PREEMPT_RT 内核(比如 Ubuntu 的 linux-image-rt-amd64),切换过去重跑同样命令,你会发现 Max 稳定很多,通常控制在 100~300 微秒以内。
五、一些常见问题¶
Q:我用树莓派可以跑 PREEMPT_RT 吗?
可以。树莓派有社区维护的 RT 内核镜像,搜索 raspberry pi preempt-rt 即可找到。延迟比 x86 平台高一些,但做软实时任务完全够用。
Q:打了补丁是不是系统就变慢了?
整体吞吐量会略有下降(通常 5%~15%),因为更多的上下文切换开销。嵌入式场景一般可以接受。
Q:光打补丁够吗?
不够。还需要配合:隔离 CPU(isolcpus)、关闭 CPU 节能模式、设置合适的调度策略(SCHED_FIFO),以及避免在实时线程里使用动态内存分配。这些是后续文章的内容。
小结¶
普通 Linux 的调度延迟不可预测,根本原因是内核中存在不可抢占的临界区和无优先级的中断机制。PREEMPT_RT 通过将自旋锁互斥化、中断线程化和全面可抢占内核三大改动,把最坏延迟压缩到可预测的微秒级。
动手的第一步很简单:装好 rt-tests,用 cyclictest 跑一跑,感受一下数字的变化。