Linux 等待进程结束 wait() 和 waitpid()
若子進(jìn)程先于父進(jìn)程結(jié)束時(shí),父進(jìn)程調(diào)用wait()函數(shù)和不調(diào)用wait()函數(shù)會產(chǎn)生兩種不同的結(jié)果:
--> 如果父進(jìn)程沒有調(diào)用wait()和waitpid()函數(shù),子進(jìn)程就會進(jìn)入僵死狀態(tài)。
--> 如果父進(jìn)程調(diào)用了wait()和waitpid()函數(shù),就不會使子進(jìn)程變?yōu)榻┦M(jìn)程。
這是為什么呢?現(xiàn)在我們來深入學(xué)習(xí)wait()函數(shù)和waitpid()函數(shù)。
?
wait() 和 waitpid() 學(xué)習(xí)
1、首先我們先看一下它們的函數(shù)原型:
在終端輸入命令:man 2 wait
就會看到它的函數(shù)原型:
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() 函數(shù)。
?
2、wait() 和 waitpid() 的功能:
1> wait()函數(shù)使父進(jìn)程暫停執(zhí)行,直到它的一個(gè)子進(jìn)程結(jié)束為止,該函數(shù)的返回值是終止運(yùn)行的子進(jìn)程的PID,參數(shù)status所指向的變量存放子進(jìn)程的退出碼,即從子進(jìn)程的main函數(shù)返回的值或子進(jìn)程中exit()函數(shù)的參數(shù)。如果status不是一個(gè)空指針,狀態(tài)信息將被寫入它指向的變量。
注意:進(jìn)程一旦調(diào)用了wait,就立即阻塞自己,由wait自動分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出,如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程,wait 就會收集這個(gè)子進(jìn)程的信息, 并把它徹底銷毀后返回;如果沒有找到這樣一個(gè)子進(jìn)程,wait就會一直阻塞在這里,直到有一個(gè)出現(xiàn)為止。
2> 頭文件sys/wait.h中定義了進(jìn)程退出狀態(tài)的宏。
我們首先看下官方的解釋
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)? 若子進(jìn)程是正常結(jié)束時(shí)則返回一個(gè)非零值。即調(diào)用exit(3),_exit(3) 或從main()函數(shù)返回的值。
?
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返回值為非零值時(shí),它返回子進(jìn)程中exit或_exit參數(shù)中的低8位。
?
c.WIFSIGNALED(status)? returns true if the child process was terminated by a signal.
翻譯:
WIFSIGNALED(status)? 若子進(jìn)程異常終止則返回一個(gè)非零值。
?
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的返回值非零,則返回使子進(jìn)程異常終止的信號編號。
?
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)? 若子進(jìn)程由于異常暫停,則返回一個(gè)非零值。當(dāng)調(diào)用WUN‐TRACED或子進(jìn)程被跟蹤時(shí)這才時(shí)可能的。
?
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返回值非零,則返回使子進(jìn)程暫停的信號編號。
?
g.WIFCONTINUED(status)???? (since? Linux? 2.6.10)? returns? true? if? the child process wasresumed by delivery of SIGCONT.
翻譯:
WIFCONTINUED(status)???? (從2.6版本后)如果孩子進(jìn)程通過SIGCONT恢復(fù)則返回一個(gè)非零值。
?
3>waitpid() 函數(shù)
(1).我們先來看一個(gè)waitpid()的經(jīng)典例子:當(dāng)我們下載了A軟件的安裝程序后,在安裝快結(jié)束時(shí)它又啟動了另外一個(gè)流氓軟件安裝程序B,當(dāng)B也安裝結(jié)束后,才告訴你所有安裝都完成了。A和B分別在不同的進(jìn)程中,A如何啟動B并知道B安裝完成了呢?可以很簡單地在A中用fork啟動B,然后用waitpid()來等待B的結(jié)束。
(2).waitpid()也用來等待子進(jìn)程的結(jié)束,但它用于等待某個(gè)特定進(jìn)程結(jié)束。參數(shù)pid指明要等待的子進(jìn)程的PID,參數(shù)status的含義與wait()函數(shù)中的status相同。options參數(shù)可以用來改變waitpid的行為,若將該參數(shù)賦值為WNOHANG,則使父進(jìn)程不被掛起而立即返回執(zhí)行其后的代碼。
(3).waitpid()函數(shù)中參數(shù)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絕對值的任一子進(jìn)程。
=-1? 等待任一子進(jìn)程
=0 等待其組ID等于調(diào)用進(jìn)程的組ID的任一進(jìn)程
> 0? 等待其進(jìn)程ID等于pid的子進(jìn)程退出
(4).waitpid()函數(shù)的一個(gè)應(yīng)用:
如果想讓父進(jìn)程周期性地檢查某個(gè)特定的子進(jìn)程是否已經(jīng)退出,可以用下面的方法:
waitpid(child_pid, (int *) 0, WNOHANG);
如果子進(jìn)程尚未退出,它將返回0;如果子進(jìn)程已經(jīng)結(jié)束,則返回child_pid。調(diào)用失敗時(shí)返回-1。失敗的原因包括沒有該子進(jìn)程,參數(shù)不合法等。
?
3、wait()和waitpid() 函數(shù)的區(qū)別
(1).在一個(gè)子進(jìn)程終止前,wait()使其調(diào)用者阻塞,而waitpid()有一個(gè)選項(xiàng),可使調(diào)用者不阻塞。
(2).waitpid()并不等待在其調(diào)用之后的第一個(gè)終止子進(jìn)程,它有若干個(gè)選項(xiàng),可以控制它所等待的進(jìn)程。
(3).對于wait(),其唯一的出錯(cuò)是調(diào)用進(jìn)程沒有子進(jìn)程;對于waitpid(),若指定的進(jìn)程或進(jìn)程組不存在,或者參數(shù)pid指定的進(jìn)程不是調(diào)用進(jìn)程的子進(jìn)程都可能出錯(cuò)。
(4).waitpid()提供了wait()沒有的三個(gè)功能:一是waitpid()可等待一個(gè)特定的進(jìn)程;二是waitpid()提供了一個(gè)wait()的非阻塞版本(有時(shí)希望取的一個(gè)子進(jìn)程的狀態(tài),但不想使父進(jìn)程阻塞,waitpid() 提供了一個(gè)這樣的選擇:WNOHANG,它可以使調(diào)用者不阻塞);三是waitpid()支持作業(yè)控制。
(5).wait(&status) 的功能就等于 waitpid(-1, &status, 0);
?
函數(shù)實(shí)例: 有時(shí)希望取的一個(gè)子進(jìn)程的狀態(tài),但不想使父進(jìn)程阻塞,waitpid() 提供了一個(gè)這樣的選擇:WNOHANG,它可以使調(diào)用者不阻塞
#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; }?
總結(jié)
無論進(jìn)程是否正常終止,內(nèi)核都會向其父進(jìn)程發(fā)送SIGCHLD 信號,當(dāng)調(diào)用wait或waitpid函數(shù)時(shí)
(a) 如果所有的子進(jìn)程都在run, 可以阻塞父進(jìn)程。
(b) 如果子進(jìn)程終止,則wait立即返回子進(jìn)程終止?fàn)顟B(tài)。
(c) 如果沒有子進(jìn)程在運(yùn)行, 立即返回error。
?
4、函數(shù)實(shí)現(xiàn):
函數(shù)實(shí)例1.(先看一個(gè)簡單的實(shí)例,看看進(jìn)程調(diào)用wait()函數(shù)后是如何執(zhí)行的?)
#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");} }函數(shù)經(jīng)過編譯:
gcc wait.c -o wait ./wait函數(shù)執(zhí)行結(jié)果:
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() 函數(shù)的執(zhí)行過程:
當(dāng)父進(jìn)程調(diào)用wait()函數(shù)后被掛起等待,直到子進(jìn)程結(jié)束為止。
?
函數(shù)實(shí)例2(現(xiàn)在我們在通過一個(gè)實(shí)例,來深入了解wait()函數(shù)的執(zhí)行過程)
#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) /* 子進(jìn)程 */{msg = "child process is running";i = 5;exit_code = 37;}else if (pid > 0) /* 父進(jìn)程 */{exit_code = 0;}else{perror("process creation failed\n");exit(1);}if (pid > 0) /* 父進(jìn)程 */{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 /* 子進(jìn)程 */{while (i-- > 0){puts(msg);sleep(1);}}}?
函數(shù)進(jìn)過編譯后:
$ gcc wait1.c -o wait1 $ ./wait1函數(shù)執(zhí)行結(jié)果 :
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
?
說明:
父進(jìn)程調(diào)用wait()函數(shù)后被掛起(我們可以再開一個(gè)終端,輸入命令:ps aux,可以看到父進(jìn)程的執(zhí)行結(jié)果為S)直到子進(jìn)程結(jié)束。子進(jìn)程結(jié)束后,wait()函數(shù)返回剛剛結(jié)束運(yùn)行的子進(jìn)程的pid,宏WEXITSTATUS獲取子進(jìn)程的退出碼。
?
?
【注意】
如果調(diào)用 wait() 的進(jìn)程沒有已終止的子進(jìn)程,不過有一個(gè)或多個(gè)子進(jìn)程仍在運(yùn)行,那么 wait 將阻塞到現(xiàn)有子進(jìn)程第一個(gè)終止為止。
waitpid 函數(shù)就等待哪個(gè)進(jìn)程以及是否阻塞給了我們更多控制。首先,pid 參數(shù)允許我們指定想等待的進(jìn)程ID,值-1表示等待第一個(gè)終止的子進(jìn)程。其次,options 參數(shù)允許我們指定附加選項(xiàng)。最常用的選項(xiàng)是 WNOHANG,它告知內(nèi)核在沒有已終止子進(jìn)程時(shí)不要阻塞。
?
?
參考:
http://www.tc5u.com/linux_unix/1635564.htm
http://blog.163.com/libo_5/blog/static/15696852010324287748/
轉(zhuǎn)載于:https://www.cnblogs.com/52php/p/5684575.html
總結(jié)
以上是生活随笔為你收集整理的Linux 等待进程结束 wait() 和 waitpid()的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到自己流产出了好多血是怎么回事
- 下一篇: Linux内核协议栈 NAT性能优化之F