常用的命令

Linux性能优化实战 —— CPU

2018-12-25  本文已影响0人  Blue_Eye

Linux性能分析概要

1. 性能指标

linux性能指标.png
随着应用负载的增加,系统资源的使用也会升高,甚至达到极限。而性能问题的本质,就是系统资源已经达到瓶颈,但请求的处理却还不够快,无法支撑更多的请求。
性能分析,其实就是找出应用或系统的瓶颈,并设法去避免或者缓解它们,从而更高效地利用系统资源处理更多的请求。这包含了一系列步骤,比如:

2. linux性能工具图谱

linux性能工具图谱.png

3. linux性能优化思维导图

linux性能优化思维导图.png

平均负载

1. 什么是平均负载?

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

可运行状态的进程: 正在使用 CPU 或者正在等待 CPU 的进程,也就是我们常用 ps 命令看到的处于 R 状态(Running 或 Runnable) 的进程。
不可中断状态的进程: 正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的 I/O 响应, 也就是我们在 ps 命令中看到的 D 状态(Uninterruptible Sleep, 也称为 Disk Sleep) 的进程。
\color{red}{不可中断状态实际上是系统对进程和硬件设备的一种保护机制。}

2. 平均负载为多少时合理

$ grep 'model name' /proc/cpuinfo | wc -l
2

3. 平均负载与CPU使用率

平均负载不仅包括了正在使用 CPU 的进程,还包括了等待CPU等待 I/O的进程。
CPU使用率是指单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应。比如:

4. 检查平均负载的工具

$ uptime
 00:19:43 up 2 min,  1 user,  load average: 3.15, 1.54, 0.60
# -d 参数表示高亮显示变化的区域
$ watch -d uptime
Every 2.0s: uptime                                      Wed Dec 26 00:21:14 2018
 00:21:14 up 3 min,  1 user,  load average: 0.87, 1.23, 0.58
# -P ALL 表示监控所有 CPU, 后面数字 5 表示间隔 5 秒输出一组数据
$ mpstat -P ALL 5
Linux 4.15.0-42-generic (c5220056-VirtualBox)   12/26/2018  _x86_64_    (2 CPU)

12:22:14 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
12:22:19 AM  all    4.55    0.00    3.31    0.00    0.00    0.21    0.00    0.00    0.00   91.93
12:22:19 AM    0    4.32    0.00    3.50    0.00    0.00    0.00    0.00    0.00    0.00   92.18
12:22:19 AM    1    4.80    0.00    2.92    0.00    0.00    0.42    0.00    0.00    0.00   91.86
# 间隔 5 秒后输出一组数据
$ pidstat -u 5 1
Linux 4.15.0-42-generic (c5220056-VirtualBox)   12/26/2018  _x86_64_    (2 CPU)

12:24:54 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
12:24:59 AM     0      1392    0.00    0.20    0.00    0.20     1  kworker/u4:25
12:24:59 AM     0      5863    0.00    0.20    0.00    0.20     0  dockerd
12:24:59 AM     0      6097    0.00    0.20    0.00    0.20     1  docker-containe
12:24:59 AM     0      7341    1.20    2.20    0.00    3.39     1  Xorg
12:24:59 AM  1000      8425    7.78    0.60    0.00    8.38     1  compiz
12:24:59 AM  1000      8500    0.20    0.00    0.00    0.20     1  vmtoolsd
12:24:59 AM  1000      8808    0.80    0.20    0.00    1.00     0  gnome-terminal-
12:24:59 AM  1000      9563    0.00    0.40    0.00    0.40     0  pidstat

Average:      UID       PID    %usr %system  %guest    %CPU   CPU  Command
Average:        0      1392    0.00    0.20    0.00    0.20     -  kworker/u4:25
Average:        0      5863    0.00    0.20    0.00    0.20     -  dockerd
Average:        0      6097    0.00    0.20    0.00    0.20     -  docker-containe
Average:        0      7341    1.20    2.20    0.00    3.39     -  Xorg
Average:     1000      8425    7.78    0.60    0.00    8.38     -  compiz
Average:     1000      8500    0.20    0.00    0.00    0.20     -  vmtoolsd
Average:     1000      8808    0.80    0.20    0.00    1.00     -  gnome-terminal-
Average:     1000      9563    0.00    0.40    0.00    0.40     -  pidstat

CPU 的上下文切换

在每个任务运行前, CPU 都需要知道任务从哪里加载、又从哪里开始运行、也就是说,需要系统事先给他设置好 CPU 寄存器和程序计数器(Program Counter, PC)
CPU 寄存器:是 CPU 内置的容量小、但速度极快的内存。
程序计数器:是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。
它们都是 CPU 在运行任何任务前,比如的依赖环境,因此也被叫做 CPU 上下文

CPU 上下文.png

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

\color{red}{根据任务的不同}, CPU 的上下文切换可以分为进程上下文切换线程上下文切换以及中断上下文切换

1. 进程上下文切换

Linux 按照特权等级,把进程的运行空间分为内核空间用户空间

进程上下文切换和系统调用的区别
进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态
系统调用过程中,并不涉及到虚拟内存等进程用户态的资源,也不会切换进程。

因此,进程的上下文切换比系统调用时多了一步:在保存当前进程的内核状态和CPU寄存器之前,需要先把该进程的虚拟内存、栈等保存下来;而加载了下一个进程的内核态后,还需要刷新进程的虚拟内存和用户栈。


进程上下文切换.png

什么时候会切换进程上文

2. 线程上下文切换

线程和进程的区别

线程的上下文切换两种情况

3. 中断上下文切换

中断处理会打断进程的正常调度和执行。在打断其他进程时,需要将进程当前的状态保存下来,中断结束后,进程仍然可以从原来的状态恢复运行。

进程上下文切换和中断上下文切换的区别

进程上下文切换和中断上文切换的相同之处

4. CPU 上下文切换小结

如何查看系统的上下文切换情况

# 每隔 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 5014224 290736 2060812    0    0    33    37  100  327  6  1 92  0  0
 1  0      0 5014204 290736 2060844    0    0     0     0  321  926  4  1 95  0  0

注:vmstat 只给出了系统总体的上下文切换情况

# 每隔 5 秒输出 1 组数据
$ pidstat -w 5
Linux 4.15.0-42-generic (c5220056-VirtualBox)   12/26/2018  _x86_64_    (2 CPU)

05:47:58 AM   UID       PID   cswch/s nvcswch/s  Command
05:48:03 AM     0         7      0.20      0.00  ksoftirqd/0
05:48:03 AM     0         8     15.57      0.00  rcu_sched
05:48:03 AM     0        11      0.20      0.00  watchdog/0
05:48:03 AM     0        14      0.20      0.00  watchdog/1

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

# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts

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

CPU 使用率

查看/proc/stat,提供的是系统的 CPU 和任务统计信息。

$ cat /proc/stat |grep ^cpu
cpu  3177 0 5996 34551 917 0 191 0 0 0
cpu0 1582 0 2980 17287 472 0 76 0 0 0
cpu1 1594 0 3016 17264 445 0 114 0 0 0

CPU使用率相关的重要指标

CPU使用率的计算

CPU使用率的计算公式.png

查看 CPU 使用率

top 的输出:

# 默认每 3 秒刷新一次
top - 14:15:36 up  1:05,  1 user,  load average: 0.27, 0.22, 0.15
Tasks: 248 total,   1 running, 179 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.2 us,  0.7 sy,  0.0 ni, 98.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8168764 total,  6196696 free,   810812 used,  1161256 buff/cache
KiB Swap:  2095100 total,  2095100 free,        0 used.  7016500 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                     
 8455 c5220056  20   0 1395968 229828  81832 S   3.0  2.8   4:44.39 compiz                                                                                                      
 6739 root      20   0  472636 106196  35576 S   1.7  1.3   0:30.13 Xorg                                                                                                        
 8751 c5220056  20   0  595952  35720  28220 S   0.7  0.4   0:01.41 gnome-terminal-                                                                                             
 6033 root      20   0  568528  68736  39208 S   0.3  0.8   0:11.10 dockerd                                                                                                     
10547 c5220056  20   0   49020   3868   3132 R   0.3  0.0   0:00.02 top                                                                                                         
    1 root      20   0  185428   6020   3968 S   0.0  0.1   0:04.82 systemd                                                                                                     
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.03 kthreadd                                                                                                    
    4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:0H                                                                                                
    6 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 mm_percpu_wq                                                                                                
    7 root      20   0       0      0      0 S   0.0  0.0   0:00.31 ksoftirqd/0                                                                                                 
    8 root      20   0       0      0      0 I   0.0  0.0   0:01.22 rcu_sched                                                                                                   
    9 root      20   0       0      0      0 I   0.0  0.0   0:00.00 rcu_bh                                                                                                      
   10 root      rt   0       0      0      0 S   0.0  0.0   0:00.03 migration/0                                                                                                 
   11 root      rt   0       0      0      0 S   0.0  0.0   0:00.01 watchdog/0                                                                                                  
   12 root      20   0       0      0      0 S   0.0  0.0   0:00.00 cpuhp/0   
   ...                      
# 每隔 1 秒输出一组数据,共输出 2 组
$ pidstat 1 2
Linux 4.15.0-43-generic (c5220056-VirtualBox)   12/27/2018  _x86_64_    (2 CPU)

02:17:31 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
02:17:32 PM     0      6739    0.99    1.98    0.00    2.97     0  Xorg
02:17:32 PM  1000      8455    8.91    0.00    0.00    8.91     1  compiz
02:17:32 PM  1000      8751    0.00    0.99    0.00    0.99     1  gnome-terminal-
02:17:32 PM  1000     10558    0.00    0.99    0.00    0.99     1  pidstat

02:17:32 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
02:17:33 PM     0      6739    0.00    4.00    0.00    4.00     1  Xorg
02:17:33 PM  1000      8455   11.00    2.00    0.00   13.00     0  compiz
02:17:33 PM  1000      8751    2.00    0.00    0.00    2.00     1  gnome-terminal-
02:17:33 PM  1000     10558    0.00    1.00    0.00    1.00     1  pidstat

Average:      UID       PID    %usr %system  %guest    %CPU   CPU  Command
Average:        0      6739    0.50    2.99    0.00    3.48     -  Xorg
Average:     1000      8455    9.95    1.00    0.00   10.95     -  compiz
Average:     1000      8751    1.00    0.50    0.00    1.49     -  gnome-terminal-
Average:     1000     10558    0.00    1.00    0.00    1.00     -  pidstat

查看占用CPU的是代码里的哪个函数

    Samples: 2K of event 'cycles:ppp', Event count (approx.): 1330995812
    Overhead  Shared Object                       Symbol
       9.20%  [kernel]                            [k] module_get_kallsym
       3.96%  [kernel]                            [k] vsnprintf
       2.73%  [kernel]                            [k] format_decode
       2.73%  perf                                [.] 0x00000000001ea373
       1.89%  [kernel]                            [k] number
       1.88%  [kernel]                            [k] kallsyms_expand_symbol.constprop.1
       1.69%  perf                                [.] 0x00000000001f6368
       1.69%  [kernel]                            [k] memcpy_erms
       1.43%  perf                                [.] 0x00000000001f77d0
       1.34%  libc-2.27.so                        [.] __libc_calloc
       1.24%  [kernel]                            [k] string
    Samples: 3K of event 'cycles:ppp', Event count (approx.): 1778811422
    Overhead  Command          Shared Object                  Symbol                            
      28.61%  swapper          [kernel.kallsyms]              [k] intel_idle                    
       4.44%  Xorg             [kernel.kallsyms]              [k] pci_conf1_read                
       2.35%  kworker/1:2      [kernel.kallsyms]              [k] _nv029827rm                   
       2.05%  Xorg             [kernel.kallsyms]              [k] _nv029827rm                   
       1.76%  deepin-wm        [kernel.kallsyms]              [k] pci_conf1_read                
       1.61%  deepin-wm        [kernel.kallsyms]              [k] _nv029827rm                   
       1.09%  irq/150-nvidia   [kernel.kallsyms]              [k] _nv029827rm                   
       1.08%  deepin-wm        [kernel.kallsyms]              [k] syscall_return_via_sysret     
       0.91%  swapper          [unknown]                      [k] 0000000000000000              
       0.71%  Xorg             [kernel.kallsyms]              [k] _nv018294rm                   
       0.41%  swapper          [kernel.kallsyms]              [k] update_load_avg               
       0.33%  Xorg             [vdso]                         [.] 0x0000000000000977 
    Samples: 36K of event 'cycles:ppp', Event count (approx.): 7290912532
      Children      Self  Shared Object                                        Symbol           
    -   15.23%     1.57%  [unknown]                                            [k] 0000000000000000                                                                            
       - 7.58% 0                                                                                
    +   12.19%     0.14%  [kernel]                                            [k] entry_SYSCALL_64_after_hwframe                                                              
    -   12.01%     0.28%  [kernel]                                            [k] do_syscall_64
       - 3.03% do_syscall_64                                                                    
    -    8.65%     0.16%  [kernel]                                            [k] do_idle      
       - 1.75% do_idle                                                                          
    +    4.22%     0.02%  [kernel]                                            [k] call_cpuidle 
    +    4.09%     0.22%  [kernel]                                            [k] cpuidle_enter_state                                                                         
    +    4.01%     0.02%  [kernel]           

进程 PID 在变的原因

查找一个进程的父进程

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

小结

不可中断进程和僵尸进程

进程状态

正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid()等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册SIGCHLD 信号的处理函数,异步回收资源。
如果父进程没这么快,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。
通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由init进程回收后也会消亡。
一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。

进程组和会话

找到僵尸进程父进程

pstree -aps 10183
systemd,1 splash
  └─dockerd,5962 -H fd://
      └─docker-containe,6151 --config /var/run/docker/containerd/containerd.toml
          └─docker-containe,10026 -namespace moby -workdir...
              └─app,10055
                  └─(app,10183)

小结

Linux 软中断

中断是一种异步的事件处理机制,可以提高系统的并发处理能力。中断处理程序会打断其他进程的运行,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行。

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

查看软中断和内核线程
proc文件系统是一种内核空间和用户空间进行通信的机制,可以用来查看内核的数据结构,或者用来动态修改内核的配置。

在查看/proc/softirqs时,要特别注意:

# cat /proc/softirqs 
                    CPU0       CPU1       
          HI:          0          0
       TIMER:     121964     112390
      NET_TX:        518         13
      NET_RX:        437       3263
       BLOCK:     263737     361157
    IRQ_POLL:          0          0
     TASKLET:         48       1811
       SCHED:      65984      49594
     HRTIMER:          0          0
         RCU:      80938      78628

查看线程运行状况

# ps aux|grep softirq
root         7  1.5  0.0      0     0 ?        S    04:25   0:45 [ksoftirqd/0]
root        16  1.6  0.0      0     0 ?        S    04:25   0:47 [ksoftirqd/1]

检查网络接收的软中断

# -n DEV 表示显示网络收发的报告,间隔 1 秒输出一组数据
$ sar -n DEV 1
15:03:46        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
15:03:47         eth0  12607.00   6304.00    664.86    358.11      0.00      0.00      0.00      0.01
15:03:47      docker0   6302.00  12604.00    270.79    664.66      0.00      0.00      0.00      0.00
15:03:47           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
15:03:47    veth9f6bbcd   6302.00  12604.00    356.95    664.66      0.00      0.00      0.00      0.05

第一列:表示报告的时间
第二列:IFACE 表示网卡
第三、四列:rxpck/stxpck/s 分别表示每秒接收、发送的网络帧数,也就是PPS。
第五、六列:rxkB/stxkB/s 分别表示每秒接收、发送的千字节数,也就是 BPS

# -i eth0 只抓取 eth0 网卡,-n 不解析协议名和主机名
# tcp port 80 表示只抓取 tcp 协议并且端口号为 80 的网络帧
$ tcpdump -i eth0 -n tcp port 80
15:11:32.678966 IP 192.168.0.2.18238 > 192.168.0.30.80: Flags [S], seq 458303614, win 512, length 0
...

如何快速分析出系统 CPU 的瓶颈

CPU 性能指标

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

2. 平均负载(Load Average)
平均负载,也就是系统的平均活跃进程数,它反映了系统的整体负载情况,主要包括三个数值,分别指过去 1 分钟、过去 5 分钟和过去 15 分钟的平均复制子。
理想情况下,平均负载等于逻辑 CPU 个数,这表示每个 CPU 都恰好被充分利用。如果平均负载大于逻辑 CPU 个数,就表示负载比较重了。

3.进程上下文切换

4.CPU 缓存的命中率
由于 CPU 发展的速度远快于内存的发展, CPU 的处理速度就比内存的访问速度快得多。这样,CPU 在访问内存的时候,免不了要等待内存的响应。为了协调这两者巨大的性能差距,CPU缓存(通常是多级缓存)就出现了。

CPU 缓存.png
根据不断增长的热点数据,这些缓存按照大小不同分为L1、L2、L3等三级缓存,其中 L1 和 L2 常用在单核中,L3则用在多核中。
从 L1 到 L3,三级缓存的大小依次增大,相应的,性能依次降低(当然比内存还是好得多)。而它们的命中率,衡量的是 CPU 缓存的复用情况,命中率越高,则表示性能越好。

CPU性能指标思维导图

CPU性能指标

性能工具

第一个维度:从 CPU 的性能指标出发
从 CPU 的性能指标出发。也就是说,当你要查看某个性能指标时,要清楚知道哪些工具可以做到。

根据指标找工具(CPU性能)

第二个维度:从工具出发
从工具出发。也就是当你已经安装了某个工具后,要知道这个工具能提供哪些指标。

根据工具查指标(CPU性能)

快速分析 CPU 的性能瓶颈

想弄清楚性能指标的关联性,就要通晓每个性能指标的工作原理。
为了缩小排查范围,我通常会先运行几个支持指标较多的工具,如topvmstatpidstat

关系图

CPU 性能优化的几个思路

怎么评估性能优化的效果

三步走:

  1. 确定性能的量化指标
  2. 测试优化前的性能指标。
  3. 测试优化后的性能指标。

多个性能问题同时存在,要怎么选择?

并不是所有的性能问题都值得优化。
第一,如果发现是系统资源达到了瓶颈,比如CPU使用率达到了 100%,那么首先优化的一定是系统资源使用问题。完成系统资源瓶颈的优化后,我们吃爱要考虑其他问题。
第二,针对不同类型的指标,首先去优化哪些由瓶颈导致的,性能指标变化幅度最大的问题。比如产生瓶颈后,用户 CPU 使用率升高了10%,而系统系统CPU使用率却升高了50%,这个时候就应该首先优化系统 CPU 使用率。

有多种优化方法时,要如何选择?

性能优化并非没有成本。性能优化通常会带来复杂度的提升,降低程序的可维护性,还可能在优化一个指标时,引发其他指标的异常。

CPU 优化

应用程序优化
从应用程序的角度来说,降低 CPU 使用率最好的方法是,排除所有不必要的工作,只保留最核心的逻辑。比如减少循环层次、减少递归、减少动态内存分配等等。

常见的几种应用程序的性能优化方法:

系统优化
从系统的角度来说,优化 CPU 的运行,一方面要充分利用 CPU 缓存的本地性,加速缓存访问;另一方面,就是要控制进程的 CPU 使用情况,减少进程间的相互影响。

常见的方法:

上一篇 下一篇

猜你喜欢

热点阅读