• 【Linux之进程间通信】05.僵尸进程


     
    【Linux之进程间通信】

    项目代码获取:https://gitee.com/chenshao777/linux-processes.git
    (麻烦点个免费的Star哦,您的Star就是我的写作动力!)

    05.僵尸进程

    僵尸进程: 指的是进程终止后,资源没有得到回收,状态为 Z+
    怎么创造一个僵尸进程出来呢,很简单,
    通过 fork 函数创建一个子进程
    父进程执行while(1)死循环,
    子进程不循环,运行几条语句后直接退出,
    例如:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /*
        僵尸进程 & 子进程给父进程发信号(通过kill(getppid(), sig)) & 子进程调用exit(),发送SIGCHLD信号给父进程
        命令: ./z+process
        现象:父进程正常运行,收到一次子进程的SIGUSR1信号
             子进程不调用exit()函数结束,则变成僵尸进程Z+
             子进程调用exit()函数结束,则向父进程发送SIGCHLD信号,父进程信号处理函数调用wait()函数回收子进程资源
    */
    void handler(int arg)
    {
        printf("信号处理handler,arg = %d\n", arg);  //arg是信号ID
        if(arg == SIGINT)
            printf("你按了 ctrl+c \n");
        if(arg == SIGUSR1)
            printf("父进程收到子进程信号\n");
        if(arg == SIGCHLD){
            printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");
            wait(NULL);   //回收子进程资源
        }
    }
    
    int main(int argc, char *argv[])
    {
        signal(SIGINT, handler);   //SIGINT(ctrl+c)信号处理
    
        pid_t pid;
        pid = fork();
    
        if(pid > 0){
            signal(SIGUSR1, handler);  //接收子进程的信号
            // signal(SIGCHLD, handler);  //接收子进程结束的信号
            printf("父进程\n");
            for(int i = 0;i < 10;i++){
                printf("父 %d\n",i);
                sleep(1);
            }
        }
        if(pid == 0){
            printf("子进程运行,接着睡眠2秒\n");
            sleep(2);
            printf("给父进程发信号\n");
            kill(getppid(), SIGUSR1);
    
            sleep(1);
            printf("子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号\n");
        }
        return 0;
    }
    
    • 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
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    本代码结合了 signal 信号处理函数,signal 函数的介绍可以借鉴一下我上一篇博客的第三小节 【Linux之进程间通信】04.Linux进程间的信号通信

    运行结果:

    hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
    hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process 
    父进程
    父 0
    子进程运行,接着睡眠2秒
    父 1
    给父进程发信号
    父 2
    信号处理handler,arg = 10
    父进程收到子进程信号
    父 3
    子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
    父 456789
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    子进程执行完语句直接结束运行,我们运行一下 ps -axj 命令,查看一下该进程的状态

      PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
      4070  30199  30199   4070 pts/1     30199 S+    1000   0:00 ./z+process
     30199  30200  30199   4070 pts/1     30199 Z+    1000   0:00 [z+process] <defunct>
      2282  30207  30207   2282 pts/0     30207 R+    1000   0:00 ps -axj
    
    • 1
    • 2
    • 3
    • 4

    第一行是父进程信息,第二行是子进程信息
    父进程是S+睡眠状态(可能正好在执行sleep函数)
    子进程是Z+僵尸进程状态

    处理僵尸进程:

    首先我们要知道两个知识点:

    1.wait()函数的作用:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经退出的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;

    如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
    2.子进程的运行状态只要发生变化,都会自动向父进程发送一个SIGCHLD信号,所以父进程对SIGCHLD信号设置处理函数,在处理函数中使用wait()函数进行子进程的资源回收.

    signal(SIGCHLD, handler);  //接收子进程结束的信号
    
    • 1

    上面的代码虽然说子进程退出是向父进程发送了SIGCHLD信号,但是该信号默认被忽略

    ---------------------------------------------
    所以处理僵尸进程的方法:

    1.在主进程中设置SIGCHLD信号处理函数
    2.在信号处理函数中执行 wait(NULL),对退出的子进程进行资源回收,则僵尸进程不存在了。

    具体操作:
    将上面代码中
    // signal(SIGCHLD, handler); //接收子进程结束的信号
    的注释取消,再次运行,查看运行结果和进程状态:

    hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
    hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process 
    父进程
    父 0
    子进程运行,接着睡眠2秒
    父 1
    给父进程发信号
    信号处理handler,arg = 10
    父进程收到子进程信号
    父 23
    子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
    信号处理handler,arg = 17
    子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己
    父 456789
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果多了一句 “子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己”
    说明子进程退出后主进程接收到了 SIGCHLD 信号,执行了 handler 函数中的以下代码

    if(arg == SIGCHLD){
        printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");
        wait(NULL);   //回收子进程资源
    }
    
    • 1
    • 2
    • 3
    • 4

    由于运行了 wait(NULL); 进程状态中也没有了僵尸进程,只有S+状态的主进程

      PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
      4070  30533  30533   4070 pts/1     30533 S+    1000   0:00 ./z+process
      2282  30535  30535   2282 pts/0     30535 R+    1000   0:00 ps -axj
    
    • 1
    • 2
    • 3

    扩展知识:

    如果父进程提前结束,而子进程还在运行,则子进程退出时谁来给子进程回收资源呢?
    答:如果子进程还在运行,父进程却提前退出,则PID为1的init进程会自动接管,成为该子进程的父进程,等子进程终止后回收资源 (Linux内核真牛)

  • 相关阅读:
    大数据——Spark
    Docsify介绍—md文件直接生成网页的工具
    (图文详解)小程序AppID申请以及在Hbuilderx中运行
    深度强化学习-DQN算法
    (仿牛客社区项目)Java开发笔记7.7:生成长图
    应用于伺服电机控制、 编码器仿真、 电动助力转向、发电机、 汽车运动检测与控制的旋变数字转换器MS5905P
    Maven基础概念【仓库和坐标】这篇看完懂了
    21 错误处理与调试
    如何设置负载均衡EasySLB后端服务器网关
    【Unity实战技巧】教你4招计算UI物体的包围盒(Bounds)
  • 原文地址:https://blog.csdn.net/HuangChen666/article/details/130837018