1.4-CPU性能-不可中断进程、僵尸进程

目录

四、不可中断进程、僵尸进程 —— 等待I/O CPU

4.1 进程状态

4.1.1 进程状态分类 —— R/D/Z/S/I

4.1.2 不可中断状态

4.1.3 僵尸进程

4.2 大量不可中断状态/僵尸状态进程 案例分析

4.2.1 进程组和会话

4.3 大量不可中断状态/僵尸状态进程 案例解决方案

4.3.1 iowait分析 —— dstat 

4.3.2 僵尸进程 —— pstree 

4.4 总结


四、不可中断进程、僵尸进程 —— 等待I/O CPU

现象:

当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。

从 ps 或者 top 命令的输出中,你可以发现它们都处于 D 状态,也就是不可中断状态。

4.1 进程状态

4.1.1 进程状态分类 —— R/D/Z/S/I

top命令输出中,S列(status列)表示进程状态,主要有以下几类:

  • R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行
  • D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠(Uninterruptible Sleep),一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断
  • Z 是 Zombie 的缩写,表示僵尸进程,也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。
  • S 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。
  • 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。前面说了,硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升高, I 状态的进程却不会。
  • T 或 t 是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状态
    • 向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你用 fg 命令,恢复到前台运行)
  • X 是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令中看到它

 

4.1.2 不可中断状态

  1. 正常情况下,为了保证进程数据与硬件状态一致,不可中断状态很短时间内就会结束。因此,短时的不可中断状态进程可以忽略。
  2. 当不可中断状态保持很久或系统中出现大量不可中断进程,可能是系统或硬件发生了故障,排查是不是系统出现了I/O等性能问题。

 

4.1.3 僵尸进程

僵尸进程通常出现在多进程应用中。

进程正常处理过程:

  1. 进程创建子进程后,通过系统调用 wait() 或 waitpid() 等待子进程结束,回收子进程资源;
  2. 子进程结束后,向父进程发送 SIGHLD 信号,因此,父进程需注册 SIGCHLD 信号的处理函数,回收异步资源

僵尸进程出现:

当父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。

通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由 init 进程回收后也会消亡。

一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建,所以这种情况一定要避免。

 

4.2 大量不可中断状态/僵尸状态进程 案例分析

4.2.1 进程组和会话

s 表示这个进程是一个会话的领导进程,+ 表示前台进程组

  • 进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员;
  • 会话是指共享同一个控制终端的一个或多个进程组。

用xshell登录服务器,相当于开启一个会话。在终端运行命令及其子进程,就形成进程组。在后台运行的命令为后台进程组,在前台运行的命令,为前台进程组。

 

 

4.3 大量不可中断状态/僵尸状态进程 案例解决方案

4.3.1 iowait分析 —— dstat 

dstat 可以同时查看 CPU 和 I/O 这两种资源的使用情况,便于对比分析。

# 间隔 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

从 dstat 的输出,我们可以看到,每当 iowait 升高(wai)时,磁盘的读请求(read)都会很大。这说明 iowait 的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。

分析步骤:

  1. 哪个进程在读磁盘? —— top
  2. 查看进程磁盘的读写情况 —— pidstat -d
    # -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒输出 3 组数据
    pidstat -d -p 4344 1 3
    
    // kB_rd 表示每秒读的 KB 数, kB_wr 表示每秒写的 KB 数
    // iodelay 表示 I/O 的延迟(单位是时钟周期)
    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
  3. 直接查看所有进程的I/O使用情况
    # 间隔 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

    查看到的确是App 进程在进行磁盘读,并且每秒读的数据有 32 MB,看来就是 app 的问题。

  4. 进程到底在执行啥 I/O 操作呢?
    1. 跟踪进程系统调用的工具 —— strace。
      $ strace -p 6082
      strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted
    2. 当发现进程strace失败时,检查下进程状态是否正常
      $ ps aux | grep 6082
      //z状态,僵尸状态
      root  6082  0.0  0.0    0     0 pts/0    Z+   13:43   0:00 [app] <defunct>
    3. 打印进程信息,查看报告
      $ perf record -g
      $ perf report

      根据报告可以看到是APP内部进行了磁盘的I/O读写,导致iowait升高

4.3.2 僵尸进程 —— pstree 

僵尸进程出现的原因:

因为父进程没有回收子进程的资源而出现的,需要找到父进程,然后在父进程里面解决。

通过pstree命令查看其父进程,通过对父进程进行查看,进而进行分析。

# -a 表示输出命令行选项
# p 表 PID
# s 表示指定进程的父进程
$ pstree -aps 3084
systemd,1
  └─dockerd,15006 -H fd://
      └─docker-containe,15024 --config /var/run/docker/containerd/containerd.toml
          └─docker-containe,3991 -namespace moby -workdir...
              └─app,4009
                  └─(app,3084)

4.4 总结

  • 不可中断状态:
    • 表示进程正在跟硬件交互,为了保护进程数据和硬件的一致性,系统不允许其他进程或中断打断这个进程。进程长时间处于不可中断状态,通常表示系统有 I/O 性能问题
  • 不可中断状态问题分析:
    • 等待 I/O 的进程一般是不可中断状态,所以用 ps 命令找到的 D 状态(即不可中断状态)的进程,多为可疑进程。
    • 用 perf 工具,来分析系统的 CPU 时钟事件,最终发现是直接 I/O 导致的问题。这时,再检查源码中对应位置的问题,就很轻松了。

 

  • 僵尸进程:
    • 表示进程已经退出,但它的父进程还没有回收子进程占用的资源。短暂的僵尸状态我们通常不必理会,但进程长时间处于僵尸状态,就应该注意了,可能有应用程序没有正常处理子进程的退出。
  • 僵尸进程问题分析:
    • pstree 找出父进程
    • 查看父进程的代码,检查wait() / waitpid() 的调用,或者SIGCHLD 信号处理函数的注册就行了

 

  • iowait 高不一定代表 I/O 有性能瓶颈。当系统中只有 I/O 类型的进程在运行时,iowait 也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度。
    • iowait 升高时的问题排查:
      • 用 dstat、pidstat 等工具,确认是不是磁盘 I/O 的问题
      • 查询是哪些进程导致了 I/O

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值