1.等待進程退出
linux中利用wait函數得到子進程的結束資訊,其函數原型如下:
#include <sys/wait.h>
pid_t wait(int * statloc);
調用wait的函數會阻塞,直到該進程的任意一個子進程結束。wait會取得結束的子進程的資訊並且返回該子進程的進程ID,結婚訴資訊儲存在參數statloc中。如果該進程沒有子進程,則立即出錯返回,傳回值為-1;如果在調用wait函數時已經有若干個子進程結束運行了,則wait函數立即返回,但是具體取得的是哪一個子進程的資訊則是不定的,需要根據wait函數的傳回值來判斷。
進程終止資訊和操作宏
狀態 判斷宏 取值宏
進程正常結束 WIFEXITED(status) WEXITSTATUS(status)
進程異常終止 WIFSIGNALED(status) WTERMSIG(status)
進程暫停 WIFSTOPPED(status) WSTOPSIG(status)
例如,當一個進程正常退出時,該進程的父進程得到其結束資訊,則需判斷:如果WIFEXITED(status)值為真,那麼說明該進程正常退出,WEITSTATUS(status)使用返回資訊中進程結束狀態。
eg:
//wait.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
int status;
pid = fork( );
if(pid < 0){
perror("fail to fork");
exit(1);
}else if(pid == 0){
printf("the first, exit normally/n");
exit(0);
}else{ /*父進程*/
if(wait(&status) == -1){
perror("fail to wait");
exit(1);
}
if(WIFEXITED(status) == 1)
/*正常退出*/
printf("the status of first is : %d/n", WEXITSTATUS(status));
}
pid = fork();
if(pid < 0){
perror("fail to fork");
exit(1);
}else if(pid == 0){
printf("the second, exit abnormally/n");
1 / 0; /*會產生SIGFPE異常訊號*/
}else{
/*父進程*/
if(wait(&status) == -1){
perror("fail to wait");
exit(1);
}
if(WIFSIGNALED(status) == 1)
/*得到終止子進程的訊號的值*/
printf("the terminated signal is : %d/n", WTERMSIG(status));
}
return 0;
}
2.等待指定的進程
wait只能等待第一個結束的子進程如果需要指定等待一個子進程則需要使用如下代碼實現:
while(pid!=wait(&status));
這裡還有另一個函數:
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int * statloc,int options);
pid指定要等待的子進程的進程ID,其還有一些其他作用
pid 等待的作用
-1 等待任意子進程
>0 等待進程id和pid相等的子進程
0 等待組id和pid相等的子進程
<-1 等待組id等於pid絕對值的組內的任意子進程
statloc的含義同wait中的
第三個是控制選項。
wait函數選項 選項說明
WCONTINUED 當子進程在暫停後繼續執行,且其狀態尚未報告,則返回其狀態
WNOHANG 當所等待進程尚未結束運行時不阻塞,waitpid函數直接返回
WUNTRACED 當子進程暫停時,並且其狀態自暫停以來還未報告國,則返回其狀態
該參數可以是0,也可以是上面所述的3種選項“或”得到。
waitpid函數等待指定的子進程,如該子進程已結束運行,傳回值是子進程的進程id。此時若pid>0則傳回值等於參數pid的值;如果該進程尚未結束,且設定了WNOHANG選項,則傳回值為-1,如果指定進程或者進程組不存在或者參數pid所指定的進程不是調用waitpid函數的進程的子進程,則立即出錯返回-1。
eg:
//waitpid.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid=fork();
if(pid<0){
printf("fail to fork/n");
exit(1);
}else if(pid==0){
printf("the child/n");
sleep(5);
exit(0);
}else{
printf("the parent/n");
if(waitpid(pid,NULL,WNOHANG)==-1) /*非阻塞等待子進程*/
printf("the child is not available now/n");
}
printf("no waiting,parent done/n");
return 0;
}
3.殭屍進程
子進程已經退出了,但是它的退出狀態資訊仍然儲存在核心中,進程的的id也儲存在系統的進程列表中。這時的進程成為殭屍進程。
它不能代行,也不會佔用cpu時間。
殭屍進程會一直存在於系統中,直到父進程得到其結束狀態資訊(即父進程調用wait)。
因為殭屍進程仍然佔用著ID,所以若殭屍進程達到一定的數目時,系統中將不能再產生新的進程。
eg:
//zombie.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("fail to fork/n");
exit(0);
}else if(pid == 0){
printf("the child/n");
sleep(5);
printf("done/n");
exit(0);
}else
printf("the parent/n");
sleep(30); /*在這30秒的時間內,子進程是一個殭屍進程*/
if(wait(NULL) == -1){
perror("fail to wait");
exit(1);
}
return 0;
}
4.避免殭屍進程的產生
為了避免殭屍進程的產生,必須在父進程中調用wait函數,但是,考慮一個處理互動式用戶端請求的伺服器程式模型:
socket();
bind();
listen();
while(1){
accept();
if(fork()==0){ //建立一個子進程處理用戶端的互動式請求
while(1){
read();
process();//處理請求
write();
}
close();
exit();
}
close();
}
如果父進程調用wait函數,那麼在子進程退出之前父進程會阻塞。因此,伺服器一次只能處理一個用戶端的請求。
如果父進程自子進程結束前結束運行,這是的子進程成為孤兒進程。init進程負責領養所用的孤兒進程,也就是說init進程會成為所有父進程先於自己退出的進程的父進程。作為系統的一個守護進程,init進程被設計成為永遠調用wait函數,也就是說init進程的所有的子進程都不會成為殭屍進程。因此就用這種方法避免產生殭屍進程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid < 0){
printf("fail to fork/n");
exit(1);
}else if(pid == 0){
printf("the child/n");
pid = fork(); //建立一個孫子進程,這個進程負責處理實際工作
if(pid < 0){
printf("fail to fork/n");
exit(1);
}else if(pid == 0){//孫子進程
printf("do something you want/n");
sleep(5);
printf("done/n");
exit(0);
}else
exit(0);//子進程退出,將孫子進程託付給init進程
}else
printf("the parent/n");
if(wait(NULL)==-1){ //父進程調用wait
perror("fail to wait");
exit(1);
}
return 0;
}
使用這種方法,上述的伺服器模型變為如下的形式:
socket();
bind();
listen();
while(1)
{
accept();
if(fork()==0){
if(fork()==0){//建立孫子進程
while(1){
read()
process();
write();
}
close();
exit();
}else
exit(); //子進程退出,孫子進程的父進程變成init進程
}
close();
}
5.輸出進程的統計資訊
要得到進程執行中的更多資訊(如進程的cpu使用量,頁面出錯次數等),可以使用wait3和wait4函數,它們分別類似於wait和waitpid函數。
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/source.h>
pid_t wait3(int *statloc,int options,struct rusage * rusage);
pid_t wait4(pid_t pid,int *statloc,int options,struct rusage * rusage);
更詳細的資訊儲存在rusage中。
//wait3.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/resource.h>
int main(void)
{
pid_t pid;
struct rusage rusage;
pid = fork();
if(pid < 0){
printf("fail to fork/n");
exit(0);
}else if(pid == 0){
printf("the child/n");
exit(0);
}else
printf("the parent/n");
if(wait3(NULL, 0, &rusage) == -1){
perror("fail to wait");
exit(1);
}
printf("utime is %d", rusage.ru_utime);
printf("stime is %d", rusage.ru_stime);
printf("maxrss is %d", rusage.ru_maxrss);
printf("ixrss is %d", rusage.ru_ixrss);
printf("idrss is %d", rusage.ru_idrss);
printf("isrss is %d", rusage.ru_isrss);
printf("minflt is %d", rusage.ru_minflt);
printf("majflt is %d", rusage.ru_majflt);
printf("nswap is %d", rusage.ru_nswap);
printf("inblock is %d", rusage.ru_inblock);
printf("oublock is %d", rusage.ru_oublock);
printf("msgsnd is %d", rusage.ru_msgsnd);
printf("msgrcv is %d", rusage.ru_msgrcv);
printf("nsignals is %d", rusage.ru_nsignals);
printf("nvcsw is %d", rusage.ru_nvcsw);
printf("nivcsw is %d", rusage.ru_nivcsw);
return 0;
}