Linux 等待进程结束 wait() 和 waitpid()
若子進程先于父進程結束時,父進程調用wait()函數和不調用wait()函數會產生兩種不同的結果:
--> 如果父進程沒有調用wait()和waitpid()函數,子進程就會進入僵死狀態。
--> 如果父進程調用了wait()和waitpid()函數,就不會使子進程變為僵尸進程。
這是為什么呢?現在我們來深入學習wait()函數和waitpid()函數。
?
wait() 和 waitpid() 學習
1、首先我們先看一下它們的函數原型:
在終端輸入命令:man 2 wait
就會看到它的函數原型:
NAME
?????? wait, waitpid, waitid - wait for process to change state
SYNOPSIS
?????? #include <sys/types.h>
?????? #include <sys/wait.h>
?????? pid_t wait(int *status);
?????? pid_t waitpid(pid_t pid, int *status, int options);
?????? int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
我們可以看到在2.6版本中新增叫了 waitid() 函數。
?
2、wait() 和 waitpid() 的功能:
1> wait()函數使父進程暫停執行,直到它的一個子進程結束為止,該函數的返回值是終止運行的子進程的PID,參數status所指向的變量存放子進程的退出碼,即從子進程的main函數返回的值或子進程中exit()函數的參數。如果status不是一個空指針,狀態信息將被寫入它指向的變量。
注意:進程一旦調用了wait,就立即阻塞自己,由wait自動分析是否當前進程的某個子進程已經退出,如果讓它找到了這樣一個已經變成僵尸的子進程,wait 就會收集這個子進程的信息, 并把它徹底銷毀后返回;如果沒有找到這樣一個子進程,wait就會一直阻塞在這里,直到有一個出現為止。
2> 頭文件sys/wait.h中定義了進程退出狀態的宏。
我們首先看下官方的解釋
a.WIFEXITED(status)???? returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main() .
翻譯:
WIFEXITED(status)? 若子進程是正常結束時則返回一個非零值。即調用exit(3),_exit(3) 或從main()函數返回的值。
?
b. WEXITSTATUS(status)?? returns the exit status of the? child.?? This? consists? of? the least? significant? 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or? as? the? argument for? a return? statement? in main().? This macro should only be employed if WIFEXITED returned true.
翻譯:
WEXITSTATUS(status)??? 如果宏WIFEXIED返回值為非零值時,它返回子進程中exit或_exit參數中的低8位。
?
c.WIFSIGNALED(status)? returns true if the child process was terminated by a signal.
翻譯:
WIFSIGNALED(status)? 若子進程異常終止則返回一個非零值。
?
d. WTERMSIG(status)?? returns the number of the signal that caused the? child? process? to terminate.? This macro should only be employed if WIFSIGNALED returned true.
翻譯:
WTERMSIG(status)????? 如果宏WIFSIGNALED的返回值非零,則返回使子進程異常終止的信號編號。
?
e.WIFSTOPPED(status)?? returns true if the child process was stopped by delivery? of a signal;? this? is? only possible if the call was done using WUN‐TRACED or when the child is being traced (see ptrace(2)).
翻譯:
WIFSTOPPED(status)? 若子進程由于異常暫停,則返回一個非零值。當調用WUN‐TRACED或子進程被跟蹤時這才時可能的。
?
f. WSTOPSIG(status)??? returns the number of the signal which caused the child to stop.This macro should only be employed if WIFSTOPPED returned true.
翻譯:
WSTOPSIG(status)????? 如果宏WIFSTOPPED返回值非零,則返回使子進程暫停的信號編號。
?
g.WIFCONTINUED(status)???? (since? Linux? 2.6.10)? returns? true? if? the child process wasresumed by delivery of SIGCONT.
翻譯:
WIFCONTINUED(status)???? (從2.6版本后)如果孩子進程通過SIGCONT恢復則返回一個非零值。
?
3>waitpid() 函數
(1).我們先來看一個waitpid()的經典例子:當我們下載了A軟件的安裝程序后,在安裝快結束時它又啟動了另外一個流氓軟件安裝程序B,當B也安裝結束后,才告訴你所有安裝都完成了。A和B分別在不同的進程中,A如何啟動B并知道B安裝完成了呢?可以很簡單地在A中用fork啟動B,然后用waitpid()來等待B的結束。
(2).waitpid()也用來等待子進程的結束,但它用于等待某個特定進程結束。參數pid指明要等待的子進程的PID,參數status的含義與wait()函數中的status相同。options參數可以用來改變waitpid的行為,若將該參數賦值為WNOHANG,則使父進程不被掛起而立即返回執行其后的代碼。
(3).waitpid()函數中參數pid的取值
還是先看下官方解釋:
?The value of pid can be:
?????? < -1?? meaning? wait? for? any? child process whose process group ID is?equal to the absolute value of pid.
?????? -1???? meaning wait for any child process.
?????? 0????? meaning wait for any child process whose? process? group? ID? is?equal to that of the calling process.
?????? > 0??? meaning? wait? for? the? child? whose process ID is equal to the?value? of pid.
翻譯:
pid的值可以為下己中情況:
< -1? 等待其組ID等于pid絕對值的任一子進程。
=-1? 等待任一子進程
=0 等待其組ID等于調用進程的組ID的任一進程
> 0? 等待其進程ID等于pid的子進程退出
(4).waitpid()函數的一個應用:
如果想讓父進程周期性地檢查某個特定的子進程是否已經退出,可以用下面的方法:
waitpid(child_pid, (int *) 0, WNOHANG);
如果子進程尚未退出,它將返回0;如果子進程已經結束,則返回child_pid。調用失敗時返回-1。失敗的原因包括沒有該子進程,參數不合法等。
?
3、wait()和waitpid() 函數的區別
(1).在一個子進程終止前,wait()使其調用者阻塞,而waitpid()有一個選項,可使調用者不阻塞。
(2).waitpid()并不等待在其調用之后的第一個終止子進程,它有若干個選項,可以控制它所等待的進程。
(3).對于wait(),其唯一的出錯是調用進程沒有子進程;對于waitpid(),若指定的進程或進程組不存在,或者參數pid指定的進程不是調用進程的子進程都可能出錯。
(4).waitpid()提供了wait()沒有的三個功能:一是waitpid()可等待一個特定的進程;二是waitpid()提供了一個wait()的非阻塞版本(有時希望取的一個子進程的狀態,但不想使父進程阻塞,waitpid() 提供了一個這樣的選擇:WNOHANG,它可以使調用者不阻塞);三是waitpid()支持作業控制。
(5).wait(&status) 的功能就等于 waitpid(-1, &status, 0);
?
函數實例: 有時希望取的一個子進程的狀態,但不想使父進程阻塞,waitpid() 提供了一個這樣的選擇:WNOHANG,它可以使調用者不阻塞
#include <sys/wait.h> #include <unistd.h> #include <stdio.h>int main() {pid_t pr, pc;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; }?
總結
無論進程是否正常終止,內核都會向其父進程發送SIGCHLD 信號,當調用wait或waitpid函數時
(a) 如果所有的子進程都在run, 可以阻塞父進程。
(b) 如果子進程終止,則wait立即返回子進程終止狀態。
(c) 如果沒有子進程在運行, 立即返回error。
?
4、函數實現:
函數實例1.(先看一個簡單的實例,看看進程調用wait()函數后是如何執行的?)
#include<stdio.h> #include<sys/types.h> #include<sys/wait.h> #include<unistd.h> #include<stdlib.h>int main() {pid_t child;int i;child = fork();if (child < 0){printf("create failed!\n");exit(1);}else if (0 == child){printf("this is the child process pid= %d\n", getpid());for (i = 0; i < 5; i++){printf("this is the child process print %d !\n", i + 1);}printf("the child end\n");}else{printf("this is the father process, ppid=%d\n", getppid());printf("father wait the child end\n");wait(&child);printf("father end\n");} }函數經過編譯:
gcc wait.c -o wait ./wait函數執行結果:
this is the father process, ppid=3303
father wait the child end
this is the child process pid= 3356
this is the child process print 1 !
this is the child process print 2 !
this is the child process print 3 !
this is the child process print 4 !
this is the child process print 5 !
the child end
father end
?
說明:
從上面的程序我們可以深入的了解wait() 函數的執行過程:
當父進程調用wait()函數后被掛起等待,直到子進程結束為止。
?
函數實例2(現在我們在通過一個實例,來深入了解wait()函數的執行過程)
#include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdlib.h>int main() {pid_t pid;char *msg;int i;int exit_code;printf("tiger study how to get exit code\n");pid = fork();if (pid == 0) /* 子進程 */{msg = "child process is running";i = 5;exit_code = 37;}else if (pid > 0) /* 父進程 */{exit_code = 0;}else{perror("process creation failed\n");exit(1);}if (pid > 0) /* 父進程 */{int status;pid_t child_pid;child_pid = wait(&status);printf("child process has exited, pid=%d\n", child_pid);if (WIFEXITED(status)){printf("child exited with code %d\n", WEXITSTATUS(status));}else{printf("child exited abnormally\n");}}else /* 子進程 */{while (i-- > 0){puts(msg);sleep(1);}}}?
函數進過編譯后:
$ gcc wait1.c -o wait1 $ ./wait1函數執行結果 :
tiger study how to get exit code
?child process is running
?child process is running
?child process is running
?child process is running
?child process is running
child process has exited,pid = 3816
child exited with code 0
?
說明:
父進程調用wait()函數后被掛起(我們可以再開一個終端,輸入命令:ps aux,可以看到父進程的執行結果為S)直到子進程結束。子進程結束后,wait()函數返回剛剛結束運行的子進程的pid,宏WEXITSTATUS獲取子進程的退出碼。
?
?
【注意】
如果調用 wait() 的進程沒有已終止的子進程,不過有一個或多個子進程仍在運行,那么 wait 將阻塞到現有子進程第一個終止為止。
waitpid 函數就等待哪個進程以及是否阻塞給了我們更多控制。首先,pid 參數允許我們指定想等待的進程ID,值-1表示等待第一個終止的子進程。其次,options 參數允許我們指定附加選項。最常用的選項是 WNOHANG,它告知內核在沒有已終止子進程時不要阻塞。
?
?
參考:
http://www.tc5u.com/linux_unix/1635564.htm
http://blog.163.com/libo_5/blog/static/15696852010324287748/
轉載于:https://www.cnblogs.com/52php/p/5684575.html
總結
以上是生活随笔為你收集整理的Linux 等待进程结束 wait() 和 waitpid()的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 梦到自己流产出了好多血是怎么回事
- 下一篇: lucene api
