添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接



在 Unix 进程模型中,父进程和其所产生的子进程是异步运行的,所以如果子进程在结束后,会留下一些信息需要父进程使用  ​ wait  /  waitpid ​ 来接收。而如果父进程太忙了,没有调用  ​ wait  /  waitpid ​  的话,子进程就会变成​ 僵尸进程 ​。

Linux 僵尸进程可以被杀死吗?_#include

僵尸进程不可能被杀死,因为它已经死了,不存在再死一次的问题。死的对立面是活,死者已死。只有活的进程才可能被杀死。

什么是僵尸进程?

首先要明确一点,僵尸进程的含义是:子进程已经死了,但是父进程还没有wait它的一个中间状态,这个时候子进程是一个僵尸。正常情况下子死,父wait,清理掉子进程的task_struct,释放子进程的PID:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

int main(void)
{
pid_t pid,wait_pid;
int status;

pid = fork();
if(pid == 1)
{
perror("Cannot create new process\n");
exit(1);
}
else if(pid == 0)
{
printf("child process id:%ld\n",(long)getpid());
pause();
_exit(0);
}
else
{
#if 0
printf("ppid :%d\n",getpid());
while(1);
#endif

do
{
wait_pid = waitpid(pid,&status,WUNTRACED | WCONTINUED);
if(WIFEXITED(status))
printf("child process is killed by signal %d\n",WIFSIGNALED(status));
}while(!WIFEXITED(status) && !WIFSIGNALED(status));
exit(0);
}
}

执行

gcc ps_wait.c -pthread && ./a.out

执行情况如下

linux@ubuntu:~/linux$ gcc ps_wait.c -pthread && ./a.out
child process id:4578

每次执行生成的子进程会不同,这里只是举例子说明

运行,我们看到2个a.out进程 Linux 僵尸进程可以被杀死吗?_#include_02

杀死子进程4578,看到父进程的打印:

Linux 僵尸进程可以被杀死吗?_僵尸进程_03

之后,4578会消失,因为父进程执行到了wait,也知道了子进程是被信号2杀掉的。但是如果子进程死了,父进程不执行到wait,比如把上图中的"#if 0"改为"#if 1",杀死子进程后,子进程就会是一个僵尸:

注意,这里说的是​ 子进程会变成一个僵尸进程

代码如下:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

int main(void)
{
pid_t pid,wait_pid;
int status;

pid = fork();
if(pid == 1)
{
perror("Cannot create new process\n");
exit(1);
}
else if(pid == 0)
{
printf("child process id:%ld\n",(long)getpid());
pause();
_exit(0);
}
else
{
#if 1
printf("ppid :%d\n",getpid());
while(1);
#endif

do
{
wait_pid = waitpid(pid,&status,WUNTRACED | WCONTINUED);
if(WIFEXITED(status))
printf("child process is killed by signal %d\n",WIFSIGNALED(status));
}while(!WIFEXITED(status) && !WIFSIGNALED(status));
exit(0);
}
}

我们重新运行,当我们用kill -2杀掉子进程4628后,我们发现4628成为一个僵尸,状态变为Z+,名字上也加了一个棺材[],成为[a.out]

Z表示的是僵尸进程,[]符号就像一口棺材一样,把这个僵尸进程给装了起来

Linux 僵尸进程可以被杀死吗?_僵尸进程_04

僵尸不可能被杀死?

我们看到上面4628是个僵尸很不爽,所以我们想把它干掉,据说Linux有个信号9,神挡杀神,佛挡杀佛,我们现在来用kill -9干掉4628 Linux 僵尸进程可以被杀死吗?_子进程_05

从上图可以看出,我们把4628用kill -9捅了好多刀,但是最后看4628这个僵尸,还是没有消失。

因为僵尸已经是死了,它不可能再次被杀死,你给它捅一万刀,它也是个死人,不可能再次死!

僵尸不可能被杀死,因为它已经死了!

两种方法来清理僵尸进程​ 1、只等父进程来wait清理尸体了 ​。​ 2、这个时候我们能够把僵尸消失掉的方法,就是杀死僵尸进程的父进程4627。

一个僵尸可以被杀死的假象

下面的这个程序证明「​ 僵尸可以被杀死 ​」这样的假象。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

static void *thread_fun(void *param)
{
while(1);
}

int main(void)
{
pthread_t tid;
int ret;

ret = pthread_create(&tid,NULL,thread_fun,NULL);
if(ret == -1)
{
perror("cannot create new thread\n");
return -1;
}

pthread_exit(0);
return 0;
}

我们在主线程里面,pthread_create()创建线程后,​ pthread_exit()退出,这个时候我们会发现,在ps命令里面,a.out显示为一个僵尸进程 ​。这个僵尸进程出现是我们在thread_fun 里面写了一个while(1)不能退出导致的。

Linux 僵尸进程可以被杀死吗?_僵尸进程_06

继续,开始我们的表演,我们使用下面的命令来杀死这个僵尸进程。

kill -9 4730

我们会惊奇地发现,​ 4730真地会从ps命令里面消失 ​。

Linux 僵尸进程可以被杀死吗?_僵尸进程_07

在没有执行命令杀死 4730 之前,我们「​ 猜测 ​」能杀死僵尸的本质原因是,​ 当主线程4730调用pthread_exit()退出后 ​,主线程4730的状态确实是僵尸了,但是该进程里面的4731线程,却没有死。

我们看看proc 文件系统下面的进程状态

Linux 僵尸进程可以被杀死吗?_子进程_08

看看4731: Linux 僵尸进程可以被杀死吗?_子进程_09

4731是活着的,证明整个进程并没有挂。所以4730的退出,只是让整个进程半死。而由于ps这些命令的误会,4730凑巧又是整个进程的PID,它显示地好像整个4370成了僵尸一样。 Linux 僵尸进程可以被杀死吗?_#include_10

4731这个子进程什么时候死了呢? ​那么,根据POSIX标准关于信号(signal)的定义,当我们执行kill -9 4730 (4730是4730和4731的TGID,也是整个进程用户态视角的PID)的时候,是要杀死整个4730进程的,所以这个时候4731被我们杀死,整个进程就都死了,这个时候,执行到父进程的wait逻辑,导致僵尸消失。

所以,在本例中,kill -9 4730看起来是"杀死了僵尸”,实际是杀死了4730整个进程(里面的每个线程),导致整个进程死。在此之前,整个进程实际还是活的。


Linux 僵尸进程可以被杀死吗?_#include_11

扫码或长按关注

回复「篮球的大肚子」进入技术群聊