目录

Linux性能优化-CPU

各种延时

CPU 性能指标

  1. CPU 使用率
    1. 用户 CPU 使用率, 包括用户态(user)和低优先级用户态(nice). 该指标过高说明应用程序比较繁忙.
    2. 系统 CPU 使用率, CPU 在内核态运行的时间百分比(不含中断). 该指标高说明内核比较繁忙.
    3. 等待 I/O 的 CPU 使用率, iowait, 该指标高说明系统与硬件设备 I/O 交互时间比较长.
    4. 软/硬中断 CPU 使用率, 该指标高说明系统中发生大量中断.
    5. steal CPU / guest CPU, 表示虚拟机占用的 CPU 百分比.
  2. 平均负载
    1. 理想情况下平均负载等于逻辑 CPU 个数,表示每个 CPU 都被充分利用. 若大于则说明系统负载较重.
  3. 进程上下文切换
    1. 包括无法获取资源的自愿切换和系统强制调度时的非自愿切换.
    2. 上下文切换本身是保证 Linux 正常运行的一项核心功能.
    3. 过多的切换则会将原本运行进程的 CPU 时间消耗在寄存器,内核占及虚拟内存等数据保存和恢复上
  4. CPU 缓存命中率
    1. CPU 缓存的复用情况,命中率越高性能越好. 其中 L1/L2 常用在单核,L3 则用在多核中

性能工具

  1. 平均负载案例
    1. 先用 uptime 查看系统平均负载
    2. 判断负载在升高后再用 mpstat 和 pidstat 分别查看每个 CPU 和每个进程 CPU 使用情况.找出导致平均负载较高的进程.
  2. 上下文切换案例
    1. 先用 vmstat 查看系统上下文切换和中断次数
    2. 再用 pidstat 观察进程的自愿和非自愿上下文切换情况
    3. 最后通过 pidstat 观察线程的上下文切换情况
  3. 进程 CPU 使用率高案例
    1. 先用 top 查看系统和进程的 CPU 使用情况,定位到进程
    2. 再用 perf top 观察进程调用链,定位到具体函数
  4. 系统 CPU 使用率高案例
    1. 先用 top 查看系统和进程的 CPU 使用情况,top/pidstat 都无法找到 CPU 使用率高的进程
    2. 重新审视 top 输出
    3. 从 CPU 使用率不高,但是处于 Running 状态的进程入手
    4. perf record/report 发现短时进程导致 (execsnoop 工具)
  5. 不可中断和僵尸进程案例
    1. 先用 top 观察 iowait 升高,发现大量不可中断和僵尸进程
    2. strace 无法跟踪进程系统调用
    3. perf 分析调用链发现根源来自磁盘直接 I/O
  6. 软中断案例
    1. top 观察系统软中断 CPU 使用率高
    2. 查看/proc/softirqs 找到变化速率较快的几种软中断
    3. sar 命令发现是网络小包问题
    4. tcpdump 找出网络帧的类型和来源,确定 SYN FLOOD 攻击导致

根据不同的性能指标来找合适的工具:

为了缩小排查范围,我通常会先运行几个支持指标较多的工具,如 top、vmstat 和 pidstat 。看看下面三者的关系:

平均负载和 CPU 使用率

平均负载

平均负载是指单位时间内,系统处于可运行状态(包括正在运行)和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。

当平均负载高于 CPU 数量 70% 的时候,你就应该分析排查负载高的问题了。

CPU 使用率

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

CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU 上运行任务的不同,又被分为用户 CPU、系统 CPU、等待 I/O CPU、软中断和硬中断等。

  • CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
  • I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
  • 大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。

排查步骤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# -d 参数表示高亮显示变化的区域
$ watch -d uptime
11:34:42 up  2:02,  0 users,  load average: 0.06, 1.39, 1.94

# -P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据
$ mpstat -P ALL 2

11:35:18     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
11:35:20     all    0.00    0.00    0.12    0.00    0.00    0.00    0.00    0.00    0.00   99.88
11:35:20       0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
11:35:20       1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00

# 间隔 5 秒后输出一组数据,-u 表示 CPU 指标
$ pidstat -u 5 1

11:38:16      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
11:38:21        0      7137   50.00    0.00    0.00   50.00   50.00     2  stress
11:38:21        0      7138   49.80    0.00    0.00   50.20   49.80     3  stress
  1. 如果%usr 和%nice 比较高,%iowait 比较低则表明是 CPU 密集型
  2. 如果%usr 和%nice 比较低,%iowait 比较高则表明是 IO 密集型
  3. 如果%usr 和%nice 比较低,%iowait 比较低但是%wait 比较高说明进程太多了

上下文切换

CPU 的上下文切换就可以分为几个不同的场景,也就是进程上下文切换、线程上下文切换以及中断上下文切换。

由于上下文切换需要陷入内核,所以上下文切换属于系统 CPU 使用率。

根据上下文切换的类型,再做具体分析。

  • 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O、内存等其他问题;
  • 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈;
  • 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 每隔 5 秒输出 1 组数据
$ 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
 0  0      0 7846784  32708 148268    0    0    12   208   33   86  0  0 99  0  0
# cs(context switch)是每秒上下文切换的次数。
# in(interrupt)则是每秒中断的次数。
# r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
# b(Blocked)则是处于不可中断睡眠状态的进程数。

# -w参数表示输出进程切换指标,-t参数表示输出线程的上下文切换指标,-u 参数则表示输出 CPU 使用指标
$ pidstat -wtu 5
11:25:28      UID      TGID       TID    %usr %system  %guest   %wait    %CPU   CPU  Command
11:25:33        0        20         -    0.00    0.20    0.00    0.00    0.20     2  fsnotifier-wsl
11:25:33        0         -        20    0.00    0.20    0.00    0.00    0.20     2  |__fsnotifier-wsl

11:25:28      UID      TGID       TID   cswch/s nvcswch/s  Command
11:25:33        0        20         -      2.00      0.00  fsnotifier-wsl
11:25:33        0         -        20      2.00      0.00  |__fsnotifier-wsl

# cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数,
# 自愿上下文切换是指进程无法获取所需资源,导致的上下文切换。
# nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数,
# 非自愿上下文切换是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。

具体某个应用 CPU 飙高

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

  • 用户(%user) CPU 和 Nice(%nice) CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题。
  • 系统(%system) CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题。
  • I/O(%iowait) 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题。
  • 软中断(%softirq)和硬中断(%irq)高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序。

再使用 perf 等工具,排查出引起性能问题的具体函数。

1
2
3
4
5
6
$ top
$ pidstat -u 5 1
$ vmstat 5

# -g 开启调用关系分析,-p 指定应用的的进程号 12096
$ perf top -g -p 21515

CPU 使用率整体高,但各个应用使用率都很低

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

  1. 应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top 等工具也不容易发现。
  2. 应用本身在不停地崩溃重启,而启动过程的资源初始化,很可能会占用相当多的 CPU。

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

iowait 高

碰到 iowait 升高时,需要先用 dstat、pidstat 等工具,确认是不是磁盘 I/O 的问题,然后再找是哪些进程导致了 I/O。

等待 I/O 的进程一般是不可中断状态,所以用 ps 命令找到的 D(Disk Sleep) 状态(即不可中断状态)的进程,多为可疑进程。

  • 可以用 strace -p [pid] 直接分析这个进程的系统调用。
  • strace 没查到可使用 perf record -g 记录,perf report 再输出报告

僵尸进程

僵尸进程表示进程已经退出,但它的父进程还没有回收子进程占用的资源。短暂的僵尸状态我们通常不必理会,但进程长时间处于僵尸状态,就应该注意了,可能有应用程序没有正常处理子进程的退出。

使用 pstree 找出父进程后,去查看父进程的代码,检查 wait() / waitpid() 的调用,或是 SIGCHLD 信号处理函数的注册就行了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# -a 表示输出命令行选项
# p 表 PID
# s 表示指定进程的父进程
$ pstree -aps 12757
init,1
  └─init,6604
      └─init,6605
          └─bash,6606
              └─go,12757 run main.go
                  ├─main,12809
                  │   ├─{main},12810
                  │   ├─{main},12811
                  ├─{go},12758
                  ├─{go},12759

中断

中断是系统用来响应硬件设备请求的一种机制,它会打断进程的正常调度和执行,然后调用内核中的中断处理程序来响应设备的请求。

Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部:

  • 上半部用来快速处理中断,也就是我们常说的硬中断,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作。
  • 下半部则是由内核触发,也就是我们常说的软中断,下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行。

上半部会打断 CPU 正在执行的任务,然后立即执行中断处理程序。而下半部以内核线程的方式执行,并且每个 CPU 都对应一个软中断内核线程,名字为 “ksoftirqd/CPU 编号”,比如说, 0 号 CPU 对应的软中断内核线程的名字就是 ksoftirqd/0。

软中断不只包括了刚刚所讲的硬件设备中断处理程序的下半部,一些内核自定义的事件也属于软中断,比如网络收发、定时、内核调度和 RCU 锁(Read-Copy Update 的缩写,RCU 是 Linux 内核中最常用的锁之一)等。

1
2
3
4
# 查看软中断
cat /proc/softirqs
# 查看硬中断
cat /proc/interrupts

软中断 CPU 使用率(softirq)升高是一种很常见的性能问题。虽然软中断的类型很多,但实际生产中,我们遇到的性能瓶颈大多是网络收发类型的软中断,特别是网络接收的软中断。

在碰到这类问题时,你可以借用 sar、tcpdump 等工具,做进一步分析。

1
2
3
4
5
6
7
# -i eth0 只抓取 eth0 网卡,-n 不解析协议名和主机名
# tcp port 80 表示只抓取 tcp 协议并且端口号为 80 的网络帧
$ tcpdump -i eth0 -n tcp port 80
# -n DEV 表示显示网络收发的报告,间隔 1 秒输出一组数据
$ sar -n DEV 1
20:52:29        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
20:52:30           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

参考

https://www.eet-china.com/mp/a117035.html

性能之巅-洞悉系统、企业与云计算