【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函數更加靈活了,可以指定需要等待的子進程,可以設置等待的規則!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM