wait/waitpid函数与僵尸进程、fork 2 times
一、僵尸進程
當子進程退出的時候,內核會向父進程發送SIGCHLD信號,子進程的退出是個異步事件(子進程可以在父進程運行的任何時刻終止)
子進程退出時,內核將子進程置為僵尸狀態,這個進程稱為僵尸進程,它只保留最小的一些內核數據結構,以便父進程查詢子進程的退出狀態。
minimal?set?of?information??about?the??zombie??process?(PID,?termination?status,?resource?usage?
information)?in?order?to?allow?the?parent?to?later?perform?a?wait?to?obtain?information?about?the?
child.??As?long?as?a?zombie?is?not?removed?from?the?system?via?a?wait,?it?will?consume?a?slot?in? ? the?kernel??process??table,??and?if?this?table?fills,?it?will?not?be?possible?to?create?further?
processes.??If?a?parent?process?terminates,?then?its?"zombie"?children?(if?any)?are?adopted?by?
init(8),?which?automatically?performs?a?wait?to?remove?the?zombies.
?
父進程查詢子進程的退出狀態可以用wait/waitpid函數。
二、如何避免僵尸進程
當一個子進程結束運行時,它與其父進程之間的關聯還會保持到父進程也正常地結束運行或者父進程調用了wait/waitpid才告終止。
進程表中代表子進程的數據項是不會立刻釋放的,雖然不再活躍了,可子進程還停留在系統里,因為它的退出碼還需要保存起來以備父進程中后續的wait/waitpid調用使用。它將稱為一個“僵進程”。
調用wait或者waitpid函數查詢子進程退出狀態,此方法父進程會被掛起(waitpid可以設置不掛起)。
如果不想讓父進程掛起,可以在父進程中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父進程忽略SIGCHLD信號,該信號是子進程退出的時候向父進程發送的。也可以不忽略SIGCHLD信號,而接收在信號處理函數中調用wait/waitpid。
// 讓子進程退出后自動回收,避免成為僵尸或者需要父進程 wait。
struct sigaction sat_cld = { .sa_handler = SIG_IGN, .sa_flags = SA_NOCLDWAIT };
sigaction(SIGCHLD, &sat_cld, NULL);
而在運維中常用的手段是殺死父進程,這樣子進程會由init 進程接管,由它來清理子進程的狀態。
三、wait函數
頭文件<sys/types.h>和<sys/wait.h>
函數功能:當我們用fork啟動一個進程時,子進程就有了自己的生命,并將獨立地運行。有時,我們需要知道某個子進程是否已經結束了,我們可以通過wait安排父進程在子進程結束之后。
函數原型
pid_t wait(int *status)
函數參數
status:該參數可以獲得你等待子進程的信息
返回值:
成功等待子進程, ?函數返回等待子進程的ID
wait系統調用會使父進程暫停執行,直到它的一個子進程結束為止。
返回的是子進程的PID,它通常是結束的子進程
狀態信息允許父進程判定子進程的退出狀態,即從子進程的main函數返回的值或子進程中exit語句的退出碼。
如果status不是一個空指針,狀態信息將被寫入它指向的位置
通過以下的宏定義可以獲得子進程的退出狀態
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把調用者的執行掛起等待(return immediately if no child has exited.)
返回值:如果成功, 返回等待子進程的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函數的一個特例。
RETURN VALUE
? ? ? ?wait(): on success, returns the process ID of the terminated child; on error, -1 is returned.
? ? ? ?waitpid(): on success, returns the process ID of the child whose state has changed; if WNOHANG ?was ?specified ?and ?one ?or ?more
? ? ? ?child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. ?On error, -1 is returned.
示例程序:
?
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 ?,即正常退出。
也就是所謂兩次 fork 調用,主進程并不直接創建目標子進程,而是通過創建一個 Son,然后再由Son 創建實際的目標子進程 Grandson。Son 在創建?
Grandson 后立即返回,并由主進程 waitpid回收掉。而真正的目標 Grandson 則因為 "生父" Son 死掉而被 init 收養,然后直接被人道毀滅。
?
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 | ? | void?create_child() { ????pid_t?son?=?fork(); ????if?(son?==?0) ????{ ????????pid_t?grandson?=?fork(); ????????if?(grandson?==?0) ????????{ ????????????printf("child:?%d,?parent:?%d\n",?getpid(),?getppid()); ????????????exit(EXIT_SUCCESS); ????????} ????????exit(EXIT_SUCCESS); ????} ????else?if?(son?>?0) ????{ ????????waitpid(son,?NULL,?0); ????} ????else ????{ ????????perror("fork"); ????} } int?main(int?argc,?char?*argv[]) { ????for?(int?i?=?0;?i?<?10;?i++) ????{ ????????create_child(); ????} ????while(true)?pause(); ????return?EXIT_SUCCESS; } |
?
參考:《APUE》
轉載于:https://www.cnblogs.com/alantu2018/p/8477199.html
總結
以上是生活随笔為你收集整理的wait/waitpid函数与僵尸进程、fork 2 times的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Activity之间的通信方式
- 下一篇: CSS三大特性:层叠性、继承性、优先级