我們知道當(dāng)一個父進程創(chuàng)建一個子進程時,會調(diào)用wait()和waitpid()函數(shù)清理僵?進程,?進程可以阻塞等待?進程結(jié)束,也可以?阻塞地查詢是否有?進程結(jié)束等待清理(也就是輪詢的?式)。采?第?種?式,?進程阻塞了就不 能處理??的?作了;采?第?種?式,?進程在處理??的?作的同時還要記得時不時地輪詢? 下,程序?qū)崿F(xiàn)復(fù)雜。
1.wait()和waitpid()
(1)wait 函數(shù):用來等待任何一個子進程退出,由父進程調(diào)用。
1 #include<sys/types.h>2 #include<sys/wait.h>3 pid_t wait(int* status);
返回值:成功返回被等待子進程的pid,失敗返回-1。 status:輸出型參數(shù),拿回子進程的退出信息。如果參數(shù)status不為空,則進程終止?fàn)顟B(tài)被保存于其中。 wait方式:阻塞式等待,等待的子進程不退出時,父進程一直不退出。 目的:回收子進程,系統(tǒng)回收子進程的空間。
依據(jù)傳統(tǒng),返回的整形狀態(tài)字是由實現(xiàn)定義的,其中有些位表示退出狀態(tài)(正常返回),其他位表示信號編號(異常返回),有一位表示是否產(chǎn)生了一個core文件等。 終止?fàn)顟B(tài)是定義在 sys/wait.h中的各個宏,有四可互斥的宏可以用來取得進程終止的原因。
WIFEXITED:正常返回時為真,可以執(zhí)行宏函數(shù) WEXITSTATUS獲取子進程傳送給exit、_exit或_Exit的參數(shù)的低8位。
WIFSIGNALED :異常返回時為真,可以執(zhí)行宏函數(shù)WTERMSIG取得子進程終止的信號編號,另外,對于一些實現(xiàn),定義有宏WCOREDUMP宏,若以經(jīng)昌盛終止進程的core文件,則為真。
WIFSTOPPED :若為當(dāng)前暫停子進程的返回的狀態(tài),則為真,可執(zhí)行WSTOPSIG取得使子進程暫停的信號編號。
WIFCONTINUED:若在作業(yè)控制暫停后已經(jīng)繼續(xù)的子進程返回了狀態(tài),則為真,僅用于waitpid。
(2)waitpid:
1 #include<sys/types.h>2 #include<sys/wait.h>3 pid_t waitpid(pid_t pid,int* status,int options);
1>參數(shù)
從參數(shù)的名字pid和類型pid_t中就可以看出,這里需要的是一個進程ID。但當(dāng)pid取不同的值時,在這里有不同的意義。
pid>0時,只等待進程ID等于pid的子進程,不管其它已經(jīng)有多少子進程運行結(jié)束退出了,只要指定的子進程還沒有結(jié)束,waitpid就會一直等下去。
pid=-1時,等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的作用一模一樣。
pid=0時,等待同一個進程組中的任何子進程,如果子進程已經(jīng)加入了別的進程組,waitpid不會對它做任何理睬。
pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值。
status參數(shù)與wait()函數(shù)的基本相同。
options參數(shù) 當(dāng)options參數(shù)為0時,與wait功能相同,仍是阻塞式等待,不提供額外功能,如果為下列常量按位或則提供更多功能:
WCONTINUED:若實現(xiàn)支持作業(yè)控制,那么由pid指定的任一子進程在暫停后已經(jīng)繼續(xù),但狀態(tài)尚未報告,則返回狀態(tài)
WNOHANG:若由pid指定的子進程并不是立即可用的,則waitpid不阻塞,即此時以非阻塞方式(輪詢式訪問的必要條件)等待子進程,并且返回0。
WUNTRACED:若實現(xiàn)支持作業(yè)控制,而pid指定的任一子進程已經(jīng)暫停,且其狀態(tài)尚未報告,則返回其狀態(tài)
2>返回值:
當(dāng)正常返回的時候,waitpid返回收集到的子進程的進程ID;
如果設(shè)置了選項WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒有已退出的子進程可收集,則返回0;
如果調(diào)用中出錯,則返回-1,這時errno會被設(shè)置成相應(yīng)的值以指示錯誤所在;
當(dāng)pid所指示的子進程不存在,或此進程存在,但不是調(diào)用進程的子進程,waitpid就會出錯返回,這時errno被設(shè)置為ECHILD;
3>waitpid提供了三個wait所沒有的功能:
1. waitpid可等待一個特定的進程
2. waitpid提供了一個wait的非阻塞版本
3. waitpid支持作業(yè)控制
2.關(guān)于SIGCHLD信號
其實,?進程在終?時會給?進程發(fā)SIGCHLD信號,該信號的默認(rèn)處理動作是忽略,?進程可以? 定義SIGCHLD信號的處理函數(shù),這樣?進程只需專?處理??的?作,不必關(guān)??進程了,?進程 終?時會通知?進程,?進程在信號處理函數(shù)中調(diào)?wait清理?進程即可。
一般情況下父進程收到這個信號的默認(rèn)處理是忽略這個信號,即就是不做任何處理,但是我們可以通過系統(tǒng)調(diào)用API:signal()來進行自定義處理句柄,進行驗證,具體代碼如下:
(1)驗證子進程退出時會給父進程發(fā)送SIGCHLD信號
程序完成以下功能:?進程fork出?進程,?進程調(diào)?exit(1) 終?,?進程?定義SIGCHLD信號的處理函數(shù),在其中調(diào)?wait獲得?進程的退出狀態(tài)并打印。
用kill -l指令查看17號信號:
3.子進程的異步等待方式
(1)異步回收僵尸進程:
fork()之后,非阻塞(異步)等待子進程(回收僵尸)。
fork()之后,子進程和父進程分叉執(zhí)行,僵尸進程的產(chǎn)生是因為父進程沒有給子進程“收尸”造成的,又可以根據(jù)危害程度分為下述兩類:
總體來說:當(dāng)子進程結(jié)束之后,但父進程未結(jié)束之前,子進程將成為僵尸進程。
1)當(dāng)子進程結(jié)束之后,但父進程未結(jié)束之前,子進程將成為僵尸進程,父進程結(jié)束后僵尸被init進程回收。
2)如果子進程結(jié)束了,但是父進程始終沒有結(jié)束,那么這個僵尸將一直存在,而且隨著exec,僵尸越來越多。
(2)實現(xiàn)代碼
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<signal.h> 4 #include<unistd.h> 5 #include<sys/types.h> 6 #include<sys/wait.h> 7 void catchSig(int sig) 8 { 9 printf("father is catching,child is quit\n"); 10 //以非阻塞方式等待所有異常退出的子進程 11 pid_t id; 12 while((id = waitpid(-1,NULL,WNOHANG)) > 0) 13 { 14 printf("wait child success:%d\n",id); 15 } 16 } 17 int main() 18 { 19 signal(SIGCHLD,catchSig); 20 pid_t pid1 = fork(); 21 if(pid1 == 0)//child1 22 { 23 printf("child1:my pid is %d\n",getpid()); 24 exit(-1);//子進程1異常終止 25 } 26 pid_t pid2 = fork();//child2 27 if(pid2 == 0) 28 { 29 printf("child2:my pid is %d\n",getpid()); 30 exit(-1);//子進程2異常退出 31 } 32 pid_t pid3 = fork();//child3 33 if(pid3 == 0) 34 { 35 printf("child3:my pid is %d\n",getpid());//子進程3正常運行 36 } 37 while(1) 38 {39 printf("father is waiting...\n"); 40 sleep(1); 41 } 42 return 0;
運行結(jié)果:
http://www.cnblogs.com/33debug/p/7017215.html