實現對子進程的回收,以及回收狀態的打印
#include<stdio.h> #include<unistd.h> #include<error.h> #include<sys/types.h> #include<stdlib.h> #include<sys/wait.h> #include<signal.h> voidsys_err(char *errmes,int errno){ perror(errmes); exit(errno); } voiddo_sigchild(int signum){ pid_tpid; intstatus; sleep(2); while((pid=waitpid(0,&status,WNOHANG))>0){ if(WIFEXITED(status)){ printf("child%d is exit %d\n",pid,status); }elseif(WIFSIGNALED(status)){ printf("child%d id exit %d\n",pid,WTERMSIG(status)); } } } intmain(void){ pid_tpid; inti=0; intn=10; structsigaction act; act.sa_handler=do_sigchild; sigemptyset(&act.sa_mask); act.sa_flags=0; sigaction(SIGCHLD,&act,NULL); while(i<10){ pid=fork(); i++; if(pid==0){ break; }elseif(pid<0){ sys_err("fork",-1); } } if(pid==0){ while(n--){ printf("Itis chid,pid=%d\n",getpid()); sleep(1); } }elseif(pid>0){ while(1){ printf("Itis parent,pid=%d\n",getpid()); sleep(1); } } return0; } |
輸出結果:
It is chid,pid=6805
child 6796 is exit 0
child 6797 is exit 0
child 6798 is exit 0
child 6799 is exit 0
child 6800 is exit 0
child 6801 is exit 0
child 6802 is exit 0
child 6803 is exit 0
child 6804 is exit 0
child 6805 is exit 0
It is parent,pid=6795
在上面的函數中,需要討論的問題是:
Linux是不支持信號排隊的,爲什麼這裏就算有多個子進程發出SIGCHLD信號,也不會被忽視?
第一點,這裏還要回到信號集的問題上,當信號開始被處理的時候,未決集就已經翻轉了(這時候,handler可能還在執行中),所以,可以接收下一個信號,也就是下一個信號已經保存進來了,這裏子進程同時發信號,這個同時是個假的同時,因爲內核處理進程終究還是時間差的。
第二點,函數的可重入性,關於什麼是可重入函數,什麼是不可重入函數:
不可重入函數:函數執行中不能被打斷再執行當前函數,打斷之後,再執行,第一次調用的不能實現預期的效果
可重入函數:函數被打斷,再調用當前函數,原來的函數還是會實現預期的效果
在信號捕捉函數中禁止調用不可重入函數,也就是在捕捉函數中,只能調用可重入的系統函數,以保證運行結果的安全性
也就是waitpid()是可重入函數,這一點很重要
處理信號的關鍵點:1.信號接收到2.信號未被阻塞3.可進入處理函數
所以從上面就可以分析出來,子進程結束以後,由於未決集的翻轉的特點,以及waitpid()的可重入性,就實現了“假信號排隊”的現象。