码迷,mamicode.com
首页 > 系统相关 > 详细

多进程wait

时间:2018-05-20 22:18:47      阅读:255      评论:0      收藏:0      [点我收藏+]

标签:https   产生   循环   严格   exit   break   数据结构   tab   系统资源   

1、概念

1、孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,从而保证每个进程都会有一个父进程。而Init进程会自动wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。

       补充:孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程结束了其生命周期的时候,init进程就会处理它的一切善后工作。因此孤儿进程并不会有什么危害。

 

2、僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。当用ps命令观察进程的执行状态时,看到这些进程的状态栏为defunct。僵尸进程是一个早已死亡的进程,但在进程表(processs table)中仍占了一个位置(slot)。

        补充(内核):一个进程终止后,内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等。但内核为每一个终止子进程保存了一定量的信息,设置僵死状态来维护子进程的信息,以便父进程在以后某个时候获取,这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

 

        严格来说,僵尸进程并不是问题的根源,罪魁祸首是产生出大量僵尸进程的那个父进程。因此,把产生大量僵尸进程的那个父进程kill掉(通过kill发送SIGTERM或者SIGKILL信号)之后,它产生的僵死进程就变成了孤儿进程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源。

 

2、如何避免僵尸进程?

1、通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

2、父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞waitpid可以通过传递WNOHANG使父进程不阻塞立即返回

3、如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。

4、通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

       第一种方法忽略SIGCHLD信号,这常用于并发服务器的一个技巧,因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

 

3、实验 

以下实验了多种情况,用于理解父进程wait多个子进程

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;
    if(fork() == 0)
    {
        printf("1--This is the child process. pid =%d\n", getpid());
        sleep(3);
        printf("%d exit\n", getpid());
        exit(0);
    }
    if(fork() == 0)
    {
        printf("2--This is the child process. pid =%d\n", getpid());
        sleep(10);
        printf("%d exit\n", getpid());
        exit(0);
    }
    if(fork() == 0)
    {
        printf("3--This is the child process. pid =%d\n", getpid());
        sleep(17);
        printf("%d exit\n", getpid());
        exit(0);
    }
    // while(1)
    // {
    //     pid = wait(NULL);
    //     printf("parent:%d,return of wait:%d\n", getpid(), pid);
    //     if(pid == -1)
    //     {
    //         break;
    //     }
    // }
    //循环wait每一个子进程输出,最后返回值是-1表示没有子进程了,该父进程也退出


    //while(wait(NULL) != -1);
    //wait最后一个子进程退出后,该父进程退出


    //printf("parent:%d,return of wait:%d\n", getpid(), wait(NULL));
    //wait第一个返回的进程后,该父进程就返回,其他未返回进程变孤儿进程,由init进程接管
 

    // while(wait(NULL) != -1)
    // {
    //     printf("parent:%d,return of wait:%d\n", getpid(), wait(NULL));
    // }
    //while里wait第一个退出的子进程,printf里wait第二个子进程输出,while再wait第三个子进程,最后printf里是-1,表明已经没有子进程,父进程退出

    signal(SIGCHLD,SIG_IGN);//忽略子进程exit,内核直接转交init处理defunct,这样就算父进程没退出也不会有僵尸进程
    sleep(20);//父进程没有wait任何子进程,没有设置SIG_IGN,且还在运行中时子进程就退出了,此时所有子进程变僵尸进程,直到父进程退出后,子进程被init进程接管处理
    printf("exit\n");
    return 0;
}

 

总结:

调用一个wait,则第一个子进程返回后该父进程也返回,那其他子进程还在运行,为何没有变成僵尸进程,而是直接由init接管

-----父进程返回了,所以其他子进程变孤儿进程,直接由init接管,所以ps –ef | grep defunct没有僵尸进程

 

如果父进程fork多个子进程,子进程退出后,该父进程还在运行中,但没wait任何子进程,则此时的子进程会变僵尸进程,直到父进程结束,然后由init进程接管

-----父进程不wait阻塞,而且在子进程结束后还在运行。此时为了不产生僵尸进程,可以在父进程中设置signal(SIGCHLD,SIG_IGN); 使得父进程忽略子进程退出,把子进程直接交由init接管

 

概念参考:

https://www.cnblogs.com/wuchanming/p/4020463.html

https://www.cnblogs.com/Anker/p/3271773.html 

多进程wait

标签:https   产生   循环   严格   exit   break   数据结构   tab   系统资源   

原文地址:https://www.cnblogs.com/beixiaobei/p/9064815.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!