僵尸进程

概念

在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程……

僵尸进程在系统内部的存在方式

进程调用exit,内核释放资源(打开的文件、占用的内存等)但是会保留一定的信息(进程号、退出状态、运行时间等)。父进程通过wait/waitpid获得子进程的这些状态,然后释放子进程保留的资源。

所以说极端的情况下,出现了大量的僵尸进程,那么进程号会被大量的占用,当系统所有进程号都被僵尸进程占用时,就无法创建新的进程了。

产生僵尸进程

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

int main(void)
{
        pid_t child;
        int n=0;

        while(n < 10) {
                child = fork();
                if(child == 0) {
                        printf("child process id %d
",getpid());
                        exit(0);
                }
                sleep(5);
                n++;
        }
        return 0;
}
View Code

执行程序:

XXXX> ./a.out 
child process id 21517
child process id 21522

查看进程状态:

XXXX> ps  -aux | grep a.out
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     21516  0.0  0.0   3844   308 pts/1    S+   20:41   0:00 ./a.out
1000     21517  0.0  0.0      0     0 pts/1    Z+   20:41   0:00 [a.out] <defunct>
1000     21522  0.0  0.0      0     0 pts/1    Z+   20:41   0:00 [a.out] <defunct>

如何避免产生僵尸进程

1.父进程通过waitwaitpid等函数等待子进程结束,这会导致父进程挂起。

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

int main(void)
{
        pid_t child;

        child = fork();
        if(child == 0) {
                printf("child process id %d
",getpid());
                exit(0);
        }
        else if (child > 0) {
                sleep(10);
                wait();
                printf("father process. wait done.
");
                sleep(10);
        }
        else
                printf("fork() error
");
        return 0;
}
View Code

执行程序以及使用ps后的结果:

XXXX> ./a.out 
child process id 32541
XXXX> ps u
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     32540  0.0  0.0   3844   304 pts/2    S+   00:17   0:00 ./a.out
1000     32541  0.0  0.0      0     0 pts/2    Z+   00:17   0:00 [a.out] <defunct>

等待父进程执行wait后:

father process. wait done.
XXXX> ps u
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1000     32540  0.0  0.0   3848   304 pts/2    S+   00:17   0:00 ./a.out

父进程调用wait,替子进程收尸,处理了僵尸进程

2.如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。

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

static void sig_chld(int);

int main(void)
{
        pid_t child;

        signal(SIGCHLD, sig_chld);

        child = fork();
        if(child == 0) {
                printf("child process id %d
",getpid());
                exit(0);
        }
        else if(child < 0)
                printf("fork() error
");

        while(1);
        return 0;
}

void sig_chld(int signo)
{
        printf("func sig_chld() before wait()
");
        sleep(10);
        wait();
        printf("func sig_chld() after wait()
");
        sleep(10);
}
View Code

可以分别在信号处理函数执行wait前和执行wait后使用ps查看是否产生僵尸进程

3.如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号。

4.还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做

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

int main(void)
{
        pid_t child,grdson;

        if((child=fork()) == 0) {
                printf("child process id %d
",getpid());
                if((grdson=fork())==0) {
                        printf("grdson process id %d
", getpid());
                        sleep(10);
                        exit(0);
                }
                else if(grdson > 0) {
                        exit(0);
                }
                else {
                        printf("fork grdson error.
");
                        exit(0);
                }

        }
        else if (child > 0) {
                wait();
        }
        else
                printf("fork() error
");

        sleep(60);
        return 0;
}
View Code

执行以及查看结果:

XXXX> ./a.out 
child process id 13479
grdson process id 13480
XXXX> ps -ef | grep a.out
1000     13478 19733  0 00:41 pts/2    00:00:00 ./a.out
1000     13480     1  0 00:41 pts/2    00:00:00 ./a.out
1000     13482 25700  0 00:41 pts/0    00:00:00 grep a.out

孙子进程13480init进程领养了

原文地址:https://www.cnblogs.com/yanxin880526/p/4705228.html