第三章|CPU 使用率的八种状态拆解

来源:《Linux 性能实战系列》· 第3篇
适用场景:智能驾驶域控制器 | ECU性能分析 | 仿真平台线上问题排查

在第二章中,我们已经明确了一件事:Load Average 反映的是 R + D 状态的“排队压力”,而不是 CPU 使用率本身。
但当我们确认 “Load 高,确实是 CPU 忙导致的” 之后,真正的问题才刚开始:

CPU 到底在忙什么?

这一章,我们不再笼统地谈“CPU 利用率”,而是把 CPU 的时间拆成八种状态,一步步定位:是业务代码在跑?是内核在忙?还是中断、I/O 在偷偷吃掉算力?
我们把 CPU 时间切成八个状态,精确定位"忙在用户态、内核态、还是中断处理",建立细粒度的观测能力。


为什么需要拆解 CPU 状态?

常见误区:看到 CPU 利用率 80%,就认为"系统很忙"。

实际情况可能是:

  • 80% 都在 wa(iowait)→ CPU 在等 IO,实际计算很少。
  • 80% 都在 si(软中断)→ 网络包处理压力大,业务代码没跑多少。
  • 80% 都在 sy(内核态)→ 系统调用频繁,可能有锁竞争。

不拆解状态,就无法对症下药。


CPU 时间的“八种口味”:us/sy/ni/id/wa/hi/si/st

CPU 的时间就像一块蛋糕,可以被切成不同部分,分配给不同类型的任务。理解这些“口味”的比例,是诊断性能问题的关键。我们可以通过 topmpstat -P ALL 1sar -P ALL 1 5 等工具轻松获取这些值。

状态 名称 描述 自动驾驶场景举例
us User 用户态:执行应用程序代码的时间。 感知算法(如 YOLO、Lidar 点云处理)、规划决策、SOME/IP 序列化。
sy System 内核态:执行系统调用、处理中断、管理内存等内核任务的时间。 频繁的文件读写(录包)、网络发包(SOME/IP)、进程间通信。
ni Nice 低优先级用户态nice 值调整过的进程所消耗的时间。 后台的日志压缩、数据清洗等非实时任务。
id Idle 空闲:CPU 没有任务可执行的时间。 系统负载低,资源充足。
wa I/O Wait 等待 I/O:CPU 等待块设备(如磁盘)I/O 操作完成的时间。 等待摄像头数据写入磁盘、等待模型文件加载。注意:此时 CPU 是空闲的。
hi Hardware IRQ 硬中断:处理硬件设备(如网卡、定时器)中断请求的时间。 网卡收到数据包、CAN 卡接收报文、GPS 的 PPS 秒脉冲信号。
si Software IRQ 软中断:为分担硬中断压力而延迟处理的任务。 网络数据包的协议栈处理(ksoftirqd 进程)、块设备 I/O 的后续处理。
st Steal 被窃取:在虚拟化环境中,被宿主机(Hypervisor)分配给其他虚拟机的时间。 在多 VM 环境中,本 VM 的 CPU 资源被抢占。

这些状态共同构成了 CPU 100% 的时间。一个更直观的关系图如下:

CPU 总时间 = 100%

CPU 空闲时间 (Idle)

CPU 忙碌时间 (Busy)

us (用户态)

sy (内核态)

ni (低优先级)

hi (硬中断)

si (软中断)

st (被窃取)

id (纯空闲)

wa (等 I/O)

性能瓶颈分析方向

这个图清晰地划分了 CPU 忙碌CPU 空闲 两大阵营。特别要注意,wa(I/O Wait)虽然名字里有 “Wait”,但它属于 CPU 空闲时间。这是最常见的误区之一,我们稍后会深入探讨。


关键指标深入分析

1. iowait 的再审视:CPU 真的在“等”吗?

这是 Linux CPU 指标中最容易被误解的一个。

核心纠正:iowait 本质上是 CPU 的一种 空闲 状态。 它衡量的是 CPU 既没有其他任务可执行,又恰好有至少一个任务正在等待块设备 I/O(如磁盘) 的时间比例。

  • wa 高意味着什么?

    • 真正含义:系统中有 I/O 压力,并且在 I/O 等待期间,CPU 没有其他计算任务(R 状态进程)来“填补空白”。
    • 常见误解:wa 高 = 磁盘慢。虽然有关联,但 wa 高也可能是因为同步 I/O 请求过多,即使磁盘性能正常,CPU 也会因为“没事做”而累积 wa 时间。
  • wa 不高,但 load 很高?

    • 这是另一个经典场景。load 由 R 状态和 D 状态进程贡献。如果大量进程处于 D 状态,但等待的不是块设备 I/O(例如,等待网络、进程间锁),那么 load 会飙升,但 wa 可能很低。

诊断 iowait 的三步法:

  1. 确认 I/O 压力: 使用 iostat -x 1sar -d 1

    • r/s, w/s:每秒读写请求数。
    • await:平均每次 I/O 请求的等待时间(包括队列和设备处理)。await 显著增大是 I/O 瓶颈的强信号。
    • avgqu-sz:平均请求队列长度。如果持续很高,说明 I/O 设备已经饱和。
    • %util:设备利用率。接近 100% 表明设备非常繁忙。
  2. 定位等待 I/O 的进程: 使用 pidstat -d 1

    • 这个工具能直接显示出哪些进程正在产生 I/O,以及它们的读写速率。
  3. 检查 D 状态进程: 使用 ps -aux | grep ' D'top

    • 查看 wchan (Wait Channel) 列,了解进程具体在等待哪个内核函数,例如 io_schedulepage_fault 等。

2. 案例:iowait 50% 的自动驾驶数据录制模块

场景:一个自动驾驶测试车在路采时,系统周期性卡顿,top 显示 wa 指标高达 50%。

分析过程

  1. iostat -x 1:发现 /dev/sda(数据盘)的 %util 接近 100%,await 时间超过 500ms,avgqu-sz 持续大于 10。结论:磁盘 I/O 达到瓶颈。

  2. pidstat -d 1:定位到 data_recorder 进程有大量的写操作(kB_wr/s 极高)。

  3. 代码审查:发现 data_recorder 模块为了保证数据不丢失,对每一帧传感器数据(如 Lidar)都执行了同步写入(O_SYNC 标志或每次 write 后调用 fsync)。这种设计在数据量大时会给磁盘带来巨大压力。

解决方案

  • 应用层优化:将同步写改为异步写。数据先写入内存缓冲区(Buffer),由一个独立的线程批量写入磁盘。这样可以将多次小的 I/O 合并为一次大的 I/O,大幅提升效率。
  • 系统层优化
    • 更换硬件:改用更高性能的 NVMe SSD。
    • 文件系统调优:调整文件系统的 commit 间隔,或调整脏页回写(dirty pages writeback)相关的内核参数(/proc/sys/vm/dirty_*),让系统更平滑地刷盘,避免 I/O 突刺。
    • 任务分离:如果可能,将数据录制任务绑定到独立的 CPU 核心和 I/O 控制器,避免与其他关键任务(如感知、规划)争抢资源。

3. 硬中断 (hi) 与软中断 (si):系统响应的“快车道”

中断是系统处理外部事件的核心机制,但如果处理不当,就会成为性能瓶颈。

  • hi (Hardware IRQ):硬中断,由硬件设备(如网卡、键盘、定时器)直接触发。它的优先级非常高,会立即打断当前 CPU 上正在执行的任务。硬中断处理程序(ISR, Interrupt Service Routine)被设计为尽可能快地执行,因为它会“冻结”其他所有操作。

  • si (Software IRQ):软中断,也称为“下半部”(Bottom Half)。它是为了分担硬中断的压力而设计的。硬中断处理程序在完成最紧急的操作后(例如,将网卡的数据拷贝到内存),会触发一个软中断,然后快速退出。剩余的大部分处理工作(例如,将数据包送往协议栈)则由软中断在稍后的、更合适的时机完成。常见的软中断处理由内核线程 ksoftirqd/cpu_id 执行。

为什么 si 过高是常见问题?
因为大量的网络、存储 I/O 都会触发软中断。如果软中断任务过重,ksoftirqd 进程会持续占用大量 CPU,导致用户态的应用程序(us)无法获得足够的 CPU 时间,表现为“系统 CPU 使用率高,但业务上不去”。

4. 案例:网络摄像头导致的 si 飙升

场景:在一个 ADAS 系统中,接入一个新的高分辨率摄像头后,top 显示某个 CPU 核心的 si 持续在 80% 以上,导致图像处理应用丢帧。

分析过程

  1. mpstat -P ALL 1:确认了 CPU3 的 si 使用率异常高。

  2. cat /proc/interrupts:观察中断号的变化速率。发现与 eth0 网卡相关的中断计数在 CPU3 上飞速增长。这表明来自摄像头的网络流量都集中到了 CPU3 上。

    # watch -d 'cat /proc/interrupts'
               CPU0       CPU1       CPU2       CPU3
    ...
    110:          0          0          0    8934521   IR-IO-APIC  eth0
    ...
    
  3. cat /proc/softirqs:观察软中断计数。发现 NET_RX(网络接收)在 CPU3 上增长最快。这进一步确认了瓶颈在于网络数据包的接收处理。

解决方案

  • 开启并配置 RPS (Receive Packet Steering):RPS 是一个纯软件的解决方案,它允许内核将网络数据包的处理(软中断 si)分发到多个 CPU 核心,即使物理网卡只将硬中断(hi)发送到单个核心。

    # echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
    

    f 是一个位掩码,代表 1111,表示允许将软中断分发到 CPU0-CPU3。

  • 配置 IRQ 亲和性 (IRQ Affinity):如果网卡支持多队列(RSS, Receive Side Scaling),可以直接将硬件中断分发到多个 CPU 核心,从根源上解决单点瓶颈。

    # echo 2 > /proc/irq/110/smp_affinity
    

    2 的二进制是 0010,表示将这个中断绑定到 CPU1。通过为不同队列设置不同的 smp_affinity,可以将中断压力均摊。


诊断流程总结:从全局到根源

面对一个 CPU 使用率高的问题,可以遵循以下步骤进行拆解:

  1. 全局观察 (top, uptime)

    • 先看 load average,判断系统负载压力。
    • 再看 top 的 CPU 状态行,对 us, sy, wa, si 等有初步印象。
  2. 多核细节 (mpstat -P ALL 1)

    • 查看每个核心的 CPU 状态。是否存在某个核心“一核有难,八方围观”?单核瓶颈(如 si 过高)和多核瓶颈(如 us 普遍高)的排查方向完全不同。
  3. 专项分析

    • us/sy:CPU 正在忙于计算。使用 perf toppidstat -u 1 定位到具体的热点进程和函数。
    • wa:CPU 在闲等 I/O。使用 iostat -x 1pidstat -d 1 定位 I/O 瓶颈设备和进程。
    • hi/si:中断压力大。使用 cat /proc/interruptscat /proc/softirqs 结合 mpstat 定位中断来源和不均衡问题。
  4. 深度钻取 (perf record)

    • 对于复杂的内核态或用户态热点,使用 perf record -g 抓取调用栈,生成火焰图,可以清晰地看到函数调用关系和耗时分布。

小结

  • CPU 的八种状态是进行性能诊断的“地图”,它指导我们从哪里入手。
  • iowait 是 CPU 的空闲时间,高 wa 意味着“CPU 想干活但没得干,同时有任务在等磁盘”。
  • hisi 是系统响应的关键路径,si 过高通常与网络/存储 I/O 处理不均衡有关,需要通过中断绑核、RPS/RSS 等手段进行优化。
  • 掌握 mpstat, iostat, pidstatperf 等工具的组合使用,是建立 CPU 性能问题“直觉”的必经之路。

下一篇预告

下一章,我们将深入探讨进程的生命周期,特别是神秘的 D 状态(不可中断睡眠),揭示它与 load averageiowait 之间千丝万缕的联系。


在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐