【问题标题】:waitpid returns ECHILD - but pid was validwaitpid 返回 ECHILD - 但 pid 有效
【发布时间】:2013-04-12 20:42:57
【问题描述】:

我有一个使用 execve 生成​​其他进程的程序:

  s32 ret = execve( argv[0], argv.data(), (char* const*) req.posixEnv() );

稍后在循环中我调用 waitpid 来观察进程何时终止:

while( 1 )
{
  readOutputFromChildProcess( pid );

  int status;
  s32 retPid = waitpid( pid, &status, WNOHANG );

  if ( retPid < 0 )
  {
     if ( errno == ECHILD )
     {
         // I don't expect to ever get this error - but I do. why?
         printf( "Process gone before previous wait. Return status lost.\n" );
         assert(0); 
     } else {
         // other real errors handled here.
         handleError();
         break;
     }
  }

  if ( retPid == 0 )
  {
     waitSomeTime();
     continue; 
  }

  processValidResults( status );
  break;
}

我已经大大简化了代码。我的理解是,一旦你产生了一个进程,进程表条目就会一直存在,直到调用者调用“waitpid”并获得大于零的返回值和有效的返回状态。

但在某些情况下似乎发生的是进程自行终止,当我调用 waitpid 时,它返回 -1,错误为 ECHILD

ECHILD 表示在我调用 waitpid 时,进程表中没有具有该 ID 的进程。所以要么我的 pid 无效 - 而且我已经仔细检查过 - 它是有效的。

或者 - 在这个过程完成后已经调用了 waitpid - 在这种情况下,我无法从这个过程中获取返回码。

程序是多线程的。我也检查过我没有太早调用waitpid。它发生在几次“等待”之后。

有没有其他方法可以在不调用 waitpid 的情况下清理进程表条目?我怎样才能确保我总是得到返回码?

@明确忽略 SIGCHLD:

好的,所以我知道明确忽略它会导致 waitpid() 失败。我没有明确地忽略它,但我确实设置了一些信号处理程序来捕获另一个地方的崩溃,如下所示:

void kxHandleCrashes()
{
   struct sigaction sa;
   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = abortHandler;
   sigemptyset( &sa.sa_mask );

   sigaction( SIGABRT, &sa, NULL );
   sigaction( SIGSEGV, &sa, NULL );
   sigaction( SIGBUS,  &sa, NULL );
   sigaction( SIGILL,  &sa, NULL );
   sigaction( SIGFPE,  &sa, NULL );
   sigaction( SIGPIPE, &sa, NULL );

   // Should I add aline like this:
   // sigaction( SIGCHLD, &sa, NULL );
}

【问题讨论】:

  • 它发生在几次“等待”之后你是否试图在同一个进程上等待多次?
  • 是的。这就是为什么我用NOHANG来称呼它。我需要能够定期返回我的线程以报告进度,并在调用进程挂起时终止它。
  • 我认为你只能在一个孩子身上成功等待一次。之后内核清理进程信息,不留痕迹。
  • 仅当回报为正时。如果您使用 NOHANG 调用并获得零回报,则不应清理该过程。
  • 你可能会忽略SIGCHLD 吗?

标签: c++ c linux process posix


【解决方案1】:

我遇到了类似的问题 - waitpid 会因 ECHLD 而失败。子进程正在运行,我没有触摸 SIGCHLD 处理程序(默认处理程序到位),但每次仍然在 waitpid 上获取 ECHLD。

经过几个小时的调查,结果证明我分叉了孩子,然后妖魔化了父母(它分叉了它),这实际上把所有孩子都变成了孤儿..

我在分叉子代之前将父守护进程移到发生,一切都开始完美运行。

因此,如果您收到这个神秘的 ECHLD 错误,并且您没有弄乱 SIGCHLD 信号处理程序 - 请检查这些孩子是否实际上仍然是您的孩子,并且孩子的 PPID 等于父母的 PID。

【讨论】:

    【解决方案2】:

    您的程序示例缺少一条重要信息:您如何声明errno

    您应该确保包含errno.h

    请参考Thread-safety and POSIX.1errno的重新定义部分。

    【讨论】:

    • 嗯...我没有重新定义 errno。我会检查一下。
    • 所以我需要做的就是确保我包含 errno.h?检查了它,我已经这样做了。
    • 缺少 hte include 是个好问题,但是 errno 的重新定义是否适用于 fork?我以为它只是用于线程线程(pthread)。
    • 另外,这应该不是问题,但在某些文档中,需要在继续之前将 errno 设置为局部变量。 int localErr = errno;
    猜你喜欢
    • 2014-05-06
    • 2016-03-03
    • 2014-02-24
    • 2014-05-16
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多