系统相关
首页 > 系统相关> > 第三章 九析带你处理 zombie(defunct) 进程

第三章 九析带你处理 zombie(defunct) 进程

作者:互联网

目录

1 前言

2 僵尸进程

    2.1 进程简介

    2.2 僵尸进程例子

    2.3 僵尸进程危害

3 处理僵尸进程

    3.1 kill 命令

    3.2 kill 父进程

    3.3 reboot

    3.4 magic sysrq key 方法


1 前言

        在 centos7 跑 Docker 和 k8s 时,偶尔会出现 systemctl 失效的情况,现象如下:

Failed to get properties...

        查看系统进程,发现僵尸进程(zombie/defunct):

ps -ef | grep defunct


2 僵尸进程

2.1 进程简介

        在 linux 中,父进程通过 fork 调用创建子进程。

spacer.gifclipboard.png

        子进程执行完毕之后,内核会释放该子进程所占用的资源,包括打开的文件,占用的内存等,但仍然会在进程表中保留一个槽位(slot)存放该子进程的文件描述符(比如进程PID、进程退出状态、进程运行时间等),直到父进程发送 wait() 或 waitpid() 调用,内核才会把子进程文件描述符从进程表中彻底清除。如果父进程不调用 wait() 或 waitpid()对子进程进行清理,那子进程将处于僵尸状态。

        但是如果父进程先于子进程结束的话,会导致子进程变成僵尸进程吗?答案是不会。因为每当进程结束的时候,系统都会扫描当前所有运行的进程,查找是否有这个结束进程的子进程,如果有,就由 init 进程(或者 systemd 进程)来接管子进程,成为子进程的新父进程,并自动 wait 这个子进程,确保以后该子进程不会变成僵尸进程。

2.2 僵尸进程例子

        下面展示一个 c 语言编写的僵尸进程样例,样例中主进程并不会 wait 子进程,生成文件 zombie.c:

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

int main(void)  {

    int i = 60;

    pid_t pid = fork();


    if ( pid < 0 )  {

        perror( "fork error." );

        exit(1);

    }


    if ( pid == 0 ) {

        printf( "This is the child process. My PID is: %d. My PPID is: %d\n", getpid(), getppid() );

    }


    if (pid > 0)    {

        printf( "This is the parent process. My PID is %d.\n", getpid() );

        for( ; i > 0; i-- ) {

            sleep(1);

        }

    }


    return 0;

}

        编译 zombie.c 并执行 zombie:

yum install gcc

gcc zombie.c -o zombie

./ zombie

spacer.gifclipboard2.png

        上图中主进程 PID:11552,子进程 PID:11553。执行如下语句:发现 PID 为 11553 的子进程正好处于僵尸状态(defunct),由程序可知,因为主进程并没有 wait 子进程。

ps aux | grep -i defunct

spacer.gifclipboard3.png        分析一下 zombie.c,特别注意 fork() 调用,在 pid_t pid = fork() 语句之前,只有一个进程,但是执行到这条语句之后,就变成2个进程了,这2个进程几乎完全相同,将要执行的下一条语句都是 if ( pid < 0 )。

        fork() 函数比较特殊,它被调用一次,却能够返回两次结果,它的返回值也根据进程的不同而不同:

1)在父进程中,fork 返回新创建子进程的 PID

2) 在子进程中,fork 返回 0

3)如果出现错误,则 fork 返回负值

2.3 僵尸进程危害

        如果父进程没有 wait 子进程,子进程将变成僵尸状态,处于僵尸状态的进程将保留进程号(PID),众所周知,操作系统对进程号是有限制的,如果出现大量僵尸进程占用进程号,系统有可能无法创建新的进程。

3 处理僵尸进程

        一般情况下处于僵尸状态的进程很难杀掉,当然你可以试着删除:

3.1 kill 命令

kill -9 PID

3.2 kill 父进程

kill -9 PPID

3.3 reboot

        如果采用上面两种方式依然杀不掉,那么只能通过重启了。

reboot

        如果重启也不生效,可以需要加选项 -nf

reboot -nf

3.4 magic sysrq key 方法

        有时执行 reboot 命令还是无法重启,可以执行 magic sysrq 方法来通过提供给用户的 proc 接口直接向 kernel 发底层命令。

        重启命令如下:

echo 1 > /proc/sys/kernel/sysrq

echo b > /proc/sysrq-trigger

        强制关机命令:

echo 1 > /proc/sys/kernel/sysrq

echo b > /proc/sysrq-trigger

标签:fork,defunct,九析,PID,pid,zombie,进程,僵尸
来源: https://blog.51cto.com/14625168/2464656