3. 实时Linux基础与PREEMPT_RT
在工业控制场景中,“实时”并不等于“快”,而是指系统能够在确定的时间上限内对外部事件作出响应。一个 1 GHz 的处理器如果因为内核临界区不可抢占而延迟 3 ms 才响应编码器中断,它依然不满足 250 μs 周期的运动控制需求;相反,一个 400 MHz 的 Sitara 处理器只要最坏响应时间稳定在 80 μs 以内,就可以胜任大量伺服与 IO 任务。因此,理解实时 Linux,首先要建立正确的指标体系,再理解内核为什么会产生延迟,最后才谈 PREEMPT_RT 如何消除这些延迟。
一、实时性指标:用数字描述“确定性”
评价一个实时系统,通常关注以下四类指标:
- 中断延迟(Interrupt Latency):从硬件中断信号到达 CPU 引脚,到中断服务程序(ISR)第一条指令执行的时间。它主要受中断关闭(关中断)区间长度影响。
- 调度延迟(Scheduling Latency):从中断唤醒一个高优先级实时线程,到该线程真正获得 CPU 开始执行的时间。它受内核不可抢占区间、调度器路径长度影响。
- 最坏情况响应时间(WCRT):从事件发生到实时任务完成关键动作的总时间,是中断延迟、调度延迟、任务自身执行时间的叠加,也是工业验收真正关心的指标。
- 抖动(Jitter):多次响应时间的最大值与最小值之差。周期控制中,抖动往往比平均延迟更致命,因为它直接造成采样时刻漂移。
举例说明:某 Sitara AM335x 平台运行 1 kHz 控制周期,若实测响应时间分布为最小值 42 μs、最大值 96 μs,则平均延迟约 60 μs,抖动约 54 μs。若控制算法允许的采样偏差为 ±20 μs,则该抖动已超标,需要进一步优化内核配置或调整任务优先级。这里的数据属于教学示例,用于说明指标之间的关系,并非实测结论。
二、内核抢占模型:延迟从哪里来
标准 Linux 内核为追求吞吐量,在大量位置禁止抢占:自旋锁临界区、中断处理上半部、软中断、RCU 读侧临界区等。当高优先级实时任务被唤醒时,如果 CPU 正处于这些不可抢占区间,它只能等待,这段时间就是调度延迟的主要来源。Linux 内核因此提供了几种抢占模型:
- PREEMPT_NONE:服务器型配置,几乎不允许内核态抢占,吞吐最高,延迟最大。
- PREEMPT_VOLUNTARY:在显式抢占点允许调度,延迟有所改善,但仍不满足硬实时。
- PREEMPT:允许内核态被抢占,但自旋锁临界区、中断上下文仍不可抢占。
- PREEMPT_RT:将绝大多数不可抢占区间转化为可抢占,目标是把最坏延迟压到几十微秒量级。
理解这一点非常关键:PREEMPT_RT 并不是让 CPU 变快,而是通过减少“不可抢占窗口”的长度,把延迟的尾部削平,从而提升确定性。
三、PREEMPT_RT 补丁原理:把不可抢占变成可抢占
PREEMPT_RT 的核心思想可以概括为“把内核中所有长延迟的不可抢占路径,改造成可被高优先级任务打断的路径”。主要机制包括:
- 自旋锁转为可睡眠的 rt_mutex:普通 spinlock 在持有期间禁止抢占,PREEMPT_RT 将其替换为支持优先级继承的 rt_mutex。当高优先级任务需要同一把锁时,持有锁的低优先级任务会临时继承高优先级,避免优先级反转。
- 中断线程化:除少数必须直接处理的“硬中断”外,大部分中断处理程序被转换为内核线程,拥有自己的优先级,可被更高优先级的实时任务抢占。
- 软中断与 tasklet 线程化:软中断、tasklet 被移入内核线程上下文执行,不再在中断返回路径上长时间占用 CPU。
- RCU 与本地锁优化:将 RCU 读侧临界区、per-CPU 锁等不可抢占区间缩短或改为可抢占。
- 高精度定时器与时钟源:配合 hrtimer,使周期任务唤醒时刻更精确,减少定时抖动。
这些机制共同作用后,内核中剩余的不可抢占区间通常只有几微秒量级,实时任务被唤醒后可以迅速获得 CPU。
四、在 Sitara 上的适配要点
Sitara 系列(如 AM335x、AM437x、AM57x)采用 ARM Cortex-A8/A9/A15 内核,适配 PREEMPT_RT 时需要关注以下几点:
- 内核版本与 RT 补丁匹配:PREEMPT_RT 补丁与主线内核版本严格对应,必须选择 TI SDK 所支持的内核版本,再应用对应版本的 RT 补丁,不能跨版本混用。
- 配置选项:开启
CONFIG_PREEMPT_RT,同时确认CONFIG_HIGH_RES_TIMERS、CONFIG_NO_HZ_FULL(按需)、CONFIG_PREEMPT_RT_FULL等选项正确设置。 - 中断亲和与隔离:将实时任务绑定到独立 CPU 核,把非实时中断迁移到其他核,减少对实时核的干扰。例如在双核 AM335x 上,可将控制任务绑定到 CPU1,网络与文件系统中断绑定到 CPU0。
- 时钟源与定时器:确认使用高精度定时器,避免使用低精度 jiffies 定时器导致周期抖动。
- 驱动实时性:检查所用外设驱动是否存在长临界区或忙等待,必要时替换为支持 RT 的驱动版本。
- 电源管理与频率调节:关闭 CPU 频率动态调节(cpufreq)或将其固定在高性能档,避免频率切换引入额外延迟。
cyclictest 进行长时间压力测试。例如:cyclictest -m -p 80 -n -i 1000 -l 1000000,表示以 80 优先级、1 ms 周期运行 100 万次,观察最大延迟。若最大延迟稳定在 100 μs 以内,通常可满足多数工业控制需求。
五、从指标到验收:一个可参考的检查清单
在实际项目中,可以按以下清单逐项确认实时性是否达标:
| 检查项 | 目标值(示例) | 验证方法 |
|---|---|---|
| 中断延迟 | < 30 μs | 示波器测量 GPIO 中断到 ISR 翻转 |
| 调度延迟 | < 50 μs | cyclictest 最大延迟 |
| 周期抖动 | < ±20 μs | 长时间 cyclictest 统计 |
| 最坏响应时间 | < 100 μs | 端到端信号测量 |
| CPU 隔离 | 实时核无干扰 | 查看 /proc/interrupts 与 taskset |
需要强调的是,表中数值为教学示例,实际目标应根据具体控制周期、工艺精度和硬件平台确定。PREEMPT_RT 提供的是确定性的基础,而最终能否达标,还取决于任务设计、驱动质量、CPU 隔离和系统负载管理。只有在指标、内核模型、补丁机制和平台适配四个层面都理解清楚,才能在 Sitara 上构建真正可靠的实时 Linux 控制系统。