Linux性能優化實戰學習筆記:第八講

1、環境準備

一、在第6節的基礎上安裝dstat

wget http://mirror.centos.org/centos/7/os/x86_64/Packages/dstat-0.7.2-12.el7.noarch.rpm
rpm -ivh dstat-0.7.2-12.el7.noarch.rpm	

二、故障現象

# 按下數字 1 切換到全部 CPU 的使用狀況,觀察一下子按 Ctrl+C 結束
$ top
top - 05:56:23 up 17 days, 16:45,  2 users,  load average: 2.00, 1.68, 1.39
Tasks: 247 total,   1 running,  79 sleeping,   0 stopped, 115 zombie
%Cpu0  :  0.0 us,  0.7 sy,  0.0 ni, 38.9 id, 60.5 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.7 sy,  0.0 ni,  4.7 id, 94.6 wa,  0.0 hi,  0.0 si,  0.0 st
...

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 4340 root      20   0   44676   4048   3432 R   0.3  0.0   0:00.05 top
 4345 root      20   0   37280  33624    860 D   0.3  0.0   0:00.01 app
 4344 root      20   0   37280  33624    860 D   0.3  0.4   0:00.01 app
    1 root      20   0  160072   9416   6752 S   0.0  0.1   0:38.59 systemd
...

①iowait過高,致使平均負載升高,而且達到了系統CPU的個數
②殭屍進程不斷增多mysql

2、iowait升高的緣由分析

一、用dstat 命令同時查看cpu和i/o對比狀況

(如 dstat 1 10 間隔1秒輸出10組數據),經過結果能夠發現iowait升高時,磁盤讀請求(read)升高因此推斷iowait升高是磁盤讀致使linux

# 間隔 1 秒輸出 10 組數據
$ dstat 1 10
You did not select any stats, using -cdngy by default.
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read  writ| recv  send|  in   out | int   csw
  0   0  96   4   0|1219k  408k|   0     0 |   0     0 |  42   885
  0   0   2  98   0|  34M    0 | 198B  790B|   0     0 |  42   138
  0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  42   135
  0   0  84  16   0|5633k    0 |  66B  342B|   0     0 |  52   177
  0   3  39  58   0|  22M    0 |  66B  342B|   0     0 |  43   144
  0   0   0 100   0|  34M    0 | 200B  450B|   0     0 |  46   147
  0   0   2  98   0|  34M    0 |  66B  342B|   0     0 |  45   134
  0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  39   131
  0   0  83  17   0|5633k    0 |  66B  342B|   0     0 |  46   168
  0   3  39  59   0|  22M    0 |  66B  342B|   0     0 |  37   134

實際測試截圖以下sql

二、定位磁盤讀的進程

使用top命令查看處於不可中斷狀態(D)的進程PIDdocker

# 觀察一下子按 Ctrl+C 結束
$ top
...
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 4340 root      20   0   44676   4048   3432 R   0.3  0.0   0:00.05 top
 4345 root      20   0   37280  33624    860 D   0.3  0.0   0:00.01 app
 4344 root      20   0   37280  33624    860 D   0.3  0.4   0:00.01 app
...

實際測試截圖centos

三、查看對應進程的磁盤讀寫狀況

使用pidstat命令,加上-d參數,能夠看到i/o使用狀況(如 pidstat -d -p <pid> 1 3),發現處於不可中斷狀態的進程都沒有進行磁盤讀寫bash

# -d 展現 I/O 統計數據,-p 指定進程號,間隔 1 秒輸出 3 組數據
$ pidstat -d -p 4344 1 3
06:38:50      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:38:51        0      4344      0.00      0.00      0.00       0  app
06:38:52        0      4344      0.00      0.00      0.00       0  app
06:38:53        0      4344      0.00      0.00      0.00       0  app

實際測試截圖app

四、使用pidstat命令查看全部進程的i/o狀況

可是去掉進程號,查看全部進程的i/o狀況(pidstat -d 1 20),能夠定位到進行磁盤讀寫的進程。咱們知道進程訪問磁盤,須要使用系統調用,
下面的重點就是找到該進程的系統調用函數

# 間隔 1 秒輸出多組數據 (這裏是 20 組)
$ pidstat -d 1 20
...
06:48:46      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:47        0      4615      0.00      0.00      0.00       1  kworker/u4:1
06:48:47        0      6080  32768.00      0.00      0.00     170  app
06:48:47        0      6081  32768.00      0.00      0.00     184  app

06:48:47      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:48        0      6080      0.00      0.00      0.00     110  app

06:48:48      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:49        0      6081      0.00      0.00      0.00     191  app

06:48:49      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command

06:48:50      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:51        0      6082  32768.00      0.00      0.00       0  app
06:48:51        0      6083  32768.00      0.00      0.00       0  app

06:48:51      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:52        0      6082  32768.00      0.00      0.00     184  app
06:48:52        0      6083  32768.00      0.00      0.00     175  app

06:48:52      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:48:53        0      6083      0.00      0.00      0.00     105  app
...

實際測試命令以下工具

[root@luoahong ~]# pidstat -d 1 20
Linux 3.10.0-957.5.1.el7.x86_64 (luoahong) 	05/05/2019 	_x86_64_	(2 CPU)

11:01:21 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command

11:01:22 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
11:01:23 AM     0     12174  82431.50      0.00      0.00       0  app
11:01:23 AM     0     12175  30207.50      0.00      0.00       0  app
......

11:01:35 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command

11:01:36 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command

11:01:37 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
11:01:38 AM     0     12180  47103.50      0.00      0.00       0  app
11:01:38 AM     0     12181  37375.50      0.00      0.00       0  app

11:01:38 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
11:01:39 AM     0     12180 768512.00      0.00      0.00      51  app
11:01:39 AM     0     12181 552448.50      0.00      0.00      49  app
......
Average:        0     12179  65307.42      0.00      0.00       0  app
Average:        0     12180  65307.42      0.00      0.00      28  app
Average:        0     12181  65307.42      0.00      0.00      29  app

五、使用strace查看進程的系統調用 strace -p <pid>

strace -p 6082
strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted

實際測試截圖測試

發現報了 strace:attach :ptrace(PTRACE_SIZE,6028):Operation not peritted,說沒有權限,我是使用的root權限,因此這個時候就要查看進程的狀態是否正常

六、ps aux | grep <pid> 發現進程處於Z狀態,已經變成了殭屍進程

因此不能進行系統調用分析了

ps aux | grep 6082
root      6082  0.0  0.0      0     0 pts/0    Z+   13:43   0:00 [app] <defunct>

實際測試截圖

七、既然top和pidstat都不能找出問題,使用基於事件記錄的動態追蹤工具

perf record -g
$ perf report

要是你是centos系統,在容器外面把分析記錄保存,到容器裏面查看結果
操做:
(1)在centos系統上運行 perf record -g ,執行一下子按ctrl+c中止
(2)把生成的perf.data(一般文件生成在命令執行的當前目錄下,固然能夠經過find | grep perf.data或 find / -name perf.data查看路徑)文件拷貝到容器裏面分析:

docker cp perf.data app:/tpm
docker exec -i -t app bash
cd /tmp/
apt-get update && apt-get install -y linux-perf linux-tools procps
perf_4.9 report

八、看來罪魁禍首是app內部進行了磁盤的直接I/O啊!

open(disk, O_RDONLY|O_DIRECT|O_LARGEFILE, 0755)

而後觀察調用棧信息,檢查是否有磁盤讀操做,這個案例是定位到了進行磁盤的直接讀,固然也能夠查看源碼進行驗證。由於個人centos系統用老師的 iowait鏡像iowait升高不明顯,因此使用的是iowait-new2鏡像,能夠獲得想要的結果,可是這個鏡像把個人系統能搞崩潰,因此我只能執行到cd /tmp/這步,下面那部就執行不下去了。可是整個分析過程仍是理解了。

3、iowait升高解決方案

一、運行修改後的鏡像包

# 首先刪除原來的應用
$ docker rm -f app
# 運行新的應用
$ docker run --privileged --name=app -itd feisky/app:iowait-fix1

二、在用top查看一下

top
top - 14:59:32 up 19 min,  1 user,  load average: 0.15, 0.07, 0.05
Tasks: 545 total,   1 running, 130 sleeping,   0 stopped, 414 zombie
%Cpu(s):  0.3 us, 25.9 sy,  0.0 ni, 65.5 id,  0.5 wa,  0.0 hi,  7.7 si,  0.0 st
KiB Mem :  8056840 total,  6620396 free,   686076 used,   750368 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7029968 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 12967 root      20   0       0      0      0 Z  35.2  0.0   0:01.28 app
 12968 root      20   0       0      0      0 Z  31.6  0.0   0:01.20 app
 12961 root      20   0  162416   2728   1596 R   1.3  0.0   0:00.26 top
  7473 root      20   0  227160   6356   4944 S   0.3  0.1   0:14.91 vmtoolsd
  9369 root      20   0  851800  64620  25608 S   0.3  0.8   0:11.29 dockerd
  9379 root      20   0  452964  36060  15040 S   0.3  0.4   0:12.83 containerd
  9779 mysql     20   0 1119708 186364   5796 S   0.3  2.3   0:14.60 mysqld
 10061 polkitd   20   0 1267680 210772   9336 S   0.3  2.6   0:15.74 mysqld
 11341 root      20   0       0      0      0 S   0.3  0.0   0:06.97 kworker/0:1
     1 root      20   0   43640   3952   2552 S   0.0  0.0   0:03.90 systemd

4、殭屍進程分析

一、找出父進程、而後在父進程裏解決

# -a 表示輸出命令行選項
# p 表 PID
# s 表示指定進程的父進程
[root@luoahong ~]# pstree -aps 12582
systemd,1 --switched-root --system --deserialize 22
  └─containerd,9379
      └─containerd-shim,12484 -namespace moby -workdir...
          └─app,12502
              └─(app,12582

二、查看修復後的源碼文件

int status = 0;
  for (;;) {
    for (int i = 0; i < 2; i++) {
      if(fork()== 0) {
        sub_process();
      }
    }
    sleep(5);
  }

  while(wait(&status)>0);

5、殭屍進程解決方案

一、運行修復後文件打包的鏡像

# 先中止產生殭屍進程的 app
$ docker rm -f app
# 而後啓動新的 app
$ docker run --privileged --name=app -itd feisky/app:iowait-fix2

二、top命令查看

top
top - 13:36:25 up  2:27,  2 users,  load average: 0.44, 0.23, 0.11
Tasks: 134 total,   2 running, 132 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us, 12.5 sy,  0.0 ni, 83.2 id,  0.7 wa,  0.0 hi,  3.6 si,  0.0 st
KiB Mem :  8056840 total,  5975080 free,   832016 used,  1249744 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  6880292 avail Mem
...

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 3198 root      20   0    4376    840    780 S   0.3  0.0   0:00.01 app
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kthreadd
    3 root      20   0       0      0      0 I   0.0  0.0   0:00.41 kworker/0:0
...

殭屍進程出現的緣由是父進程沒有回收子進程的資源出現的。解決辦法是找到父進程,在父進程中處理,使用pstree查父進程,而後查看父進程的源碼檢查wait()/waitpid()的調用或SIGCHLD信號處理函數的註冊

相關文章
相關標籤/搜索