僵尸进程

僵尸进程

什么是僵尸进程

提到僵尸,首先想到的肯定是僵尸系列的电影或者植物大战僵尸。一个活人为什么会成为僵尸?一般普通人是被僵尸攻击了,在变成僵尸之前没有得到有效处理(比如击毙),等他被僵尸病毒侵入脑子后就成为僵尸一员了。

那什么是僵尸进程呢?同僵尸人一样,一个进程由于各种原因终止后,没有得到有效处理,就成为了僵尸进程。特别需要说明的是,linux系统中,子进程的终止状态会发送给父进程,意味着只有父进程可以有效处理子进程。如果父进程没有处理,子进程变成僵尸进程。当然,如果父进程也终止了,子进程就成了孤儿进程,变成init进程的子进程,此时也就没有僵尸进程了。

下面通过一个实例来说明僵尸进程的产生过程。

/**
 * zombie.c
 * 显示僵尸进程的产生过程
 **/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char **argv[])
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        exit(1);
    }
    else if (pid == 0) // child process
    {
        printf("child process %d, parent process %d\n", getpid(), getppid());
        printf("after child process died....\n");
        exit(0);
    }
    else // parent process
    {
        sleep(1);
        printf("parent process %d\n", getpid());
        system("ps -o pid,ppid,state,tty,command");
        printf("parent process is exiting\n");
        exit(0);
    }
    exit(0);
}

编译和运行程序,如下:

/source/zombie# gcc zombie.c -o zombie
/source/zombie# ./zombie
child process 10750, parent process 10749
after child process died....
parent process 10749
  PID  PPID S TT       COMMAND
  473   455 S pts/3    sudo su
  474   473 S pts/3    su
  475   474 S pts/3    bash
  562   475 S pts/3    ./daytimetcpsrv
10749   475 S pts/3    ./zombie
10750 10749 Z pts/3    [zombie] <defunct>
10751 10749 S pts/3    sh -c ps -o pid,ppid,state,tty,command
10752 10751 R pts/3    ps -o pid,ppid,state,tty,command
parent process is exiting

简要说明一下程序。

fork之后,先让父进程sleep 1秒,确保子进程先运行。子进程先结束后,父进程中调用shell指令ps,查看当前进程状态,显而易见,子进程10750已经处于Z状态,即僵尸状态(10750 10749 Z pts/3 [zombie] )。

僵尸进程的危害

说了这么多,僵尸进程会有危害吗?坦白讲,单个的僵尸进程貌似并没什么危害,但是考虑这样一种场景,父进程一直循环产生子进程,而且不处理子进程的退出状态,会是什么样的情况?此时,成千上万的子进程退出后成为了僵尸进程,僵尸进程会一直占据资源(内存,文件描述符等)不释放,久而久之,系统就会因为资源耗尽而崩溃。

僵尸进程解决办法

说了僵尸进程的危害,那么,有什么办法可以解决僵尸进程呢?

当然有!

信号机制

最容易想到的办法,就是当子进程终止时,会发送自己的状态给父进程。父进程处理子进程的状态即可。

/**
 * waitpid.c
 * 僵尸进程解决方法:父进程接收子进程信号
 **/

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

static void sig_child(int signo);

int main(int argc, char **argv[])
{
    pid_t pid;

    signal(SIGCHLD, sig_child);   // catch child signal

    pid = fork();

    if (pid < 0)
    {
        perror("fork error");
        exit(1);
    }
    else if (pid == 0) // child process
    {
        printf("child process %d is exiting\n", getpid());
        exit(0);
    }
    else // parent process
    {
        printf("parent process %d\n", getpid());
        sleep(2);
        system("ps -o pid,ppid,state,tty,command");
        printf("parent process is exiting\n");
        exit(0);
    }
    exit(0);
}

static void sig_child(int signo)
{
    pid_t pid;
    int stat;

    while ((pid = waitpid(pid, &stat, WNOHANG)) > 0)    
    {
        printf("child process %d terminated\n", pid);
    }
}

编译和运行程序,如下:

/source/zombie# gcc waitpid.c -o waitpid
/source/zombie# ./waitpid
parent process 11068
child process 11069 is exiting
child process 11069 terminated
  PID  PPID S TT       COMMAND
  473   455 S pts/3    sudo su
  474   473 S pts/3    su
  475   474 S pts/3    bash
  562   475 S pts/3    ./daytimetcpsrv
11068   475 S pts/3    ./waitpid
11070 11068 S pts/3    sh -c ps -o pid,ppid,state,tty,command
11071 11070 R pts/3    ps -o pid,ppid,state,tty,command
parent process is exiting

如上所示,子进程安全退出,ps显示此时已经没有僵尸进程了。

fork两次

当然,还可以找到一直产生僵尸进程的罪魁祸首,也就是它们的父进程,把此进程杀死,从而让僵尸进程成为孤儿进程。此时,init进程收养和妥善处理这些孤儿进程。

/**
 * init.c
 * 僵尸进程解决方法:fork两次,让僵尸进程成为孤儿进程
 **/

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

int main(int argc, char **argv[])
{
    pid_t pid;

    pid = fork();

    if (pid < 0)
    {
        perror("fork error");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I am first child process %d\n", getpid());
        pid = fork();

        if (pid < 0)
        {
            perror("fork error");
            exit(2);
        }
        else if (pid > 0)
        {
            printf("first child %d is exited\n", getpid());
            exit(0);
        }

        sleep(3);
        printf("I am second child process %d, parent process %d\n", getpid(), getppid());
        exit(0);
    }
    else
    {
        if (waitpid(pid, NULL, 0) != pid)
        {
            perror("waitpid error");
            exit(1);
        }
    }
    exit(0);
}

编译和运行程序,如下:

/source/zombie# gcc init.c -o init
/source/zombie# ./init
I am first child process 11305
first child 11305 is exited
/source/zombie# I am second child process 11306, parent process 1
^C

如上,可以看出,第一个子进程发送的信号被父进程处理后,安全退出。在第一个子进程中创建的子进程成为孤儿进程,被init进程收养,成功解决僵尸进程的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值