什么是 CPU 利用率?怎么算的?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 基础概念理解:面试官想看的不是你知不知道这个名词,而是你是否真正理解它衡量的是什么、反映了系统什么状态。
  2. 底层细节掌握:能否说出 CPU 时间的细分构成(用户态、内核态、iowait、软中断等),这是区分 "用过 top 命令" 和 "真懂操作系统" 的分水岭。
  3. 计算原理:知道 /proc/stat 这个数据源,知道 Linux 是通过两次采样做差值来算的,这才是工程师该有的深度。

核心答案

CPU 利用率,简单说就是:在一段时间内,CPU 处于非空闲状态的时间占这段时间的比例。它衡量的是 CPU 有多忙。

核心计算公式:

CPU 利用率 = (总时间 - 空闲时间) / 总时间 × 100%

换个写法:

CPU 利用率 = (1 - idle / total) × 100%

这个公式看着简单,但 Linux 内核实际把 CPU 时间切成了 8 个维度,每个维度都代表不同的 "忙"。

CPU 利用率计算
CPU 利用率计算

深度解析

一、Linux 下 CPU 时间的 8 个维度

在 Linux 里,CPU 时间并不是一个笼统的数,而是被拆得非常细。topvmstatsar 这些工具看到的那些字母,对应的就是这 8 个维度:

缩写 名称 含义
us user 运行用户态进程的时间(不含 nice 调整)
ni nice 低优先级用户态进程的 CPU 时间
sy system 运行内核态的时间(系统调用、内核线程)
id idle CPU 完全空闲的时间
wa iowait CPU 等待 I/O 完成的时间(磁盘、网络)
hi hardirq 处理硬中断的时间
si softirq 处理软中断的时间
st steal 虚拟化环境下,被宿主或其他虚拟机 "偷走" 的时间

这 8 个加起来,就是这段时间 CPU 的总时间

补充一句,/proc/stat 这个原始文件里其实字段更多,从 Linux 2.6.33 起每核有 10 个字段,后面还多了 guest(运行虚拟 CPU)和 guest_nice(低优先级虚拟 CPU)两项。不过 top 等工具展示出来的就是上面这 8 个最常用的。

重点强调几个容易踩坑的点:

  • us 高 → 你的应用代码在狂跑,正常情况,但也可能是死循环
  • sy 高 → 频繁系统调用(比如疯狂 read/write、锁竞争),这块高了要警惕,正常业务 sy 一般不超过 20%
  • wa 高 → I/O 瓶颈,CPU 其实没忙,是在等磁盘/网络,这不是 CPU 算力问题,是磁盘慢
  • si/hi 高 → 中断太多,网络包打爆的典型症状

这些细分维度才是排查线上问题的真正抓手。光看一个总的 CPU 利用率,排查方向都是蒙的。

CPU 时间维度
CPU 时间维度

二、数据从哪来:/proc/stat

Linux 内核把每个 CPU(以及总计)的时间片都维护在 /proc/stat 这个虚拟文件里,单位是 USER_HZ(在 x86 上固定为 100,对应一个 jiffy = 1/100 秒 = 0.01 秒)。

cat /proc/stat

典型输出:

cpu  2255 34 2290 66632 1123 53 128 0 0 0
cpu0 1132 17 1145 33316  561 26  64 0 0 0
cpu1 1123 17 1145 33316  562 27  64 0 0 0

第一行 cpu 是所有核的汇总,后面 cpu0cpu1 是每核单独的。那些数字按顺序就是:

user  nice  system  idle  iowait  irq  softirq  steal  guest  guest_nice

前 8 个就是咱们上面表格里的那 8 个维度。

关键点:这些数字是自系统启动以来的累计值,只会一直往上涨,不会回落。直接除一下得到的是从开机到现在的平均利用率,意义不大。实际中要的是某一段时间内的利用率,所以得做两次采样求差。

三、Linux 到底怎么算的

整个计算过程画成流程图就是这样:

CPU 利用率采样流程
CPU 利用率采样流程

上图就是 Linux 工具(topvmstatsarpidstat)计算 CPU 利用率的核心逻辑,本质上都是两次采样求差。具体步骤拆解如下:

CPU 利用率采样计算
CPU 利用率采样计算

  • 第一步:T1 时刻读一次 /proc/stat,把 cpu 行的字段都记下来,记为 V1
  • 第二步:等待一个采样间隔,通常 1 秒,这就是为什么 top 默认每隔几秒刷新一次
  • 第三步:T2 时刻再读一次 /proc/stat,记为 V2
  • 第四步:算 Δt 内的总时间,把 V2 的字段分别减去 V1 对应字段,加起来就是这段时间 CPU 总共 "走过" 的 jiffy 数
  • 第五步:算 Δt 内的空闲时间,只用 idle 字段做差:V2.idle - V1.idle
  • 第六步:套公式利用率 = (total - idle) / total × 100%

这里有个非常容易答错的点iowait 算不算在利用率里?

答案是:按标准公式算的话,算进去了一部分。因为标准公式 (total - idle) / total 里,totaliowait 也加进去了,但减的只有 idle,没减 iowait。这就导致 wa 高的时候,算出来的 CPU 利用率也会偏高。

但有意思的是,从 CPU 调度器的角度看,iowait 本质上是 idle time 的一种。CPU 在等 I/O 时其实是闲着的,没在执行指令,只是内核发现 "这会儿没别的任务可跑,又刚好有 I/O 在等",就把这段时间记成了 iowait,而不是普通的 idle。

所以排查线上问题时,wa 高导致的 CPU "高",跟 us/sy 高完全是两码事,处理方向也不同。前者是磁盘/网络慢,后者才是 CPU 算力不够。

四、怎么用代码自己算一个

面试官如果追问 "你自己能实现一个简单的 CPU 利用率监控吗",可以这么写:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class CpuUsageMonitor {

    public static void main(String[] args) throws IOException, InterruptedException {
        // 第一次采样
        long[] sample1 = readCpuStat();
        // 采样间隔 1 秒
        Thread.sleep(1000);
        // 第二次采样
        long[] sample2 = readCpuStat();

        // 计算差值
        long idle1 = sample1[3];
        long idle2 = sample2[3];
        long total1 = 0;
        long total2 = 0;
        for (long v : sample1) total1 += v;
        for (long v : sample2) total2 += v;

        long totalDelta = total2 - total1;
        long idleDelta = idle2 - idle1;

        // 核心公式:CPU 利用率 = (1 - idle/total) × 100%
        double usage = (1.0 - (double) idleDelta / totalDelta) * 100;
        System.out.printf("CPU 利用率: %.2f%%\n", usage);
    }

    /**
     * 解析 /proc/stat 第一行 cpu 的数据
     * 返回前 8 个字段:user, nice, system, idle, iowait, irq, softirq, steal
     */
    private static long[] readCpuStat() throws IOException {
        List<String> lines = Files.readAllLines(Path.of("/proc/stat"));
        String firstLine = lines.get(0); // 形如 "cpu  2255 34 2290 ..."
        String[] parts = firstLine.split("\\s+");
        long[] values = new long[8];
        for (int i = 0; i < 8; i++) {
            values[i] = Long.parseLong(parts[i + 1]);
        }
        return values;
    }
}

这个小程序基本就是 top 命令算 CPU 利用率的核心逻辑,只不过 top 还做了多核聚合、进程级归属、平滑处理等一堆工程优化。

五、几个容易答错的坑

  1. "CPU 利用率低 = 系统没问题" —— 错。wa 高的时候总利用率看着低,但其实 I/O 已经瓶颈了,系统响应很慢。
  2. "top 看到的 CPU% 是瞬时值" —— 不算错,但更准确说是最近一个采样间隔的平均值,不是某个瞬间的瞬时值。
  3. "多核 CPU 怎么算" —— 总的 CPU 利用率是把所有核的时间加起来再算;也可以看每核利用率,top 里按 1 就能看到每核。
  4. "us 高一定有问题" —— 不一定。计算密集型任务(压缩、加密、训练)本来就 us 高,这是正常的。

面试高频追问

  1. iowait 高,但 CPU 利用率不高,怎么处理?

    • 典型的 I/O 瓶颈。用 iostatiotop 看磁盘负载,考虑换 SSD、加缓存、做异步化。
  2. sy(system)很高,一般是什么原因?

    • 频繁的系统调用、锁竞争、上下文切换过多。用 straceperf 排查。
  3. CPU 利用率 100%,系统就一定卡吗?

    • 不一定。如果是单核跑满(其他核闲着),整体看利用率不高,但关键任务可能已经卡了。要结合每核利用率和**负载(load average)**一起看。
  4. load average 和 CPU 利用率啥区别?

    • load average运行队列的平均长度(运行中 + 等待运行的进程数),CPU 利用率是 CPU 忙碌比例。两者相关但不同,load 高但 CPU 不忙,往往是 I/O 等待造成的。

常见面试变体

  • "解释一下 top 命令输出里那几个 ussywa 是什么意思?"
  • "/proc/stat 里存的是什么?top 是怎么算 CPU 利用率的?"
  • "线上服务器 CPU 利用率突然飙到 100%,怎么排查?"

记忆口诀

公式一句话:总时间减空闲时间,除以总时间,就是利用率。

八个维度us ni sy id wa hi si st,用户、nice、内核、空闲、等 I/O、硬中、软中、被偷。

总结

CPU 利用率就是 "CPU 不闲着的时间占比",核心公式 (total - idle) / total。真正值钱的是那 8 个细分维度,搞懂 us/sy/wa/si/hi 分别代表什么、什么时候要警惕,才算把这块吃透了。数据源是 /proc/stat,算法是两次采样求差,把这些理清楚了,自己写个 CPU 监控都不在话下。