Linux CPU

Linux性能优化学习知识

part02 平均负载

uptime

1.每次发现系统变慢时,先执行top 或者 uptime命令,了解系统的负载情况.

flyingzc@ubuntu:~$ uptime
 09:24:28 up 25 min,  2 users,  load average: 0.00, 0.02, 0.16
当前时间,系统运行时间,正在登录用户数 过去1分钟,5分钟,15分钟的平均负载(Load Average) 

平均负载:
单位时间内,系统处于可运行状态不可中断状态的平均进程数,即平均活跃进程数,它和CPU使用率并没有直接关系.

可运行状态的进程:
正在使用CPU正在等待CPU的进程,ps命令中: 处于R状态(Running 或 Runnable)的进程.

不可中断状态的进程:
正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的I/O响应,ps命令中: D状态(Uninterruptible Sleep)的进程.

比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态.
若此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题.

不可中断状态实际上是系统对进程和硬件设备的一种保护机制.

平均负载值含义

比如当平均负载为2时,意味着:

在有2个CPU的系统上,则所有的CPU都刚好被完全占用.

在有4个CPU的系统上,则CPU有50%的空闲.

在只有1个CPU的系统中,则有一半的进程竞争不到CPU.

mac查看cpu个数

1. 查看物理CPU个数
➜  myshell sysctl hw.physicalcpu
hw.physicalcpu: 4

2. 查看逻辑CPU个数
➜  myshell sysctl hw.logicalcpu
hw.logicalcpu: 8

3. 查看硬件信息
➜  myshell system_profiler SPHardwareDataType

Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: MacBookPro16,3
      Processor Name: Quad-Core Intel Core i5
      Processor Speed: 1.4 GHz
      Number of Processors: 1
      Total Number of Cores: 4
      L2 Cache (per Core): 256 KB
      L3 Cache: 6 MB
      Hyper-Threading Technology: Enabled
      Memory: 16 GB
      Boot ROM Version: 1037.147.1.0.0 (iBridge: 17.16.16065.0.0,0)
      Serial Number (system): FVFDD085P3YV
      Hardware UUID: 344D207F-FE80-5E6B-87E2-4CEA2146DED2
      Activation Lock Status: Disabled

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7trv73j-1627403340456)(2021-05-21-13-57-03.png)]

平均负载合理值

平均负载最理想的情况是等于 CPU个数.

在评判平均负载时,先要知道系统有几个 CPU,可通过 top 命令或从文件 /proc/cpuinfo 中读取.

flyingzc@ubuntu:~$ grep 'model name' /proc/cpuinfo | wc -l
1

有了CPU 个数,就能判断出,当平均负载比 CPU 个数还大的时候,系统已经出现了过载.

三个不同时间间隔的负载平均值,提供了分析系统负载趋势的数据来源,能更全面地理解目前的负载状况.

若1分钟,5分钟,15分钟的三个值基本相同,或相差不大,则说明系统负载很平稳.

若1分钟的值远小于15 分钟的值,说明系统最近1分钟的负载在减少,而过去15分钟内却有很大的负载.

若1分钟的值远大于 15 分钟的值,说明最近1分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,需要持续观察.

一旦1分钟的平均负载接近或超过了CPU的个数,说明系统正在发生过载的问题,这时就得分析调查是哪里导致的问题,并要想办法优化了.

假设我们在一个单 CPU(1个cpu) 系统上看到平均负载为 1.73,0.60,7.98,说明在过去 1 分钟内,系统有 73% 的超载,而在 15 分钟内,有 698% 的超载,从整体趋势来看,系统的负载在降低.

当平均负载高于 CPU 数量 70%的时候,你就应该分析排查负载高的问题了.一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能.

平均负载 与 CPU使用率

平均负载: 即平均活跃进程数.单位时间内,系统处于可运行状态不可中断状态的平均进程数,也就是平均活跃进程数.
它包括正在使用 CPU 的进程,和 等待 CPU 和 等待 I/O 的进程.

CPU 使用率: 单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应.

比如:
CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;

大量等待 CPU 的进程调度也会导致平均负载升高,此时的CPU使用率也会比较高.

I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;

平均负载案例

预先安装 stress 和 sysstat 包,如:

sudo apt install stress sysstat

stress: Linux 系统压力测试工具,可用作异常进程 模拟平均负载升高的场景.

sysstat 包含了常用的 Linux 性能工具,用来监控和分析系统的性能.

命令:
mpstat 是一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有CPU的平均指标.

pidstat 是一个常用的进程性能分析工具,用来实时查看进程的 CPU,内存,I/O 以及上下文切换等性能指标.

CPU密集型进程

session1
stress --cpu 1 --timeout 600
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5AdoAvcf-1627403340459)(2019-12-29-23-00-14.png)]

session2
watch -d uptime
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HkUE4b3v-1627403340461)(2019-12-29-22-59-34.png)]

session3
mpstat -P ALL 5
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xrjfkPNL-1627403340463)(2019-12-29-23-01-07.png)]
cpu1使用率100%,iowait为0

pidstat 来查询到底是哪个进程导致了 CPU 使用率为 100%
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhVVFgBF-1627403340465)(2019-12-29-23-03-05.png)]

IO密集型进程

1分钟的平均负载load升高,其中一个cpu使用率升高,它对应的iowait高达67%,说明平均负载的升高是由于 iowait 的升高.

平均负载总结

平均负载高有可能是 CPU 密集型进程导致的;

平均负载高并不一定代表 CPU 使用率高,还有可能是 I/O 更繁忙了;

当发现负载高的时候,你可以使用 mpstat,pidstat 等工具,辅助分析负载的来源.

part03~04 cpu上下文切换

上下文切换

多个进程竞争 CPU,也会导致系统的负载升高.

在每个进程任务运行前,CPU 都需要知道任务从哪加载,又从哪里开始运行.
即 需要系统事先帮它设置好 CPU 寄存器 和 程序计数器(Program Counter,PC).

CPU 寄存器: 是 CPU 内置的容量小,但速度极快的内存.

程序计数器: 则是用来存储 CPU 正在执行的指令位置,或者即将执行的下一条指令位置.

二者都是 CPU 在运行任何任务前,必须的依赖环境,又叫 CPU 上下文.

CPU 上下文切换:
先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务.

这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来.
这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行.

每次上下文切换都需要 几十纳秒 到 数微秒 的 CPU 时间.
这个时间还是相当可观的,特别是在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间耗费在 寄存器,内核栈 以及 虚拟内存 等资源的 保存 和 恢复 上.
进而缩短了真正运行进程的时间,会导致平均负载升高.

vmstat

vmstat: 查看系统总体的上下文切换情况

flyingzc@ubuntu:~$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 2410196 177196 862672    0    0   172    65   55   77  0  5 92  3  0
 1  0      0 2410188 177196 862708    0    0     0     0   39   54  0  0 100  0  0
cs (context switch): 每秒上下文切换的次数.

in (interrupt): 每秒中断的次数.

r (Running or Runnable): 就绪队列的长度,即正在运行和等待CPU的进程数,比如cpu个数是2,但是有8个在等待.

b (Blocked): 处于不可中断睡眠状态的进程数.

us(user)和 sy(system): 用户态cpu使用率 和 内核态cpu使用率. 这两列的CPU 使用率加起来上升到了 100%,其中系统 CPU 使用率,也就是 sy 列高达 84%,说明 CPU 主要是被内核占用了.

系统的就绪队列过长,即正在运行和等待CPU的进程数过多,导致了大量的上下文切换,而上下文切换又导致了系统 CPU 的占用率升高.

pidstat

pidstat -w 5:
查看每个进程上下文切换的情况

flyingzc@ubuntu:~$ pidstat -w 5
Linux 5.0.0-37-generic (ubuntu) 	01/04/2020 	_x86_64_	(2 CPU)

08:31:20 PM   UID       PID   cswch/s nvcswch/s  Command
08:31:25 PM     0        10      5.19      0.00  rcu_sched
08:31:25 PM     0        11      0.20      0.00  migration/0
08:31:25 PM     0        17      0.20      0.00  migration/1
08:31:25 PM     0        25      3.39      0.00  kworker/0:2-events
08:31:25 PM     0       348      0.20      0.00  kworker/0:1H-kblockd
08:31:25 PM     0       746     10.78      0.00  vmtoolsd
08:31:25 PM     0       799      0.20      0.00  irqbalance
08:31:25 PM     0       869      0.20      0.00  wpa_supplicant
08:31:25 PM     0      1513      0.20      0.00  packagekitd
08:31:25 PM   121      1658      1.00      0.00  gsd-color
08:31:25 PM     0      2048      2.99      0.00  kworker/1:3-events
08:31:25 PM  1000      2183      1.80      0.00  sshd
08:31:25 PM  1000      2225      1.80      1.60  watch
08:31:25 PM     0      2770      2.79      0.00  kworker/u256:1-events_unbound
08:31:25 PM  1000      5179      0.20      0.00  pidstat

cswch: 表示每秒自愿上下文切换(voluntary context switches)的次数
nvcswch: 表示每秒非自愿上下文切换(non voluntary context switches)的次数.

自愿上下文切换:
进程无法获取所需资源,导致的上下文切换.
比如: I/O,内存等系统资源不足时,就会发生自愿上下文切换.

非自愿上下文切换:
进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换.
比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换.

模拟上下文切换问题和定位

Ques: 上下文切换频率是多少次才算正常呢
1.安装sysstat

sudo apt install sysbench sysstat

2.以10个线程运行5分钟的基准测试,模拟多线程切换的问题

# 以10个线程运行5分钟的基准测试,模拟多线程切换的问题
$ sysbench --threads=10 --max-time=300 threads run

3.第二个终端运行 vmstat ,观察上下文切换情况
系统的就绪队列过长,也就是正在运行和等待CPU的进程数过多,导致了大量的上下文切换,而上下文切换又导致了系统 CPU 的占用率升高.

4.查看哪个进程导致了这些问题

# 每隔1秒输出1组数据(需要 Ctrl+C 才结束)
# -w参数表示输出进程切换指标,而-u参数则表示输出CPU使用指标
$ pidstat -w -u 1

CPU 使用率的升高果然是 sysbench 导致的,它的 CPU 使用率已经达到了 100%.
但上下文切换则是来自其他进程,包括非自愿上下文切换频率最高的 pidstat ,以及自愿上下文切换频率最高的内核线程 kworker 和 sshd.

pidstat 默认显示进程的指标数据,加上 -t 参数后,才会输出线程的指标.

# 每隔1秒输出一组数据(需要 Ctrl+C 才结束)
# -wt 参数表示输出线程的上下文切换指标
$ pidstat -wt 1

虽然 sysbench 进程(也就是主线程)的上下文切换次数看起来并不多,但它的子线程的上下文切换次数却有很多.
看来,上下文切换罪魁祸首,还是过多的 sysbench 线程.

part05 cpu使用率100%

CPU 使用率: 单位时间内 CPU 使用情况的统计

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

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

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 使用率: 除了 空闲时间(idle) 外的 其他时间 占总 CPU 时间的百分比

查看cpu使用率

top: 系统总体的 CPU 和内存使用情况,各个进程的资源使用情况.

ps: 每个进程的资源使用情况.

pidstat: 分析每个进程 CPU 使用情况的工具.

top
# 默认每 3 秒刷新一次
$ top
top - 11:58:59 up 9 days, 22:47,  1 user,  load average: 0.03, 0.02, 0.00
Tasks: 123 total,   1 running,  72 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8169348 total,  5606884 free,   334640 used,  2227824 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  7497908 avail Mem
 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    1 root      20   0   78088   9288   6696 S   0.0  0.1   0:16.83 systemd
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.05 kthreadd
    4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:0H
...

第三行 %Cpu 就是系统的 CPU 使用率.
top 默认显示的是所有 CPU 的平均值,此时按下数字 1 ,即可切换到每个 CPU 的使用率了.

空白行之后是进程的实时信息,每个进程都有一个 %CPU 列,表示进程的 CPU 使用率。
它是用户态和内核态 CPU 使用率的总和,包括进程用户空间使用的 CPU、通过系统调用执行的内核空间 CPU 、以及在就绪队列等待运行的 CPU。在虚拟化环境中,它还包括了运行虚拟机占用的 CPU。

top 并没有细分进程的用户态 CPU 和内核态 CPU

pidstat

专门分析每个进程 CPU 使用情况的工具.

间隔 1 秒展示了进程的 5 组 CPU 使用率,包括:

用户态CPU使用率 (%usr);

内核态CPU使用率(%system);

运行虚拟机CPU使用率(%guest);

等待 CPU使用率(%wait);

以及总的CPU使用率(%CPU).

# 每隔 1 秒输出一组数据,共输出 5 组
$ pidstat 1 5
15:56:02      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
15:56:03        0     15006    0.00    0.99    0.00    0.00    0.99     1  dockerd
 
...
 
Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
Average:        0     15006    0.00    0.99    0.00    0.00    0.99     -  dockerd

cpu使用率过高分析

用户 CPU(user) 和 Nice CPU(nice) 高:
用户态进程占用了较多的 CPU,应着重排查进程的性能问题.

系统 CPU(system) 高:
内核态占用了较多的 CPU,应着重排查内核线程或者系统调用的性能问题.

I/O 等待 CPU 高:
等待 I/O 的时间较长,应着重排查系统存储是不是出现了 I/O 问题.

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

碰到 CPU 使用率升高的问题,可以借助 top,pidstat 等工具,确认引发 CPU 性能问题的来源.

perf

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

使用 perf 分析 CPU 性能问题:

perf top,它能实时显示占用 CPU 时钟最多的函数或者指令,可以用来查找热点函数.

$ perf top
Samples: 833  of event 'cpu-clock', Event count (approx.): 97742399
Overhead  Shared Object       Symbol
   7.28%  perf                [.] 0x00000000001f78a4
   4.72%  [kernel]            [k] vsnprintf
   4.32%  [kernel]            [k] module_get_kallsym
   3.65%  [kernel]            [k] _raw_spin_unlock_irqrestore
...

part06 cpu使用率高 但找不到高cpu应用

$ top
...
%Cpu(s): 80.8 us, 15.1 sy,  0.0 ni,  2.8 id,  0.0 wa,  0.0 hi,  1.3 si,  0.0 st
...
 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6882 root      20   0    8456   5052   3884 S   2.7  0.1   0:04.78 docker-containe
 6947 systemd+  20   0   33104   3716   2340 S   2.7  0.0   0:04.92 nginx
 7494 daemon    20   0  336696  15012   7332 S   2.0  0.2   0:03.55 php-fpm
 7495 daemon    20   0  336696  15160   7480 S   2.0  0.2   0:03.55 php-fpm
10547 daemon    20   0  336696  16200   8520 S   2.0  0.2   0:03.13 php-fpm
10155 daemon    20   0  336696  16200   8520 S   1.7  0.2   0:03.12 php-fpm
10552 daemon    20   0  336696  16200   8520 S   1.7  0.2   0:03.12 php-fpm
15006 root      20   0 1168608  66264  37536 S   1.0  0.8   9:39.51 dockerd
 4323 root      20   0       0      0      0 I   0.3  0.0   0:00.87 kworker/u4:1
...

CPU 使用率最高的进程才 2.7%,并不高.

系统 CPU 使用率( %Cpu )这一行,系统的整体 CPU 使用率是比较高的:
用户 CPU 使用率(us)已经到了 80%,系统 CPU 为 15.1%,空闲 CPU (id)则只有 2.8%.

为什么用户 CPU 使用率这么高呢?
再重新分析一下进程列表,看看有没有可疑进程:

所有进程cpu使用率都不高.

再用 pidstat 分析, pidstat 用来分析进程的 CPU 使用情况.

# 间隔 1 秒输出一组数据(按 Ctrl+C 结束)
$ pidstat 1
...
04:36:24      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
04:36:25        0      6882    1.00    3.00    0.00    0.00    4.00     0  docker-containe
04:36:25      101      6947    1.00    2.00    0.00    1.00    3.00     1  nginx
04:36:25        1     14834    1.00    1.00    0.00    1.00    2.00     0  php-fpm
04:36:25        1     14835    1.00    1.00    0.00    1.00    2.00     0  php-fpm
04:36:25        1     14845    0.00    2.00    0.00    2.00    2.00     1  php-fpm
04:36:25        1     14855    0.00    1.00    0.00    1.00    1.00     1  php-fpm
04:36:25        1     14857    1.00    2.00    0.00    1.00    3.00     0  php-fpm
04:36:25        0     15006    0.00    1.00    0.00    0.00    1.00     0  dockerd
04:36:25        0     15801    0.00    1.00    0.00    0.00    1.00     1  pidstat
04:36:25        1     17084    1.00    0.00    0.00    2.00    1.00     0  stress
04:36:25        0     31116    0.00    1.00    0.00    0.00    1.00     0  atopacctd
...

所有进程的 CPU 使用率也不高,最高的 Docker 和 Nginx 也只有 4% 和 3%,即使所有进程的 CPU 使用率都加起来,也不过是 21%,离 80% 还差得远呢!

重新运行 top 命令,并观察一会儿

$ top
top - 04:58:24 up 14 days, 15:47,  1 user,  load average: 3.39, 3.82, 2.74
Tasks: 149 total,   6 running,  93 sleeping,   0 stopped,   0 zombie
%Cpu(s): 77.7 us, 19.3 sy,  0.0 ni,  2.0 id,  0.0 wa,  0.0 hi,  1.0 si,  0.0 st
KiB Mem :  8169348 total,  2543916 free,   457976 used,  5167456 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  7363908 avail Mem
 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6947 systemd+  20   0   33104   3764   2340 S   4.0  0.0   0:32.69 nginx
 6882 root      20   0   12108   8360   3884 S   2.0  0.1   0:31.40 docker-containe
15465 daemon    20   0  336696  15256   7576 S   2.0  0.2   0:00.62 php-fpm
15466 daemon    20   0  336696  15196   7516 S   2.0  0.2   0:00.62 php-fpm
15489 daemon    20   0  336696  16200   8520 S   2.0  0.2   0:00.62 php-fpm
 6948 systemd+  20   0   33104   3764   2340 S   1.0  0.0   0:00.95 nginx
15006 root      20   0 1168608  65632  37536 S   1.0  0.8   9:51.09 dockerd
15476 daemon    20   0  336696  16200   8520 S   1.0  0.2   0:00.61 php-fpm
15477 daemon    20   0  336696  16200   8520 S   1.0  0.2   0:00.61 php-fpm
24340 daemon    20   0    8184   1616    536 R   1.0  0.0   0:00.01 stress
24342 daemon    20   0    8196   1580    492 R   1.0  0.0   0:00.01 stress
24344 daemon    20   0    8188   1056    492 R   1.0  0.0   0:00.01 stress
24347 daemon    20   0    8184   1356    540 R   1.0  0.0   0:00.01 stress
...

Tasks 这一行看起来有点奇怪,就绪队列中居然有 6 个 Running 状态的进程(6 running),是不是有点多呢?

再仔细看进程列表,这次主要看 Running® 状态的进程.
你有没有发现, Nginx 和所有的 php-fpm 都处于 Sleep(S)状态,而真正处于 Running®状态的,却是几个 stress 进程.这几个 stress 进程就比较奇怪了,需要我们做进一步的分析.

我们还是使用 pidstat 来分析这几个进程,并且使用 -p 选项指定进程的 PID.
首先,从上面 top 的结果中,找到这几个进程的 PID.比如,先随便找一个 24344,然后用 pidstat 命令看一下它的 CPU 使用情况:

$ pidstat -p 24344
 
16:14:55      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command

还是没有输出.现在终于发现问题,原来这个进程已经不存在了,所以 pidstat 就没有任何输出.

既然进程都没了,那性能问题应该也跟着没了吧.
再用 top 命令确认一下:

$ top
...
%Cpu(s): 80.9 us, 14.9 sy,  0.0 ni,  2.8 id,  0.0 wa,  0.0 hi,  1.3 si,  0.0 st
...
 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6882 root      20   0   12108   8360   3884 S   2.7  0.1   0:45.63 docker-containe
 6947 systemd+  20   0   33104   3764   2340 R   2.7  0.0   0:47.79 nginx
 3865 daemon    20   0  336696  15056   7376 S   2.0  0.2   0:00.15 php-fpm
  6779 daemon    20   0    8184   1112    556 R   0.3  0.0   0:00.01 stress
...

好像又错了.结果还跟原来一样,用户 CPU 使用率还是高达 80.9%,系统 CPU 接近 15%,而空闲 CPU 只有 2.8%,Running 状态的进程有 Nginx,stress 等.

可是,刚刚我们看到 stress 进程不存在了,怎么现在还在运行呢?

再细看一下 top 的输出,原来,这次 stress 进程的 PID 跟前面不一样了,原来的 PID 24344 不见了,现在的是 6779.

进程的 PID 在变,这说明: 1.要么是这些进程在不停地重启;2.要么就是全新的进程

原因1:进程在不停地崩溃重启,比如因为段错误,配置错误等等,这时,进程在退出后可能又被监控系统自动重启了.

原因2:这些进程都是短时进程,也就是在其他应用内部通过 exec 调用的外面命令.这些命令一般都只运行很短的时间就会结束,你很难用 top 这种间隔时间比较长的工具发现.

stress,是一个常用的压力测试工具.它的 PID 在不断变化中,看起来像是被其他进程调用的短时进程.要想继续分析下去,还得找到它们的父进程.

查找一个进程的父进程呢: pstree 就可以用树状形式显示所有进程之间的关系:

$ pstree | grep stress
         |-docker-containe-+-php-fpm-+-php-fpm---sh---stress
         |         |-3*[php-fpm---sh---stress---stress]

stress 是被 php-fpm 调用的子进程,并且进程数量不止一个(这里是 3 个).

找到父进程后,我们能进入 app 的内部分析了.

首先,当然应该去看看它的源码.运行下面的命令,把案例应用的源码拷贝到 app 目录,然后再执行 grep 查找是不是有代码再调用 stress 命令:

拷贝源码到本地

$ docker cp phpfpm:/app .

grep 查找看看是不是有代码在调用 stress 命令

$ grep stress -r app
app/index.php:// fake I/O with stress (via write()/unlink()).
app/index.php:$result = exec("/usr/local/bin/stress -t 1 -d 1 2>&1", $output, $status);

看错误消息 mkstemp failed: Permission denied ,以及 failed run completed in 0s.
原来 stress 命令并没有成功,它因为权限问题失败退出了.
看来,我们发现了一个 PHP 调用外部 stress 命令的 bug:没有权限创建临时文件.

正是由于权限错误,大量的 stress 进程在启动时初始化失败,进而导致用户 CPU 使用率的升高.

perf 可以用来分析 CPU 性能事件.

perf record -g 命令 ,并等待一会儿(比如 15 秒)后按 Ctrl+C 退出.然后再运行 perf report 查看报告:

记录性能事件,等待大约 15 秒后按 Ctrl+C 退出

$ perf record -g

查看报告

$ perf report

这样,你就可以看到下图这个性能报告:

stress 占了所有 CPU 时钟事件的 77%,而 stress 调用调用栈中比例最高的,是随机数生成函数 random(),看来它的确就是 CPU 使用率升高的原因.

随后的优化就很简单了,只要修复权限问题,并减少或删除 stress 的调用,就可以减轻系统的 CPU 压力.

当然,实际生产环境中的问题一般都要比这个案例复杂,在你找到触发瓶颈的命令行后,却可能发现,这个外部命令的调用过程是应用核心逻辑的一部分,并不能轻易减少或者删除.

这时,你就得继续排查,为什么被调用的命令,会导致 CPU 使用率升高或 I/O 升高等问题.

最后,在案例结束时,不要忘了清理环境,执行下面的 Docker 命令,停止案例中用到的 Nginx 进程:

$ docker rm -f nginx phpfpm

execsnoop
在这个案例中,我们使用了 top,pidstat,pstree 等工具分析了系统 CPU 使用率高的问题,并发现 CPU 升高是短时进程 stress 导致的,但是整个分析过程还是比较复杂的.对于这类问题,有没有更好的方法监控呢?

execsnoop 就是一个专为短时进程设计的工具.它通过 ftrace 实时监控进程的 exec() 行为,并输出短时进程的基本信息,包括进程 PID,父进程 PID,命令行参数以及执行的结果.

比如,用 execsnoop 监控上述案例,就可以直接得到 stress 进程的父进程 PID 以及它的命令行参数,并可以发现大量的 stress 进程在不停启动:

$ execsnoop
PCOMM            PID    PPID   RET ARGS
sh               30394  30393    0
stress           30396  30394    0 /usr/local/bin/stress -t 1 -d 1
sh               30398  30393    0
stress           30399  30398    0 /usr/local/bin/stress -t 1 -d 1
sh               30402  30400    0
stress           30403  30402    0 /usr/local/bin/stress -t 1 -d 1
sh               30405  30393    0
stress           30407  30405    0 /usr/local/bin/stress -t 1 -d 1
...

execsnoop 所用的 ftrace 是一种常用的动态追踪技术,一般用于分析 Linux 内核的运行时行为,后面课程我也会详细介绍并带你使用.

小结

碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题,比如有可能是下面这两种情况.

1.应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top 等工具也不容易发现.

2.应用本身在不停地崩溃重启,而启动过程的资源初始化,很可能会占用相当多的 CPU.

对于这类进程,我们可以用 pstree 或者 execsnoop 找到它们的父进程,再从父进程所在的应用入手,排查问题的根源.

短时应用的运行时间比较短,很难在 top 或者 ps 这类展示系统概要和进程快照的工具中发现,需要使用记录事件的工具来配合诊断,比如 execsnoop 或者 perf top.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FlyingZCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值