某个应用的 CPU 使用率100% 如何处理

Linux作为一个多任务操作系统,将每个CPU的时间划分为很短的时间片,再通过调度器轮流分配给各个任务使用,因此造成多任务同时运行的错觉。

CPU 使用率

为了维护 CPU 时间,Linux 通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量 Jiffies 记录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加 1。

节拍率HZ是内核的可配选项,可以自定义配置,可通过/boot/config来查询

1
2
3
[root@linux ~]# cat /boot/config-3.10.0-957.10.1.el7.x86_64  | grep ^'CONFIG_HZ'
CONFIG_HZ_1000=y
CONFIG_HZ=1000

同时,正因为节拍率HZ是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供了一个用户空间节拍率USER_ HZ,它总是固定为100,也就是1/100秒。这样,用户空间程序并不需要关心内核中HZ被设置成了多少,因为它看到的总是固定值USER_ HZ。

Linux通过/proc虚拟文件系统,向用户空间提供了系统内部状态的信息,而/proc/stat提供的就是系统的CPU和任务统计信息。比方说,如果你只关注CPU的话,可以执行下面的命令:

1
2
3
4
[root@linux ~]# cat /proc/stat | grep ^cpu
cpu 57739 102 160531 603983 5316 0 70 301 0 0
cpu0 28193 51 80340 302599 2407 0 59 161 0 0
cpu1 29545 51 80190 301383 2908 0 11 140 0 0

这里的输出结果是一个表格。其中,第一列表示的是 CPU 编号,如 cpu0、cpu1 ,而第一行没有编号的 cpu ,表示的是所有 CPU 的累加。其他列则表示不同场景下 CPU 的累加节拍数,它的单位是 USER_HZ,也就是 10 ms(1/100 秒),所以这其实就是不同场景下的 CPU 时间。

CPU 使用率指标

CPU使用率有很多重要指标,具体含义如下:

user(通常缩写为us),代表用户态CPU时间。注意,它包括下面的nice时间,但包括了guest时间。

nice(通常缩写为ni),代表低优先级用户态CPU时间,也就是进程的nice值被调整为1-19之间是的CPU时间。

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时间

而我们通常所说的CPU使用率,就是除了空闲时间外的其他时间占总CPU时间的百分比,用公式表示为:

上面这个计算方式是不具备参考意义的,因为总CPU时间是机器开机以来的,事实上,为了计算CPU使用率,性能工具都会取间隔一段时间(比如5秒)的两次值,做差后,再计算出这段时间内的平均CPU使用率,即:

这个公式,就是我们用各种性能工具所看到的 CPU 使用率的实际计算方法。

Linux 也给每个进程提供了运行情况的统计信息,也就是 /proc/[pid]/stat。不过,这个文件包含的数据就比较丰富了,总共有 52 列的数据。

不过需要注意的是,性能分析工具给出的都是间隔一段时间的平均CPU使用率,所以要注意间隔时间的设置,特别是多个工具对比分析时,需要保证它们的间隔时间是相同的。

比如,对比一下top和ps这两个工具报告的CPU使用率,默认的结果可能不一样,因为top默认使用3秒时间间隔,而ps使用的却是进程的整个生命周期。

查看 CPU 使用率的方法

top显示了系统总体的CPU和内存使用情况,以及各个进程的资源使用情况

ps则是显示了每个进程的资源使用情况

需要注意的,top默认显示的所有CPU的平均值,这个时候只需要按下数字1,就可以切换到每个CPU的使用率了。
继续往下看,空白行之后是进程的实时信息,每个进程都有一个%CPU列,表示进程的CPU使用率,它是用户态和内核态CPU使用率的总和,包括进程用户空间、使用的CPU、通过系统调用执行的内核空间CPU、以及在就绪队列等待运行的CPU。

分析进程的命令,比如pidstat,该命令包括:

  • 用户态CPU使用率(%user)
  • 内核态CPU使用率(%system)
  • 运行虚拟机CPU使用率(%guest)
  • 等待CPU使用率(%wait)
  • 以及总的CPU使用率(%CPU)

CPU 使用率过高怎么办

  • GDB 会导致程序中断,不适合线上环境使用,
  • perf

1.perf top 能够显示占用CPU 时钟最多的函数或者指令,因此可以用来查看热点函数

输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。

采样数需要我们特别注意。如果采样数过少(比如只有十几个),那下面的排序和百分比就没什么实际参考价值了。

再往下看是一个表格式样的数据,每一-行包含四列,分别是:

  • 第一列Overhead,是该符号的性能事件在所有采样中的比例,用百分比来表示。

  • 第二列Shared,是该函数或指令所在的动态共享对象(Dynamic Shared Object) ,如内核、进程名、动态链接库名、内核模块名等。

  • 第三列Object,是动态共享对象的类型。比如[.] 表示用户空间的可执行程序、或者动态链接库,而[k]则表示内核空间。

  • 最后一列Symbol是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

接着再来看第二种常见用法,也就是 perf record 和 perf report。 perf top 虽然实时展示了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续的分析。而 perf record 则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示。

1
2
3
4
5
$ perf record # 按 Ctrl+C 终止采样
[ 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 top 和 perf record 加上 -g 参数,开启调用关系的采样,方便我们根据调用链来分析性能问题。

案例分析

发送请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@vpn fwj]# ab -c 10 -n 10000  http://192.168.10.55:10000/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.10.55 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests

查看调用链关系

1
2
# -g 开启调用关系分析,-p 指定 php-fpm 的进程号 21515
$ perf top -g -p 21515

image-20190326192503224

总结

CPU使用率是最直观和最常用的系统性能指标,更是我们在排查性能问题时,通常会关注的第-个指标。所以我们更要熟悉它的含义,尤其要弄清楚用户(%user) 、Nice (%nice)、 系统(%system)、等待 I/O (%iowait)、 中断 (%irq) 以及软中断(%softirq) 这几种不同CPU的使用率。比如说:

  • 用户 CPU和Nice CPU高,说明用户态进程占用 了较多的 CPU,所以应该着重排查进程的性能问题。

  • 系统CPU 高,说明内核态占用 了较多的CPU,所以应该着重排查内核线程或者系统调用的性能问题。

  • I/O等待CPU高,说明等待I/O的时间比较长,所以应该着重排查系统存储是不是出现了I/O问题。

  • 软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的CPU,所以应该着重排查内核中的中断服务程序。

碰到CPU使用率升高的问题,你可以借助top、pidstat 等工具,确认引发CPU性能问题的来源;再使用perf等工具,排查出引起性能问题的具体函数。

本文总结笔记,来源于极客时间 《Linux 性能优化实战》