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

1.CPU使用率

1.1 节拍率HZ

  • Linux通过事先定义的节拍率,触发时间中断
  • 使用全局变量Jiffies记录了开机以来的节拍数(中断+1)。
  • 在/boot/config查看内核选项;命令:grep 'CONFIG_HZ=' /boot/config-$(uname -r)(目前并没找到这个文件)

1.2 /proc

Linux通过/proc虚拟文件系统,向用户空间提供了系统内部状态的信息,而/proc/stat提供的就是系统的 CPU 和任务统计信息。

1
2
3
4
5
6
7
8
# 只保留各个 CPU 的数据
$ cat /proc/stat | grep ^cpu
# 打印
cpu  72654 0 240530 37150942 231 0 408 0 0 0
cpu0 17980 0 58109 9280824 40 0 200 0 0 0
cpu1 18734 0 62739 9298874 67 0 81 0 0 0
cpu2 18220 0 63949 9265870 82 0 76 0 0 0
cpu3 17717 0 55731 9305373 41 0 49 0 0 0

每一列的涵义:

  • 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),代表以低优先级运行虚拟机的时间。

1.3 CPU使用率公式

1
CPU使用率 = 1-(空闲时间/总CPU时间)

事实上,性能工具一般都会取间隔一段时间(如3s)的两次值,作差后,再计算出这段时间内的平均CPU使用率

1
CPU使用率 = 1-{(空闲时间new-空闲时间old)/(总CPU时间new-总CPU时间old)}

2.怎么查看CPU使用率

最常使用top和ps

2.1 top

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 默认每 3 秒刷新一次
$ top
# 打印
top - 10:12:12 up 1 day,  2:48,  0 users,  load average: 0.00, 0
Tasks:   7 total,   1 running,   6 sleeping,   0 stopped,   0 zo
%Cpu(s):  0.2 us,  0.6 sy,  0.0 ni, 99.2 id,  0.0 wa,  0.0 hi,
MiB Mem :   1991.1 total,   1323.6 free,    314.2 used,    353.3
MiB Swap:   1024.0 total,   1024.0 free,      0.0 used.   1549.4

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM
    1 root      20   0    4112   3276   2848 S   0.0   0.2
    8 root      20   0    4112   3404   2848 S   0.0   0.2
   18 root      20   0    4112   3428   2876 S   0.0   0.2
   39 root      20   0    4112   3412   2860 S   0.0   0.2
   75 root      20   0    4112   3420   2868 S   0.0   0.2
  283 root      20   0    4236   3496   2932 S   0.0   0.2
  301 root      20   0    6072   3236   2736 R   0.0   0.2

分析:%CPU是系统使用率,后面一排是每隔进程的CPU使用率

需要使用pidstat分析每隔进程的详情情况

1
2
3
4
5
6
7
8
# 每隔 1 秒输出一组数据,共输出 5 组
$ pidstat 1 5
# 打印
Linux 4.19.76-linuxkit (4f913ef1ae8f) 	05/28/20 	_x86_64_	(4 CPU)
...
10:22:12      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
10:22:13        0       311    0.00    0.99    0.00    0.00    0.99     3  pidstat
...

2.2 ps

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

3. CPU使用率过高怎么办?

使用perf分析CPU性能问题。它以性能事件采样为基础,不仅可以分析系统的各种事 件和内核性能,还可以用来分析指定应用程序的性能问题。

1. docker中的ubuntu中安装perf

1
2
3
4
5
6
# mac上docker run 要给权限
$ docker run -t -i --privileged --name ubuntu ubuntu
# 进入ubuntu容器内,安装
$ apt-get install linux-tools-generic
# 创建一个符号链接到/usr/bin/perf
$ ln -s /usr/lib/linux-tools/5.4.0-33-generic/perf /usr/bin/perf
1
2
3
4
5
6
7
8
9
$ perf top
# 打印
Samples: 85K of event 'cpu-clock:pppH', 4000 Hz, Event count (approx.): 12867561796 lost: 0/0 drop: 0/0
Overhead  Shared Object       Symbol
  97.83%  [kernel]            [k] 0xffffffffa37f8223
   0.35%  [kernel]            [k] 0xffffffffa314c746
   0.34%  [kernel]            [k] 0xffffffffa3123a43
   0.26%  [kernel]            [k] 0xffffffffa3108137
 ...

分析:

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

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

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

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

4.实验

4.1 坑与环境配置

由于本地机子是macbook,无法安装perf(可能),所以在运行php-fpm时要给容器赋予主机的权限

1
$ docker run --privileged --name phpfpm -itd --network container:nginx feisky/php-fpm:cpu

在phpfpm容器内安装perf

1
$ apt-get install -y linux-perf

4.2 实验场景

  • 系统的用户 CPU 使用率(usr%)过高
  • 可以找出高 CPU 使用率的进程(php-fpm 进程的 CPU 使用率过高)

4.2 查找分析

  1. 使用top命令, 查看cpu使用率高的pid
  2. perf 分析
1
2
# -g 开启调用关系分析,-p 指定 php-fpm 的进程号<pid>
$ perf top -g -p <pid>

然后就是上下左右键,查找调用了什么系统函数导致的,再去检测代码问题