如何防止子进程成为僵尸进程,即使父进程已经退出?

3

我的主进程会生成一个子进程。如果主进程被杀死,子进程会被分配ppid为1。当子进程退出时,它将成为僵尸进程,因为init没有调用wait()来处理这个子进程。是否有办法避免这种情况发生?


由于父进程未等待而导致的失败,而非父进程过早退出引起的。 绝对正确,给予答案+1。 - jim mcnamara
2个回答

8

init会在继承的进程上调用wait()。僵尸进程只有在子进程退出但父进程仍然存在且尚未收割退出码时才存在。从init手册中可以看到:

init是系统上所有进程的父进程,由内核执行,负责启动所有其他进程;它是所有自然父进程已死亡并且在其死亡时需要回收这些父进程的进程的父进程。

应该区分孤儿进程(它们仍然活着,其父进程已经死亡,因此它们已被init接管)和僵尸进程(它们已经死亡,但其父进程仍然活着且尚未收割它们)。

孤儿进程在退出后会短暂地成为僵尸进程,但在init收割它们之前,这段时间应该足够短,以至于没有人会注意到。实际上,除了可能的init本身外,所有退出的进程都会经历这个短暂的僵尸阶段,只有在父进程没有快速收割时才会注意到。


init子进程死亡(SIGCHLD)处理程序中的实际代码大致如下(我的注释):

void chld_handler (int sig) {
    CHILD  *ch;
    int    pid, st;
    int    saved_errno = errno;

    while ((pid = waitpid(-1, &st, WNOHANG)) != 0) { // << WAIT done here
        if (errno == ECHILD) break;
        for (ch = family; ch; ch = ch->next) {
            if (ch->pid == pid && (ch->flags & RUNNING)) {
                INITDBG (L_VB, child_handler: marked %d as zombie", ch->pid);
                ADDSET (got_signals, SIGCHLD);
                ch->exstat = st;
                ch->flags |= ZOMBIE;                 // Set to zombie here.
                if (ch->new) {
                    ch->new->exstat = st;
                    ch->new->flags |= ZOMBIE;
                }
                break;
            }
        }
        if (ch == NULL) {
            INITDBG (L_VB, "chld_handler: unknown child %d exited.", pid);
        }
    }
    errno = saved_errno;
}

然后,在主循环中(而不是信号处理程序中),任何被标记为ZOMBIE的进程都会被清理:

if (ISMEMBER (got_signals, SIGCHLD)) {
    INITDBG(L_VB, "got SIGCHLD");
    /* First set flag to 0 */
    DELSET(got_signals, SIGCHLD);

    /* See which child this was */
    for (ch = family; ch; ch = ch->next) {
        if (ch->flags & ZOMBIE) {                    // ZOMBIE detected here
            INITDBG (L_VB, "Child died, PID= %d", ch->pid);
            ch->flags &= ~(RUNNING|ZOMBIE|WAITING);  // And cleared here.
            if (ch->process[0] != '+') {
                write_utmp_wtmp ("", ch->id, ch->pid, DEAD_PROCESS, NULL);
            }
        }
    }
}

+1,但我不同意“孤儿进程可能会变成僵尸进程”的说法。所有终止的进程都会变成僵尸进程;这是毫无疑问的。良好行为进程的子进程只会短暂地成为僵尸进程,但在它们被其父进程等待之前,它们都会变成僵尸进程。 - William Pursell
完全正确,@William,我已经更改措辞,希望能够让它更清晰明了。 - paxdiablo

0
在程序的最后加入一个wait(NULL)语句,这样父进程和子进程就会互相等待,直到它们都完成执行,并且从两者中任何一个都不会成为僵尸进程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接