zoukankan      html  css  js  c++  java
  • 【linux】——linux僵死进程的产生与避免

    一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁, 而是留下一个称为僵死进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵死进程,并不能将其完全销毁)。

    一、僵死进程的产生
        在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等,但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the 
    termination status of the process,运行时间the amount of CPU time taken by the process等), 直到父进程通过wait/waitpid来取时才释放。此时该进程处于僵死状态,该进程成为僵死进程(Zombie Process)。 这保证了父进程可以获取到子进程结束时的状态信息。
        在Linux进程的状态中,僵死进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵死进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵死状态,如果这时父进程结束了,僵死的子进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵死进程,它产生的所有僵死进程也跟着消失(每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init来接管他,成为他的父进程)。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵死状态,这就是为什么系统中有时会有很多的僵死进程。怎么查看僵死进程,利用命令ps,可以看到有标记为Z的进程就是僵死进程。

    二、僵死进程的危害
        如果父进程不调用wait/waitpid的话, 那么保留的那段信息就不会释放,其进程号会一定被占用,但是系统所能使用的进程号是有限的,如果产生了大量的僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。 

    三、僵死进程的避免 
    1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起 
    2、如果父进程很忙,那么可以用signal函数为SIGCHLD安装信号处理函数。子进程结束后,父进程会收到该信号,可以在信号处理函数中调用wait回收 。
    3、如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程
    发送信号。
    或用sigaction函数为SIGCHLD设置SA_NOCLDWAIT,这样子进程结束后,就不会进入僵死状态
     struct sigaction sa; 
     sa.sa_handler = SIG_IGN; 
     sa.sa_flags = SA_NOCLDWAIT; 
     sigemptyset(&sa.sa_mask); 
     sigaction(SIGCHLD, &sa, NULL); 
    4、fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要父进程来做。

     1  int nStatus;
     2     pid_t pid;
     3     
     4     pid = vfork();            //生成子进程
     5     if (pid > 0)            //父进程
     6     {
     7         waitpid(pid, &nStatus, 0);    //等待子进程结束,否则子进程会成为僵死进程,一直存在,即便子进程已结束执行
     8     }
     9     else if (0 == pid)        //子进程
    10     {
    11         pid = vfork();        //生成孙进程
    12         if (pid > 0) 
    13         {
    14             exit(0);        //子进程退出,孙进程过继给init进程,其退出状态也由init进程处理,与原有父进程无关
    15         }
    16         else if (0 == pid)    //孙进程
    17         {
    18             if (execlp("ls", "ls", NULL) < 0)
    19             {
    20                 perror("execlp");
    21                 exit(-1);
    22             }
    23         }
    24         else
    25         { 
    26             perror("vfork(child)"); 
    27         } 
    28     }
    29     else
    30     { 
    31         perror("vfork(parent)"); 
    32     } 
    33 }
  • 相关阅读:
    Linq的一些常见Demo
    有一名员工发现日历已经7天没有翻了,于是他连着翻了7页,7天的总和刚好是138,问这一天是几号?
    20块钱,1块钱1瓶,两个空瓶子可以换一瓶,问最多可以喝几瓶?
    【转】Java编程之字符集问题研究
    Reset / Validate Buffer
    Article Master Data Deviation
    STAD Parameters
    Linux11.2 MySQL常用命令
    Linux11.1 设置更改Mysql的root密码及连接mysql
    Linux5.10 告警系统
  • 原文地址:https://www.cnblogs.com/ngnetboy/p/3530491.html
Copyright © 2011-2022 走看看