zoukankan      html  css  js  c++  java
  • 僵尸进程的产生原因和避免方法


    分类: linux2013-01-14 22:11 1867人阅读 评论(0) 收藏 举报

    僵尸进程的产生:

    当一个进程创建了一个子进程时,他们的运行时异步的。即父进程无法预知子进程会在什么时候结束,那么如果父进程很繁忙来不及wait 子进程时,那么当子进程结束时,会不会丢失子进程的结束时的状态信息呢?处于这种考虑unix提供了一种机制可以保证只要父进程想知道子进程结束时的信息,它就可以得到。

    这种机制是:在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存。但是仍然保留了一些信息(如进程号pid 退出状态 运行时间等)。这些保留的信息直到进程通过调用wait/waitpid时才会释放。这样就导致了一个问题,如果没有调用wait/waitpid的话,那么保留的信息就不会释放。比如进程号就会被一直占用了。但系统所能使用的进程号的有限的,如果产生大量的僵尸进程,将导致系统没有可用的进程号而导致系统不能创建进程。所以我们应该避免僵尸进程

     

    这里有一个需要注意的地方。如果子进程先结束而父进程后结束,即子进程结束后,父进程还在继续运行但是并未调用wait/waitpid那子进程就会成为僵尸进程。

     

    但如果子进程后结束,即父进程先结束了,但没有调用wait/waitpid来等待子进程的结束,此时子进程还在运行,父进程已经结束。那么并不会产生僵尸进程。应为每个进程结束时,系统都会扫描当前系统中运行的所有进程,看看有没有哪个进程时刚刚结束的这个进程的子进程,如果有,就有init来接管它,成为它的父进程。

     

    同样的在产生僵尸进程的那种情况下,即子进程结束了但父进程还在继续运行(并未调用wait/waitpid)这段期间,假如父进程异常终止了,那么该子进程就会自动被init接管。那么它就不再是僵尸进程了。应为intit会发现并释放它所占有的资源。(当然如果进程表越大,init发现它接管僵尸进程这个过程就会变得越慢,所以在init为发现他们之前,僵尸进程依旧消耗着系统的资源)

     

     

    我们先来讨论 父进程先结束的情况:

     

    比如这段代码。我们让子进程循环打印5次语句 父进程循环打印3次语句。并在父进程中调用wait()等待子进程的结束//

    1. #include<stdio.h>  
    2. #include<stdlib.h>  
    3. #include<unistd.h>  
    4. #include<sys/types.h>  
    5. #include<sys/wait.h>  
    6. int main()  
    7. {  
    8.        int count;  
    9.        pid_t pid;  
    10.        char *message;   
    11.        printf("fork program starting ");  
    12.    
    13.        pid=fork();  
    14.        switch(pid)  
    15.        {    
    16.                 case -1:perror("forkerror");  
    17.                         exit(EXIT_FAILURE);  
    18.                         break;  
    19.                 case 0 :message="This isthe children";  
    20.                         count=10;  
    21.                         break;  
    22.                 default:message="This isthe parent.";  
    23.                         count=3;  
    24.                         break;  
    25.         }    
    26.        for(;count>0;count--)  
    27.        {    
    28.                printf("%s ",message);  
    29.                 sleep(1);  
    30.        }    
    31.        if(pid)  
    32.                 wait((int *)0);  
    33.        if(pid)  
    34.                 printf("Father programDone. ");  
    35.        else  
    36.                 printf("Child ProgramDnoe ");  
    37.        exit(0);  
    38.       
    39. }  


    我们让程序在后台运行,并用ps命令查看进程状态。

     

     

    我们从输出中看到

    第一行显示了我们运行的进程pid是27324

    Ps 的输出中我们看到了他有一个2735的子进程,

    父进程循环三次后并不会结束,而是等待子进程结束后再结束。

    这里并未产生僵尸进程

     

     

    如果我们不等带子进程的结束

     if(pid)  

    wait((int *)0)   注释掉

    将产生如下输出

     

    从第一行我们看到我们运行的程序pid为2804

    Ps输出中的pid为2805 是创建的子进程。我们是在父进程结束后(未调用wait,所以父进程先结束)再用ps命令查看的。所以2805的父进程变成了1 (init 的pid),因为2804已经结束了,所以2805这个子进程被 init接管,同样这里并未产生僵尸进程

     

    现在我们来分析子进程后结束的情况:

    我们  给出下面这个程序

    1. #include<stdio.h>  
    2. #include<stdlib.h>  
    3. #include<unistd.h>  
    4. #include<sys/types.h>  
    5.    
    6. int main()  
    7. {  
    8.        int count;  
    9.        char *message;  
    10.        pid_t pid;  
    11.    
    12.        pid=fork();  
    13.        switch(pid)  
    14.        {    
    15.                 case -1:  
    16.                         perror("forkerror");  
    17.                         exit(EXIT_FAILURE);  
    18.                 case 0:message="This isthe child.";  
    19.                         count=5;  
    20.                         break;  
    21.                 default:message="This isth parent.";  
    22.                         count=10;  
    23.                        break;  
    24.        }    
    25.        for(;count>0;count--)  
    26.        {    
    27.                printf("%s ",message);  
    28.                 sleep(2);  
    29.        }    
    30.    
    31.        if(pid)  
    32.                 printf("Father programDone. ");  
    33.        else  
    34.                 printf("Child prigramDone ");  
    35.        exit(EXIT_SUCCESS);  
    36.    
    37. }  

    这里的代码改动很小,我们只是改变了父进程和子进程的 打印次数

    并且在父进程中我们不调用wait/waitpid来释放子进程的一些信息

     

     

     在子进程结束,但父进程还未结束时我们查看进程信息

    第一行我们看到 我们运行的程序pid 是2874,它的子进程我们可以从ps输出中看到为2875

     我们注意到pid为2875的进程这时候成了僵尸进程。如果主线程运行的时间足够长,那么该僵尸进程就会一直存在着并占用着系统的一些资源。

     

    我们已尽知道了僵尸进程的产生原因,那么如何避免僵尸进程呢

     

    如果父进程并不是很繁忙我们就可以通过直接调用wait/waitpid来等待子进程的结束。当然这会导致父进程被挂起。比如第一种情况中(父进程循环了三次,子进程循环了五次,子进程先结束,父进程调用wait等待子进程)父进程循环结束后并不会结束,而是被挂起等待子进程的结束。

     

    但是如果父进程很忙。我们不希望父进程一直被挂起直到子进程的结束

    那么我们可以使用信号函数sigaction为SIGCHLD设置wait处理函数。这样子进程结束后,父进程就会收到子进程结束的信号。并调用wait回收子进程的资源

    这里给出一个例程

     

    1. #include<sys/wait.h>  
    2. #include<stdio.h>  
    3. #include<stdlib.h>  
    4. #include<unistd.h>  
    5. #include<sys/types.h>  
    6. #include<signal.h>  
    7. void fun_act(int sig)  
    8. {  
    9.        wait((int *)0);  
    10. }  
    11. int main()  
    12. {  
    13.        int count;  
    14.        char *message;  
    15.        pid_t pid;  
    16.    
    17.        struct sigaction act;  
    18.        act.sa_handler=fun_act;  
    19.        sigemptyset(&act.sa_mask);  
    20.        act.sa_flags=0;  
    21.    
    22.        pid=fork();  
    23.        switch(pid)  
    24.        {    
    25.                 case -1:  
    26.                         perror("forkerror");  
    27.                         exit(EXIT_FAILURE);  
    28.    
    29.                 case 0:message="This isthe child.";  
    30.                         count=5;  
    31.                         break;  
    32.                              
    33.                 default:message="This isth parent.";  
    34.                         count=10;  
    35.                         break;  
    36.        }  
    37. if(pid)  
    38.                if(sigaction(SIGCHLD,&act,0)==-1)  
    39.                 {  
    40.                         perror("Settingsignal failed.");  
    41.                         exit(1);  
    42.                 }  
    43.        for(;count>0;count--)  
    44.        {  
    45.                printf("%s ",message);  
    46.                 sleep(1);  
    47.        }  
    48.        if(pid)  
    49.                 printf("Father programDone. ");  
    50.        else  
    51.                 printf("Child prigramDone ");  
    52.        exit(EXIT_SUCCESS);  
    53.    
    54. }  




    我们在子进程结束前 用 ps 查看了一次结束后也查看了一次。

    从输出我们看到,pid为2949的子进程正常结束了,并未产生僵尸进程。说明子进程结束后,父进程收到了它结束的消息,并调用了wait回收了子进程的资源。从而避免了僵尸进程的产生。

     

    转至:http://blog.csdn.net/feng574912883/article/details/8502667 

  • 相关阅读:
    SDN课程阅读作业(2)
    2019 SDN上机第4次作业
    第11组 Alpha事后诸葛亮
    第11组 Alpha冲刺(6/6)
    第11组 Alpha冲刺(5/6)
    2019 SDN上机第3次作业
    2019 SDN阅读作业
    第11组 Alpha冲刺(4/6)
    第11组 Alpha冲刺(3/6)
    模式识别和机器学习、数据挖掘的区别与联系(转发)
  • 原文地址:https://www.cnblogs.com/zqz365001/p/4505000.html
Copyright © 2011-2022 走看看