zoukankan      html  css  js  c++  java
  • 僵尸进程

    fork和僵尸进程

     

    1. 关于fork

    fork()函数:

       用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #include <iostream>
    #include <unistd.h>
    using namespace std;
    int val = 10;
    int main(int argc, char *argv[])
    {
        pid_t pid;
        int lval = 20;
        pid = fork();
        if(pid == 0){
            val += 2;
            lval += 5;
        }else{
            val -= 2;
            lval += 5;
        }
        if(pid == 0){
            cout << "val:" << val << ", lval = " << lval << endl;
        }else{
            cout << "val:" << val << ", lval = " << lval << endl;
        }
        return 0;
    }

    对于父进程而言,fork()函数返回子进程的ID(子进程的PID);而对于子进程而言,fork函数返回0。

    僵尸进程

      父进程创建子进程后,子进程运行到终止时刻(例如,调用exit()函数,或者运行到main中的return语句时,都会将返回的值传递给 操作系统),此时如果父进程还在运行,子进程并不会立即被销毁,直到这些值传到了产生该子进程的父进程。也就是说,如果父进程没有主动要求获得子进程的结束状态值,操作系统就会一直保存该进程的相关信息,并让子进程长时间处于僵尸状态,例如下面程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int main(){
        pid_t pid = fork();
        if(pid == 0){
            cout << "I am a Child Process." <<endl;
        }else{
            cout << "I am a Father Process and Child Process is " << pid << endl;
            sleep(30);  //让父进程休眠30秒,此时便于观察子进程的状态
        }
        if(pid == 0){
            cout << " Child Process exits " << endl;
        }else{
            cout << "Father Process exits " << endl;
        }
        return 0;
    }

    此时,运行该程序,查看后台进程可知(test16是该测试程序的名称,defunct表示僵尸进程):

    复制代码
    gqx@gqx-Lenovo-Product:~$ ps -au
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root       923  0.6  0.9 480840 159824 tty7    Ssl+ 4月09  36:07 /usr/lib/xorg/
    root      1351  0.0  0.0  17676  1768 tty1     Ss+  4月09   0:00 /sbin/agetty -
    ...
    gqx      24856  0.0  0.0      0     0 pts/11   Z+   11:03   0:00 [tes16] <defunct>
    gqx      24859  0.0  0.0  39104  3300 pts/3    R+   11:03   0:00 ps -au
    复制代码

    僵尸进程的消除

    方法一:调用wait()函数:

    复制代码
    /* Wait for a child to die.  When one does, put its status in *STAT_LOC
       and return its process ID.  For errors, return (pid_t) -1.
    ​
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern __pid_t wait (__WAIT_STATUS __stat_loc);
    复制代码

    成功返回终止的进程ID,失败返回-1;子进程的最终返回值将指向该函数参数所指向的内存空间,但函数所指向的内存单元总还含有其他的信息,需要使用宏进行分离。

    # define WIFEXITED(status)  __WIFEXITED (__WAIT_INT (status))  //子进程正常终止返回"true"
    # define WEXITSTATUS(status)    __WEXITSTATUS (__WAIT_INT (status)) //返回子进程的返回值

    要注意的是:如果没有已终止的子进程,那么程序将被阻塞,直到有子进程终止。

    方法二:调用waitpid()函数

    复制代码
    /* Wait for a child matching PID to die.
       If PID is greater than 0, match any process whose process ID is PID.
       If PID is (pid_t) -1, match any process.
       If PID is (pid_t) 0, match any process with the
       same process group as the current process.
       If PID is less than -1, match any process whose
       process group is the absolute value of PID.
       If the WNOHANG bit is set in OPTIONS, and that child
       is not already dead, return (pid_t) 0.  If successful,
       return PID and store the dead child's status in STAT_LOC.
       Return (pid_t) -1 for errors.  If the WUNTRACED bit is
       set in OPTIONS, return status for stopped children; otherwise don't.
    ​
       This function is a cancellation point and therefore not marked with
       __THROW.  */
    extern __pid_t waitpid (__pid_t __pid, int *__stat_loc, int __options);
    复制代码

    第一个参数:如果__pid的值是-1,则与wait()函数相同,可以等待任意的子程序终止;如果是0,则等待进程组识别码与目前进程相同的任何子进程;如果pid>0,则等待任何子进程识别码为 pid 的子进程。

    第二个参数:与前一个函数wait()的参数意义相同。

    第三个参数:常用WNOHANG——若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。

    示例程序如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <iostream>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/wait.h>
    using namespace std;
    void read_childproc(int sig){
        int status;
        pid_t id = waitpid(-1, &status, WNOHANG);
        if(WIFEXITED(status)){
            printf("Remove proc id: %d ", id);
            printf("Child send: %d ", WEXITSTATUS(status));
        }
    }
    int main(){
        pid_t pid;
        struct sigaction act;
        act.sa_handler = read_childproc;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGCHLD, &act, 0);
        pid = fork();
        if(pid == 0){
            puts("Hi, I am a child process!");
            sleep(6);
            return 12;
        }else{
            printf("Child proc id: %d ", pid);
            pid = fork();
            if(pid == 0){
                puts("Hi, I am a child process!");
                sleep(13);
                exit(24);
            }else{
                int i;
                printf("Child proc id: %d ", pid);
                for(i  = 0; i < 4; i++){
                    puts("wait...");
                    sleep(5);
                }
            }
        }
        return 0;
    }

      

     

  • 相关阅读:
    HTML5 JS 实现浏览器全屏(F11的效果)
    SpringMVC学习笔记之二(SpringMVC高级参数绑定)
    二十三种设计模式总结
    系统开发中使用拦截器校验是否登录并使用MD5对用户登录密码进行加密
    Mybatis学习笔记之二(动态mapper开发和spring-mybatis整合)
    Mybatis学习笔记之一(环境搭建和入门案例介绍)
    Java中clone方法的使用
    列举Java中常用的包、类和接口
    Spring中bean的注入方式
    [ SSH框架 ] Spring框架学习之三(AOP开发和注解的使用)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/11106175.html
Copyright © 2011-2022 走看看