僵尸进程:子进程退出后,父进程还没有回收子进程的资源,那么这个子进程就处于僵尸状态。
Q1:“资源”是些什么?
Q2:父进程如何回收子进程的资源?
内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息。
这些信息至少包括进程ID,该进程的终止状态,以及该进程使用的CPU时间总量.
内核可以释放终止进程所使用的所有存储区,关闭其所有打开的文件.
如果编写一个长期运行的程序,他调用fork产生了很多子进程,那么除非父进程等待来取得子进程的终止状态,否则这些子进程终止后就会变成僵尸进程。
避免僵尸进程的方法
方法1:调用两次fork可以避免僵尸进程的产生。
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 "); 12 exit(-1); 13 } 14 else if (0==pid) /*first child*/ 15 { 16 printf("1first child pid=%d ", getpid()); 17 if ((pid = fork())<0) 18 { 19 printf("fork error "); 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 ", getpid()); 25 exit(0);//first child exit 26 } 27 28 printf("second child pid=%d ", 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 ", getppid()); 33 exit(0); 34 } 35 36 printf("pararent pid=%d ", getpid()); 37 if (waitpid(pid, NULL, 0) != pid) /*wait for first child*/ 38 { 39 printf("waitpid error "); 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()以避免僵尸进程的产生,此方法中,父进程阻塞。
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 ", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s ", 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 ", 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 ", 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 "); 35 else if (signo == SIGUSR2) 36 printf("received SIGUSR2 "); 37 else 38 printf("received signal %d ", 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 "); 50 return -1; 51 } 52 else if (pid == 0) 53 { 54 printf("child exit "); 55 return 0; 56 } 57 else 58 { 59 //printf("parent sleep(100) "); 60 //sleep(100); 61 ret = wait(&status); 62 print_exit(status); 63 printf("parent exit "); 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 ", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s ", 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 ", 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 ", 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 "); 35 else if (signo == SIGUSR2) 36 printf("received SIGUSR2 "); 37 else 38 printf("received signal %d ", 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 "); 56 return -1; 57 } 58 else if (pid == 0) 59 { 60 printf("child exit "); 61 return 0; 62 } 63 else 64 { 65 printf("parent sleep(100) "); 66 sleep(100); 67 printf("parent exit "); 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 ", WEXITSTATUS(status)); 11 else if (WIFSIGNALED(status)) 12 printf("abnormal termination, signal number = %d%s ", 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 ", WSTOPSIG(status)); 20 } 21 22 void sig_usr(int signo) 23 { 24 if (signo == SIGUSR1) 25 printf("received SIGUSR1 "); 26 else if (signo == SIGUSR2) 27 printf("received SIGUSR2 "); 28 else 29 printf("received signal %d ", 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 "); 47 return -1; 48 } 49 else if (pid == 0) 50 { 51 printf("child exit "); 52 return 0; 53 } 54 else 55 { 56 printf("parent sleep(100) "); 57 sleep(100); 58 //ret = wait(&status); 59 //print_exit(status); 60 printf("parent exit "); 61 } 62 63 64 exit(0); 65 }