linux wait與waitpid函數的深刻分析

一)系統調用wait  1)概述
wait函數的原型爲:pid_t wait(int *status)
當進程退出時,它向父進程發送一個SIGCHLD信號,默認狀況下老是忽略SIGCHLD信號,此時進程狀態一直保留在內存中,直到父進程使用wait函數收集狀態信息,纔會清空這些信息.
用wait來等待一個子進程終止運行稱爲回收進程.
當父進程忘了用wait()函數等待已終止的子進程時,子進程就會進入一種無父進程的狀態,此時子進程就是殭屍進程.
wait()要與fork()配套出現,若是在使用fork()以前調用wait(),wait()的返回值則爲-1,正常狀況下wait()的返回值爲子進程的PID.
若是先終止父進程,子進程將繼續正常進行,只是它將由init進程(PID 1)繼承,當子進程終止時,init進程捕獲這個狀態.
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的進程狀態
ps -C zombie -o ppid,pid,stat,cmd
PPID   PID STAT CMD
24233 26056 S+   ./zombie
26056 26057 Z+   [zombie] <defunct>
20秒後,查看程序的運行結果,以下:
./zombie
This is child process with pid of 26057
This is partent with pid of 26056
結論:
當父進程忘了用wait()函數等待已終止的子進程時,子進程就會進入一種無父進程清理本身屍體的狀態,此時的子進程就是殭屍進程.
在上面的例子中,子進程(26057)雖然執行完畢,但父進程沒有調用wait(),出現子進程雖然死亡,而不能在內核中清理屍體的狀況.
在sleep(20);前面加入wait(NULL);再編譯運行,運行結果沒有了殭屍進程,說明已經被父進程用wait()回收掉了.
以下:
ps -C zombie -o ppid,pid,stat,cmd
PPID   PID STAT CMD
12051 12107 S+   ./zombie
3)關於父進程調用wait()與fork()配套使用的問題.
對上面的程序進行修改,在fork()調用前使用wait()函數,以下:
#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);
}
編譯執行:
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進程的狀態,發現wait()並無回收掉子進程.

ps -C wait1 -o ppid,pid,stat,cmd

PPID   PID STAT CMD

23286 24007 S+   ./wait1

24007 24008 Z+   [wait1] <defunct>

調整wait()的位置,以下:

else{

pr = wait(NULL)

sleep(20);

printf("I catched a child process with pid of %d\n",pr);

}

再編譯執行:

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進程的狀態,發現wait()回收掉了子進程.

ps -C wait1 -o ppid,pid,stat,cmd

PPID   PID STAT CMD

23286 24084 S+   ./wait1

4)wait()的參數

若是參數的值不是NULL,wait就會把子進程退出時的狀態取出並存入其中,這是一個整數值(int),指出了子進程是正常退出仍是被非正常結束的,

因爲這些信息被存放在一個整數的不一樣二進制位中,因此就設計了一套專門的宏來完成這項工做,其中最經常使用的兩個:

1,WIFEXITED(status)這個宏用來指出子進程是否爲正常退出的,若是是,它會返回一個非零值.

2,WEXITSTATUS(status)當WIFEXITED返回非零值時,咱們能夠用這個宏來提取子進程的返回值,若是子進程調用exit(5)退出,WEXITSTATUS(status)就會返回5;

若是子進程調用exit(7),WEXITSTATUS(status)就會返回7.請注意,若是進程不是正常退出的,也就是說,WIFEXITED返回0,這個值就毫無心義.

如下面的例子說明wait()參數與宏的調用:

#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.

把上面的程序進行調整,在子進程中增長了sleep(30),並輸出子進程非正常退出的狀態.

修改後的程序以下:

#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)回收子進程,將子進程的狀態存到status中,status爲整型變量.

二)系統調用waitpid

1)waitpid的概述:

.waitpid函數的原型爲pid_t waitpid(pid_t pid,int *status,int options)

.從本質上講,系統調用waitpid是wait的封裝,waitpid只是多出了兩個可由用戶控制的參數pid和options,爲編程提供了靈活性.

2)waitpid的參數說明:

參數pid的值有如下幾種類型:

pid>0時,只等待進程ID等於pid的子進程,無論其它已經有多少子進程運行結束退出了,只要指定的子進程尚未結束,waitpid就會一直等下去.

pid=-1時,等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的做用如出一轍.

pid=0時,等待同一個進程組中的任何子進程,若是子進程已經加入了別的進程組,waitpid不會對它作任何理睬.

pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的ID等於pid的絕對值.

參數options的值有如下幾種類型:

若是使用了WNOHANG參數,即便沒有子進程退出,它也會當即返回,不會像wait那樣永遠等下去.

若是使用了WUNTRACED參數,則子進程進入暫停則立刻返回,但結束狀態不予以理會.

Linux中只支持WNOHANG和WUNTRACED兩個選項,這是兩個常數,能夠用"|"運算符把它們鏈接起來使用,好比:

ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

若是咱們不想使用它們,也能夠把options設爲0,如:ret=waitpid(-1,NULL,0);

waitpid的返回值比wait稍微複雜一些,一共有3種狀況:spa

3)waitpid的返回值:

當正常返回的時候waitpid返回收集到的子進程的進程ID;

若是設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0;

若是調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;

當pid所指示的子進程不存在,或此進程存在,但不是調用進程的子進程,waitpid就會出錯返回,這時errno被設置爲ECHILD.

4)關於waitpid調用的例子:

#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

父進程通過10次失敗的嘗試以後,終於收集到了退出的子進程.設計

相關文章
相關標籤/搜索