zoukankan      html  css  js  c++  java
  • 【Linux】僵尸进程,孤儿进程以及wait函数,waitpid函数(有样例,分析很详细)

    本文内容:

    1.僵尸进程,孤儿进程的定义,区别,产生原因,处理方法

    2.wait函数,waitpid函数的分析,以及比较


    背景:由于子进程的结束和父进程的运行是一个异步的过程,即父进程永远无法预测子进程什么时候结,所以就产生了孤儿进程和僵尸进程


    定义:

    孤儿进程:即父进程退出后,它的一个或多个子进程还在运行,那么这些子进程叫做孤儿进程

    僵尸进程:如果子进程退出,但是父进程没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符pid仍然保存在系统中,那么该子进程叫做僵尸进程


    区别:

    孤儿进程是父进程已退出,而子进程没有退出,僵尸进程是父进程没有退出,而子进程退出了

    处理方法:

    1.孤儿进程将会被1号进程init进程收养,并且由init进程完成对他们的状态收集工作

    2.当一个进程终止后,它的父进程需要调用wait函数(阻塞等待)或waitpid函数(非阻塞等待)获得子进程的终止状态!【父进程通过进程等待的方式,回收子进程的资源,获取子进程退出信息】


    僵尸进程样例:

    #include <iostream>
    #include<pthread.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<semaphore.h>
    using namespace std;
    
    int main()
    {
        pid_t pid=fork();
    
        if(pid>0)
        {
            cout<<"in paraten process,sleep..."<<endl;
            sleep(3);
            cout<<"after sleep"<<endl;
        }
        else if(pid==0)
        {
            cout<<"in child process"<<endl;
            exit(0);
        }
        return 0;
    }

    QQ截图20190717143923_thumb[1]

    分析:父进程睡眠3s等待子进程结束,父进程没有调用函数等待子进程回城子进程的资源,所以子进程变成了一个僵尸进程

    当这个父进程醒来然后也退出之后,该子进程就变成孤儿进程然后被1号init进程收养了,而init进程会周期性的调用wait函数来清除各个僵尸子进程


    wait函数的原理:进程调用wait,然后阻塞自己,然后寻找僵尸子进程,找到了则销毁子进程然后返回,没有找到则一直阻塞直到找打僵尸子进程为止

    样例程序:使用wait函数来回收僵尸进程

    #include <iostream>
    #include<pthread.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<semaphore.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    using namespace std;
    
    int main()
    {
        pid_t pid=fork();
    
        if(pid>0)
        {
            cout<<"in parate process"<<endl;
            pid_t pr=wait(NULL);
            cout<<"in parent process,I catched a child process with pid of "<<pid<<endl;
        }
        else if(pid==0)
        {
            cout<<"in child process,PID="<<getpid()<<" PPID:"<<getppid()<<endl;
            exit(0);
        }
        return 0;
    }

    QQ截图20190717145316_thumb[1]

    wait函数原型:

    pid_t wait(int *status)

    如果status的值不是NULL,wait就会把子进程退出时的状态码取出存入status中,这样我们可以知道子进程是正常退出还是非正常退出

    需要注意的是,子进程状态信息被放在一个整数的不同二进制位中,所以有一个专门的宏macro来读取信息,比如WIFEXITED(status),子进程正常退出返回非0值,当子进程是正常退出时,我们可以使用WEXITSTATUS来获取子进程的返回值,比如子进程返回5,那么我们就可以获取这个返回值

    样例程序:利用宏获得子进程的返回值:

    #include <iostream>
    #include<pthread.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<semaphore.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    using namespace std;
    
    int main()
    {
        pid_t pid=fork();
    
        if(pid<0)
        {
            cout<<"fork error"<<endl;
            return 0;
        }
        else if(pid>0)
        {
            cout<<"in parate process"<<endl;
    
            int status=-1;
            pid_t pr=wait(&status);
    
            if(WIFEXITED(status))
            {
                cout<<"the child process exit normal"<<endl;
                cout<<"the child return code is "<<WEXITSTATUS(status)<<endl;
            }
            else
            {
                cout<<"the child process exit  abnormal,pid="<<pr<<endl;
            }
        }
        else if(pid==0)
        {
            cout<<"in child process,PID="<<getpid()<<" PPID:"<<getppid()<<endl;
            exit(6);
        }
        return 0;
    }

    QQ截图20190717152924_thumb[1]

    分析:子进程返回6,父进程通过宏获得了子进程的返回值,需要注意的是:返回值有一定的大小,太大了不可以,因为整个整数的比特位存储的是子进程返回的一些信息,有位数限制


    waitpid函数原型:

    pid waitpid(pid_t pid,int *status,int options)

    pid参数:

    1)pid>0:只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去

    2)pid=-1:等待任何一个子进程,没有限制,此时和wait函数作用一模一样

    3)pid<-1:等待一个指定进程组中的任何子进程,进程组号=pid的绝对值

    4)pid=0:等待一个指定进程组中的任何子进程,进程组号为pid的值

    options参数:

    1)options=WNOHANG,即使子进程没有退出,它也会立即返回,不会像wait那样永远等下去

    2)options=WUNTRECED,子进程睡眠则马上返回,但结束状态不予理会

    3)options=0,不使用该参数

    和wait函数相比,waitpid其实是wait函数的封装,waitpid可以指定等待的子进程,并且指定返回的条件!

    样例程序:使用waitpid收集子进程的信息

    #include <iostream>
    #include<pthread.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<semaphore.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    using namespace std;
    
    int main()
    {
        pid_t pid=fork();
        pid_t pr;
    
        if(pid<0)
        {
            cout<<"fork error"<<endl;
            return 0;
        }
        else if(pid>0)
        {
            cout<<"in parate process"<<endl;
            do
            {
                pr=waitpid(pid,NULL,WNOHANG);
                if(pr==0)
                {
                    cout<<"no child exit"<<endl;
                    sleep(1);
                }
            }
            while(pr==0);
            if(pr==pid)
            {
                cout<<"successfuly get child "<<pid<<endl;
            }
            else
            {
                cout<<"some error cvcured"<<endl;
            }
        }
        else if(pid==0)
        {
            cout<<"in child process,PID="<<getpid()<<" PPID:"<<getppid()<<endl;
            sleep(10);
            exit(11);
        }
        return 0;
    }

    QQ截图20190717160647_thumb[1]

    分析:waitpid采用了WNOHANG参数,所以waitpid不会停留在那里等待,也就是说父进程不会阻塞在那里等待子进程返回,它会立即返回然后去做自己的事情,所以需要加个循环直到等待到进程为止,相当于轮询


    总结:waitpid函数是wait函数的封装,作用一样,但是比wait函数更加灵活了,可以指定需要等待的子进程,可以设置等待的规则!

  • 相关阅读:
    react的50个面试题
    什么是宏队列跟微队列
    宏队列与微队列
    数组都有哪些方法
    vuex 跟 vue属性
    高阶组件
    如何创建视图簇(View cluster)-SE54/SM34
    ◆◆0如何从维护视图(Maintenace view)中取数据-[VIEW_GET_DATA]
    ◆◆0如何在SM30维护表时自动写入表字段的默认值-事件(EVENT)
    ◆◆0SAP Query 操作教程
  • 原文地址:https://www.cnblogs.com/yinbiao/p/11201937.html
Copyright © 2011-2022 走看看