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

    概念

    在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进程领养了

  • 相关阅读:
    C#进阶之路——10.C# 接口
    C#进阶之路——9.C# 抽象类
    C#进阶之路——8.C# 继承
    C#进阶之路——7.ASP.NET常用控件
    C#进阶之路——6.C#字符与字符串
    C#进阶之路——5.C#数组与集合
    C#进阶之路——4.C#类属性和方法
    C#进阶之路——3.C#应用程序编译与执行
    mongodb
    Mycat配置文件详解
  • 原文地址:https://www.cnblogs.com/yanxin880526/p/4705228.html
Copyright © 2011-2022 走看看