回收子进程——wait/waitpid 与 信号机制

孤儿/僵尸进程——回收子进程

参考博客:https://blog.csdn.net/qq_35396127/article/details/78725915

    :https://www.cnblogs.com/Anker/p/3271773.html

  在Linux下,子进程可由父进程创建,子进程也可以创建新的进程。但是父进程无法预测子进程的运行状态,不知道子进程何时会结束。由此会产生孤儿进程与僵尸进程。所以当一个进程结束后,它的父进程需要调用wait(),waitpid()系统调用获取子进程终止状态,回收子进程。

  什么是孤儿进程与僵尸进程?

  孤儿进程:

  父进程先于子进程结束,则子进程失去父进程,子进程就会被init 进程收养,子进程就成为孤儿进程。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 
 5 int main(void)
 6 {
 7     pid_t pid;
 8     int i=0;
 9     //创建一个子进程
10     pid = fork();
11 
12     if(pid == -1)
13     {
14         perror("fork");
15         exit(1); //1:异常退出 0:正常退出
16     }
17     else if(pid>0)
18     {
19         printf("I am parent, my pid=%d
",getpid());
20         sleep(4); //父进程运行4秒后结束
21         printf("---------parent going to die-----------
");
22 
23     }
24     else
25     {
26         while(i<80)
27         {
28             //待父进程结束后会被init收养
29             printf("I am child, pid = %d, parentpid = %d
",getpid(),getppid());
30             sleep(1);
31             i++;
32         }
33     }
34     return 0;
35 }

结果:

 有的Ubuntu版本,会设置user init 进程专门处理孤儿进程

僵尸进程:

  子进程终止,父进程未回收子进程的资源PCB,使其变成僵尸进程。

测试程序:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 
 6 int main(){
 7 
 8     pid_t pid;
 9     pid = fork();
10     if(pid < 0)
11     {
12         perror("fork error:");
13         exit(1);
14     }
15     else if (pid == 0)
16     {
17         printf("I am child, I am exiting.
");
18         exit(0);
19     }
20 
21     printf("I am parent,I will sleep 2s
");
22     //等待子进程先退出
23     sleep(2);
24     //输出进程信息
25     system("ps -o pid,ppid,state,tty,command");
26     printf("father process exiting
");
27 
28     return 0;
29 }
30 
31 源自:https://www.cnblogs.com/Anker/p/3271773.html

结果:

  孤儿进程与僵尸进程的危害

  在Linux中,每个进程退出时,内核会释放该进程所有资源,包括打开的文件,占用的内存等,但仍为之保留一定信息,包括:进程ID,退出状态,运行时间等。直到父进程通过wait/waitpid来取时,才会释放。因此,只要进程一直调用wait与waitpid,进程占用的资源就不会释放,进程号也不会释放,由于系统能使用的进程号是有限的,就可能因为大量僵尸进程占用进程号而不能产生新进程。当系统中产生大量僵尸进程时,应该把产生僵尸进程的父进程给杀死掉。可以通过kill发送SIGTERM或者SIGKILL信号,之后僵尸进程会因为没了父进程变成孤儿,被init收养再释放。

  对于孤儿进程,会被init进程收养,而且init进程会循环地wait()它收养的子进程。所以孤儿进程并无危害。

  通过信号机制解决僵尸进程

  子进程退出时会向父进程发送SIGCHLD信号,父进程调用信号处理函数,进而调用wait处理僵尸进程。测试程序:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 #include <signal.h>
 6 
 7 static void handlefunc(int sig)
 8 {
 9     pid_t pid;
10     int   stat;
11 
12     //处理僵尸进程
13     //waitpid(-1:回收任一子进程,子进程结束状态,不阻塞父进程)
14     //waitpid成功返回子进程pid
15     while((pid = waitpid(-1, &stat, WNOHANG))>0)
16         printf("child %d terminated. 
",pid);
17 }
18 
19 int main()
20 {
21     pid_t pid;
22     //创建signal,捕捉子进程退出信号
23     signal(SIGCHLD,handlefunc);
24     pid = fork();
25     if(pid<0)
26     {
27         perror("fork error:");
28         exit(1);
29     }
30     else if(pid == 0)
31     {
32         printf("I am chid, pid=%d. I exiting
",getpid());
33         exit(0);
34     }
35     printf("I am parent. I sleep 3S
");
36     //等待子进程退出
37     sleep(3);
38     //输出进程信息
39     system("ps -o pid,ppid,state,tty,command");
40     printf("parent exiting");
41 
42     return 0;
43 
44 }

结果:僵尸进程消失

signal(SIGCLD,SIG_IGN);

因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

详见:https://blog.csdn.net/u012317833/article/details/39253793

原文地址:https://www.cnblogs.com/y4247464/p/12091032.html