殭屍進程:子進程退出後,父進程尚未回收子進程的資源,那麼這個子進程就處於殭屍狀態。
Q1:「資源」是些什麼?
Q2:父進程如何回收子進程的資源?函數
內核爲每一個終止子進程保存了必定量的信息,因此當終止進程的父進程調用wait或waitpid時,能夠獲得這些信息。
這些信息至少包括進程ID,該進程的終止狀態,以及該進程使用的CPU時間總量.
內核能夠釋放終止進程所使用的全部存儲區,關閉其全部打開的文件.spa
若是編寫一個長期運行的程序,他調用fork產生了不少子進程,那麼除非父進程等待來取得子進程的終止狀態,不然這些子進程終止後就會變成殭屍進程。code
避免殭屍進程的方法
方法1:調用兩次fork能夠避免殭屍進程的產生。orm
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 5 int main(void) 6 { 7 pid_t pid; 8 9 if ((pid = fork()) < 0) 10 { 11 printf("fork error\n"); 12 exit(-1); 13 } 14 else if (0==pid) /*first child*/ 15 { 16 printf("1first child pid=%d\n", getpid()); 17 if ((pid = fork())<0) 18 { 19 printf("fork error\n"); 20 exit(-1); 21 } 22 else if (pid > 0) /*parent from second fork == first child*/ 23 { 24 printf("2first child pid=%d, first child exit\n", getpid()); 25 exit(0);//first child exit 26 } 27 28 printf("second child pid=%d\n", getpid()); 29 /*I am the second child, my parent become init as soon as my real parent calls exit() in the statement above 30 * Here's where we'd continue executing, knowing that when we're done, init will reap out status*/ 31 sleep(30); 32 printf("second child, parent pid = %d, second child exit\n", getppid()); 33 exit(0); 34 } 35 36 printf("pararent pid=%d\n", getpid()); 37 if (waitpid(pid, NULL, 0) != pid) /*wait for first child*/ 38 { 39 printf("waitpid error\n"); 40 exit(-1); 41 } 42 printf("parent exit"); 43 44 45 /*I am the parent(the original process); I continue executing, knowing that I'm not the parent of the second child*/ 46 47 exit(0); 48 }
方法2:當SIGCHLD的處理方式是系統默認時,父進程調用了wait()以免殭屍進程的產生,此方法中,父進程阻塞。blog
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/wait.h> 4 #include <errno.h> 5 #include <signal.h> 6 7 void print_exit(int status) 8 { 9 if (WIFEXITED(status)) 10 printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), 13 #ifdef WCOREDUMP 14 WCOREDUMP(status) ? ("core file generated") : ("")); 15 #else 16 ""); 17 #endif 18 else if (WIFSTOPPED(status)) 19 printf("child stopped, signal number=%d\n", WSTOPSIG(status)); 20 } 21 22 void sig_child(int signo) 23 { 24 int status; 25 int ret; 26 ret = wait(&status); 27 printf("pid:%d, res:%d, status=%d, %s\n", getpid(), ret, status, strerror(errno)); 28 print_exit(status); 29 } 30 31 void sig_usr(int signo) 32 { 33 if (signo == SIGUSR1) 34 printf("received SIGUSR1\n"); 35 else if (signo == SIGUSR2) 36 printf("received SIGUSR2\n"); 37 else 38 printf("received signal %d\n", signo); 39 } 40 41 int main(int argc, char** argv) 42 { 43 pid_t pid; 44 int status; 45 int ret; 46 47 if ((pid=fork()) < 0) 48 { 49 printf("fork error\n"); 50 return -1; 51 } 52 else if (pid == 0) 53 { 54 printf("child exit\n"); 55 return 0; 56 } 57 else 58 { 59 //printf("parent sleep(100)\n"); 60 //sleep(100); 61 ret = wait(&status); 62 print_exit(status); 63 printf("parent exit\n"); 64 } 65 66 67 exit(0); 68 }
方法3:當SIGCHLD的處理方式是捕獲時,在其信號處理程序中調用wait()函數以免殭屍進程的產生,此方法中,父進程不阻塞。進程
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/wait.h> 4 #include <errno.h> 5 #include <signal.h> 6 7 void print_exit(int status) 8 { 9 if (WIFEXITED(status)) 10 printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), 13 #ifdef WCOREDUMP 14 WCOREDUMP(status) ? ("core file generated") : ("")); 15 #else 16 ""); 17 #endif 18 else if (WIFSTOPPED(status)) 19 printf("child stopped, signal number=%d\n", WSTOPSIG(status)); 20 } 21 22 void sig_child(int signo) 23 { 24 int status; 25 int ret; 26 ret = wait(&status); 27 printf("pid:%d, res:%d, status=%d, %s\n", getpid(), ret, status, strerror(errno)); 28 print_exit(status); 29 } 30 31 void sig_usr(int signo) 32 { 33 if (signo == SIGUSR1) 34 printf("received SIGUSR1\n"); 35 else if (signo == SIGUSR2) 36 printf("received SIGUSR2\n"); 37 else 38 printf("received signal %d\n", signo); 39 } 40 41 int main(int argc, char** argv) 42 { 43 pid_t pid; 44 struct sigaction act, oact; 45 int status; 46 int ret; 47 48 act.sa_handler = sig_child; 49 sigemptyset(&act.sa_mask); 50 //act.sa_flags = 0|SA_NOCLDWAIT; 51 sigaction(SIGCHLD, &act, &oact); 52 53 if ((pid=fork()) < 0) 54 { 55 printf("fork error\n"); 56 return -1; 57 } 58 else if (pid == 0) 59 { 60 printf("child exit\n"); 61 return 0; 62 } 63 else 64 { 65 printf("parent sleep(100)\n"); 66 sleep(100); 67 printf("parent exit\n"); 68 } 69 70 71 exit(0); 72 }
從打印中能夠看出,信號處理程序由父進程調用
方法4:設置SIGCHLD爲SA_NOCLDWAIT,當子進程終止時,不建立殭屍進程。父進程中不需調用wait。資源
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/wait.h> 4 #include <errno.h> 5 #include <signal.h> 6 7 void print_exit(int status) 8 { 9 if (WIFEXITED(status)) 10 printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status), 13 #ifdef WCOREDUMP 14 WCOREDUMP(status) ? ("core file generated") : ("")); 15 #else 16 ""); 17 #endif 18 else if (WIFSTOPPED(status)) 19 printf("child stopped, signal number=%d\n", WSTOPSIG(status)); 20 } 21 22 void sig_usr(int signo) 23 { 24 if (signo == SIGUSR1) 25 printf("received SIGUSR1\n"); 26 else if (signo == SIGUSR2) 27 printf("received SIGUSR2\n"); 28 else 29 printf("received signal %d\n", signo); 30 } 31 32 int main(int argc, char** argv) 33 { 34 pid_t pid; 35 struct sigaction act, oact; 36 int status; 37 int ret; 38 39 act.sa_handler = sig_usr; 40 sigemptyset(&act.sa_mask); 41 act.sa_flags = 0|SA_NOCLDWAIT; 42 sigaction(SIGCHLD, &act, &oact); 43 44 if ((pid=fork()) < 0) 45 { 46 printf("fork error\n"); 47 return -1; 48 } 49 else if (pid == 0) 50 { 51 printf("child exit\n"); 52 return 0; 53 } 54 else 55 { 56 printf("parent sleep(100)\n"); 57 sleep(100); 58 //ret = wait(&status); 59 //print_exit(status); 60 printf("parent exit\n"); 61 } 62 63 64 exit(0); 65 }