#include <sys/wait.h>
#include <sys/types.h>
?
pid_t waitpid(pid_t pid, int *status, int options);
作用:同wait,但可指定pid進程清理,可以不阻塞。
waitpid函數的第二個參數int *status跟wait函數的形參一樣,且都是利用相同的宏函數來進一步獲取結束進程的狀態和終止原因。
第一個形參pid:>0,則回收指定參數(pid)的PID進程;=-1,則回收該父進程的任意一個子進程,相當于wait函數;=0,則回收和當前調用waitpid函數一個組的任意子進程(即跟父進程在同一個組的所有子進程);<-1,則回收指定進程組內的任意子進程。因此,-1的范圍最大,所有子進程;其次是小于-1和0,最后是指定具體的子進程。注意,wait函數和waitpid函數只能回收父進程自己的子進程,且一次wait或waitpid調用只能清理一個子進程,清理多個子進程應使用循環。在默認情況下,父進程fork后產生的子進程跟父進程在同一個進程組,因此參數為0時,在這種情況下,相當于回收fork產生的所有子進程。
再次強調:一次wait或waitpid調用只能回收一個子進程,如果回收的是多個子進程,則哪一個子進程先結束,則回收哪一個。如果都回收,則可以采用循環(for、while、do while等)。
第三個參數options:為0,則代表阻塞等待子進程結束,再回收,跟wait一樣;為WNOHANG,則不再等待,如果要回收的子進程都在運行,則直接返回0,然后接著執行后續程序;為WUNTRACED,如果子進程由于被停止產生的SIGCHLD,waitpid則立即返回;為WCONTINUED,如果子進程由于被SIGCONT喚醒而產生的SIGCHLD,waitpid則立即返回。
對于waitpid的返回值:如果沒有子進程或其它錯誤原因,則返回-1;如果成功回收子進程,則返回回收的那個子進程的ID;如果第三個參數為WNOHANG,且子進程都在運行,則返回0。
因此:waitpid( -1, NULL,0) 與 wait( NULL )是等效的,都是阻塞等待回收所有子進程。
?
//代碼
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>int main(void)
{pid_t pid, pid2, wpid;int flg = 0;pid = fork();pid2 = fork(); //此時總共有4個進程(不包括shell)if(pid == -1){perror("fork error");exit(1);} else if(pid == 0){ //注意:pid=0的進程有兩個,子進程和子進程的子進程printf("I'm process child, pid = %d\n", getpid());sleep(5);exit(4);} else { //注意:pid>0的進程有兩個,父進程和子進程do {wpid = waitpid(pid, NULL, WNOHANG);printf("---wpid = %d--------%d\n", wpid, flg++);if(wpid == 0){printf("NO child exited\n");sleep(1);}} while (wpid == 0); //子進程不可回收if(wpid == pid){ //回收了指定子進程printf("I'm parent, I catched child process,""pid = %d\n", wpid);} else {printf("other...\n");}}return 0;
}
[root@localhost wait]# ./waitpid
---wpid = 0--------0
NO child exited
I'm process child, pid = 33493
---wpid = -1--------0
I'm process child, pid = 33495
other...
---wpid = 0--------1
NO child exited
---wpid = 0--------2
NO child exited
---wpid = 0--------3
NO child exited
---wpid = 0--------4
NO child exited
---wpid = 33493--------5
I'm parent, I catched child process,pid = 33493
?
?
//代碼:阻塞回收一個子進程
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>int main(int argc, char *argv[])
{int n = 5, i; //默認創建5個子進程pid_t p, q;if(argc == 2) {n = atoi(argv[1]); //將字符串轉化為整數}for(i = 0; i < n; i++) {//出口1,父進程專用出口p = fork();if(p == 0)break; //出口2,子進程出口,i不自增else if (i == 3){q = p; //將第4個子進程的ID保存在q中}}if(n == i){sleep(n);printf("I am parent, pid = %d\n", getpid());pid_t pid = waitpid(-1,NULL,0); //等價于 pid_t pid = wait(NULL);if( pid == -1){perror("waitpid");exit(1);}while(1); //讓父進程陷入死循環,防止子進程被init回收} else {sleep(i);printf("I'm %dth child, pid = %d\n", i+1, getpid());}return 0;
}
//代碼:阻塞回收指定的進程(第4個子進程)if(n == i){sleep(n);printf("I am parent, pid = %d\n", getpid());//pid_t pid = waitpid(-1,NULL,0); //等價于 pid_t pid = wait(NULL);pid_t pid = waitpid(q,NULL,0);
if( pid == -1){perror("waitpid");exit(1);}while(1); //讓父進程陷入死循環,防止子進程被init回收} else {sleep(i);printf("I'm %dth child, pid = %d\n", i+1, getpid());}//代碼:阻塞回收所有的子進程if(n == i){sleep(n);printf("I am parent, pid = %d\n", getpid());//pid_t pid = waitpid(-1,NULL,0); //等價于 pid_t pid = wait(NULL);//pid_t pid = waitpid(q,NULL,0);while(waitpid(-1,NULL,0)); // 等價于while(wait(NULL));while(1); //讓父進程陷入死循環,防止子進程被init回收
認回收} else {sleep(i);printf("I'm %dth child, pid = %d\n", i+1, getpid());}//代碼:非阻塞(WNOHANG)回收所有的子進程if(n == i){sleep(n);printf("I am parent, pid = %d\n", getpid());//pid_t pid = waitpid(-1,NULL,0); //等價于 pid_t pid = wait(NULL);//pid_t pid = waitpid(q,NULL,0);//while(waitpid(-1,NULL,0)); // 等價于while(wait(NULL));do{
pid_t pid = waitpid(-1,NULL,WNOHANG);if ( pid > 0 )n--;}while( n > 0 )while(1); //讓父進程陷入死循環,防止子進程被init回收} else {sleep(i);printf("I'm %dth child, pid = %d\n", i+1, getpid());}
[root@localhost wait]# ps aux
root????? 34224 57.1? 0.0?? 4164?? 356 pts/0??? R+?? 22:58?? 0:06 ./loop_fork
root????? 34230? 0.0? 0.0 123360? 1380 pts/2??? R+?? 22:58?? 0:00 ps aux
可以看到,所有子進程都被回收,沒有僵尸進程。
當第一個參數為-1時,為指定進程組:
[root@localhost wait]# ps ajx
? PPID?? PID?? PGID??? SID TTY???? TPGID ?STAT?? UID?? TIME COMMAND
?? ?0????? 2????? 0????? 0 ???????????? -1 ????S??????? 0?? 0:00 ?[kthreadd]
ps ajx指令:PPID為父進程ID;PID為進程ID;PGID為進程組ID。SID為會話ID。
[root@localhost wait]# cat | cat | cat | cat?? //執行這個命令,然后查看進程組ID
[root@localhost wait]# ps ajx
29904? 34326? 34326? 29904 pts/0???? 34326 S+?????? 0?? 0:00 cat
?29904? 34327? 34326? 29904 pts/0???? 34326 S+?????? 0?? 0:00 cat
?29904? 34328? 34326? 29904 pts/0???? 34326 S+?????? 0?? 0:00 cat
?29904? 34329? 34326? 29904 pts/0???? 34326 S+?????? 0?? 0:00 cat
可以看到這四個進程的進程組ID都一樣,屬于同一個進程組,為34326。因此要回收這四個子進程,第一個參數為:-34326。 ?要殺死這四個進程: kill -34326或kill -9 -34326
練習作業:父進程fork 3 個子進程,三個子進程一個調用ps命令, 一個調用自定義程序1(正常),一個調用自定義程序2(會出段錯誤)。父進程使用waitpid對其子進程進行回收,并指出其狀態和退出的原因。