现在的位置: 首页 > 综合 > 正文

孤儿进程和僵尸进程[详解]

2014年04月05日 ⁄ 综合 ⁄ 共 2947字 ⁄ 字号 评论关闭

一、定义:什么是孤儿进程和僵尸进程
 
 僵尸进程:一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出。这个子进程就是僵尸进程。
 
 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
注:
 
  僵尸进程将会导致资源浪费,而孤儿则不会。

 

子进程持续10秒钟的僵尸状态(EXIT_ZOMBIE)
------------------------------------------------------
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

main()
{
 
   pid_tpid;
 
   pid =fork();
 
   if(pid< 0)
 
      printf("error
occurred!\n");
 
   else if(pid== 0) {
 
      printf("Hi
father! I'm a ZOMBIE\n");
 
      exit(0);     //(1)
 
   }
 
   else {
 
      sleep(10);
 
      wait(NULL);  
//(2)
 
   }
}

(1) 向父进程发送SIGCHILD信号
(2) 父进程处理SIGCHILD信号

执行exit()时根据其父进程的状态决定自己的状态:
 
  如果父进程已经退出(没有wait),则该子进程将会成为孤儿进程过继给init进程
 
  如果其父进程还没有退出,也没有wait(),那么该进程将向父进程发送SIGCHILD信号,进入僵尸状态等待父进程为其收尸。如果父进程一直没有执行wait(),那么该子进程将会持续处于僵尸状态。


子进程将成为孤儿进程
------------------------------------------------------
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

main()
{
 
   pid_tpid;
 
   pid =fork();
 
   if(pid< 0)
 
      printf("error
occurred!\n");
 
   else if(pid== 0) {
 
      sleep(6);
 
      printf("I'm
a orphan\n");
 
      exit(0);
 
   }
 
   else {
 
      sleep(1);
 
      printf("Children
Bye!\n");
 
   }
}

# ./a.out
Children Bye!
# I'm a orphan
(回车后将会进入#)
#
二、影响:
僵尸进程会占用系统资源,如果很多,则会严重影响服务器的性能
孤儿进程不会占用系统资源
处理流程:
只要父进程不等wait(sys/wait.h)子进程,子进程都将成为孤魂野鬼zombie(zombie),unix中默认父进程总是想看子进程死后的状态 

 
if 父进程比子进程先退出 
 
  子进程将被init(id   1)收养,最后的结果是zombie子进程彻底再见,系统资源释放 
 
else   
 
    
 
      子进程的zombie将一直存在,系统资源占用... 
 
      if  父进程dead   
 
          子进程将被init(id   1)收养,最后的结果是zombie子进程彻底再见,系统资源释放 
 
 
 
    else  类似的子进程zombie越来越多,系统就等死了!!! 
 
  
三、如何防止僵尸进程
首先明白如何产生僵尸进程:
1、子进程结束后向父进程发出SIGCHLD信号,父进程默认忽略了它
2、父进程没有调用wait()或waitpid()函数来等待子进程的结束
第一种方法: 
捕捉SIGCHLD信号,并在信号处理函数里面调用wait函数

转贴Richard Steven的Unix Network Programming代码

int main(int argc, char **argv)
{
 
              ...
 
      Signal(SIGCHLD,
sig_chld);
 
              for(;
 
              }
 
              ...
}

void sig_chld(int signo)
{
 
      pid_t       pid;
 
      int        stat;

       while
( (pid = waitpid(-1, &stat, WNOHANG))>; 0)
 
              printf("child
%d terminated\n", pid);
 
      return;
}
第二种方法:两次fork():转载
在《Unix 环境高级编程》里关于这个在8.6节有非常清楚的说明。

实例
回忆一下8 .5节中有关僵死进程的讨论。如果一个进程要fork一个子进程,但不要求它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用fork两次。程序8-5实现了这一点。在第二个子进程中调用sleep以保证在打印父进程ID时第一个子进程已终止。在fork之后,父、子进程都可继续执行——我们无法预知哪一个会先执行。如果不使第二个子进程睡眠,则
在fork之后,它可能比其父进程先执行,于是它打印的父进程ID将是创建它的父进程,而不是init进程(进程ID1)。

#include       <sys/types.h>
#include 
      <sys/wait.h>
#include 
      "ourhdr.h"

int main(void)
{
 
      pid_t       pid;

       if
( (pid = fork()) < 0)
 
              err_sys("fork
error");
 
      else
if (pid == 0){ 
              
 
              if
( (pid = fork()) < 0)
 
                      err_sys("fork
error");
 
              else
if (pid > 0)
 
                      exit(0);       

               

               sleep(2);
 
              printf("second
child, parent pid = %d\n", getppid());
 
              exit(0);
 
      }

       if
(waitpid(pid, NULL, 0) !=pid) 
      
 
              err_sys("waitpid
error");

       

       exit(0);
}
//avoid zombie process by forking twice

抱歉!评论已关闭.