什么是 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+ 小伙伴加入学习,欢迎点击围观
面试考察点
- 基础概念理解:面试官想看的不是你知不知道这个名词,而是你是否真正理解它衡量的是什么、反映了系统什么状态。
- 底层细节掌握:能否说出 CPU 时间的细分构成(用户态、内核态、iowait、软中断等),这是区分 "用过
top命令" 和 "真懂操作系统" 的分水岭。 - 计算原理:知道
/proc/stat这个数据源,知道 Linux 是通过两次采样做差值来算的,这才是工程师该有的深度。
核心答案
CPU 利用率,简单说就是:在一段时间内,CPU 处于非空闲状态的时间占这段时间的比例。它衡量的是 CPU 有多忙。
核心计算公式:
CPU 利用率 = (总时间 - 空闲时间) / 总时间 × 100%
换个写法:
CPU 利用率 = (1 - idle / total) × 100%
这个公式看着简单,但 Linux 内核实际把 CPU 时间切成了 8 个维度,每个维度都代表不同的 "忙"。
深度解析
一、Linux 下 CPU 时间的 8 个维度
在 Linux 里,CPU 时间并不是一个笼统的数,而是被拆得非常细。top、vmstat、sar 这些工具看到的那些字母,对应的就是这 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 利用率,排查方向都是蒙的。
二、数据从哪来:/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 是所有核的汇总,后面 cpu0、cpu1 是每核单独的。那些数字按顺序就是:
user nice system idle iowait irq softirq steal guest guest_nice
前 8 个就是咱们上面表格里的那 8 个维度。
关键点:这些数字是自系统启动以来的累计值,只会一直往上涨,不会回落。直接除一下得到的是从开机到现在的平均利用率,意义不大。实际中要的是某一段时间内的利用率,所以得做两次采样求差。
三、Linux 到底怎么算的
整个计算过程画成流程图就是这样:
上图就是 Linux 工具(top、vmstat、sar、pidstat)计算 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 里,total 把 iowait 也加进去了,但减的只有 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 还做了多核聚合、进程级归属、平滑处理等一堆工程优化。
五、几个容易答错的坑
- "CPU 利用率低 = 系统没问题" —— 错。
wa高的时候总利用率看着低,但其实 I/O 已经瓶颈了,系统响应很慢。 - "
top看到的 CPU% 是瞬时值" —— 不算错,但更准确说是最近一个采样间隔的平均值,不是某个瞬间的瞬时值。 - "多核 CPU 怎么算" —— 总的 CPU 利用率是把所有核的时间加起来再算;也可以看每核利用率,
top里按1就能看到每核。 - "
us高一定有问题" —— 不一定。计算密集型任务(压缩、加密、训练)本来就us高,这是正常的。
面试高频追问
-
iowait高,但 CPU 利用率不高,怎么处理?- 典型的 I/O 瓶颈。用
iostat、iotop看磁盘负载,考虑换 SSD、加缓存、做异步化。
- 典型的 I/O 瓶颈。用
-
sy(system)很高,一般是什么原因?- 频繁的系统调用、锁竞争、上下文切换过多。用
strace、perf排查。
- 频繁的系统调用、锁竞争、上下文切换过多。用
-
CPU 利用率 100%,系统就一定卡吗?
- 不一定。如果是单核跑满(其他核闲着),整体看利用率不高,但关键任务可能已经卡了。要结合每核利用率和**负载(load average)**一起看。
-
load average和 CPU 利用率啥区别?load average是运行队列的平均长度(运行中 + 等待运行的进程数),CPU 利用率是 CPU 忙碌比例。两者相关但不同,load高但 CPU 不忙,往往是 I/O 等待造成的。
常见面试变体
- "解释一下
top命令输出里那几个us、sy、wa是什么意思?" - "
/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 监控都不在话下。
