• 回收子进程——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

  • 相关阅读:
    H50068:html页面清除缓存
    CSS0019: 样式中高度百分比无效时,这样写 height:calc(100%)
    H50067:body 背景颜色 背景图片 background 的 简写属性
    40、在last_update后面新增加一列名字为create_date
    39、使用强制索引查询
    38、针对actor表创建视图actor_name_view
    37、对first_name创建唯一索引uniq_idx_firstname,对last_name创建普通索引idx_lastname
    36、创建一个actor_name表,将actor表中的所有first_name以及last_name导入改表
    35、批量插入如下数据,不使用replace操作
    34、批量插入如下数据
  • 原文地址:https://www.cnblogs.com/y4247464/p/12091032.html
Copyright © 2020-2023  润新知