一個進程使用fork建立子進程,若是子進程退出,而父進程並無調用 wait 或 waitpid
獲取子進程的狀態信息,那麼子進程的進程描述符仍然保存在系統中。這種進程稱之爲殭屍進程
那麼問題來了,子進程退出了,父進程怎麼知道呢?segmentfault
對該機制有稍微瞭解的話,不可貴知一個關鍵因素:SIGCHLD
。正是這個SIGCHLD
起到了通知的做用,因此後面的處理也是基於它而實現。spa
SIGCHLD
信號,則顯示調用 wait
或 waitpid
;父進程直接忽略該信號。signal(SIGCHLD, SIG_IGN)
,這樣子進程直接會退出。操作系統
須要注意的是,雖然進程對於 `SIGCHLD`的默認動做是忽略,可是仍是顯示寫出來,纔能有效;
init
,由 init
伺候着。init
會掛着一堆殭屍, init
自己的設計就有專門回收的處理,因此有多少回收多少;剛纔咱們在處理到父子進程相關的問題時,多多少少接觸到SIGCHLD
, 那麼,只有在回收子進程的時候才須要用到麼?感受好浪費不是麼?設計
別擔憂 ~ 這個做用確定是不止這樣的!code
其實對於SIGCHLD
,咱們通常的理解是,子進程退出發送的信號,但其實不是的,這個信號表明的含義是:blog
子進程狀態變動了,例如中止、繼續、退出等,都會發送這個信號通知父進程。而父進程就能經過 wait/waitpid 來獲悉這些狀態了。
看起來有點意思,咱們彷彿能借此作些有趣的事情了。進程
#include <sys/wait.h> pid_t wait(int * statloc); pid_t waitpid(pid_t pid,int *statloc,int options);
wait
相對來講會經常使用點,由於不須要指定 pid
,而waitpid
就在一些須要指定特定pid
時纔會比較常見,那麼它們之間的關係就真的是隻是這樣麼?ip
其實wait
是對waitpid
的封裝,專門用來回收子進程退出的信息,一樣的,它簡單粗暴的設置成了堵塞方式,若是沒有任何子進程退出,那麼就堵塞住。get
而waitpid
功能很是強大,pid
和options
都提供了很是靈活的用法:string
pid: < -1: 取該 pid 的絕對值,若是任何子進程的進程組ID等於該值,則該進程組的任一子進程中的進程狀態發生變化,都會觸發`waitpid`的回調; == -1: 監聽範圍擴大到任意子進程,也就是 wait(status); == 0: 監聽進程組ID和父進程同樣的子進程; > 0: 監聽該pid的子進程; options: WNOHANG: 調用時,指定的 pid 仍未結束運行,則 wait 當即返回 0; WUNTRACED: 當子進程被暫停時,則當即返回子進程的 pid; WCONTINUED: 當被暫停的子進程,又被信號恢復時,則當即返回子進程的pid;
而下面這些宏,將搭配status
一塊兒使用:
WIFEXITED(status): 當子進程調用 exit、_exit或者正常從 main 返回等正常結束時,返回 true --> WEXITSTATUS(status): 獲取上面的 exit_code WIFSIGNALED(status): 當子進程被信號殺死時,返回 true; --> WTERMSIG(status): 獲取信號的值(int) WIFSTOPPED(status): 當本身弄成被信號暫停執行時,返回 true; --> WSTOPSIG(status): 獲取該信號的值 WIFCONTINUED(status): 子進程接收到SIGCONT信號繼續執行時,返回 true
咱們來個最小的 demo
來講明上面的怎麼用:
#include<stdio.h> #include<string.h> #include<signal.h> #include<stdlib.h> #include<sys/wait.h> #include<unistd.h> int main(){ int pid; if((pid = fork()) == 0){ while(1){ printf("Child: %d\n", getpid()); sleep(1); } } else{ int status; pid_t w; while(1){ // 但願堵塞,因此沒用 WNOHANG w = waitpid(pid, &status,WCONTINUED | WUNTRACED); if(WIFEXITED(status)){ printf("子進程正常退出,狀態碼: %d\n", WEXITSTATUS(status)); exit(0); } else if(WIFSIGNALED(status)){ printf("子進程被信號殺死了! 信號值: %d\n", WTERMSIG(status)); exit(0); } else if(WIFSTOPPED(status)){ printf("子進程被信號暫停了! 信號值: %d\n", WSTOPSIG(status)); } else if(WIFCONTINUED(status)){ printf("子進程又恢復繼續運行了\n"); } } } }
終端輸出:
Child: 10848 Child: 10848 # 子進程的 pid 子進程被信號暫停了!信號值:21 # kill -SIGTTIN 10848 子進程又恢復繼續運行了 # kill -SIGTTIN 10848 ... 子進程被信號暫停了! 信號值: 19 # kill -SIGSTOP 10848 子進程又恢復繼續運行了 # kill -SIGTTIN 10848 ... 子進程被信號殺死了! 信號值: 15 # kill -SIGTERM 10848 若是本身在子進程上面加個退出,就會打印:正常退出了
在上面的實驗中,咱們已經發現經過SIGCHLD
除了用來回收子進程,還能獲悉子進程的狀態!
在操做系統上,也有不少利用這個在工做的,例如:後臺進程,若是向標準輸入讀取內容時,是會被暫停的
爲何呢?
由於後臺進程,是和終端斷開鏈接的,當它從標準輸入讀取內容時,終端的驅動程序會發現這個操做,會發一個 SIGTTIN 給後臺進程,讓其暫停,而且通知用戶,只有用戶經過 fg 命令將其轉換成 前臺進程時,才能繼續工做
正是有這樣的一套機制,因此咱們也能作出不少比較實在的東西了~
歡迎各位大神指點交流, QQ討論羣: 258498217
轉載請註明來源: https://segmentfault.com/a/11...