一、殭屍進程
當子進程退出的時候,核心會向父進程發送SIGCHLD訊號,子進程的退出是個非同步事件(子進程可以在父進程啟動並執行任何時刻終止)
子進程退出時,核心將子進程置為殭屍狀態,這個進程稱為殭屍進程,它只保留最小的一些核心資料結構,以便父進程查詢子進程的退出狀態。
父進程查詢子進程的退出狀態可以用wait/waitpid函數。
二、如何避免殭屍進程
當一個子進程結束運行時,它與其父進程之間的關聯還會保持到父進程也正常地結束運行或者父進程調用了wait/waitpid才告終止。
進程表中代表子進程的資料項目是不會立刻釋放的,雖然不再活躍了,可子進程還停留在系統裡,因為它的退出碼還需要儲存起來以備父進程中後續的wait/waitpid調用使用。它將稱為一個“僵進程”。
調用wait或者waitpid函數查詢子進程退出狀態,此方法父進程會被掛起(waitpid可以設定不掛起)。
如果不想讓父進程掛起,可以在父進程中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父進程忽略SIGCHLD訊號,該訊號是子進程退出的時候向父進程發送的。也可以不忽略SIGCHLD訊號,而接收在訊號處理函數中調用wait/waitpid。
而在營運中常用的手段是殺死父進程,這樣子進程會由init 進程接管,由它來清理子進程的狀態。
三、wait函數
標頭檔<sys/types.h>和<sys/wait.h>
函數功能:當我們用fork啟動一個進程時,子進程就有了自己的生命,並將獨立地運行。有時,我們需要知道某個子進程是否已經結束了,我們可以通過wait安排父進程在子進程結束之後。
函數原型
pid_t wait(int *status)
函數參數
status:該參數可以獲得你等待子進程的資訊
傳回值:
成功等待子進程函數返回等待子進程的ID
wait系統調用會使父進程暫停執行,直到它的一個子進程結束為止。
返回的是子進程的PID,它通常是結束的子進程
狀態資訊允許父進程判定子進程的退出狀態,即從子進程的main函數返回的值或子進程中exit語句的退出碼。
如果status不是一個null 指標,狀態資訊將被寫入它指向的位置
通過以下的宏定義可以獲得子進程的退出狀態
WIFEXITED(status)
如果子進程正常結束,返回一個非零值
WEXITSTATUS(status) 如果WIFEXITED非零,返回子進程退出碼
WIFSIGNALED(status) 子進程因為捕獲訊號而終止,返回非零值
WTERMSIG(status) 如果WIFSIGNALED非零,返回訊號代碼
WIFSTOPPED(status) 如果子進程被暫停,返回一個非零值
WSTOPSIG(status) 如果WIFSTOPPED非零,返回一個訊號代碼
四、waitpid函數
函數功能:用來等待某個特定進程的結束
函數原型:
pid_t waitpid(pid_t pid, int *status,int options)
參數:
status:如果不是空,會把狀態資訊寫到它指向的位置
options:允許改變waitpid的行為,最有用的一個選項是WNOHANG,它的作用是防止waitpid把調用者的執行掛起等待
傳回值:如果成功返回等待子進程的ID,失敗返回-1
對於waitpid的p i d參數的解釋與其值有關:
pid == -1 等待任一子進程。於是在這一功能方面waitpid與wait等效。
pid > 0 等待其進程I D與p i d相等的子進程。
pid == 0 等待其組I D等於調用進程的組I D的任一子進程。換句話說是與調用者進程同在一個組的進程。
pid < -1 等待其組I D等於p i d的絕對值的任一子進程。
五、wait和waitpid函數的區別
兩個函數都用於等待進程的狀態變化,包括正常退出,被訊號異常終止,被訊號暫停,被訊號喚醒繼續執行等。
在一個子進程終止前, wait 使其調用者阻塞,而waitpid 有一選擇項,可使調用者不阻塞。
waitpid並不只能等待第一個終止的子進程—它有若干個選擇項,可以控制它所等待的特定進程。
實際上wait函數是waitpid函數的一個特例。
樣本程式:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
|
/************************************************************************* > File Name: process_.c > Author: Simba > Mail: dameng34@163.com > Created Time: Sat 23 Feb 2013 02:34:02 PM CST ************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/wait.h>#define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int main(int argc, char *argv[]) { pid_t pid; pid = fork(); if (pid == -1) ERR_EXIT("fork error"); if (pid == 0) { sleep(3); printf("this is child\n"); // exit(100); abort(); } printf("this is parent\n"); int status; int ret; ret = wait(&status); // 阻塞等待子進程退出 // ret = waitpid(-1, &status, 0); // ret = waitpid(pid, &status, 0); /* waitpid可以等待特定的進程,而不僅僅是第一個退出的子進程 * 且可以設定option為WNOHANG,即不阻塞等待 */ printf("ret=%d, pid=%d\n", ret, pid); if (WIFEXITED(status)) printf("child exited normal exit status=%d\n", WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("child exited abnormal signal number=%d\n", WTERMSIG(status)); else if (WIFSTOPPED(status)) printf("child stopped signal number=%d\n", WSTOPSIG(status)); return 0; } |
輸出為:
simba@ubuntu:~/Documents/code/linux_programming/APUE/process$ ./wait
this is parent
this is child
ret=7156, pid=7156
child exited abnormal signal number=6
說明子進程被訊號異常終止,因為我們調用了abort(), 即產生SIGABRT訊號將子進程終止,可以查到此訊號序號為6。如果我們不使用abort 而是exit(100), 則應該輸出 child exited normal exit status=100 ,即正常退出。
參考:《APUE》