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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FlyingZCC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值