Linux僵尸进程的产生原因和避免方法

创建于 2013-04-24

迁移自本人的百度空间

--------------------------------

        当一个进程创建了一个子进程时,他们的运行时异步的。unix提供了一种机制可以保证只要父进程想知道子进程结束时的信息,它就可以得到。在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存。但是仍然保留了一些信息(如进程号pid 退出状态 运行时间等)。这些保留的信息直到父进程通过调用wait/waitpid时才会释放。这样就导致了一个问题,如果没有调用wait/waitpid的话,那么保留的信息就不会释放。比如进程号就会被一直占用了。但系统所能使用的进程号的有限的,如果产生大量的僵尸进程,将导致系统没有可用的进程号而导致系统不能创建进程。所以我们应该避免僵尸进程的产生

       

        如果子进程先结束而父进程后结束,即子进程结束后,父进程还在继续运行,但是并未调用wait/waitpid,那子进程就会成为僵尸进程。如果子进程后结束,即父进程先结束了,但没有调用wait/waitpid来等待子进程的结束,此时子进程还在运行,父进程已经结束。那么并不会产生僵尸进程。应为每个进程结束时,系统都会扫描当前系统中运行的所有进程,看看有没有哪个进程时刚刚结束的这个进程的子进程,如果有,就有init来接管它,成为它的父进程。

 

        同样的在产生僵尸进程的那种情况下,即子进程结束了但父进程还在继续运行(并未调用wait/waitpid)这段期间,假如父进程异常终止了,那么该子进程就会自动被init接管。那么它就不再是僵尸进程了。应为intit会发现并释放它所占有的资源。(当然如果进程表越大,init发现它接管僵尸进程这个过程就会变得越慢,所以在init为发现他们之前,僵尸进程依旧消耗着系统的资源)

        下面这段代码。我们让子进程循环打印5次语句 父进程循环打印3次语句。并在父进程中调用wait()等待子进程的结束。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
	int count;
	pid_t pid;
	char *message; 
	printf("fork program starting\n");

	pid=fork();
	switch(pid)
	{  
		case -1:perror("forkerror");
			exit(EXIT_FAILURE);
			break;
		case 0 :message="This is the child process.";
			count=10;
			break;
		default:message="This is the parent process.";
			count=3;
			break;
	}

	for(;count>0;count--)
	{  
		printf("%s\n",message);
		sleep(5);
	}

	if(pid)
	{
		wait((int *)0);
	}

	if(pid)
	{
		printf("Father process done.\n");
	}
	else
	{
		printf("Child process dnoe.\n");
	}

	exit(0);

}

我们让程序在后台运行,并用ps命令查看进程状态。

从输出中看到

第一行显示了我们运行的进程pid是1640

ps 的输出中我们看到了它有一个1641的子进程,

父进程循环三次后并不会结束,而是等待子进程结束后再结束。

这里并未产生僵尸进程。

如果我们不等带子进程的结束

将 if(pid)  

wait((int *)0)   注释掉

将产生如下输出

从第一行我们看到我们运行的程序pid为1834

ps输出中的pid为1835 是创建的子进程。我们在父进程结束后(未调用wait,所以父进程先结束)再用ps命令查看时,1835的父进程变成了1 (init 的pid),表明子进程被 init接管,同样这里并未产生僵尸进程。

 

现在我们来分析子进程后结束的情况,给出下面这个程序

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>

int main()
{
	int count;
	char *message;
	pid_t pid;

	pid=fork();
	switch(pid)
	{  
		case -1:
			perror("forkerror");
			exit(EXIT_FAILURE);
		case 0:message="This is the child process.";
		       count=3;
		       break;
		default:message="This is the parent process.";
			count=5;
			break;
	} 

	for(; count>0; count--)
	{  
		printf("%s\n",message);
		sleep(5);
	}  

	if(pid)
	{
		printf("Father process Done.\n");
	}
	else
	{
		printf("Child process Done\n");
	}

	exit(EXIT_SUCCESS);

}

里的代码改动很小,我们只是改变了父进程和子进程的 打印次数

并且在父进程中,不调用wait/waitpid

在子进程结束,但父进程还未结束时我们查看进程信息

第一行我们看到 我们运行的程序pid 是1894,它的子进程我们可以从ps输出中看到为1895

 我们注意到pid为1895的进程这时候成了僵尸进程。如果主进程运行的时间足够长,那么该僵尸进程就会一直存在着并占用着系统的一些资源。

 

我们已尽知道了僵尸进程的产生原因,那么如何避免僵尸进程呢。

如果父进程并不是很繁忙我们就可以通过直接调用wait/waitpid来等待子进程的结束。当然这会导致父进程被挂起。比如第一种情况中(父进程循环了三次,子进程循环了五次,子进程先结束,父进程调用wait等待子进程)父进程循环结束后并不会结束,而是被挂起等待子进程的结束。

但是如果父进程很忙。我们不希望父进程一直被挂起直到子进程的结束

那么我们可以使用信号函数sigaction为SIGCHLD设置wait处理函数。这样子进程结束后,父进程就会收到子进程结束的信号。并调用wait回收子进程的资源

下面给出一个例程

#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<signal.h>

void fun_act(int sig)
{
	wait((int *)0);
}

int main()
{
	int count;
	char *message;
	pid_t pid;

	struct sigaction act;
	act.sa_handler=fun_act;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;

	pid=fork();
	switch(pid)
	{  
		case -1:
			perror("forkerror");
			exit(EXIT_FAILURE);

		case 0:
			message="This is the child process.";
			count=3;
			break;

		default:
			message="This is th parent process.";
			count=6;
			break;
	}

	if(pid)
	{
		if(sigaction(SIGCHLD, &act, 0) == -1)
		{
			perror("Settingsignal failed.");
			exit(1);
		}
	}

	for(; count>0; count--)
	{
		printf("%s\n",message);
		sleep(5);
	}

	if(pid)
	{
		printf("Father process done.\n");
	}
	else
	{        
		printf("Child process done\n");
	}

	exit(EXIT_SUCCESS);

}

输出如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值