一個進程在調用exit命令結束自己的生命的時候,其實它並沒有真正的被 殭屍進程銷燬, 而是留下一個稱爲殭屍進程(Zombie)的數據結構(系統調用exit,它的作用是 使進程退出,但也僅僅限於將一個正常的進程變成一個殭屍進程,並不能將其完全銷燬)
殭屍進程是怎麼樣產生
在Linux進程的狀態中,殭屍進程是非常特殊的一種,它已經放棄了幾乎所有內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等信息供其他進程收集,除此之外,殭屍進程不再佔有任何內存空間。它需要它的父進程來爲它收屍。
如果他的父進程沒安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該信號,那麼它就一直保持殭屍狀態,如果這時父進程結束了,那麼init進程自動會接手這個子進程,爲它收屍,它還是能被清除的。
但是如果父進程是一個循環,不會結束,那麼子進程就會一直保持殭屍狀態,這就是爲什麼系統中有時會有很多的殭屍進程。系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因爲沒有可用的進程號而導致系統不能產生新的進程.
異步回收殭屍進程:
fork()之後,非阻塞(異步)等待子進程(回收殭屍)。
fork()之後,子進程和父進程分叉執行,殭屍進程的產生是因爲父進程沒有給子進程“收屍”造成的,又可以根據危害程度分爲下述兩類:
總體來說:當子進程結束之後,但父進程未結束之前,子進程將成爲殭屍進程。
(1)當子進程結束之後,但父進程未結束之前,子進程將成爲殭屍進程,父進程結束後殭屍被init進程回收。
(2)如果子進程結束了,但是父進程始終沒有結束,那麼這個殭屍將一直存在,而且隨着exec,殭屍越來越多。
如下面的代碼,在父進程執行的5s內,子進程將爲殭屍:
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <unistd.h>
- int main() {
- //子進程的pid
- int c_pid;
- int pid;
- if ((pid = fork())) {
- //父進程
- c_pid = pid;
- printf("The child process is %d\n", c_pid);
- sleep(5);
- exit(0);
- } else {
- //子進程
- printf("I 'm a child.\n");
- exit(0);
- }
- }
如上面的代碼,在父進程的5s內,子進程一直是殭屍!
因此,需要對殭屍進程進行回收,傳統的回收方法是,使用wait()函數,等待子進程,wait()是阻塞模式的,當子進程沒有結束之前,wait一直等待,不往下面的語句執行。
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <unistd.h>
- #include <sys/wait.h>
- int main() {
- //子進程的pid
- int c_pid;
- int pid;
- if ((pid = fork())) {
- //父進程
- c_pid = pid;
- printf("The child process is %d\n", c_pid);
- //阻塞等待子進程
- int status;
- if ((pid = wait(&status)) != -1 && pid == c_pid) {
- //成功回收子進程
- printf("The child exit with %d\n", WEXITSTATUS(status));
- fflush(stdin);
- } else {
- printf("wait() fail.\n");
- }
- printf("Now , The child has been exit , and I will sleep.\n");
- sleep(20);
- exit(0);
- } else {
- //子進程
- printf("I 'm a child.\n");
- sleep(5);
- exit(0);
- }
- }
如上面的代碼,在子進程執行5秒後,即被回收,在夫進程的20秒內,子進程已經被結束,不再是殭屍。
但是這種利用wait()阻塞等待的方法也有一定的缺陷,那就是父進程必須等待子進程,無法做其他事情,如何非阻塞的等待子進程呢?
man wait,查看NOTES章節,可以找到:
子進程退出的時候,會發送SIGCHLD信號,默認的POSIX不響應,所以,我們只需要把處理SIGCHLD的函數自己實現就OK了,怎麼作呢?
signal用於設置處理信號量的規則(或跳轉到的函數)
- signal(SIGCHLD,handler);
- void handler(int num)
- {
- //我接受到了SIGCHLD的信號啦
- int status;
- int pid = waitpid(-1,&status,WNOHANG);
- if(WIFEXITED(status))
- {
- printf("The child exit with code %d",WEXITSTATUS(status));
- }
- }
OK,全部代碼如下,注意父進程不要再用wait阻塞啦!
- #include <stdio.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <unistd.h>
- #include <sys/wait.h>
- void handler(int num) {
- //我接受到了SIGCHLD的信號啦
- int status;
- int pid = waitpid(-1, &status, WNOHANG);
- if (WIFEXITED(status)) {
- printf("The child %d exit with code %d\n", pid, WEXITSTATUS(status));
- }
- }
- int main() {
- //子進程的pid
- int c_pid;
- int pid;
- signal(SIGCHLD, handler);
- if ((pid = fork())) {
- //父進程
- c_pid = pid;
- printf("The child process is %d\n", c_pid);
- //父進程不用等待,做自己的事情吧~
- for (int i = 0; i < 10; i++) {
- printf("Do parent things.\n");
- sleep(1);
- }
- exit(0);
- } else {
- //子進程
- printf("I 'm a child.\n");
- sleep(2);
- exit(0);
- }
- }