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

 
【Linux之进程间通信】

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

05.僵尸进程

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

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

/*
    僵尸进程 & 子进程给父进程发信号(通过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;
}

本代码结合了 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

子进程执行完语句直接结束运行,我们运行一下 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

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

处理僵尸进程:

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

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

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

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

上面的代码虽然说子进程退出是向父进程发送了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

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

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

由于运行了 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

扩展知识:

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值