【原创】PREEMPT-RT系统某些应用场景sys cpu使用率周期飙高问题
版权声明:本文为博主原创文章,未经本人同意,禁止转载。如有问题,欢迎指正。博客地址:https://www.cnblogs.com/wsg1100/
一、背景
在 2022 年一次 PREEMPT-RT 系统问题调试中,之前在 CPU 性能优化小记-使用火焰图定位性能问题 中仅定位并解决了其中一个问题,还有一个潜在问题当时没有续写。
然而,最近几乎所有 PREEMPT-RT 产品上都出现了该问题,它严重影响了非实时任务的 CPU 吞吐量,引起了广泛关注。因此,有必要对该问题进行记录和总结,希望对大家有所帮助。
本文侧重于原因分析和结论总结,省略问题定位的详细流程。
二、问题现象
在 PREEMPT-RT 系统的某些应用场景下,即使没有运行特定的应用程序,整个系统的 CPU 负载也会周期性飙升——间隔一段时间后突然升高,持续几百毫秒甚至几秒钟。同一型号不同单板上的持续时间和间隔时间会有所不同。
使用 top 或 pidstat 进行观察,只能看到 system CPU 使用率飙升,且相关线程不固定,与具体线程无关——这说明 CPU 开销发生在内核层面,而非用户进程层面。
典型现象示意:
CPU使用率 (%)
100 | ██
| ██
80 | ██
| ██
60 | ██
| ██
40 | ████████ ██ ████████
| ████████ ██ ████████
20 | ████████ ...... ██ ...... ████████
|__████████___......___██___......___████████___
+----------------------------------------------> 时间
正常 飙高 正常 飙高 正常
(数秒) (数百ms) (数秒) (数百ms)
三、复现条件
找到一个具有良好实时性的机器(可以是 PREEMPT-RT 系统或 Xenomai + RTNet 系统),创建一个高实时任务,步骤如下:
- 创建一个实时任务,使用 raw socket 周期性地向目标机器发送广播帧;
- 发帧周期设置为 500μs、1ms 或 2ms,要求发帧周期必须非常准确;
- 在目标机器上观察 CPU 使用率,即可看到周期性飙高现象。
关键条件:系统中存在一个以上的外部周期事件,且该事件的周期精度要求高。
四、根因分析
该问题为 PREEMPT-RT 的通病(至少作者当前接触到的内核从 3.2 到 5.10 均存在该问题)。当整个系统中存在一个以上的外部周期事件时就会出现,常见场景包括:
- 接收 PLC 发送的周期以太网帧
- 外部 FPGA 触发的周期 IO 中断事件
- EtherCAT 主站同步到从站参考时钟后中断收发以太网帧
- 其他各类外部硬件周期中断
注意:此处"时钟漂移"并非指 Linux clocksource 子系统层面(
/sys/devices/system/clocksource/)的时钟源切换,而是指底层硬件定时器的物理振荡器频率偏差。
时钟漂移与周期交越
问题的核心在于:外部周期事件(中断)基于的时钟源与本机PREEMPT-RT 系统调度时钟源不同(不同硬件定时器/振荡器),这两个时钟之间存在时钟漂移(Clock Drift)。
因此,外部周期事件会与 PREEMPT-RT 本身的**系统调度事件(scheduler tick) **发生周期交越(Beat Frequency),具体过程如下:
- 当两个事件的时间点远离时,各自独立处理,系统正常运行;
- 当两个事件逐渐接近时,需要在短时间内处理两个事件,触发频繁的上下文切换;
- 频繁的上下文切换导致 CPU 使用率飙升;
- 交越结束后恢复正常,等待下一次交越,形成周期性飙高。
交越周期计算:若系统调度 tick 频率为 f₁,外部中断频率为 f₂,则交越周期 T = 1/|f₁ - f₂|。
例如:调度 tick 为 1000 Hz(LAPIC),外部中断为 1000.1 Hz(独立振荡器),则交越周期 T = 1/0.1 = 10 秒,即每 10 秒出现一次 CPU 飙高。两个振荡器频率偏差越大,交越周期越短,CPU 飙高越频繁。
实际中,不同单板的振荡器偏差不同,因此交越周期和持续时间会因硬件而异——这与文中"同一型号不同单板上的持续时间和间隔时间会有所不同"的现象吻合。偏差越大交越周期越短,飙高越频繁;偏差越小交越周期越长,但每次飙高持续时间也相应更长。
上下文切换开销
在 PREEMPT-RT 系统中,为了保证外部事件的实时性,系统采用了牺牲 CPU 吞吐量的调度机制。具体表现为:
| 阶段 | 事件间距 | 系统行为 | CPU 使用率 |
|---|---|---|---|
| 正常阶段 | 较大 | 各事件独立处理,上下文切换少 | 正常 |
| 交越阶段 | 极小 | 两事件几乎同时触发,密集上下文切换 | 飙高 |
| 恢复阶段 | 逐渐增大 | 事件间距拉开,恢复正常调度 | 恢复正常 |
问题示意
以下是两个不同时钟源驱动的周期事件发生交越的示意:
时钟源A (外部中断周期, 如 500μs):
| | | | | | | | | | | |
A A A A A A A A A A A A
时钟源B (系统调度tick, 如 1ms):
| | | | | |
B B B B B B
交越前: 交越中: 交越后:
A A A A AA A A A A
B B B
↑ 间距大,正常 ↑ 间距极小,CPU飙高 ↑ 间距恢复,正常
Mermaid 时序图——周期交越导致 CPU 飙高:
Mermaid 流程图——问题形成机制:
五、解决措施
该问题目前没有彻底解决的方法,但可以采取以下缓解措施:
对于单 CPU 核系统:
系统 tick 无法关闭,该问题无解。唯一的方向是从硬件层面消除时钟漂移的根源,例如使用同一时钟源驱动外部中断和系统调度。
对于 SMP 多核系统:
使能 CONFIG_NO_HZ_FULL(Tickless Kernel),将系统周期 Tick 降至最低,并通过中断亲和性(IRQ Affinity)设置,将周期事件中断绑定到专用 CPU 上:
┌──────────────────────────────────────────────────┐
│ SMP 多核系统 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ CPU 0 │ │ CPU 1 │ │
│ │ (NO_HZ_FULL)│ │ (NO_HZ_FULL)│ │
│ │ │ │ │ │
│ │ 周期实时 │ │ 周期中断 │ │
│ │ 任务运行 │ │ 亲和性绑定 │ │
│ │ │ │ │ │
│ └──────────────┘ └──────────────┘ │
│ ↑ 无周期tick ↑ 外部中断集中处理 │
│ (tickless) (IRQ affinity) │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ CPU 2 │ │ CPU 3 │ │
│ │ (NO_HZ_FULL)│ │ (NO_HZ_FULL)│ │
│ │ │ │ │ │
│ │ 非实时任务 │ │ 非实时任务 │ │
│ │ (不受影响) │ │ (不受影响) │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────┘
具体操作:
- 内核配置使能
CONFIG_NO_HZ_FULL; - 内核启动参数添加
nohz_full=0-1(指定哪些 CPU 进入 tickless 模式); - 通过
/proc/irq/<irq_number>/smp_affinity将外部周期中断的亲和性设置到专用 CPU; - 确保该专用 CPU 上没有周期任务运行。
已知限制:
CONFIG_NO_HZ_FULL仅在 64 位架构上可用(依赖HAVE_VIRT_CPU_ACCOUNTING_GEN)。- 即使启用了
NO_HZ_FULL,内核仍可能大约每秒发送一次 tick 用于统计和负载核算——因此是"近乎"tickless 而非"完全"tickless。- 有报告指出
PREEMPT_RT补丁与NO_HZ_FULL存在交互问题:在某些内核版本上,local APIC timer 中断可能仍在 tickless CPU 上触发。若遇到此问题,建议升级内核或查阅对应版本的 RT 补丁 changelog。NO_HZ_FULL仅在每个 CPU 上只有一个可运行任务时才完全停止 tick,若该 CPU 上有内核线程活动,tick 可能被临时重启,通常结合内核参数isolcpus使用。
关于 Linux 时钟子系统的详细原理,详见本博客之前的文章 linux 时间子系统简介。
六、总结
本文分析了 PREEMPT-RT 系统中 CPU 使用率周期性飙高的问题,其根因是外部周期事件与系统调度事件基于不同时钟源,时钟漂移导致两者周期性交越,引发密集上下文切换。这是 PREEMPT-RT 为保证外部事件实时性而牺牲 CPU 吞吐量的机制所固有的问题。
| 项目 | 说明 |
|---|---|
| 问题 | PREEMPT-RT 系统 CPU 使用率周期性飙高 |
| 根因 | 不同时钟源的周期事件发生时钟漂移与周期交越 |
| 影响 | 非实时任务 CPU 吞吐量下降 |
| 范围 | 至少内核 3.2 ~ 5.10 均存在 |
| 缓解方案要求 | CONFIG_NO_HZ_FULL 从内核 3.10 起可用,且仅支持 64 位架构 |
| 彻底解决 | 目前无彻底方案 |
| 缓解措施 | SMP 系统:CONFIG_NO_HZ_FULL + 中断亲和性 |
| 单核系统 | 无解 |
下一篇文章,我们将探讨由 PREEMPT-RT 实时机制导致的网络风暴下系统死机问题。

PREEMPT-RT 系统某些应用场景sys cpu使用率周期CPU飙高问题记录。
浙公网安备 33010602011771号