Chinaunix首页 | 论坛 | 博客
  • 博客访问: 586047
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-23 22:57
个人简介

我本仁慈,奈何苍天不许

文章分类

全部博文(165)

文章存档

2018年(1)

2016年(33)

2015年(5)

2014年(34)

2013年(92)

分类: LINUX

2013-10-23 23:27:10

zombie进程(僵尸进程):
任何一个进程,当其运行结束(exit)之后,其使用的所有资源和内存空间都会释放,以供其他进程使用;但其在OS内核的process table中仍旧保留一条记录,包含该进程的process ID,退出状态exit status,运行时间等信息。这些信息是方便该进程的父进程随时获取它的exit status(即子进程结束后为什么要进入zombie状态)。

Unix/Unix-like系统中,把已经执行结束但process table中记录未被清除的进程称为zombie进程。任何进程都会进入该状态。zombie只有在父进程调用wait(system call)的时候才会被清除。

子进程在运行结束的时候会发送一个SIGCHLD信号给父进程,因此一般通过在父进程的代码中为该信号指定一个handler,并在handler中执行wait系统调用,即可立即清除子进程的zombie信息。

假如父进程没有或未能清除子进程的zombie信息自己运行结束,则该子进程会被init(process ID 1)收养,该进程会每隔一段时间自动调用wait清除其所有子进程的zombie信息。因此zombie进程早晚都会被清除掉。

zombie的危害:
zombie状态的进程不会占据任何资源和内存空间,唯一的危害是在process table中占据一行,且占据一个process ID,如果进程过多可能系统没有过多的process ID来生成新的进程。

由上面可知zombie进程早晚都会被清除掉,因此常见的zombie problem是指父进程运行时间较长,且父进程产生大量子进程,那么可能在较长一段时间内存在大量zombie进程,出现process ID不够用的问题。

什么情况下容易产生过多的zombie进程:
常见的产生zombie进程的情况有:

父进程中没有处理SIGCHLD的handler,且其运行时间较长(一个循环)产生子进程过多,这样一定时间内zombie进程就过多 
最常见的是socket编程中,如果对每个新的client进程都生成一个子进程专门处理,切记在父进程中添加SIGCHLD的handler(其中调用wait),否则处理client过多之后会产生大量的zombie进程

fork两次 
当父进程fork一个子进程,然后子进程又fork一个孙进程的情况注意。

查看进程的zombie状态:
kill等命令只可以杀死正在运行的进程,并不能清除zombie进程的zombie信息;

通过ps aux命令可以查看哪些进程处在zombie状态:STAT列为Z的。另外S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;T代表停止或跟踪状态。

orphan进程(孤儿进程)
注意zombie process和orphan process的区别。orphan进程指正在运行的进程其父进程died,orphan进程不是zombie进程。当一个进程变为orphan进程之后,init进程(process ID 1)会收养它称为其新的父进程。




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


 

子进程持续10秒钟的僵尸状态(EXIT_ZOMBIE)
------------------------------------------------------
#include
#include
#include
#include

main()
{
    pid_t pid;
    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
#include
#include
#include

main()
{
    pid_t pid;
    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        
#include        
#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


zombie进程(僵尸进程):
任何一个进程,当其行结束(exit)之后,其使用的所有资源和内存空间都会释放,以供其他进程使用;但其在OS内核的process table中仍旧保留一条记录,包含该进程的process ID,退出状态exit status,运行时间等信息。这些信息是方便该进程的父进程随时获取它的exit status(即子进程结束后为什么要进入zombie状态)。

Unix/Unix-like系统中,把已经执行结束但process table中记录未被清除的进程称为zombie进程。任何进程都会进入该状态。zombie只有在父进程调用wait(system call)的时候才会被清除。

子进程在运行结束的时候会发送一个SIGCHLD信号给父进程,因此一般通过在父进程的代码中为该信号指定一个handler,并在handler中执行wait系统调用,即可立即清除子进程的zombie信息。

假如父进程没有或未能清除子进程的zombie信息自己运行结束,则该子进程会被init(process ID 1)收养,该进程会每隔一段时间自动调用wait清除其所有子进程的zombie信息。因此zombie进程早晚都会被清除掉。

zombie的危害:
zombie状态的进程不会占据任何资源和内存空间,唯一的危害是在process table中占据一行,且占据一个process ID,如果进程过多可能系统没有过多的process ID来生成新的进程。

由上面可知zombie进程早晚都会被清除掉,因此常见的zombie problem是指父进程运行时间较长,且父进程产生大量子进程,那么可能在较长一段时间内存在大量zombie进程,出现process ID不够用的问题。

什么情况下容易产生过多的zombie进程:
常见的产生zombie进程的情况有:

父进程中没有处理SIGCHLD的handler,且其运行时间较长(一个循环)产生子进程过多,这样一定时间内zombie进程就过多 
最常见的是socket编程中,如果对每个新的client进程都生成一个子进程专门处理,切记在父进程中添加SIGCHLD的handler(其中调用wait),否则处理client过多之后会产生大量的zombie进程

fork两次 
当父进程fork一个子进程,然后子进程又fork一个孙进程的情况注意。

查看进程的zombie状态:
kill等命令只可以杀死正在运行的进程,并不能清除zombie进程的zombie信息;

通过ps aux命令可以查看哪些进程处在zombie状态:STAT列为Z的。另外S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;T代表停止或跟踪状态。

orphan进程(孤儿进程)
注意zombie process和orphan process的区别。orphan进程指正在运行的进程其父进程died,orphan进程不是zombie进程。当一个进程变为orphan进程之后,init进程(process ID 1)会收养它称为其新的父进程。


 
阅读(669) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~