下面按你要求,把 /proc/stat 和 /proc/$pid/stat 分开整理成表格,并补上 CPU 使用率怎么计算。
一、/proc/stat 是什么
/proc/stat 记录的是系统整体和各 CPU 核的累计统计值。
最常见的是第一行:
cpu user nice system idle iowait irq softirq steal guest guest_nice
后面每个值都是累计时间,单位通常是 jiffies / ticks,不是百分比。
二、/proc/stat 字段说明表
1)总 CPU 行字段说明
示例:
cpu 12234 34 4567 890123 56 0 78 0 0 0
| 序号 | 字段名 | 示例值 | 含义 |
|---|---|---|---|
| 1 | cpu |
cpu |
表示所有 CPU 总和 |
| 2 | user |
12234 |
用户态运行时间,不含 nice |
| 3 | nice |
34 |
低优先级用户态运行时间 |
| 4 | system |
4567 |
内核态运行时间 |
| 5 | idle |
890123 |
空闲时间 |
| 6 | iowait |
56 |
等待 IO 的时间 |
| 7 | irq |
0 |
硬中断处理时间 |
| 8 | softirq |
78 |
软中断处理时间 |
| 9 | steal |
0 |
虚拟化环境中被其他虚拟机占用的时间 |
| 10 | guest |
0 |
运行虚拟 CPU 的时间 |
| 11 | guest_nice |
0 |
低优先级虚拟 CPU 时间 |
2)每核 CPU 行
除了总的 cpu 行,还会有:
cpu0 ...
cpu1 ...
cpu2 ...
这些字段含义和总 cpu 行相同,只是表示单个核心。
三、/proc/stat 怎么算 CPU 使用率
/proc/stat 里是累计值,所以必须取两个时刻做差值。
1)先定义 total
常见写法:
total = user + nice + system + idle + iowait + irq + softirq + steal
有的实现也会把 guest、guest_nice 加进去,但常见监控一般按上面这个算。
2)定义 idle
有两种常见口径:
口径 A:只把 idle 当空闲
idle_time = idle
口径 B:把 idle + iowait 都当空闲
idle_time = idle + iowait
很多监控工具会用第二种,因为 IO wait 时 CPU 也基本没在执行任务。
3)两次采样做差
设:
- 第一次:
total1idle1
- 第二次:
total2idle2
则:
delta_total = total2 - total1
delta_idle = idle2 - idle1
4)系统 CPU 使用率公式
CPU使用率 = (delta_total - delta_idle) / delta_total * 100
也等于:
CPU使用率 = 100 - (delta_idle / delta_total * 100)
这就是你前面说的 100 - idle 的完整含义。
5)举例
第一次:
cpu 100 20 30 400 10 0 5 0
第二次:
cpu 110 20 40 420 10 0 5 0
计算
第一次总和:
total1 = 100+20+30+400+10+0+5+0 = 565
idle1 = 400
第二次总和:
total2 = 110+20+40+420+10+0+5+0 = 605
idle2 = 420
差值:
delta_total = 40
delta_idle = 20
CPU 使用率:
(40 - 20) / 40 * 100 = 50%
四、/proc/$pid/stat 是什么
/proc/$pid/stat 记录的是某个进程的统计信息,整行固定顺序。
其中和 CPU 相关最重要的是:
utimestimecutimecstime
五、/proc/$pid/stat 字段说明表
下面是标准 52 个字段。
| 序号 | 字段名 | 含义 |
|---|---|---|
| 1 | pid |
进程ID |
| 2 | comm |
进程名,括号括起来 |
| 3 | state |
进程状态 |
| 4 | ppid |
父进程PID |
| 5 | pgrp |
进程组ID |
| 6 | session |
会话ID |
| 7 | tty_nr |
控制终端号 |
| 8 | tpgid |
前台进程组ID |
| 9 | flags |
内核标志位 |
| 10 | minflt |
次缺页次数 |
| 11 | cminflt |
子进程次缺页次数 |
| 12 | majflt |
主缺页次数 |
| 13 | cmajflt |
子进程主缺页次数 |
| 14 | utime |
用户态 CPU 时间,单位 ticks |
| 15 | stime |
内核态 CPU 时间,单位 ticks |
| 16 | cutime |
子进程用户态 CPU 时间 |
| 17 | cstime |
子进程内核态 CPU 时间 |
| 18 | priority |
优先级 |
| 19 | nice |
nice 值 |
| 20 | num_threads |
线程数 |
| 21 | itrealvalue |
已废弃 |
| 22 | starttime |
进程启动时刻(相对系统启动) |
| 23 | vsize |
虚拟内存大小,字节 |
| 24 | rss |
常驻内存页数 |
| 25 | rsslim |
RSS 限制 |
| 26 | startcode |
代码段起始地址 |
| 27 | endcode |
代码段结束地址 |
| 28 | startstack |
栈起始地址 |
| 29 | kstkesp |
栈指针(旧字段) |
| 30 | kstkeip |
指令指针(旧字段) |
| 31 | signal |
待处理信号位图 |
| 32 | blocked |
阻塞信号位图 |
| 33 | sigignore |
忽略信号位图 |
| 34 | sigcatch |
捕获信号位图 |
| 35 | wchan |
等待通道地址 |
| 36 | nswap |
交换页数(通常无意义) |
| 37 | cnswap |
子进程交换页数 |
| 38 | exit_signal |
退出信号 |
| 39 | processor |
最近运行的 CPU 核 |
| 40 | rt_priority |
实时优先级 |
| 41 | policy |
调度策略 |
| 42 | delayacct_blkio_ticks |
块 IO 等待时间 |
| 43 | guest_time |
guest 运行时间 |
| 44 | cguest_time |
子进程 guest 时间 |
| 45 | start_data |
数据段起始地址 |
| 46 | end_data |
数据段结束地址 |
| 47 | start_brk |
heap 起始地址 |
| 48 | arg_start |
参数区起始地址 |
| 49 | arg_end |
参数区结束地址 |
| 50 | env_start |
环境区起始地址 |
| 51 | env_end |
环境区结束地址 |
| 52 | exit_code |
退出码 |
六、/proc/$pid/stat 里 CPU 相关重点字段表
| 字段序号 | 字段名 | 含义 |
|---|---|---|
| 14 | utime |
进程在用户态消耗的 CPU 时间 |
| 15 | stime |
进程在内核态消耗的 CPU 时间 |
| 16 | cutime |
子进程用户态 CPU 时间 |
| 17 | cstime |
子进程内核态 CPU 时间 |
| 39 | processor |
最近运行在哪个 CPU |
| 40 | rt_priority |
实时优先级 |
| 41 | policy |
调度策略 |
七、/proc/$pid/stat 怎么算进程 CPU 使用率
1)最常见:单个进程 CPU 使用率
先定义:
proc_time = utime + stime
如果需要把子进程一起算:
proc_time = utime + stime + cutime + cstime
2)两次采样
设:
- 第一次:
proc1 = utime1 + stime1
- 第二次:
proc2 = utime2 + stime2
则:
delta_proc = proc2 - proc1
单位仍然是 ticks。
3)换算成 CPU 使用率
先查每秒 ticks 数:
getconf CLK_TCK
设结果为:
HZ = 100
采样间隔是 delta_t 秒。
则:
单核口径
进程CPU使用率 = delta_proc / HZ / delta_t * 100
也就是:
进程CPU使用率 = delta_proc * 100 / (HZ * delta_t)
4)举例
第一次采样:
utime=100
stime=50
第二次采样(5秒后):
utime=130
stime=70
则:
proc1 = 150
proc2 = 200
delta_proc = 50
若:
HZ = 100
delta_t = 5
那么进程 CPU 使用率:
50 / 100 / 5 * 100 = 10%
表示这 5 秒内平均使用了 10% 的一个 CPU 核。
八、/proc/stat 和 /proc/$pid/stat 计算 CPU 使用率对比表
| 项目 | /proc/stat |
/proc/$pid/stat |
|---|---|---|
| 统计对象 | 系统整体 / 单核 | 单个进程 |
| 数据类型 | CPU 各状态累计时间 | 进程累计 CPU 时间 |
| 关键字段 | user system idle iowait ... |
utime stime |
| 是否需要两次采样 | 是 | 是 |
| 计算核心 | delta_total、delta_idle |
delta_proc |
| 常见公式 | (delta_total - delta_idle) / delta_total * 100 |
delta_proc / HZ / delta_t * 100 |
| 结果含义 | 系统整体 CPU 使用率 | 进程占用 CPU 使用率 |
九、两个公式总结
1)系统整体 CPU 使用率:来自 /proc/stat
total = user + nice + system + idle + iowait + irq + softirq + steal
idle_time = idle # 或 idle+iowait,取决于口径
delta_total = total2 - total1
delta_idle = idle2 - idle1
CPU使用率 = (delta_total - delta_idle) / delta_total * 100
2)进程 CPU 使用率:来自 /proc/$pid/stat
proc_time = utime + stime
delta_proc = proc_time2 - proc_time1
CPU使用率 = delta_proc / HZ / delta_t * 100
十、最实用命令示例
1)算系统 CPU 使用率
read cpu u1 n1 s1 i1 w1 irq1 sirq1 st1 _ < /proc/stat
t1=$((u1+n1+s1+i1+w1+irq1+sirq1+st1))
idle1=$i1
sleep 1
read cpu u2 n2 s2 i2 w2 irq2 sirq2 st2 _ < /proc/stat
t2=$((u2+n2+s2+i2+w2+irq2+sirq2+st2))
idle2=$i2
dt=$((t2-t1))
di=$((idle2-idle1))
awk -v dt="$dt" -v di="$di" 'BEGIN{printf "cpu=%.2f%%\n",(dt-di)*100/dt}'
2)算某个进程 CPU 使用率
pid=95
hz=$(getconf CLK_TCK)
read ut1 st1 < <(awk '{print $14, $15}' /proc/$pid/stat)
sleep 1
read ut2 st2 < <(awk '{print $14, $15}' /proc/$pid/stat)
dp=$(( (ut2-ut1) + (st2-st1) ))
awk -v dp="$dp" -v hz="$hz" 'BEGIN{printf "cpu=%.2f%%\n", dp*100/hz}'
这里因为 sleep 1 秒,所以公式里 delta_t=1 被省略了。
0