【问题标题】:why does this program create a zombie process and how do I fix it?为什么这个程序会创建一个僵尸进程,我该如何解决?
【发布时间】:2013-06-06 16:46:11
【问题描述】:

我的问题是我无法理解为什么这些代码仍然会生成僵尸进程。我该如何解决?谢谢。

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


int count = 0;

/**
 * signal  callback
 * @param signo
 */
void sigcallback(int signo) {
    short stime = 5;
    int status;
    wait(&status);
    while (1) {

        printf("wake up,%d\n", getpid());
        sleep((stime + 1) - (short) (time(NULL) % stime));

    }
}

int call_cmd(void) {
    count++;

    int i;
    pid_t pid;
    signal(SIGCHLD, sigcallback);
    for (i = 0; i < 3; i++) {
        if ((pid = fork()) < 0) {
            printf("error with fork\n");
            exit(1);
        } else if (pid > 0) {
            waitpid(pid, NULL, WNOHANG);
        } else {
            printf("\t\t\tsid is %d,count is %d,pid is %d\n", getsid(getpid()), count, getpid());
        }
    }
    return 1;
}

void notice_root(int signo) {
    printf("execut root signal\n");
    waitpid(0, NULL, 0);
}

int main(void) {
    pid_t pid;

    int i;
    signal(SIGCHLD, notice_root);
    for (i = 0; i < 2; i++) {
        if ((pid = fork()) < 0) {
            printf("error with fork\n");
            exit(1);
        } else if (pid > 0) {
            exit(0);
        } else {
            setsid();
            call_cmd();
        }
    }
    return 0;
}

【问题讨论】:

  • 从 main() 调用的父 pid 只是简单地退出,这意味着它分叉的子进程将没有父进程,因此退出时无法获得。
  • @grijesh:仅在 call_cmd() 函数中。 main() 中有一个没有等待的 fork。
  • @MarcB 是的,你是对的,我在你发表评论后再次注意到。
  • 从信号处理程序调用printf() 是不安全的。

标签: c linux zombie-process


【解决方案1】:

您的信号处理程序sigcallback 中有一个无限睡眠循环。一旦该函数处理了一个信号,该进程将永远不会再处理另一个SIGCHLD 信号,因为信号处理程序永远不会返回。这可能是你的僵尸的原因。

您不会为每个退出的孩子传递一个信号。如果两个孩子“同时”退出,您只会收到一个SIGCHLD。因此,当您在信号处理程序(其中任何一个)中等待时,您应该循环等待以获取所有已退出的子节点。大致如下:

for (;;) {
    pid_t p = waitpid(-1, 0, WNOHANG);
    if (p <= 0) break;
    /* ... */
}

当您的进程退出时,该进程的子进程将被 init 进程继承,因此它们将被正确地回收。但是,您的无限循环阻止了 3 件事:

  • 它不会同时收获退出的孩子。
  • 在收获第一个之后,它不会再收获任何孩子。
  • 它不允许进程退出,因此子进程无法被 init 进程收割。

【讨论】:

  • 嗨,你的解释很清楚。非常感谢。还有一件事,我们应该在第一级孩子处“退出”。僵尸进程都消失了。我认为那是因为当我们处于无限循环中时。父 waitpid(pid, NULL, WNOHANG); 永远不会执行。
  • @9nix00:正确。正如我所提到的,无限循环会阻止您的进程退出。只有当父进程设法执行waitpid() 行,让它返回0(意味着子进程没有死),并且父进程在子进程死亡之前死亡,您的代码才会起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-30
  • 2019-10-22
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 2018-08-12
相关资源
最近更新 更多