zoukankan      html  css  js  c++  java
  • 孤儿进程僵尸进程及其回收

    孤儿进程僵尸进程及其回收是进程的经典知识了。

    什么是孤儿进程?

    孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为 init进程,称为 init 进程领养孤儿进程。

    什么是僵尸进程?

    僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

    特别注意,僵尸进程是不能使用 kill 命令清除掉的。因为 kill 命令只是用来终止进程的,而僵尸进程已经终止。

    孤儿进程有init进程回收,但是僵尸进程要等父进程来回收,如果父进程不回收,僵尸进程会已经死亡得不到回收却占用进程号,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

    相关函数

    一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的 PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。

    这个进程的父进程可以调用 wait 或 waitpid 获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在 Shell 中用特殊变量$?查看,因为 Shell 是它的父进程,当它终止时 Shell 调用 wait 或 waitpid 得到它的退出状态同时彻底清除掉这个进程。

    wait 函数

    pid_t wait(int *status); 

    父进程调用 wait 函数可以回收子进程终止信息。该函数有三个功能:

      ① 阻塞等待子进程退出  ② 回收子进程残留资源  ③ 获取子进程结束状态(退出原因)。

    函数返回值:成功:清理掉的子进程 ID;失败:-1 (没有子进程)

    当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的 PCB 仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)

    status判断终止原因

    可使用 wait 函数传出参数 status 来保存进程的退出状态。借助宏函数来进一步判断进
    程终止的具体原因。宏函数可分为如下三组:
    1. WIFEXITED(status) 为非 0 → 进程正常结束
        WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit 的参数)
    2. WIFSIGNALED(status) 为非 0 → 进程异常终止
        WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
    *3. WIFSTOPPED(status) 为非 0 → 进程处于暂停状态
        WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
        WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<unistd.h>
     4 #include<sys/wait.h>
     5 
     6 int main()
     7 {
     8     pid_t pid,wpid;
     9     int status;
    10 
    11     pid=fork();
    12     if (pid==0) {    //子进程
    13         printf("i am child, pid is %d, sleep 10s ",pid);
    14         sleep(20);
    15         printf("i am diying");
    16         return 73;
    17     } else if (pid>0) {    //父进程
    18         //wait函数会阻塞,如果没有一个子进程死亡
    19         //wpid=wait(NULL);    //不关心子进程如何死亡
    20         wpid=wait(&status);    //关心子进程死亡,写到status里面
    21         if (wpid==-1) {
    22             perror("wait error");
    23             exit(1);
    24         }
    25 
    26         if (WIFEXITED(status))        //为真,子进程正常终止
    27             printf("child exit with %d
    ",WEXITSTATUS(status));
    28         
    29         if (WIFSIGNALED(status))    //为假,子进程被信号终止
    30                printf("child kill with signal %d
    ",WTERMSIG(status));
    31 
    32         printf("wait successful, %d died",pid);           
    33 
    34     } else {
    35         perror("fork error");    //fork出错了
    36         return 1;
    37     }
    38     return 0;
    39 }
    wait回收子进程

    waitpid 函数

    pid_t waitpid(pid_t pid, int *status, in options);   作用同 wait,但可指定 pid 进程清理,可以不阻塞。 

    特殊参数和返回情况:

    参数 pid:> 0 回收指定 ID 的子进程  -1 回 回于 收任意子进程(相当于 wait )  0 回收和当前调用 waitpid 一个组的所有子进程  < -1 回收指定进程组内的任意子进程

    参数status:和wait函数的status一样,判断进程终止原因

    参options:    0 :(相当于wait)阻塞回收    WBNIOHANG非阻塞回收(轮询)

    返回值:成功:返回清理掉的子进程 ID;失败:-1(无子进程)

        特殊的返回 0:参 3 为 WNOHANG,且子进程正在运行。


    注意:次 一次 wait 或 或 waitpid 调用只能清理一个子进程,清理多个子进程应使用循环while。

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 #include<unistd.h>
     5 #include<sys/wait.h>
     6 #include<pthread.h>
     7 
     8 int main(int argc,char *argv[])
     9 {
    10     int i;
    11     pid_t pid,wpid;
    12 
    13     for (i=0;i<5;i++) {
    14         pid=fork();
    15         if (pid==0) break;
    16     }
    17 
    18     if (i==5) {    //父进程
    19         
    20         //这里不断while循环等待死亡的子进程
    21         while ((wpid=waitpid(-1,NULL,WNOHANG))!=-1) {
    22             if (wpid>0) {    //回收成功
    23                 printf("wait child %d 
    ",wpid);
    24             } else if (wpid==0) {    //回收失败
    25                 sleep(1);    //sleep一秒之后再尝试回收
    26                 continue;
    27             }
    28         }
    29     } else {    //5个子进程
    30         sleep(i);    //子进程sleep以一下
    31         printf("I'm child %d ,i am dying",i);
    32     }
    33     return 0;
    34 }
    waitpid回收所以子进程

     

    更加美妙的回收方式是使用SIGCHLD信号回收子进程,这样父进程不阻塞可以干自己的事情:https://www.cnblogs.com/clno1/p/12941316.html

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 #include<unistd.h>
     5 #include<signal.h>
     6 #include<sys/wait.h>
     7 #include<errno.h>
     8 #include<pthread.h>
     9 
    10 void sys_err(const char *str) {
    11     perror(str);
    12     exit(1);
    13 }
    14 
    15 void catch_child(int signo) {
    16     pid_t wpid;
    17     int status;
    18 
    19     //这里的while非常重要,当接受到SIGCHLD信号时候,有多个子进程同时死亡,这时候就需要while来把这段时间的死亡子全部回收
    20     while ((wpid=waitpid(-1,&status,0))!=-1) {
    21         if (WIFEXITED(status))
    22             printf("-------------catch child id %d, ret=%d 
    ",wpid,WEXITSTATUS(status));
    23     }
    24     return;
    25 }
    26 
    27 int main(int argc,char *argv[])
    28 {
    29     pid_t pid;
    30     int i;
    31 
    32     for (i=0;i<15;i++)
    33         if ((pid=fork())==0)    //创建15个子进程
    34             break;
    35 
    36     if (i==15) {    //父进程
    37         
    38         //信号结构体,三个参数重要
    39         struct sigaction act;    
    40 
    41         act.sa_handler=catch_child;    //注册函数
    42         sigemptyset(&act.sa_mask);    //在执行期间,sa_mask会替换原mask
    43         act.sa_flags=0;        //设为0,在该信号处理函数期间,再次收到同样信号就屏蔽
    44 
    45         sigaction(SIGCHLD,&act,NULL);
    46 
    47         printf("I'm parent, pid=%d 
    ",getpid());
    48 
    49         while(1);
    50 
    51     } else {    //子进程
    52         printf("I'm child pid= %d
    ",getpid());
    53         return i;
    54     }
    55     return 0;
    56 }
    SIGCHLD信号回收子进程
  • 相关阅读:
    Linux 套接字编程
    Linux 网络(连接)相关参数作用
    Python WSGI
    Ubuntu Cloud Image in Openstack
    AWK
    MySQL--开发技巧(一)
    spring MVC--配置注解
    javascript-JQuery样式篇(一)
    JSP--常用标签
    spring MVC basic
  • 原文地址:https://www.cnblogs.com/clno1/p/12937547.html
Copyright © 2011-2022 走看看