02. 从零带你学习 Linux 内核 :/proc
/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. cpu、cpu0、cpu1 是什么区别
重点看这几行:
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 就够了。
如果你想看每个核心是不是负载不均衡,就读 cpu0、cpu1、cpu2。
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. irq 和 softirq 怎么理解
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_prev,idle_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 - 空闲时间增量 / 总时间增量
更多推荐




所有评论(0)