如何在Linux上杀死僵尸进程
编写不当或者性能不佳的程序可能会使僵尸进程潜伏在Linux计算机内部。了解如何创建僵尸,以及如何最终让僵尸安息。
进程状态如何在Linux上工作
当然,Linux必须跟踪计算机上运行的所有应用程序和守护程序。它执行此操作的方法之一是维护过程表。这是内核内存中的结构列表。每个进程在此列表中都有一个条目,其中包含有关它的一些信息。
每个过程表结构中都没有很多。它们包含进程ID,其他一些数据项以及指向该进程的进程控制块(PCB)的指针。
PCB包含Linux需要为每个过程查找或者设置的许多细节。在创建过程的同时,给定处理时间,PCB也将进行更新,最后销毁。
Linux PCB包含超过95个字段。它被定义为一个名为task_struct.h的结构,它的长度超过700行。 PCB包含以下类型的信息:
进程状态:状态描述如下。
进程号:它在操作系统中的唯一标识符。
程序计数器:下一次允许该进程访问CPU时,系统将使用该地址查找应执行的下一条指令。
寄存器:此进程使用的CPU寄存器列表。该列表可能包含累加器,索引寄存器和堆栈指针。
打开文件列表:与此过程关联的文件。
CPU调度信息:用于确定为此进程分配CPU处理时间的频率和时间。流程的优先级,指向调度队列的指针以及其他调度参数必须记录在PCB中。
内存管理信息:有关此进程正在使用的内存的详细信息,例如进程内存的开始和结束地址,以及指向内存页面的指针。
I / O状态信息:进程使用的任何输入或者输出设备。
进程状态可以是以下任意一种:
R:正在运行或者可运行的进程。运行意味着它正在接收CPU周期并执行。一个可运行的进程已准备就绪,可以运行并等待CPU插槽。
S:一个睡眠过程。流程正在等待动作完成(例如输入或者输出操作)或者资源可用。
D:该过程处于不间断的睡眠状态。它正在使用阻塞的系统调用,只有在系统调用完成后才能继续。与睡眠状态不同,处于此状态的进程在系统调用完成并且执行返回到该进程之前不会响应信号。
T:该进程已终止(停止),因为它收到了SIGSTOP信号。它只会响应" SIGKILL"或者" SIGCONT"信号,它们分别杀死进程或者指示其继续。当我们从前台(
fg
)切换到后台(bg)
任务时,就会发生这种情况。Z:僵尸进程。当一个过程完成时,它不仅消失了。它可以释放正在使用的所有内存,并从内存中删除自身,但仍保留在工艺表和PCB中。它的状态被设置为" EXIT_ZOMBIE",并且其父进程被通知(通过" SIGCHLD"信号)子进程已经完成。
在Zombie状态下,创建子进程时,父进程调用wait()
函数家族之一。然后,它等待子进程中的状态更改。子进程是否已停止,继续或者被信号杀死?它通过自然完成其代码而终止吗?
如果状态更改是表示子进程已停止运行的状态更改,则将读取其退出代码。然后,销毁孩子的PCB,并删除其在过程表中的条目。理想情况下,这一切都在眨眼间发生,并且僵尸状态的进程不会存在很长时间。
是什么导致Linux上的僵尸进程?
创建子进程时,写得不好的父进程可能不会调用wait()
函数。这意味着没有任何东西在监视子进程中的状态变化,并且" SIGCHLD"信号将被忽略。或者,可能是由于不良的编程或者恶意的意图,另一个应用程序正在影响父进程的执行。
但是,如果父进程不在子进程中监视状态变化,则不会发生适当的系统内务处理。子进程终止时,不会删除PCB和进程表中的条目。这导致僵尸状态永远不会从PCB上移除。
僵尸确实会占用一些内存,但通常不会造成问题。进程表中的条目很小,但是在发布之前,进程ID无法重用。在64位操作系统上,这不太可能引起任何问题,因为PCB比工艺表条目大得多。
可以想象,大量的僵尸可能会影响可供其他进程使用的内存量。但是,如果我们有那么多僵尸,那么父应用程序或者操作系统错误就是一个严重的问题。
如何删除僵尸进程
我们无法杀死僵尸进程,因为它已经死了。它不会响应任何信号,因为它已从内存中删除,因此无处发送" SIGKILL"信号。我们可以尝试将SIGCHLD信号发送给父进程,但是如果子进程终止时它不起作用,那么现在也不大可能起作用。
唯一可靠的解决方案是杀死父进程。终止时,其子进程将由init
进程继承,这是在Linux系统中运行的第一个进程(其进程ID为1)。
" init"进程会定期执行必要的僵尸清理,因此要杀死它们,我们只需要杀死创建它们的进程即可。 top命令是查看是否有僵尸的便捷方法。
输入以下内容:
top
该系统具有八个僵尸进程。我们可以使用" ps"命令列出这些内容,并将其通过管道传递到" egrep"中。同样,僵尸进程的状态标志为Z,并且我们通常还会看到已失效。
输入以下内容:
ps aux | egrep "Z|defunct"
列出了僵尸进程。
与通过" top"来回滚动相比,这是发现僵尸进程ID的一种更巧妙的方法。我们还看到一个名为badprg的应用程序产生了这些僵尸。
第一个僵尸的进程ID是7641,但是我们需要找到其父进程的进程ID。我们可以再次使用ps
来做到这一点。我们将使用输出选项(-o)告诉ps仅显示父进程的进程ID,然后将其与ppid =标志一起传递。
我们要查找的进程将通过使用-p选项(进程)来指示,然后传入僵尸的进程ID。
因此,我们键入以下命令以查找进程7641的进程信息,但它将仅报告父进程的ID:
ps -o ppid= -p 7641
我们被告知父进程ID为7636. 我们现在可以再次使用ps
对其进行交叉引用。
我们看到这与之前的父进程的名称匹配。要终止父进程,请使用SIGKILL选项和kill命令,如下所示:
kill -SIGKILL 7636
根据父进程的所有者,我们可能还需要使用sudo
。