`
脸同学
  • 浏览: 38682 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

怎样避免产生僵尸进程

阅读更多

1.什么是僵尸进程?
In UNIX System terminology, a process that has terminated,but whose

parent has not yet waited for it, is called a zombie.

在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,

那么他将变成一个僵尸进程.

但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程,

因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,

看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init

来接管他,成为他的父进程,从而保证每个进程都会有一个父进程.

而Init进程会自动wait 其子进程,因此被Init接管的所有进程都不会变成僵尸进程.

2. 僵尸进程的危害
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程

到底什么时候结束. 那么不会因为父进程太忙来不及waid子进程,或者说不知道

子进程什么时候结束,而丢失子进程结束时的状态信息呢?

不会.因为UNIX提供了一种机制可以保证 只要父进程想知道子进程结束时的状态信息,

就可以得到. 这种机制就是:

在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等.

但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination

status of the process,运行时间the amount of CPU time taken by the process等),

直到父进程通过wait / waitpid来取时才释放.

但这样就导致了问题,如果你进程不调用wait / waitpid的话, 那么保留的那段信息就不会

释放,其进程号就会一定被占用,但是系统所能使用的进程号是有限的,如果大量的产生

僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.

此即为僵尸进程的危害,应当避免.

3.僵尸进程的避免
1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起

2. 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,

父进程会收到该信号,可以在handler中调用wait回收


3. 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN)

通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,

并不再给父进程发送信号

4. 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一

个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收

还要自己做。 下面就是Stevens给的采用两次folk避免僵尸进程的示例.
Example
Recall our discussion in Section 8.5 about zombie processes. If we want to write a process so that it forks a child but we don't want to wait for the child to complete and we don't want the child to become a zombie until we terminate, the trick is to call fork twice. The program in Figure 8.8 does this.

We call sleep in the second child to ensure that the first child terminates before printing the parent process ID. After a fork, either the parent or the child can continue executing; we never know which will resume execution first. If we didn't put the second child to sleep, and if it resumed execution after the fork before its parent, the parent process ID that it printed would be that of its parent, not process ID 1.[code]
#include "apue.h"
#include <sys/wait.h>

int
main(void)
...{
     pid_t    pid;

    if ((pid = fork()) < 0) ...{
         err_sys("fork error");
     } else if (pid == 0) ...{     /**//* first child */
        if ((pid = fork()) < 0)
             err_sys("fork error");
        else if (pid > 0)
             exit(0);    /**//* parent from second fork == first child */
        /**//*
          * We're the second child; our parent becomes init as soon
          * as our real parent calls exit() in the statement above.
          * Here's where we'd continue executing, knowing that when
          * we're done, init will reap our status.
         */
         sleep(2);
         printf("second child, parent pid = %d ", getppid());
         exit(0);
     }
   
    if (waitpid(pid, NULL, 0) != pid)  /**//* wait for first child */
         err_sys("waitpid error");

    /**//*
      * We're the parent (the original process); we continue executing,
      * knowing that we're not the parent of the second child.
     */
     exit(0);
}

[/code]

duanjigang



根据理论我们做个测试的例子。
子进程要执行的程序test_prog[code]
//test.c
#include <stdio.h>
int main()
{
        int i = 0;
        for (i = 0 ; i < 10; i++)
        {
                printf ("child time %d\n", i+1);
                sleep (1);
        }
        return 0;
}
[/code]父进程father的代码father.c[code]
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
        int pid = fork ();
        if (pid == 0)
        {
                system ("./test_prog");
                _exit (0);
        }else
        {
                int i = 0;
                /*
                                int status = 0;
                while (!waitpid(pid, &status, WNOHANG))
                {
                        printf ("father waiting%d\n", ++i);
                        sleep (1);
                }*/
                while (1)
                {
                        printf ("father waiting over%d\n", ++i);
                        sleep (1);
                }
                return 0;
        }

}
[/code]执行./father,当子进程退出后,由于父进程没有对它的退出进行关注,会出现僵尸进程[code]
20786 pts/0    00:00:00 father
20787 pts/0    00:00:00 father <defunct>
[/code]

duanjigang

如果你把上面father.c中的waitpid的注释打开,会出现如下现象。
子进程退出的一瞬间,父进程在sleep还未调用waitpid,这时会出现一个僵尸进程,但是马上这个僵尸进程就消失了。因为waitpid调用,子进程的资源的到释放了。
这时ps就只能看到一个正常的father进程了

duanjigang

另外你还可以让子进程多跑一会儿,父进程立马退出,就能看到子进程的父进程pid为1了,也就是被init进程接管了的现象,这些都能一一验证。
或者两次fork(),也就能看到对应的解释了。

分享到:
评论

相关推荐

    僵尸进程的产生和如何避免

    僵尸进程的产生和如何避免僵尸进程的产生和如何避免僵尸进程的产生和如何避免僵尸进程的产生和如何避免

    Linux 僵尸进程产生原因及解决方法

    Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止。如果子进程已经终止,那么,...

    dumb-init完美处理docker进程,解决僵尸进程

    dumb-init 跨框架init初始化进程,有效回收僵尸进程

    详解linux下避免僵尸进程的几种方法

    我们可以使用如下几种方法避免僵尸进程的产生: 1.在fork后调用wait/waitpid函数取得子进程退出状态。 2.调用fork两次(第一次调用产生一个子进程,第二次调用fork是在第一个子进程中调用,同时将父进程退出(第一...

    python僵尸进程产生的原因

    主要给大家讲解的是在Python中是如何产生僵尸进程的,以及如何清除僵尸进程的方法,有需要的小伙伴可以参考下

    谈谈守护进程和僵尸进程

    04年时维护的第一个商业服务就用了两次fork产生守护进程的做法,前两天在网上看到许多帖子以及一些unix书籍,认为一次fork后产生守护进程足够了,各有道理吧,不过多了一次fork到底是出于什么目的呢?

    PHP多进程编程之僵尸进程问题的理解

    PHP多进程编程之僵尸进程问题的理解 使用pcntl_fork函数可以让PHP实现多进程并发或者异步处理的效果:https://www.jb51.net/article/125789.htm 那么问题是我们产生的进程需要去控制,而不能置之不理。最基本的方式...

    基于树突细胞算法的P2P僵尸程序检测

    用于检测P2P僵尸程序的原始数据通过APItrace工具获得,进程(包括正常进程和僵尸程序进程)的ID被映射为"抗原",进程所产生的行为数据被映射为"信号",它们组成了检测算法的时间序列输入数据并用于数据融合和相互关联。...

    python多线程DAY01.txt

    如何避免僵尸进程产生 * 处理子进程退出状态 pid,status = os.wait() 功能 :在父进程中阻塞等待处理子进程退出 返回值: pid 退出的子进程的PID号 status 获取子进程退出状态 pid,sta

    php多进程中的阻塞与非阻塞操作实例分析

    子进程退出后,父进程没有及时回收,就会产生僵尸进程。 例1: &lt;?php define('FORK_NUMS', 5); $pids = array(); //我们创建5个子进程 for($i = 0; $i &lt; FORK_NUMS; ++$i) { $pids[$i] = pcntl_fork(); if...

    Chrombie:使用Puppeteer API的无头Chrome僵尸

    关于这是一个基于Debian的Docker容器,其中包含Chrome(NOT Chromium)和Node.js,用于运行Puppeteer脚本/测试。为什么会存在? 有几个项目在无头浏览器中打开URL,但是该项目旨在提供一些额外的功能,同时保持其...

    linux系统编程之线程.zip

    进程若有该机制,将不会产生僵尸进程。僵尸进程的产生主要由于进程死后,大部分资源被释放,一点残留资源仍存于系统中,导致内核认为该进程仍存在。 也可使用 pthread_create函数参2(线程属性)来设置线程分离。 ...

    dorothy2:一个用 Ruby 编写的恶意软件僵尸网络分析框架

    此外,它能够通过将新产生的进程与之前创建的基线进行比较来识别它们。 静态二进制分析和改进的系统行为分析将在下一版本中介绍。 Dorothy2 通过使用预先配置的分析配置文件来分析二进制文件。 分析概要文件由以下...

    tini:一个很小但有效的容器初始化

    它可以保护您免受意外创建僵尸进程的软件的侵害,因为僵尸进程可能(随着时间的推移!)使整个系统缺少PID(并使其无法使用)。 它可确保默认信号处理程序适用于您在Docker映像中运行的软件。 例如,对于Tini,...

    Makehttpd:制作一个http服务器,仅供学习

    Makehttpd ...完成步骤: 1、socket demo ...3、添加响应报文 4、解析请求报文 ...9、增加信号处理,防止僵尸进程产生 10、增加200 OK的响应头 11、使用多线程实现并发(server_10.c) 12、使用epoll处理IO(server11.c)

    async-process:异步接口,用于处理流程

    首次使用时会延迟创建一个名为“ async-process”的后台线程,该线程等待生成的子进程退出,然后调用wait() syscall来清理“僵尸”进程。 这与标准库中的process API不同,在标准库中,删除正在运行的Child泄漏其...

    TCP多人聊天.zip

    个TCP吐槽聊天室,接收来自各方TCP客户端的吐槽信息,并将信息直接输出到屏幕上,要求: 能随时接收任何客户端的连接和信息。 吐槽不能太快,建立一定的防刷屏...如果使用多线程或者多进程,注意不要产生大量僵尸。

    Linux程序设计 第4版.haozip01

    11.3.2 僵尸进程 401 11.3.3 输入和输出重定向 403 11.3.4 线程 404 11.4 信号 404 11.4.1 发送信号 408 11.4.2 信号集 412 11.5 小结 415 第12章 posix线程 416 12.1 什么是线程 416 12.2 线程的优点和...

Global site tag (gtag.js) - Google Analytics