【原创】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 系统),创建一个高实时任务,步骤如下:

  1. 创建一个实时任务,使用 raw socket 周期性地向目标机器发送广播帧;
  2. 发帧周期设置为 500μs、1ms 或 2ms,要求发帧周期必须非常准确;
  3. 在目标机器上观察 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 飙高:

sequenceDiagram participant EXT as 外部周期中断 participant SYS as 系统调度Tick participant CPU as CPU Note over EXT,CPU: 正常阶段 - 两个事件间距大 EXT->>CPU: 中断处理 (独占) Note right of CPU: 上下文切换少 SYS->>CPU: 调度处理 (独占) EXT->>CPU: 中断处理 (独占) SYS->>CPU: 调度处理 (独占) Note over EXT,CPU: 交越阶段 - 两个事件逐渐靠近 EXT->>CPU: 中断处理 SYS->>CPU: 调度处理 (几乎同时) EXT->>CPU: 中断处理 SYS->>CPU: 调度处理 (几乎同时) Note right of CPU: ⚠ 密集上下文切换 Note right of CPU: CPU使用率飙升! Note over EXT,CPU: 恢复阶段 - 两个事件间距拉开 EXT->>CPU: 中断处理 (独占) SYS->>CPU: 调度处理 (独占) Note right of CPU: 恢复正常

Mermaid 流程图——问题形成机制:

flowchart TD A[两个不同时钟源] --> B[时钟漂移] B --> C[周期事件时间点漂移] C --> D{事件间距变化} D -->|间距大| E[独立处理各事件] E --> F[正常上下文切换] F --> G[CPU使用率正常] D -->|间距极小| H[两事件几乎同时到达] H --> I[密集上下文切换] I --> J[CPU使用率飙升] J --> K[非实时任务吞吐量下降] G --> C K --> C style J fill:#ff6b6b,color:#fff style K fill:#ff6b6b,color:#fff style G fill:#51cf66,color:#fff

五、解决措施

该问题目前没有彻底解决的方法,但可以采取以下缓解措施:

对于单 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)│               │
│  │              │  │              │               │
│  │  非实时任务  │  │  非实时任务  │               │
│  │  (不受影响)  │  │  (不受影响)  │               │
│  └──────────────┘  └──────────────┘               │
└──────────────────────────────────────────────────┘

具体操作:

  1. 内核配置使能 CONFIG_NO_HZ_FULL;
  2. 内核启动参数添加 nohz_full=0-1(指定哪些 CPU 进入 tickless 模式);
  3. 通过 /proc/irq/<irq_number>/smp_affinity 将外部周期中断的亲和性设置到专用 CPU;
  4. 确保该专用 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 实时机制导致的网络风暴下系统死机问题。

七、参考链接

  1. CPU 性能优化小记-使用火焰图定位性能问题
  2. linux 时间子系统简介
  3. PREEMPT-RT 官方文档
  4. CONFIG_NO_HZ_FULL 内核文档
posted @ 2024-11-17 18:21  沐多  阅读(611)  评论(0)    收藏  举报