【发布时间】:2012-12-14 00:30:59
【问题描述】:
我的主进程产生一个子进程。如果主进程被杀死,子进程的 ppid 将被分配为 1。当子进程退出时,它将变成僵尸,因为 init 没有在这个子进程上调用 wait()。有没有办法避免这种情况?
【问题讨论】:
-
是父进程等待失败造成的,不是父进程提前退出造成的。对给定的分析器绝对正确 +1
我的主进程产生一个子进程。如果主进程被杀死,子进程的 ppid 将被分配为 1。当子进程退出时,它将变成僵尸,因为 init 没有在这个子进程上调用 wait()。有没有办法避免这种情况?
【问题讨论】:
init 将在它继承的进程上调用wait()。僵尸应该只存在于孩子已经退出但父母仍在附近但尚未获得退出代码的地方。来自init 联机帮助页:
init是系统上所有进程的父进程,由内核执行,负责启动所有其他进程;它是其亲生父母已经死亡的所有进程的父进程,它负责在他们死后收获这些进程。
你应该区分孤儿(他们还活着,他们的父母已经死了,因此他们被init收养了)和僵尸(他们已经死了,但他们的父母还活着,但还没有收获他们)。
孤儿在他们退出后但在init 收割它们之前会在很短的时间内变成僵尸,但这段时间应该足够小以至于没有人注意到。事实上,所有退出进程(可能init 本身除外)都会经历这个短暂的僵尸阶段,只有在父进程收割速度不够快的情况下才会引起您的注意。
init 儿童死亡 (SIGCHLD) 处理程序中的实际代码是这样的(我的 cmets):
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);
}
}
}
}
【讨论】:
在程序的最后放置一个 wait(NULL) 语句,这样父子进程就会互相等待,直到它们都完成执行,并且任何人都不会成为僵尸进程。
【讨论】: