一、平均负载
平均负载指 单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。
所谓可运行状态的进程,是指正在使用 CPU 或者正在等待 CPU 的进程,也就是我们常用 ps aux 命令看到的,处于 R 状态(Running 或 Runnable)的进程。
不可中断状态的进程则是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的 I/O 响应,也就是我们在 ps aux 命令中看到的 D 状态(Uninterruptible Sleep,也称为 Disk Sleep)的进程。
所以, top 命令中的
load average: 0.10, 0.04, 0.05 表示:
一分钟内 平均活跃进程数为 0.10个
五分钟内 平均活跃进程数为 0.04个
十五分钟内 平均活跃进程数为 0.05个
计算规则 : 在Linux系统中,是每间隔$5HZ$或者$5秒$统计一次运行的任务数,然后计算均值。
平均负载与cpu个数的关系 :
获取cpu个数命令 :
grep 'model name' /proc/cpuinfo | wc -l
理想情况下, 应该是每个cpu上运行一个程序, 平均负载的值等于cpu个数。
通过三个时间可以判定cpu负载的趋势。
当平均负载高于 CPU 数量 70% 的时候,你就应该分析排查负载高的问题了。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能(70%不是绝对的)。
查看负载命令:
uptime
查看各cpu使用率变化的命令 :
# -P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据
$ mpstat -P ALL 5
查看当前有哪些进程在使用 cpu 命令 :
# 间隔 5 秒后输出一组数据
$ pidstat -u 5 1
二、cpu使用率
Linux 通过 /proc 虚拟文件系统,向用户空间提供了系统内部状态的信息,而 /proc/stat 提供的就是系统的 CPU 和任务统计信息。
# 只保留各个 CPU 的数据
$ cat /proc/stat | grep ^cpu
cpu 280580 7407 286084 172900810 83602 0 583 0 0 0
cpu0 144745 4181 176701 86423902 52076 0 301 0 0 0
cpu1 135834 3226 109383 86476907 31525 0 282 0 0 0
这里的输出结果是一个表格。其中,第一列表示的是 CPU 编号,如 cpu0、cpu1 ,而第一行没有编号的 cpu ,表示的是所有 CPU 的累加。其他列则表示不同场景下 CPU 的累加节拍数,它的单位是 USER_HZ,也就是 10 ms(1/100 秒),所以这其实就是不同场景下的 CPU 时间。
user(通常缩写为 us),代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。
nice(通常缩写为 ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。
system(通常缩写为 sys),代表内核态 CPU 时间。
idle(通常缩写为 id),代表空闲时间。注意,它不包括等待 I/O 的时间(iowait)。
iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。
irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。
softirq(通常缩写为 si),代表处理软中断的 CPU 时间。
steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。
guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。
通常所说的 CPU 使用率,就是除了空闲时间外的其他时间占总 CPU 时间的百分比,用公式来表示就是
也可以用每一个场景的 CPU 时间,除以总的 CPU 时间,计算出每个场景的 CPU 使用率。
比如 :
| user | nice | system | idle | iowait | irq | softirq | steal | guest | guest_nice |
cpu | 280580 | 7407 | 286084 | 172900810 | 83602 | 0 | 583 | 0 | 0 | 0 |
cpu0 | 144745 | 4181 | 176701 | 86423902 | 52076 | 0 | 301 | 0 | 0 | 0 |
cpu1 | 135834 | 3226 | 109383 | 86476907 | 31525 | 0 | 282 | 0 | 0 | 0 |
Cpu总使用率 = 1-172900810/(280580+7407+286084+172900810+83602+0+583+0+0+0) = 0.003792691532460757 = 0.3792691532460757%
不过这里记录的是一直累计的结果, 而我们获取cpu使用率一般是当前使用率, 所以我们会取一个时间差,得到这段时间的平均cpu使用率。
性能分析工具给出的都是间隔一段时间的平均 CPU 使用率,所以要注意间隔时间的设置,特别是用多个工具对比分析时,你一定要保证它们用的是相同的间隔时间。
比如 :
总Cpu平均使用时间 =
1 - float64(1235020245-1235017470)/float64((1189661+78356+2269914+1235020245+2533097+0+103995+0+0+0)-(1189657+78356+2269913+1235017470+2533097+0+103995+0+0+0))
= 0.0017985611510791255 = 0.17985611510791255%
而 top(默认每 3 秒刷新一次,按下数字 1 ,就可以切换到每个 CPU 的使用率) 看到的结果也差不多
显示进程较详细cpu使用率 :
# 每隔 1 秒输出一组数据,共输出 5 组
$ pidstat 1 5
通过以上操作, 基本上可以定位到是哪些程序cpu使用率过高。
但是找到程序之后, 如果想要优化, 就要确定是哪一个函数、那段代码导致的cpu使用率过高, 怎么做到呢?
使用 perf 工具
类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找热点函数。
三、实例
go 程序如下 :
func test4(a int64) {
fmt.Println(a)
for {
a++
}
}
func test3(a int64) {
for a < 1000000000 {
a++
}
test4(a)
}
func test2(a int64) {
for a < 1000000 {
a++
}
test3(a)
}
func test(a int64) {
for a < 100000 {
a++
}
test2(a)
}
func main() {
test(1)
}
运行后,
1、使用top命令查看cpu使用情况
2、使用 pidstat 1 5 命令找到使用 cpu 的进程的进程号
3、使用 perf top -g -p 进程号 命令查看调用情况
4、可以使用上下键选中 main.test4 这行, 然后按回车展开函数调用栈:
将采样保存到文件中回放 :
$ perf record # 按 Ctrl+C 终止采样 指定进程 : perf record -g -p 71617
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]
$ perf report # 展示类似于 perf top 的报告 指定进程 : perf report -g -p 71617