调试器无法终止一个或多个进程_运维大佬教你“打僵尸”——处理Linux系统中大量的僵尸进程(1)...

a41d6fef445e00aa924a225f2cf77278.png

进程状态

当iowait升高时,进程很有可能因为得不到硬件的响应,而长时间处于不可中断的状态。从ps或top命令输出中,可以发现他们都处于D状态,也就是不可中断状态(Uninterruptible Sleep)。

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

8965ac212c82ddf4e5a5e9409832f3c3.png

我们一个个看一下:

27628d0862805fca3d18459fcac1cd34.png

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

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

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

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

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

当然了,上面的示例并没有包括进程的所有状态。除了以上5个状态,进程还包括下面这2个状态。

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

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

而当你用调试器(如gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程的运行。

另一个是X,也就是Dead的缩写,表示进程已经消亡,所以你不会在top或者ps命令中看到。

了解了这些,我们再回到今天的主题。先看不可中断状态,这其实是为了保证进程数据与硬件状态一致,并且正常情况下,不可中断状态在很短时间内就会结束。所以,短时的不可中断状态进程,我们一般可以忽略。

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

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

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

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

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

e8664e24ec96921ffefc22c8386f5ce2.png

案例分析

接下来,我将用一个多进程应用的案例,带你分析大量不可中断状态和僵尸状态进程的问题。这个应用基于C开发,由于它的编译和运行步骤比较麻烦,我把它打包成了一个 Docker镜像。

环境准备

下面的案例仍然基于Ubuntu18.04,同样适用于其他的Linux系统。我使用的案例环境如下:

机器配置:2 CPU,8GB 内存

预先安装 docker、sysstat、dstat 等工具,如 apt install docker.io dstat sysstat

dstat是一个新的性能工具,它具备vmstat、iostat和ifstat等几种工具的有点,可以同时观察系统的CPU、磁盘 I/O、网络以及内存使用情况。

操作和分析

首先执行下面的命令运行案例应用:

docker run --privileged --name=app -itd feisky/app:iowait

然后,输入ps命令,确认案例应用已经正常启动。如果一切正常,就可看到如下的输出了:

cf57cb50aca7a610c0e76c2744e67c2f.png

从这个界面,我们可以发现多个app进程已经启动,并且它们的状态分别是Ss+和D+。其中,S表示可中断睡眠状态,D表示不可中断睡眠状态,我们在前面刚学过,那后面的s和+什么意思呢?不知道也没关系,查一下 man ps就可以。现在记住,s表示这个进程是一个会话的领导进程,而+表示前台进程组。

这里又出现了两个新概念,进程组会话。它们用来管理一组相互关联的进程,意思其实很好理解。

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

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

比如,我们通过SSH登录服务器,就会打开一个控制终端(TY),这个控制终端就对应一个会话。而我们在终端中运行的命令以及它们的子进程,就构成了一个个的进程组,其中,在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。

明白了这些,我们再用top看一下系统的资源使用情况:

719bf657b45efdec2a431530166d3237.png

从这里你能看出什么问题吗?细心逐行观察,别放过任何一个地方。忘了哪行参数意思的话,也要及时返回去复习。好的,如果你已经有了答案,那就继续往下走,看看跟我找的问题是否一样。这里,我发现了四个可疑的地方。

1、第一行的平均负载( Load Average),过去1分钟、5分钟和15分钟内的平均负载在依次减小,说明平均负载正在升高;而1分钟内的平均负载已经达到系统的CPU个数,说明统很可能已经有了性能瓶颈。

2、第二行的Task5,有1个正在运行的进程,但僵尸进程比较多,而且还在不停增加,说明有子进程在退出时没被清理。

3、来看两个CPU的使用率情况,用户CPU和系统CPU都不高,但 nowait分别是60.5%和94.6%,好像有点儿不正常。

4、每个进程的情况使用率最高的进程只有0.3%,看起来并不高;但有两个进程处于D状态,它们可能在等待O,但光凭这里并不能确定是它们导致了 nowait升高。

我们把这四个问题再汇总一下,就可以得到很明确的两点:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值