大綱
- 孤兒進程
- 僵尸進程
-
wait函數(shù) -
waitpid函數(shù)
在Linux中正常情況下,子進程是通過父進程創(chuàng)建的,子進程再創(chuàng)建新的進程。子進程的結(jié)束和父進程的運行是一個異步過程,也就是說,父進程永遠無法預知到子進程會在什么時候結(jié)束。當一個進程完成工作終止后,它的父進程需要調(diào)用wait()或waitpid()系統(tǒng)調(diào)用來獲取子進程的終止狀態(tài)。
子進程有兩種比較重要的狀態(tài),分別是孤兒進程和僵尸進程。
孤兒進程
孤兒進程是指父進程先于子進程結(jié)束,通俗來講也就是爹比兒子死得早,此時子進程成為孤兒進程,此時父進程會成為init進程(進程號pid為1的進程),init進程可視為進程孤兒院用于領養(yǎng)孤兒進程。
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.4 225808 9512 ? Ss 00:43 0:02 /sbin/init splash
$ ps aux | grep init
root 1 0.1 0.4 225808 9512 ? Ss 00:43 0:02 /sbin/init splash
jc 3613 0.0 0.0 21536 1000 pts/0 S+ 01:18 0:00 grep --color=auto init
當一個父進程退出而它的子進程還在運行,此時這些子進程將會成為孤兒進程,孤兒進程將被init進程收養(yǎng),并由init進程對它們完成狀態(tài)收集工作。
危害
孤兒進程是沒有父進程的子進程,因此孤兒進程的管理重任會落到init進程身上,init進程好像一個民政局或孤兒所,專門負責處理孤兒進程的善后工作。每當出現(xiàn)一個孤兒進程時內(nèi)核就會把孤兒進程的父進程設置為init進程,init進程會循環(huán)地wait已經(jīng)退出的子進程。這樣,當一個孤兒進程的生命周期結(jié)束時,init進程會代表黨和政府處理善后工作,因此孤兒進程不會有什么危害。
實例1
$ vim orphan.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();//創(chuàng)建子進程
//如果當前進程是init進程則報錯退出
if(pid == 1)
{
perror("init process");
exit(1);
}
else if(pid > 0)
{
sleep(1);
printf("parent pid = %d, parent parent pid is %d\n", getpid(), getppid());
}
else if(pid == 0)
{
printf("child pid is %d, parent pid is %d\n", getpid(), getppid());
sleep(3);
printf("child pid is %d, parent pid is %d\n", getpid(), getppid());
}
return 0;
}
$ gcc orphan.c -o orphan -Wall -g
$ ./orphan
child pid is 3652, parent pid is 3651
parent pid = 3651, parent parent pid is 3633
root@junchow:/home/jc/projects/c# pachild pid is 3652, parent pid is 2700
$ ps aux | grep 2700
jc 2700 0.0 0.3 77120 8292 ? Ss 00:57 0:00 /lib/systemd/systemd --user
root 3672 0.0 0.0 21536 1052 pts/0 S+ 01:21 0:00 grep --color=auto 2700
實例2
$ vim orphan.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid == 0)
{
while(1)
{
printf("child: parent pid=%d\n", getppid());
sleep(1);
}
}
else if(pid > 0)
{
printf("parent: pid=%d\n", getpid());
sleep(10);
printf("parent: going to die\n");
}
else
{
perror("fork");
return 1;
}
return 0;
}
$ gcc orphan.c -o orphan -Wall -g
$ ./orphan
僵尸進程
僵尸進程是如何產(chǎn)生的呢?
當運行一個程序時會產(chǎn)生一個父進程以及多個子進程,所有子進程都會消耗內(nèi)核分配的內(nèi)存和CPU資源。子進程完成執(zhí)行后會發(fā)送一個exit信號然后死掉。這個exit型號需要被父進程讀取。父進程需要隨后調(diào)用wait命令來讀取進程的退出狀態(tài),并將子進程從進程表中移除。
在UNIX系統(tǒng)中一旦進程結(jié)束,如果父進程沒有使用wait或waitpid系統(tǒng)調(diào)用等待子進程結(jié)束,又沒有顯式的忽略SIGCHILD信號,那么它將變成一個僵尸進程并一直存在。僵尸進程是一個早已死亡的進程,但在進程表中任占據(jù)著一個位置。
如果子進程的父進程已先行結(jié)束,那么子進程就不會變成僵尸進程,因為每個進程結(jié)束的時候,系統(tǒng)會掃描當前操作系統(tǒng)中所有運行的進程,檢查有沒有哪個進程是剛剛結(jié)束的進程的子進程。若有則由init進程來接管并作為它的父進程,從而保證每個進程都會有一個父進程。init進程會自動wait其子進程,因此被init接管的進程都不會變成僵尸進程。

僵尸進程是指進程終止,父進程尚未回收,子進程殘留資源PCB存放于內(nèi)核kernel中變?yōu)榻┦?code>zombie進程。
父進程有義務將子進程回收,如果子進程死亡后父進程不幫子進程收尸,此時子進程就會變成僵尸。
如何查找僵尸進程呢?
$ ps -ef | grep defunct
$ ps aux | grep Z
僵尸進程存在會有什么樣的危害呢?
正常進程死亡以后,0到4G的進程地址空間會主動釋放,但是PCB依然會殘留在內(nèi)核中,其目的是為了讓父進程為它報仇。如果子進程死亡后沒有任何痕跡留下的話,父進程將無法知道子進程是由于什么原因造成的死亡,是自殺還是他殺呢?子進程死亡后會在內(nèi)核中殘留PCB,父進程通過PCB可以獲取子進程的死亡狀態(tài)。
任何一個子進程(init進程除外)在exit退出后并非立即消失,而會留下一個稱為僵尸進程的數(shù)據(jù)結(jié)構,以等待父進程處理。
UNIX提供了一種機制讓父進程獲取子進程結(jié)束時的狀態(tài)信息,這種機制就是在每個子進程退出的時候,內(nèi)核會釋放該進程所有的資源,包括打開的文件、占用的內(nèi)存等。但內(nèi)核仍然會保留一定的信息,如進程號、退出狀態(tài)、運行時間等信息。直到父進程通過wait或waitpid系統(tǒng)調(diào)用來獲取時才完全釋放。
這樣就導致了一個問題,如果進程不調(diào)用 wait或waitpid,那么保留在內(nèi)核中的殘留信息將不會主動被釋放,進程號會一直被占用。由于系統(tǒng)所能使用的進程號是有限的,如果大量產(chǎn)生僵尸進程的話,系統(tǒng)將沒有可用的進程號,從而導致系統(tǒng)不能產(chǎn)生新的進程。
此時如果使用ps命令查看會發(fā)現(xiàn)子進程的狀態(tài)為Z,即僵尸進程。
嚴格來說,僵尸進程并非罪魁禍首,問題的根源在于產(chǎn)生大量僵尸進程的那個父進程??梢允褂?code>kill命令發(fā)送信號SIGTERM或SIGKILL信號槍斃元兇進程。槍斃完成后,僵尸進程將會變成了孤兒進程,這些孤兒進程會被ini進程接管。init進程會wait`這些孤兒進程,釋放他們占用的系統(tǒng)資源。
需要注意的是僵尸進程不能使用kill命令清除,因為kill命令只是用來終止進程,而僵尸進程已經(jīng)是終止的。
如何避免僵尸進程呢?
僵尸進程的產(chǎn)生是因為父進程沒有wait子進程,當系統(tǒng)中出現(xiàn)了僵尸進程,是無法通過kill命令來清除的,但可以殺死僵尸進程的父進程,讓其變成孤兒進程,系統(tǒng)會統(tǒng)一管理和清理孤兒進程。
有什么辦法可以清除掉僵尸進程呢?
正常情況下可以使用SIGKILL信號來殺死進程,由于僵尸進程已經(jīng)早死了,所以不能使用kill命令殺死已經(jīng)死掉的進程。
$ kill -s SIGCHILD pid
可使用wait和waitpid回收僵尸進程
實例
$ vim zoombie.c
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
//創(chuàng)建子進程
pid = fork();
printf("pid=%d\n", pid);
//若子進程為調(diào)度進程
if(pid == 0)
{
printf("child: parent=%d\n", getppid());
printf("child: going to sleep\n");
sleep(10);
printf("child: child die\n");
}
else if(pid > 0)
{
while(1)
{
printf("parent: pid=%d child=%d\n", getpid(), pid);
sleep(1);
}
}
else
{
perror("fork error");
return 1;
}
return 0;
}
$ gcc zoombie.c -o zoombie -Wall -g
$ ./zoombie
運行結(jié)果
pid=3654
parent: pid=3653 child=3654
pid=0
child: parent=3653
child: going to sleep
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
child: child die
parent: pid=3653 child=3654
parent: pid=3653 child=3654
parent: pid=3653 child=3654
...
注意若運行出現(xiàn)implicit declaration of function ‘fork’ [-Wimplicit-function-declaration]則需注意是否引入了unistd.h頭文件。
wait函數(shù)
一個進程在終止時會關閉所文件描述符,釋放用戶空間并分配內(nèi)存,只是子進程的PCB還保留著,內(nèi)核在其中保持努著一些信息。如果是正常終止或保存退出狀態(tài)。如果是異常終止則保存著導致該進程的信號,這個進程可使用Shell中特殊。
父進程調(diào)用wait系統(tǒng)調(diào)用可以回收子進程的終止信息,然后徹底清除掉這個進程,一個進程的狀態(tài)可以在Shell中的特殊變量$?來查看。
wait函數(shù)的三個功能分別是
- 阻塞等待子進程退出
- 回收子進程殘留的信息
- 獲取子進程結(jié)束狀態(tài)與退出原因
pid_t wait(int *status)
wait函數(shù)成功則清理掉子進程ID,若失敗則返回-1表示沒有子進程。
當進程終止時操作系統(tǒng)的隱式回收機制
- 關閉所有文件描述符
- 釋放用戶空間分配的內(nèi)存
進程終止時內(nèi)核的PCB仍舊存在,其中保存該進程的退出狀態(tài),簡單來說,正常終止時返回退出值,異常終止時返回終止信號。
實例:父進程回收子進程
$ vim wait.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;//創(chuàng)建子進程的進程ID
pid_t wpid;//回收子進程的返回值
//創(chuàng)建子進程
pid = fork();
printf("pid=%d\n", pid);
//若子進程為調(diào)度進程
if(pid == 0)
{
//子進程休眠10秒后死亡
printf("child: parent=%d\n", getppid());
printf("child: going to sleep\n");
sleep(10);
printf("child: child die\n");
}
else if(pid > 0)
{
//回收子進程若失敗則提示錯誤
wpid = wait(NULL);
if(wpid == -1)
{
perror("wait error");
exit(1 );
}
while(1)
{
printf("parent: pid=%d child=%d\n", getpid(), pid);
sleep(1);
}
}
else
{
perror("fork error");
return 1;
}
return 0;
}
$ gcc wait.c -o wait -Wall -g
$ ./wait
pid=3757
pid=0
child: parent=3756
child: going to sleep
child: child die
parent: pid=3756 child=3757
parent: pid=3756 child=3757
parent: pid=3756 child=3757
...
此時若查看進程狀態(tài)會發(fā)現(xiàn)并未出現(xiàn)僵尸進程
$ ps aux|grep wait
jc 3756 0.0 0.0 4508 740 pts/0 S+ 22:42 0:00 ./wait
jc 3767 0.0 0.0 21536 1060 pts/1 S+ 22:42 0:00 grep --color=auto wait
子進程死亡狀態(tài)
當進程終止時刻使用wait函數(shù)傳出參數(shù)status來保存進程的推出狀態(tài),借助宏函數(shù)來進一步判斷進程終止的具體原因。宏函數(shù)可分為三組:
- 正常退出
-
WIFEXITED(status):全稱wait if exited表示判斷是否是正常退出的
若非0則表示進程正常退出 -
WEXITSTATUS(status):全稱wait exit status表示退出狀態(tài)
若WIFEXITED(status)為真則使用宏WEXITSTATUS(status)以獲取進程退出的狀態(tài),即exit的參數(shù)。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;//創(chuàng)建子進程的進程ID
pid_t wpid;//回收子進程的返回值
int status;//回收子進程返回的狀態(tài)
//創(chuàng)建子進程
pid = fork();
printf("pid=%d\n", pid);
//若子進程為調(diào)度進程
if(pid == 0)
{
//子進程休眠3秒后死亡
printf("child-%d: parent=%d\n", pid, getppid());
printf("child-%d: going to sleep 3 seconds\n", pid);
sleep(3);
printf("child-%d: child die\n", pid);
exit(76);//退出值不超過128
}
else if(pid > 0)
{
//回收子進程
wpid = wait(&status);
//回收子進程若失敗則提示錯誤
if(wpid == -1)
{
perror("wait error");
exit(1);
}
//若子進程回收成功
printf("child-%d: wait if existed with %d\n", pid, WIFEXITED(status));
if(WIFEXITED(status) != 0)
{
//獲取子進程成功退出的狀態(tài)值
printf("child-%d: wait exit status with %d\n", pid, WEXITSTATUS(status));
}
while(1)
{
printf("parent-%d: child=%d\n", getpid(), pid);
sleep(1);
}
}
else
{
perror("fork error");
return 1;
}
return 0;
}
編譯運行程序并查看狀態(tài)
$ gcc wait.c -o wait -Wall -g
$ ./wait
pid=3934
pid=0
child-0: parent=3933
child-0: going to sleep 3 seconds
child-0: child die
child-3934: wait if existed with 1
child-3934: wait exit status with 76
parent-3933: child=3934
parent-3933: child=3934
parent-3933: child=3934
...
注意:此處使用exit(76);也可以使用return 76;進行替換。
- 異常退出
-
WIFSIGNALED(status):全稱wait if signaled
若非0則表示進程異常終止 -
WTERMSIG(status):全稱wait term signal
若WIFSIGNALED(status)為真則使用宏WTERMSIG(status)以獲得使進程終止的那個信號的編號。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;//創(chuàng)建子進程的進程ID
pid_t wpid;//回收子進程的返回值
int status;//回收子進程返回的狀態(tài)
//創(chuàng)建子進程
pid = fork();
printf("pid=%d\n", pid);
//若子進程為調(diào)度進程
if(pid == 0)
{
//子進程休眠后死亡
printf("child-%d: parent=%d\n", pid, getppid());
printf("child-%d: going to sleep 60 seconds\n", pid);
sleep(60);
printf("child-%d: child die\n", pid);
exit(76);//退出值不超過128
}
else if(pid > 0)
{
//回收子進程
wpid = wait(&status);
//回收子進程若失敗則提示錯誤
if(wpid == -1)
{
perror("wait error");
exit(1 );
}
//若子進程正常退出
printf("child-%d: wait if existed with %d\n", pid, WIFEXITED(status));
if(WIFEXITED(status) != 0)
{
//獲取子進程成功退出的狀態(tài)值
printf("child-%d: wait exit status with %d\n", pid, WEXITSTATUS(status));
}
//若子進程異常退出
printf("child-%d: wait if signaled with %d\n", pid, WIFSIGNALED(status));
if(WIFSIGNALED(status) != 0)
{
printf("child-%d: wait term sig with %d\n", pid, WTERMSIG(status));
}
while(1)
{
printf("parent-%d: child=%d\n", getpid(), pid);
sleep(1);
}
}
else
{
perror("fork error");
return 1;
}
return 0;
}
編譯并運行程序
$ gcc wait.c -o wait -Wall -g
$ ./wait
此時讓子進程休眠60秒,為什么呢,在60秒內(nèi)新開命令行窗口并殺死子進程和父進程查看輸出狀態(tài)。
$ ps aux|grep wait
root 1139 0.0 0.7 194320 17560 ? Ssl 22:03 0:00 /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal
jc 4030 0.0 0.0 4508 856 pts/0 S+ 23:44 0:00 ./wait
jc 4031 0.0 0.0 4508 72 pts/0 S+ 23:44 0:00 ./wait
jc 4034 0.0 0.0 21536 1148 pts/1 S+ 23:44 0:00 grep --color=auto wait
此時會發(fā)現(xiàn)有兩個進程正在運行,4030對應的應該是父進程,4031對應的應該是子進程,接下來分別殺死進程。
$ kill -9 4031
$ kill -9 4030
最后查看日志輸出信息
pid=4031
pid=0
child-0: parent=4030
child-0: going to sleep 60 seconds
child-4031: wait if existed with 0
child-4031: wait if signaled with 1
child-4031: wait term sig with 9
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
parent-4030: child=4031
已殺死
此處需注意child-4031: wait term sig with 9中的9是由于使用kill -9 4031,也就是向進程發(fā)送的9號信號。表示什么意思呢?
$ kill -L
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
根據(jù)命令可以知道9號信號表示SIGKILL
- 暫停狀態(tài)
-
WIFSTOPPED(status):全稱wait if stopped進程處于暫停狀態(tài)
若非0則表示進程處于暫停狀態(tài) -
WSTOPSIG(status)
若WIFSTOPPED(status)為真則使用宏WSTOPSIG(status)以獲取使進程暫停的那個信號的編號。 -
WIFCONTINUED(status)
若WIFCONTINUED(status)為真則表示進程暫停后已經(jīng)繼續(xù)運行
子進程死亡一般會存在兩種情況,一種是正常死亡壽終正寢,此時程序?qū)⒎祷?。另一種情況是異常退出,在Linux中所有的異常退出都是由于信號導致的,由于子進程收到了某個特殊信號它才異常退出。所以異常終止時父進程需要回收子進程異常終止的信號。
waitpid函數(shù)
waitpid函數(shù)作用于wait函數(shù)相同,但可以指定pid進程清理且不阻塞。
pid_t waitpid(pid_t pid, int *status, int options)
waitpid若成功則返回清理掉的進程ID,若失敗則返回-1表示無子進程。
wait函數(shù)與waitpid的區(qū)別在于一次wait調(diào)用只能回收一個子進程,而waitpid可以指定進程ID進行回收更加靈活。
waitpid參數(shù)pid返回情況
- 若
pid小于-1表示回收指定進程組中的任意子進程 - 若
pid等于0表示回收和當前調(diào)用waitpid一個組的所有子進程 - 若
pid等于-1表示回收任意子進程,作用相當于wait函數(shù)。 - 若
pid大于0表示回收指定進程ID的子進程
waitpid參數(shù)options可以設置是否阻塞,設置非阻塞可使用WNOHANG宏,設置非阻塞后需要使用輪詢的方式定時查看子進程是否回收成功。
$ vim waipid.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n = 5;//默認創(chuàng)建5個子進程
int i;//循環(huán)變量
pid_t pid;//子進程ID
pid_t tmp;
//若命令行參數(shù)個數(shù)是2個
if(argc == 2)
{
n = atoi(argv[1]);
}
//循環(huán)創(chuàng)建子進程
for(i=0; i<n; i++)
{
pid = fork();
//若進程為系統(tǒng)進程
if(pid == 0)
{
break;
}
else if(i ==3)
{
tmp = pid;
}
}
printf("tmp=%d\n", tmp);
if(n == i)
{
sleep(n);//多少個循環(huán)則休眠多少秒
printf("parent: pid=%d\n", getpid());//打印父進程
//子進程死亡后回收
wait(NULL);//隨機回收子進程且只回收一個
while(1);//死循環(huán)
}
else
{
sleep(i);//休眠
printf("child%d: pid=%d\n", i+1, getpid());//打印子進程
//while(1);//死循環(huán)
}
return 0;
}
$ gcc waitpid.c -o waitpid -Wall -g
$ ./waitpid
tmp=5043
tmp=5043
tmp=0
tmp=0
tmp=0
tmp=0
child1: pid=5040
child2: pid=5041
child3: pid=5042
child4: pid=5043
child5: pid=5044
parent: pid=5039
$ ps aux|grep waitpid
jc 5039 81.6 0.0 4508 756 pts/0 R+ 03:52 0:25 ./waitpid
jc 5041 0.0 0.0 0 0 pts/0 Z+ 03:52 0:00 [waitpid] <defunct>
jc 5042 0.0 0.0 0 0 pts/0 Z+ 03:52 0:00 [waitpid] <defunct>
jc 5043 0.0 0.0 0 0 pts/0 Z+ 03:52 0:00 [waitpid] <defunct>
jc 5044 0.0 0.0 0 0 pts/0 Z+ 03:52 0:00 [waitpid] <defunct>
可以查看到當前存在4個僵尸進程Z+,使用wait回收時每次只回收了一個進程,如果需要同時回收5個進程,應該怎么辦呢?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n = 5;//默認創(chuàng)建5個子進程
int i;//循環(huán)變量
pid_t pid;//子進程ID
pid_t tmp;
//若命令行參數(shù)個數(shù)是2個
if(argc == 2)
{
n = atoi(argv[1]);
}
//循環(huán)創(chuàng)建子進程
for(i=0; i<n; i++)
{
pid = fork();
//若進程為系統(tǒng)進程
if(pid == 0)
{
break;
}
else if(i ==3)
{
tmp = pid;
}
}
printf("tmp=%d\n", tmp);
if(n == i)
{
sleep(n);//多少個循環(huán)則休眠多少秒
printf("parent: pid=%d\n", getpid());//打印父進程
//子進程死亡后回收
while(wait(NULL));//循環(huán)回收子進程,每次只能回收一個。
while(1);//死循環(huán)
}
else
{
sleep(i);//休眠
printf("child%d: pid=%d\n", i+1, getpid());//打印子進程
//while(1);//死循環(huán)
}
return 0;
}
此處會發(fā)現(xiàn)如果需要回收多個進程,可使用while循環(huán)的方式。
如果現(xiàn)在需要指定某個進程進行回收呢?應該怎么辦?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int n = 5;//默認創(chuàng)建5個子進程
int i;//循環(huán)變量
pid_t pid;//子進程ID
pid_t tmp;
//若命令行參數(shù)個數(shù)是2個
if(argc == 2)
{
n = atoi(argv[1]);
}
//循環(huán)創(chuàng)建子進程
for(i=0; i<n; i++)
{
pid = fork();
//若進程為系統(tǒng)進程
if(pid == 0)
{
break;
}
else if(i ==3)
{
tmp = pid;//獲取第三個子進程的pid,為了指定進程進行回收使用。
}
}
printf("tmp=%d\n", tmp);
if(n == i)
{
sleep(n);//多少個循環(huán)則休眠多少秒
printf("parent: pid=%d\n", getpid());//打印父進程
//子進程死亡后回收
waitpid(tmp, NULL, 0);//指定回收某個子進程
while(1);//死循環(huán)
}
else
{
sleep(i);//休眠
printf("child%d: pid=%d\n", i+1, getpid());//打印子進程
//while(1);//死循環(huán)
}
return 0;
}
$ gcc waitpid.c -o waitpid -Wall -g
$ ./waitpid
tmp=5097
tmp=5097
tmp=0
tmp=0
tmp=0
tmp=0
child1: pid=5094
child2: pid=5095
child3: pid=5096
child4: pid=5097
child5: pid=5098
parent: pid=5093
$ ps aux|grep waitpid
jc 5093 54.5 0.0 4508 740 pts/0 R+ 04:05 0:06 ./waitpid
jc 5094 0.0 0.0 0 0 pts/0 Z+ 04:05 0:00 [waitpid] <defunct>
jc 5095 0.0 0.0 0 0 pts/0 Z+ 04:05 0:00 [waitpid] <defunct>
jc 5096 0.0 0.0 0 0 pts/0 Z+ 04:05 0:00 [waitpid] <defunct>
jc 5098 0.0 0.0 0 0 pts/0 Z+ 04:05 0:00 [waitpid] <defunct>
jc 5100 0.0 0.0 21536 1028 pts/1 S+ 04:05 0:00 grep --color=auto waitpid
此處會發(fā)現(xiàn)pid=5087的子進程被回收了
未完待續(xù)...