千家信息网

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

发表于:2025-01-22 作者:千家信息网编辑
千家信息网最后更新 2025年01月22日,目录1 前言2 僵尸进程2.1 进程简介2.2 僵尸进程例子2.3 僵尸进程危害3 处理僵尸进程3.1 kill 命令3.2 kill 父进程3.3 reboot3.4 magic sysrq key
千家信息网最后更新 2025年01月22日第三章 九析带你处理 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 调用创建子进程。

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

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

2.2 僵尸进程例子

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

#include

#include

#include

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

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

ps aux | grep -i defunct

分析一下 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

0