/proc/stat

/proc/stat 是 Linux 里最重要的 CPU 监控入口。你先记住一句话:

/proc/stat 不是直接告诉你“CPU 使用率是多少”,它给的是 CPU 从开机到现在,在各种状态下累计花了多少时间

文章里也写了,/proc/stat 会实时追踪系统启动以来的多种统计信息,其中 cpu 行后面的值表示 CPU 在用户态、系统态、空闲、I/O 等待等状态下的累计时间;它还包含中断、上下文切换、启动时间、进程数量、运行队列和阻塞队列等信息。


1. 先看一个 /proc/stat 输出

你在 Linux 里执行:

cat /proc/stat

可能看到类似:

cpu  1019260 26878 310750 15617189 103451 0 8443 0 0 0
cpu0 509710 13053 159262 7810262 47813 0 4334 0 0 0
cpu1 509550 13824 151488 7806927 55637 0 4109 0 0 0
intr 65227257 ...
ctxt 172323289
btime 1593836309
processes 44164
procs_running 2
procs_blocked 0
softirq 61303127 ...

第二篇文章里也给了类似输出,/proc/stat 被描述为“所有的 CPU 活动信息”。


2. cpucpu0cpu1 是什么区别

重点看这几行:

cpu  1019260 26878 310750 15617189 103451 0 8443 0 0 0
cpu0 509710 13053 159262 7810262 47813 0 4334 0 0 0
cpu1 509550 13824 151488 7806927 55637 0 4109 0 0 0

含义:

含义
cpu 所有 CPU 核心加起来的总统计
cpu0 第 0 个逻辑 CPU 的统计
cpu1 第 1 个逻辑 CPU 的统计
cpuN 第 N 个逻辑 CPU 的统计

小白理解:

cpu = 整台机器总 CPU 情况
cpu0 = 第一个核心
cpu1 = 第二个核心

如果你只想算整机 CPU 使用率,一般读第一行 cpu 就够了。

如果你想看每个核心是不是负载不均衡,就读 cpu0cpu1cpu2


3. cpu 后面的数字是什么意思

现代 Linux 一般是这些字段:

cpu user nice system idle iowait irq softirq steal guest guest_nice

对应:

字段 小白解释 是否算“忙”
user 普通用户态程序消耗的 CPU 时间
nice 低优先级用户态程序消耗的 CPU 时间
system 内核态消耗的 CPU 时间,比如系统调用、内核线程
idle CPU 空闲时间
iowait CPU 等 I/O 的时间,比如等磁盘 通常算闲,但表示系统可能被 I/O 拖住
irq 处理硬中断的时间
softirq 处理软中断的时间,网络包处理常见
steal 虚拟化环境里被宿主机“偷走”的 CPU 时间 特殊
guest 跑虚拟机 guest 消耗的时间 通常已包含在 user 中
guest_nice 低优先级 guest 时间 通常已包含在 nice 中

你现在第一阶段只记前 7 个:

user nice system idle iowait irq softirq

4. 为什么不能直接把这些数字当 CPU 使用率

比如:

cpu  1019260 26878 310750 15617189 103451 0 8443 0 0 0

这些不是百分比。

它们是:

从系统启动到现在,CPU 在各个状态下累计花费的时间

也就是说,它们会一直增加。

所以不能说:

user = 1019260,所以 CPU 使用率是 1019260%

这是错的。

正确逻辑是:

第一次读取 /proc/stat
    ↓
等待一段时间,比如 1 秒
    ↓
第二次读取 /proc/stat
    ↓
用第二次累计值 - 第一次累计值
    ↓
得到这一小段时间内 CPU 花在哪里
    ↓
计算使用率

你的项目资料里也明确提醒,CPU 使用率不能只说读 /proc/stat,要讲 jiffies 和前后两次采样差分,公式是 usage = 1 - idle_delta / total_delta


5. 什么是 jiffies

文章里提到,cpu 行后的统计值以 jiffies 为单位,老文章写的是 1/100 秒级别。

你先不用纠结不同内核的 HZ 配置。面试里可以这样讲:

/proc/stat 里的 CPU 时间是内核维护的时间片累计值,常用 jiffies 表示。我们不直接关心一个 jiffy 等于多少秒,因为计算 CPU 使用率时用的是两次采样之间的比例,单位会抵消。

重点是“比例”。

例如:

第一次 total = 10000, idle = 7000
第二次 total = 11000, idle = 7600

total_delta = 1000
idle_delta = 600
CPU 使用率 = 1 - 600 / 1000 = 40%

你看,单位不重要。只要两个值单位一致,比例就能算。


6. CPU 使用率公式

6.1 先定义总时间

假设一行是:

cpu user nice system idle iowait irq softirq steal guest guest_nice

常见计算:

idle_all = idle + iowait
non_idle = user + nice + system + irq + softirq + steal
total = idle_all + non_idle

然后两次采样:

total_delta = total_now - total_prev
idle_delta = idle_all_now - idle_all_prev

CPU 使用率:

cpu_usage = (total_delta - idle_delta) / total_delta

也可以写成:

cpu_usage = 1 - idle_delta / total_delta

6.2 小白版理解

假设 1 秒内 CPU 总共统计到了 100 个时间片:

40 个时间片在跑程序
60 个时间片在空闲

那么:

CPU 使用率 = 40 / 100 = 40%

换成公式:

CPU 使用率 = 1 - 空闲时间差 / 总时间差

7. 手算一个例子

假设第一次采样:

cpu  100 0 50 850 0 0 0

字段解释:

user=100
nice=0
system=50
idle=850
iowait=0
irq=0
softirq=0

第一次总时间:

total1 = 100 + 0 + 50 + 850 + 0 + 0 + 0 = 1000
idle1 = 850

第二次采样:

cpu  130 0 70 900 0 0 0

第二次总时间:

total2 = 130 + 0 + 70 + 900 + 0 + 0 + 0 = 1100
idle2 = 900

做差:

total_delta = 1100 - 1000 = 100
idle_delta = 900 - 850 = 50

CPU 使用率:

usage = 1 - 50 / 100 = 50%

意思是:这段采样周期内,CPU 有一半时间在干活,一半时间空闲。


8. iowait 怎么理解

iowait 是 CPU 等 I/O 的时间。

比如程序要读磁盘:

进程发起 read()
    ↓
磁盘数据还没回来
    ↓
进程睡眠等待 I/O
    ↓
CPU 没有真正执行这个进程

这个时间可能会体现在 iowait

小白容易误解:

iowait 高,不一定说明 CPU 算力不够,可能是磁盘、网络存储或者块设备 I/O 拖慢了系统。

面试里可以说:

CPU 使用率低但 load 很高时,要看 iowait。如果 iowait 高,说明很多任务可能卡在不可中断 I/O 等待,不是单纯 CPU 计算压力。


9. irqsoftirq 怎么理解

9.1 irq

irq 是硬中断处理时间。

比如网卡收到包、磁盘 I/O 完成、定时器触发,硬件会通知 CPU:

硬件设备
    ↓ interrupt
CPU 暂停当前执行
    ↓
进入中断处理程序

这部分 CPU 时间算 irq


9.2 softirq

softirq 是软中断处理时间。

软中断常用于把一部分不能在硬中断里做太久的工作延后处理,网络协议栈里很常见。

典型链路:

网卡收到包
    ↓
触发硬中断
    ↓
硬中断里做最小处理
    ↓
把后续网络包处理交给 softirq
    ↓
内核协议栈继续处理 skb

所以如果 softirq 很高,常见含义是:

网络包处理压力大

这和你的性能监控项目强相关,因为项目里就把 softirq 作为监控项之一;项目源里也把 CPU stat 和 softirq 作为需要重点掌握的底层亮点,要求能解释 CPU 为什么要两次采样差分,以及 softirq 为什么和网络包处理压力有关。


10. /proc/stat 里其他几行

除了 cpu,还有这些:

intr 65227257 ...
ctxt 172323289
btime 1593836309
processes 44164
procs_running 2
procs_blocked 0
softirq 61303127 ...
字段 含义 监控意义
intr 系统启动以来所有中断次数 中断压力
ctxt 上下文切换次数 调度频繁程度
btime 系统启动时间,Unix timestamp 计算启动时间
processes 启动以来创建过的进程数 fork 频率
procs_running 当前运行队列任务数 CPU 调度压力
procs_blocked 当前阻塞任务数 I/O 阻塞压力
softirq 软中断累计次数 网络/内核异步处理压力

文章里也说明了 intr 是中断次数,ctxt 是上下文切换次数,btime 是启动时间,processes 是系统启动以来创建的任务数,procs_running 是当前运行队列任务数,procs_blocked 是当前阻塞任务数。


11. 放到你的性能监控项目里怎么讲

你的项目链路是:

Worker
  -> 读取 /proc、字符设备、mmap、eBPF map
  -> MetricCollector 聚合 CPU / Mem / Disk / Net / SoftIRQ
  -> 填充 Protobuf MonitorInfo
  -> gRPC Push 到 Manager
  -> Manager 计算健康分并写 MySQL

项目分析文档也把这条核心链路定义为 Worker 采集端读取 /proc、字符设备、mmap、eBPF map,经 MetricCollector 聚合后填充 MonitorInfo,再通过 gRPC 推给 Manager。

所以 CPU 这块可以这样说:

Worker 第一次读取 /proc/stat 或内核模块暴露的 CPU 统计值
    ↓
保存 user / nice / system / idle / iowait / irq / softirq 等累计值
    ↓
下一次采样再次读取
    ↓
计算 total_delta 和 idle_delta
    ↓
得到 CPU 使用率
    ↓
填入 MonitorInfo
    ↓
上报 Manager

12. 面试标准回答

面试官问:CPU 使用率怎么计算?

你可以这样答:

Linux 的 /proc/stat 里不是直接给 CPU 使用率,而是给 CPU 从系统启动以来在 user、nice、system、idle、iowait、irq、softirq 等状态下的累计时间。监控程序会周期性采样两次,先把这些字段求和得到 total,再把 idle 和 iowait 作为空闲相关时间。然后计算 total_delta = total_now - total_previdle_delta = idle_now - idle_prev,最终 CPU 使用率 = 1 - idle_delta / total_delta。因为用的是两次采样的差值,所以得到的是采样窗口内的 CPU 使用率,而不是开机以来的平均值。

如果想加项目:

在我的项目里,Worker 端会采集 CPU stat 相关字段,经过差分计算后封装到 MonitorInfo,再通过 gRPC 上报给 Manager,用于健康评分和后续查询。


13. 你现在只需要记住这个最小公式

total = user + nice + system + idle + iowait + irq + softirq + steal

idle_all = idle + iowait

cpu_usage = 1 - (idle_all_now - idle_all_prev) / (total_now - total_prev)

小白版:

CPU 使用率 = 1 - 空闲时间增量 / 总时间增量

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐