本文內容:
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; }
分析:父進程睡眠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; }
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; }
分析:子進程返回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; }
分析:waitpid采用了WNOHANG參數,所以waitpid不會停留在那里等待,也就是說父進程不會阻塞在那里等待子進程返回,它會立即返回然后去做自己的事情,所以需要加個循環直到等待到進程為止,相當於輪詢
總結:waitpid函數是wait函數的封裝,作用一樣,但是比wait函數更加靈活了,可以指定需要等待的子進程,可以設置等待的規則!