主题 : 僵死进程 产生,及避免方法 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 19802
精华: 0
发帖: 42
金钱: 215 两
威望: 43 点
综合积分: 84 分
注册时间: 2010-04-23
最后登录: 2018-02-08
楼主  发表于: 2011-08-15 14:29

 僵死进程 产生,及避免方法

僵死进程 产生,及避免方法  
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程  
  是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退 出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信 号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。  

============================================================  

怎样来清除僵尸进程:  

  1.改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。  

  2.把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。

      
======================================================================

我怎样避免它们:

1  你需要却认父进程为每个子进程的终止调用‘wait()’(或者‘waitpid()’,‘wait3()’,等            等); 或者,在某些系统上,你可以指令系统你对子进程的退出状态没有兴趣。在SysV系统上,可以调用signal函数,设置SIGCLD信号为SIG_IGN,系统将不产生僵死进程, 节)另一种方法是*两次*‘fork()’,而且使紧跟的子进程直接退出,这样造成孙子进程变成孤儿进程(orphaned),从而init进程将负责清除它。为了忽略子进程状态,你需要做下面的步骤(查询你的系统手册页以知道这是否正
常工作):

复制代码
  1. struct sigaction sa;
  2. sa.sa_handler = SIG_IGN;
  3. #ifdef SA_NOCLDWAIT
  4. sa.sa_flags = SA_NOCLDWAIT;
  5. #else
  6. sa.sa_flags = 0;
  7. #endif
  8. sigemptyset(&sa.sa_mask);
  9. sigaction(SIGCHLD, &sa, NULL);

如果这是成功的,那么‘wait()’函数集将不再正常工作;如果它们中任何一个被
调用,它们将等待直到*所有*子进程已经退出,然后返回失败,并且
‘errno==ECHILD’。



2    另一个技巧是捕获SIGCHLD信号,然后使信号处理程序调用‘waitpid()’或‘wait3()’。  

  

3     让waitpid阻塞就可以了,这样会保证再子进程推出后父进程才结束。

4    忽略信号
int main(int argc, char **argv)
{
signal(SIGCHLD, SIG_IGN); //加入此句即可,忽略SIGCHLD信号
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}

5  fork两次在防止僵死方面来说,就是因为儿子进程先退出,孙子进程就被init接管了,实际上与最初的父进程脱离了关系,就不会僵死了。


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

int main(void)    
{    
   pid_t pid;    
  
    if ((pid = fork()) < 0)    
    {    
        fprintf(stderr,"Fork error!\n");    
        exit(-1);    
    }    
    else if (pid == 0) /* first child */  
    {    
        if ((pid = fork()) < 0)    
        {    
            fprintf(stderr,"Fork error!\n");    
            exit(-1);    
        }    
        else if (pid > 0)    
            exit(0); /* parent from second fork == first child */  
        /*  
         * We're the second child; our parent becomes init as soon  
         * as our real parent calls exit() in the statement above.  
         * Here's where we'd continue executing, knowing that when  
         * we're done, init will reap our status.  
         */  
        sleep(2);    
        printf("Second child, parent pid = %d\n", getppid());    
        exit(0);    
    }    
        
    if (waitpid(pid, NULL, 0) != pid) /* wait for first child */  
    {    
        fprintf(stderr,"Waitpid error!\n");    
        exit(-1);    
    }    
  
    /*  
     * We're the parent (the original process); we continue executing,  
     * knowing that we're not the parent of the second child.  
     */  
    exit(0);    
}    
5  
static void
sig_child()
{
waitpid(-1, NULL, 0);
}
[...]
signal(SIGCHLD, sig_child)
sig_child这个函数有问题。这个函数的作用是每当有一个进程退出时,便会调用一次sig_child,sig_child便会调用
waitpid处理一个已退出的子进程。但是假如程序正在sig_child里面,这时又有另外一个子进程退出了,这时该怎么处理呢?处理的方法应该是不
会有另外一个sig_child响应,于是这时便有2个子进程退出了,但是sig_child只能处理一个,于是便有一个退出的子进程成了僵死进程了。于是把sig_child改成:
static void
sig_child()
{
while (waitpid(-1, NULL, 0) > 0);
}

这样假如程序正在sig_child里面,这时又有另外n个子进程退出了,由于用了while,于是sig_child便会将所以退出的子进程“一网打尽”。

级别: 新手上路
UID: 19802
精华: 0
发帖: 42
金钱: 215 两
威望: 43 点
综合积分: 84 分
注册时间: 2010-04-23
最后登录: 2018-02-08
1楼  发表于: 2011-08-15 14:31
sig_child()  
{  
while (waitpid(-1, NULL, 0) > 0);  
}  


最初采用的方法,但是在有的计算机中,出现while (waitpid(-1, NULL, 0) > 0); 一直阻塞的现象,大家是如何做的呢?