Linux中waitpid函数和WNOHANG选项的使用,如何处理子进程的僵尸状态。

5

我将我的程序作为守护进程运行。

父进程只等待子进程,当意外死亡时,再次fork和等待。

for (; 1;) {
  if (fork() == 0) break;
  int sig = 0;
  for (; 1; usleep(10000)) {
    pid_t wpid = waitpid(g->pid[1], &sig, WNOHANG);
    if (wpid > 0) break;
    if (wpid < 0) print("wait error: %s\n", strerror(errno));
  }
}

但是当子进程被使用-9信号杀死时,它将变成僵尸进程。

waitpid应该立即返回子进程的pid!
但是waitpid需要大约90秒后才能获得pid号码。

cube     28139  0.0  0.0  70576   900 ?        Ss   04:24   0:07 ./daemon -d
cube     28140  9.3  0.0      0     0 ?        Zl   04:24 106:19 [daemon] <defunct>

这是父进程的strace

父进程未被卡住,wait4函数一直被调用。

strace -p 28139
Process 28139 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 0
wait4(28140, 0x7fff08a2681c, WNOHANG, NULL) = 0
nanosleep({0, 10000000}, NULL)          = 0
wait4(28140, 0x7fff08a2681c, WNOHANG, NULL) = 0

约90秒后,父进程收到了SIGCHILD信号,并且wait4函数返回了已经结束的子进程的pid。
--- SIGCHLD (Child exited) @ 0 (0) ---
restart_syscall(<... resuming interrupted call ...>) = 0
wait4(28140, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGKILL}], WNOHANG, NULL) = 28140

为什么子进程不能立即退出?相反,它会意外地变成僵尸进程。
3个回答

3

在使用lsof进行深度跟踪时,我最终发现存在一些文件描述符泄漏。

修复文件描述符泄漏问题后,该问题消失了。


1
在我看来,waitpid之所以没有立即返回子进程pid,很可能是因为该进程当前不可用。
此外,您似乎希望您的代码这样做,因为您使用了NOHANG选项指定了waitpid(),这可以防止阻塞,从而允许父进程继续进行,即使子进程pid不可用。
也许您的进程正在使用您没有预期的东西?您可以跟踪其活动以查找瓶颈吗?
这是一个非常有用的链接,可能会对您有所帮助: http://infohost.nmt.edu/~eweiss/222_book/222_book/0201433079/ch08lev1sec6.html

1
众所周知,当一个进程退出时,操作系统将回收所有已分配的资源:由malloc分配的内存,由open指定的文件名分配的资源,由socket指定的域分配的资源。文件名指定的回收会导致大量的OS IO,如果文件很大,则会进行大量的OS IO,并且在执行期间进程必须被转换为僵尸进程。lsof命令将显示所有已打开的文件。最后我发现我忘记在打开后关闭fd,导致了许多泄漏的fds引起了问题。 - Cube

1
你可以简单地使用:


  for (;;) {
    pid_t wpid = waitpid(-1, &sig, 0);
    if (wpid > 0) break;
    if (wpid < 0) print("wait error: %s\n", strerror(errno));
  }

而不是睡一会儿再试。


我遇到了与https://dev59.com/UHfZa4cB1Zd3GeqPQlxI#22734421相同的问题。 - Cube
这个进程实际上一直在执行有用的工作。该进程保存了对慢文件系统上大型文件的最后引用。当进程终止时,对文件的最后引用被释放,强制操作系统回收空间。由于该文件非常大,这需要数万次的I/O操作,需要10分钟甚至更长时间。 - Cube
@Cube,也许你观察到的类似现象是由于相似的原因引起的,即当你通过“ps”发现子进程处于僵尸状态时,子进程实际上还没有完成退出,内核可能正在代表该子进程执行一些有用的工作,例如刷新文件系统。 - Lee Duhem
@leeduherm,你知道有没有办法处理这样的“有用工作”,也许我可以在发送退出信号给子进程之前完成这些工作,子进程应该立即退出而不是变成僵尸状态一段时间。 - Cube
@Cube,你看到你提供的问题链接已经被回答了吗?如果你在Stack中找到了相同的问题,最好不要再重复提出问题并提供更多细节。你3月31日的问题可能更适合作为一个单独的问题。也许对你来说,更有趣的是研究更好的资源分配和进程排序,特别是为了避免不同情况下僵尸进程的出现,并提供一个更抽象的例子。 :) - Chris Marie
显示剩余2条评论

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