遇到僵尸进程,怎么处理---学习笔记

僵尸进程解释

当 iowait 升高时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。从 ps 或者 top 命令的输出中,你可以发现它们都处于 D 状态,也就是不可中断状态(Uninterruptible Sleep)。既然说到了进程的状态,进程有哪些状态你还记得吗?我们先来回顾一下。

top 和 ps 是最常用的查看进程状态的工具,我们就从 top 的输出开始。下面是一个 top 命令输出的示例,S 列(也就是 Status 列)表示进程的状态。从这个示例里,你可以看到 R、D、Z、S、I 等几个状态,它们分别是什么意思呢?

$ top

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

28961 root 20 0 43816 3148 4040 R 3.2 0.0 0:00.01 top

620 root 20 0 37280 33676 908 D 0.3 0.4 0:00.01 app

1 root 20 0 160072 9416 6752 S 0.0 0.1 0:37.64 systemd

1896 root 20 0 0 0 0 Z 0.0 0.0 0:00.00 devapp

2 root 20 0 0 0 0 S 0.0 0.0 0:00.10 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:06.37 ksoftirqd/0

R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行。

D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠(Uninterruptible Sleep),一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。

Z 是 Zombie 的缩写,如果你玩过“植物大战僵尸”这款游戏,应该知道它的意思。它表示僵尸进程,也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。

S 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。

I 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。前面说了,硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升高, I 状态的进程却不会。

第一个是 T 或者 t,也就是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状态。

向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你用 fg 命令,恢复到前台运行)。

而当你用调试器(如 gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程的运行。另一个是 X,也就是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令中看到它。

如果系统或硬件发生了故障,进程可能会在不可中断状态保持很久,甚至导致系统中出现大量不可中断进程。这时,你就得注意下,系统是不是出现了 I/O 等性能问题。

再看僵尸进程,这是多进程应用很容易碰到的问题。正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD 信号的处理函数,异步回收资源。

如果父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。换句话说,父亲应该一直对儿子负责,善始善终,如果不作为或者跟不上,都会导致“问题少年”的出现。

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

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

分析:

1、$ ps aux | grep /app #查看启动的APP

root 4009 0.0 0.0 4376 1008 pts/0 Ss+ 05:51 0:00 /app

root 4287 0.6 0.4 37280 33660 pts/0 D+ 05:54 0:00 /app

root 4288 0.6 0.4 37280 33668 pts/0 D+ 05:54 0:00 /app、、

状态分别是 Ss+ 和 D+。其中,

S 表示可中断睡眠状态,D 表示不可中断睡眠状态,

s 表示这个进程是一个会话的领导进程,而 + 表示前台进程组。(也可通过命令查询man ps )

进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员

而会话是指共享同一个控制终端的一个或多个进程组。

遇到僵尸进程,怎么处理---学习笔记_第1张图片

汇总

第一点,iowait 太高了,导致系统的平均负载升高,甚至达到了系统 CPU 的个数。

第二点,僵尸进程在不断增多,说明有程序没能正确清理子进程的资源。

大量不可中断进程和僵尸进程的处理方法

现象:

①iowait太高,导致平均负载升高,并且达到了系统CPU的个数

②僵尸进程不断增多

分析过程:

1.先分析iowait升高的原因

一般iowait升高,可能的原因是i/o问题

①用dstat 命令同时查看cpu和i/o对比情况(如 dstat 1 10 间隔1秒输出10组数据),通过结果可以发现iowait升高时,磁盘读请求(read)升高

所以推断iowait升高是磁盘读导致

②定位磁盘读的进程,使用top命令查看处于不可中断状态(D)的进程PID

③查看对应进程的磁盘读写情况,使用pidstat命令,加上-d参数,可以看到i/o使用情况(如 pidstat -d -p 1 3),发现处于不可中断状态的进程都没有进行磁盘读写

④继续使用pidstat命令,但是去掉进程号,查看所有进程的i/o情况(pidstat -d 1 20),可以定位到进行磁盘读写的进程。我们知道进程访问磁盘,需要使用系统调用,

下面的重点就是找到该进程的系统调用

⑤使用strace查看进程的系统调用 strace -p

发现报了 strace:attach :ptrace(PTRACE_SIZE,6028):Operation not peritted,说没有权限,我是使用的root权限,所以这个时候就要查看进程的状态是否正常

⑥ps aux | grep 发现进程处于Z状态,已经变成了僵尸进程,所以不能进行系统调用分析了

⑦既然top和pidstat都不能找出问题,使用基于事件记录的动态追踪工具

如果是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

然后观察调用栈信息,检查是否有磁盘读操作,这个案例是定位到了进行磁盘的直接读,当然也可以查看源码进行验证。因为我的centos系统用老师的 iowait镜像iowait升高不明显,所以使用的是iowait-new2镜像,可以得到想要的结果,但是这个镜像把我的系统能搞崩溃,所以我只能执行到cd /tmp/这步,下面那部就执行不下去了。但是整个分析过程还是理解了。

2.僵尸进程

僵尸进程出现的原因是父进程没有回收子进程的资源出现的。解决办法是找到父进程,在父进程中处理,使用pstree查父进程,然后查看父进程的源码检查wait()/waitpid()的调用或SIGCHLD信号处理函数的注册

你可能感兴趣的:(性能优化,linux)