什么是僵死进程?
在linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
僵死进程:一个进程使用fork创建子进程,如果子进程先于父进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
同时还可能存在另一种进程,叫做孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1,pid ==1)所收养,并由init进程对它们完成状态收集工作。(init进程将wait它们,去除僵尸状态)。
僵死进程的危害:
linux提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
如何解决僵死进程问题?
首先,我们制造一个僵死进程,然后再来一起解决掉它。
接下来把我们把这个进程放到后台,ps查看
ps,子进程先于父进程结束,产生了僵死状态(<defunct>)子进程的pid=4720,父进程的pid=4719,我们还可以发现父进程的父进程ppid=2938,即bash.
接下来,我们把这个问题解决掉:
在函数体中调用wait(或waitpid),就可以清理退出的子进程防止僵尸进程。
我们再ps查看,这时<defunct>已经被解决。此时的退出码val=3,(与exit(3)对应)。
我这里简单的用了wait处理了僵死进程,这里很多人会用到信号控制机制(一样的道理),为了防止产生僵尸进程,在fork子进程之后我们都要wait它们。子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
static void sig_chld( int signo ) {
pid_t pid;
int stat;
pid = wait(&stat);
printf( "child %d exit\n", pid );
return;
}
int main() {
signal(SIGCHLD, &sig_chld);
}