共计 2875 个字符,预计需要花费 8 分钟才能阅读完成。
如何计算 CPU 占用率?
简略来说,过程的 CPU 占用率指的是 CPU 有多少工夫破费在了运行过程上。在 Linux 零碎里,过程运行的工夫是以 jiffies
[1] 统计的,通过计算jiffies * HZ
,就能够失去过程耗费的 CPU 工夫,再除以 CPU 的总工夫,就能够失去过程的 CPU 占用率:jiffies * HZ / total_time
。
ps 和 top 的不同之处
ps
和 top
是最罕用的两种查看 CPU 占用的形式,都能够用来疾速找到以后 CPU 占用率高的过程。但实际上这两个工具的统计形式是齐全不同的。
咱们用上面这个简略的 Go 程序来测试这两个工具的差异:
package main | |
import ( | |
"bytes" | |
"fmt" | |
"strconv" | |
"sync" | |
"time" | |
) | |
var testData = []byte(`testdata`) | |
func testBuffer(idx int) {m := map[string]*bytes.Buffer{} | |
for i := 0; i < 100; i += 1 {buf, ok := m[strconv.Itoa(i)] | |
if !ok {buf = new(bytes.Buffer) | |
} | |
for j := 0; j < 1024; j += 1 {buf.Write(testData) | |
} | |
m[strconv.Itoa(i)] = buf | |
} | |
fmt.Println("done,", idx) | |
wg.Done()} | |
var wg sync.WaitGroup | |
func main() { | |
for i := 0; i < 10; i += 1 {wg.Add(1) | |
j := i | |
go testBuffer(j) | |
} | |
wg.Wait() | |
fmt.Println("sleeping") | |
time.Sleep(time.Hour) | |
} |
而后咱们运行这个程序,通过 top
和ps aux
别离查看过程的 CPU 占用状况。
top -n 1
:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND | |
39753 infini 20 0 14.663g 0.014t 1200 S 611.1 22.2 0:23.53 test-cpu |
ps aux
:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND | |
infini 39881 767 39.1 26505284 25791892 pts/16 Sl+ 07:04 0:38 ./test-cpu |
能够看到,ps
和 top
统计的 CPU 占用率是近似的(因为工夫点并不齐全吻合,统计值也会有轻微差异)。两个工具的差别体现在 testBuffer
完结后,top
统计的 CPU 占用率曾经靠近于 0,然而 ps
仍然统计到很高的 CPU 占用率:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND | |
infini 39881 82.3 42.4 28638148 27953532 pts/16 Sl+ 07:04 0:40 ./test-cpu |
为什么 ps 和 top 的统计值会有差别?
这两个工具的差别来自于各自运行形式的不同:top 只能继续运行一段时间,而 ps 是立即返回的。这个差别体现在运行 top -n 1
和ps aux
时,top
是提早后返回的,而 ps
是立即返回的。这两种不同的运行形式就会反映在两个工具的统计算法上。
文章结尾咱们提到,Linux 的 CPU 工夫是依照 jiffies
统计的,思考到效率问题,Linux 只会统计总值,不会记录历史数据。对于 ps
来说,因为只能统计到瞬时值,这个瞬时值的统计算法就必然拿不到实时的 CPU 占用率,因为实时的占用率须要通过 (current_cpu_time - last_cpu_time) / time_duration
来失去,ps
只能统计一次,所以 time_duration
为0
,也就无奈计算这个占用率。实际上,ps
统计的是整个过程运行周期内的 CPU 占用率[2]:
(total_cpu_time / total_process_uptime)
对于测试程序这种短时间的占用率回升,刚开始的时候 ps
可能统计到近似精确的均匀 CPU 占用率,然而 cpu 占用复原后,ps
的统计值并不会立即降落,而是会随着过程运行工夫 total_process_uptime
的减少迟缓降落。
top
命令不同,top
是通过继续运行来更新 CPU 占用率统计的。-n 1
这个参数指定 top
运行一个迭代后退出,top
命令就能够通过这个提早来能够实现一个迭代内的 CPU 占用率统计:
(current_cpu_time - last_cpu_time) / iteration_duration
如何继续监控 CPU 占用率?
通常来说,监控零碎分为采集和统计两个不同的组件,采集组件只会采集指标数值,统计性能通过数据库 /Dashboard 来实现。要监控 CPU 占用率,ps
是一个十分合乎采集组件行为的统计形式,每次采集都能够拿到“以后”的 CPU 占用率。然而受限于算法自身的统计形式,咱们理论采集到的是均匀 CPU 占用率,无奈反映过程的实时状态。
以 INFINI Console 为例,咱们运行一个短时间的数据迁徙工作负载,而后查看对应 INFINI 网关实例的 CPU 占用监控(payload.instance.system.cpu
,通过 ps
形式统计以后 CPU 占用率)。能够看到,CPU 占用率会以一个曲线回升,在工作完结后会迟缓降落:
如果想继续监控实时 CPU 占用率,咱们就须要借鉴 top
的统计形式,采集原始的过程 CPU 工夫,进而通过聚合数据来计算 CPU 占用率。
在 Linux 零碎下,ps
和 top
命令都会通过 /proc/[PID]/stat
提供的信息来计算 CPU 占用率[2]:
## Name Description | |
14 utime CPU time spent in user code, measured in jiffies | |
15 stime CPU time spent in kernel code, measured in jiffies | |
16 cutime CPU time spent in user code, including time from children | |
17 cstime CPU time spent in kernel code, including time from children |
获取到每个采样工夫的过程信息后,咱们就能够通过这个公式来计算采样周期内的 CPU 占用率:
delta(cpu_time) / delta(timestamp)
在 INFINI Console,咱们能够通过 deriative
函数来计算 payload.instance.system.user_in_ms
和payload.instance.system.sys_in_ms
绝对于 timestamp
的占比,进而失去精确的 CPU 占用率统计。
这样,咱们就能够统计到网关在运行工作负载前后的实时 CPU 占用率:
总结
尽管 top
和ps
都能够统计 CPU 占用率,但统计算法却齐全不同。理解这两种算法的底层原理之后,咱们就能够设计出适宜监控零碎的数据采集和数据统计形式,采集到精确的 CPU 占用率。
参考
- Jiffies
-
Top and ps not showing the same cpu result
本文由博客一文多发平台 OpenWrite 公布!