什么是 CPU 利用率?怎么算的?
CPU 利用率(CPU utilization)是在一段时间内 CPU 非空闲时间的占比,不是瞬时值。
Linux 上它来自内核按 tick 累加的 jiffies 计数器 /proc/stat,采两次求差再相除即可,
所以"利用率"永远是某个时间窗口内的平均值,窗口越短抖动越大。
1. /proc/stat 里的几个计数器
cpu 3357123 1204 88123 99812345 3321 0 4521 0 0 0
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └ irq 之后新增字段(guest 等)
│ │ │ │ │ │ └ softirq 软中断
│ │ │ │ │ └ steal 被宿主机抢走的时间(虚拟机)
│ │ │ │ └ iowait 等 IO 完成的空闲时间
│ │ │ └ idle 真空闲
│ │ └ system 内核态
│ └ nice 低优先级用户态
└ user 普通用户态
| 字段 | 含义 | 是否算"忙" |
|---|---|---|
| user | 用户态执行时间 | 算 |
| nice | 调整过优先级的用户态时间 | 算 |
| system | 内核态(系统调用、中断处理) | 算 |
| idle | 真空闲 | 不算 |
| iowait | CPU 空闲但仍有未完成磁盘 IO | 有争议,多数监控不算 |
| irq / softirq | 硬中断 / 软中断处理 | 算 |
| steal | 虚拟化环境下被宿主机占走 | 算,但要单独看 |
2. 计算公式
total = user + nice + system + idle + iowait + irq + softirq + steal
busy = total - idle - iowait (常见口径,可再减 steal)
util = (busy2 - busy1) / (total2 - total1) × 100%
注意 jiffies 的精度:USER_HZ 通常是 100,即一个 tick 10ms。窗口取 1 秒只有 100 个采样点,
再短就没有统计意义。容器里还要看 cgroup 的 cpu.stat,它按真实纳秒记账,比 /proc/stat 准。
3. 代码:自己算一次
// 读两次 /proc/stat,间隔 1 秒算区间利用率
func readCPU() (busy, total uint64) {
data, _ := os.ReadFile("/proc/stat")
var user, nice, sys, idle, iowait, irq, softirq, steal uint64
fmt.Sscanf(strings.Split(string(data), "\n")[0],
"cpu %d %d %d %d %d %d %d %d", &user, &nice, &sys, &idle, &iowait, &irq, &softirq, &steal)
total = user + nice + sys + idle + iowait + irq + softirq + steal
return total - idle - iowait, total
}
b1, t1 := readCPU()
time.Sleep(time.Second)
b2, t2 := readCPU()
fmt.Printf("util = %.1f%%\n", float64(b2-b1)*100/float64(t2-t1))
top -bn1 | head -5 # 一看 us/sy/id/wa/st
mpstat -P ALL 1 3 # 每核明细,找单核打满
pidstat -u -p 1234 1 # 单进程 us/sy
cat /sys/fs/cgroup/cpu.stat # 容器内:usage_usec / nr_throttled
4. 常见追问
Q:iowait 到底算不算忙?
它表示"CPU 空闲但系统里有未完成的磁盘 IO",说明瓶颈可能在磁盘而不是 CPU。
监控上通常单独看 %wa,既不算 100% 忙,也不能当健康。
Q:为什么 top 显示 CPU 100%,但吞吐上不去?
常见三种:单核打满而任务不可并行(看 mpstat 是否有核 100%);大量时间耗在 sy(系统调用、锁竞争);
softirq 高(网卡收包太猛,需调 RSS/多队列或开 RPS)。
Q:steal 高说明什么? 说明虚拟机想跑但物理 CPU 被别的租户占着,属于宿主机超卖。此时应用侧再怎么优化也没用, 要看宿主机配额或换规格。
Q:为什么容器里 CPU 利用率常常对不上?
/proc/stat 是宿主机全局的,容器内看到的是整机数据;限流(cgroup quota)导致的 throttling
只会体现在 cpu.stat 的 nr_throttled 上,利用率甚至可能只有 50% 却被卡住。
小结
利用率 = 非空闲 tick 占比,数据源是 /proc/stat,必须采两次求差。
真正有价值的是拆开看:us 高说明算得太多,sy 高说明内核/锁太忙,wa 高说明磁盘拖后腿,
st 高说明被虚机抢了 CPU。只会说"CPU 100%"是拿不到分的。