系統(tǒng)調(diào)用wait1)概述
wait函數(shù)的原型為:pid_t wait(int *status)
當進程退出時,它向父進程發(fā)送一個SIGCHLD信號,默認情況下總是忽略SIGCHLD信號,此時進程狀態(tài)一直保留在內(nèi)存中,直到父進程使用wait函數(shù)收集狀態(tài)信息,才會清空這些信息.
用wait來等待一個子進程終止運行稱為回收進程.
當父進程忘了用wait()函數(shù)等待已終止的子進程時,子進程就會進入一種無父進程的狀態(tài),此時子進程就是僵尸進程.
wait()要與fork()配套出現(xiàn),如果在使用fork()之前調(diào)用wait(),wait()的返回值則為-1,正常情況下wait()的返回值為子進程的PID.
如果先終止父進程,子進程將繼續(xù)正常進行,只是它將由init進程(PID 1)繼承,當子進程終止時,init進程捕獲這個狀態(tài).
2)僵尸進程的形成
源程序如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int
main ()
{
pid_t pc,pr;
pc = fork();
if (pc < 0)
printf("error ocurred!\n");
else
if(pc == 0){
printf("This is child process with pid of %d\n",getpid());
}
else{
sleep(20);
printf("This is partent with pid of %d\n",getpid());
}
exit(0);
}
編譯zombie.c
gcc zombie.c -o zombie
運行生成的程序
./zombie
同時查看zombie的進程狀態(tài)
ps -C zombie -o ppid,pid,stat,cmd
PPID PID STAT CMD
24233 26056 S+ ./zombie
26056 26057 Z+ [zombie] <defunct>
20秒后,查看程序的運行結(jié)果,如下:
./zombie
This is child process with pid of 26057
This is partent with pid of 26056
結(jié)論:
當父進程忘了用wait()函數(shù)等待已終止的子進程時,子進程就會進入一種無父進程清理自己尸體的狀態(tài),此時的子進程就是僵尸進程.
在上面的例子中,子進程(26057)雖然執(zhí)行完畢,但父進程沒有調(diào)用wait(),出現(xiàn)子進程雖然死亡,而不能在內(nèi)核中清理尸體的情況.
在sleep(20);前面加入wait(NULL);再編譯運行,運行結(jié)果沒有了僵尸進程,說明已經(jīng)被父進程用wait()回收掉了.
如下:
ps -C zombie -o ppid,pid,stat,cmd
PPID PID STAT CMD
12051 12107 S+ ./zombie
3)關(guān)于父進程調(diào)用wait()與fork()配套使用的問題.
對上面的程序進行修改,在fork()調(diào)用前使用wait()函數(shù),如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int
main ()
{
pid_t pc,pr;
pr = wait(NULL);
pc = fork();
if (pc < 0)
printf("error ocurred!\n");
else
if(pc == 0){
printf("This is child process with pid of %d\n",getpid());
}
else{
sleep(20);
printf("I catched a child process with pid of %d\n",pr);
}
exit(0);
}
編譯執(zhí)行:
gcc wait1.c -o wait1
./wait1
This is child process with pid of 24008
I catched a child process with pid of -1
wait(NULL)的返回值為-1.
在另一個終端查看wait1進程的狀態(tài),發(fā)現(xiàn)wait()并沒有回收掉子進程.
ps -C wait1 -o ppid,pid,stat,cmd
PPID PID STAT CMD
23286 24007 S+ ./wait1
24007 24008 Z+ [wait1] <defunct>
調(diào)整wait()的位置,如下:
else{
pr = wait(NULL)
sleep(20);
printf("I catched a child process with pid of %d\n",pr);
}
再編譯執(zhí)行:
gcc wait1.c -o wait1
./wait1
This is child process with pid of 24085
I catched a child process with pid of 24085
wait(NULL)的返回值為子進程PID
在另一個終端查看wait1進程的狀態(tài),發(fā)現(xiàn)wait()回收掉了子進程.
ps -C wait1 -o ppid,pid,stat,cmd
PPID PID STAT CMD
23286 24084 S+ ./wait1
4)wait()的參數(shù)
如果參數(shù)的值不是NULL,wait就會把子進程退出時的狀態(tài)取出并存入其中,這是一個整數(shù)值(int),指出了子進程是正常退出還是被非正常結(jié)束的,
由于這些信息被存放在一個整數(shù)的不同二進制位中,所以就設(shè)計了一套專門的宏來完成這項工作,其中最常用的兩個:
1,WIFEXITED(status)這個宏用來指出子進程是否為正常退出的,如果是,它會返回一個非零值.
2,WEXITSTATUS(status)當WIFEXITED返回非零值時,我們可以用這個宏來提取子進程的返回值,如果子進程調(diào)用exit(5)退出,WEXITSTATUS(status)就會返回5;
如果子進程調(diào)用exit(7),WEXITSTATUS(status)就會返回7.請注意,如果進程不是正常退出的,也就是說,WIFEXITED返回0,這個值就毫無意義.
以下面的例子說明wait()參數(shù)與宏的調(diào)用:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main ()
{
int status;
pid_t pc,pr;
pc = fork();
if (pc < 0)
printf("error ocurred!\n");
else
if(pc == 0){
printf("This is child process with pid of %d\n",getpid());
exit (3);
}
else{
pr = wait(&status);
if(WIFEXITED(status)){
printf("The child process %d exit normally.\n",pr);
printf("The WEXITSTATUS return code is %d.\n",WEXITSTATUS(status));
printf("The WIFEXITED return code is %d.\n",WIFEXITED(status));
}else
printf("The child process %d exit abnormally.\n",pr);
}
}
編譯并運行.
gcc wait2.c -o wait2
./wait2
This is child process with pid of 24819
The child process 24819 exit normally.
The WEXITSTATUS return code is 3.
The WIFEXITED return code is 1
解釋:由于子進程是正常退出,所以WIFEXITED(status)返回非零值,也就是1,這時WEXITSTATUS(status)返回3.
把上面的程序進行調(diào)整,在子進程中增加了sleep(30),并輸出子進程非正常退出的狀態(tài).
修改后的程序如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int
main ()
{
int status;
pid_t pc,pr;
pc = fork();
if (pc < 0)
printf("error ocurred!\n");
else
if(pc == 0){
printf("This is child process with pid of %d\n",getpid());
sleep(30);
exit (3);
}
else{
pr = wait(&status);
if(WIFEXITED(status)){
printf("The child process %d exit normally.\n",pr);
printf("The WEXITSTATUS return code is %d.\n",WEXITSTATUS(status));
printf("The WIFEXITED return code is %d.\n",WIFEXITED(status));
}else
printf("The child process %d exit abnormally.\n",pr);
printf("Status is %d.\n",status);
}
return 0;
}
gcc wait3.c -o wait3
./wait3
This is child process with pid of 25261
在終端2終止wait3進程
ps -C wait3 -o ppid,pid,stat,cmd
PPID PID STAT CMD
23286 25260 S+ ./wait3
25260 25261 S+ ./wait3
kill -9 25261
在終端1看到如下的信息:
The child process 25261 exit abnormally.
Status is 9.
解釋:我們看到在子進程非正常中斷后,用wait(&status)回收子進程,將子進程的狀態(tài)存到status中,status為整型變量.
二)系統(tǒng)調(diào)用waitpid
1)waitpid的概述:
.waitpid函數(shù)的原型為pid_t waitpid(pid_t pid,int *status,int options)
.從本質(zhì)上講,系統(tǒng)調(diào)用waitpid是wait的封裝,waitpid只是多出了兩個可由用戶控制的參數(shù)pid和options,為編程提供了靈活性.
2)waitpid的參數(shù)說明:
參數(shù)pid的值有以下幾種類型:
pid>0時,只等待進程ID等于pid的子進程,不管其它已經(jīng)有多少子進程運行結(jié)束退出了,只要指定的子進程還沒有結(jié)束,waitpid就會一直等下去.
pid=-1時,等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的作用一模一樣.
pid=0時,等待同一個進程組中的任何子進程,如果子進程已經(jīng)加入了別的進程組,waitpid不會對它做任何理睬.
pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值.
參數(shù)options的值有以下幾種類型:
如果使用了WNOHANG參數(shù),即使沒有子進程退出,它也會立即返回,不會像wait那樣永遠等下去.
如果使用了WUNTRACED參數(shù),則子進程進入暫停則馬上返回,但結(jié)束狀態(tài)不予以理會.
Linux中只支持WNOHANG和WUNTRACED兩個選項,這是兩個常數(shù),可以用"|"運算符把它們連接起來使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我們不想使用它們,也可以把options設(shè)為0,如:ret=waitpid(-1,NULL,0);
waitpid的返回值比wait稍微復(fù)雜一些,一共有3種情況:
3)waitpid的返回值:
當正常返回的時候waitpid返回收集到的子進程的進程ID;
如果設(shè)置了選項WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒有已退出的子進程可收集,則返回0;
如果調(diào)用中出錯,則返回-1,這時errno會被設(shè)置成相應(yīng)的值以指示錯誤所在;
當pid所指示的子進程不存在,或此進程存在,但不是調(diào)用進程的子進程,waitpid就會出錯返回,這時errno被設(shè)置為ECHILD.
4)關(guān)于waitpid調(diào)用的例子:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
main()
{
pid_t pc, pr;
pc = fork();
if(pc < 0)
printf("Error occured on forking.\n");
else
if(pc == 0){
sleep(10);
return 0;
}
do{
pr = waitpid(pc, NULL, WNOHANG);
if(pr == 0){
printf("No child exited\n");
sleep(1);
}
}while(pr == 0);
if(pr == pc)
printf("successfully get child %d\n", pr);
else
printf("some error occured\n");
return 0;
}
編譯并運行
gcc waitpid.c -o waitpid
./waitpid
No child exited
No child exited
No child exited
No child exited
No child exited
No child exited
No child exited
No child exited
No child exited
No child exited
successfully get child 27033
父進程經(jīng)過10次失敗的嘗試之后,終于收集到了退出的子進程.