Linux下的僵死进程以及其处理办法


僵死进程概念:

    1.父进程未结束,子进程结束。父进程未获取子进程的退出数据。
    2.一个进程的主体释放,pcb没有释放。


了解了僵死进程的概念之后我们就可以轻松的模拟出一个僵死进程:

父进程死循环,子进程运行一段时间后结束(或立刻结束。运行一段时间便于观察)。


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

int main ()
{
	pid_t p = fork();
	
	if (p == -1)
	{
		printf("ERROR\n");
	}
	else if (p == 0)
	{
		printf("Child pid is:%d\n",getpid());
                exit(0);
       }
	else 
	{
		while (1)
		{
			printf("Father pid is:%d\n",getpid());
			sleep(1);
		}
	}
}

这段代码就完成了创建僵死进程的任务。在运行之后,会打印输出一次子进程的pid,随后一直是父进程打印自己的pid。

这时在另一个控制台中使用ps -e命令查看进程,可以查看到僵死的子进程。进程后有<defunct>字样。



那么,产生僵死进程的根本原因是什么呢?


从概念我们可以知道是由于父进程和子进程是异步运行的,即父进程永远无法预测子进程何时结束,当然也就不知道何时去收集子进程的推出信息了。


那么,会不会出现父进程正忙(如例子中的while(1)),在子进程退出的时候没有来得及获取退出信息从而导致丢失呢?


不会。因为在UNIX系统中提供了一种保护机制:每个进程在退出(一般是调用exit、运行时发生致命错误或收到终止信号所导致)的时候内核释放该进程所有的资源,包括打开的文件,占用的内存等. 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等), 直到父进程通过wait / waitpid来取时才释放

但也正是这个机制导致了如果父进程并不调用wait,waitpid函数等,也不对信号进行忽略的话,这些信息就会一直保存,进程号被一直占用。如果僵死进程过多还会导致无法创建新进程的问题。


现在了解了僵死进程产生的原因还有危害之后,我们应该如何去处理它呢?

方法一:wait/waitpid函数:pid_t wait(int *status)

wait函数就是用来应对这种情况的,父进程在调用wait函数之后就可以将自己阻塞,wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

其中的参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:

pid = wait(NULL);

在原代码中的父进程块中也只需要添加 wait(NULL),再运行的时候就会发现僵死进程已经被处理掉了。



但是,这么做的缺点也很明显。在阻塞的过程中,父进程停止了自己的运行。在实际应用中我们不可能为了处理一个僵死进程而令父进程一直wait。并且一个wait函数只能处理一个僵死进程,作用十分有限。


方法二:将父进程中对SIGCHLD信号的处理函数设为SIG_IGN(忽略)

这里使用到了信号,需要信号的头文件:signal.h.

同样,只需要在父进程块中加入一行代码:signal(SIGCHLD,SIG_IGN);,就可以不产生僵死进程了。调用这个signal函数就定义了父进程对子进程结束后返回的SIGCHLD信号的响应方式:忽略。

这种方式可以保持异步,即处理僵死进程的同时,父进程还可以继续运行不受影响。



猜你喜欢

转载自blog.csdn.net/czc1997/article/details/78414382