waitpid - 虽然子进程正常退出但WIFEXITED返回0

4

我正在编写一个程序,它会创建一个子进程,并调用waitpid等待子进程的结束。代码如下:

  // fork & exec the child
  pid_t pid = fork();
  if (pid == -1)
    // here is error handling code that is **not** triggered

  if (!pid)
    {
      // binary_invocation is an array of the child process program and its arguments
      execv(args.binary_invocation[0], (char * const*)args.binary_invocation);
      // here is some error handling code that is **not** triggered
    }
  else
    {
      int status = 0;
      pid_t res = waitpid(pid, &status, 0);

      // here I see pid_t being a positive integer > 0
      // and status being 11, which means WIFEXITED(status) is 0.
      // this triggers a warning in my programs output.
    }
< p > waitpid 的 man 页面对于 WIFEXITED 的说明如下:

WIFEXITED(status)
    returns  true  if  the child terminated normally, that is, by calling exit(3) or
    _exit(2), or by returning from main().

我理解为函数应该返回一个非零整数表示成功,但在我的程序执行中并未发生这种情况,因为我观察到WIFEXITED(status) == 0

然而,从命令行执行同一程序会导致$? == 0,从gdb启动则结果如下:

[Inferior 1 (process 31934) exited normally]

该程序表现正常,除了触发的警告之外,这让我认为还有其他问题存在,而我自己没有意识到。
编辑:如下方评论所建议,我检查了子进程是否通过段错误终止,确实,WIFSIGNALED(status)返回1,WTERMSIG(status)返回11,即SIGSEGV。
然而,我不明白为什么通过execv调用会失败并导致段错误,而通过gdb或shell进行同样的调用却会成功?
编辑2:我的应用程序行为在很大程度上取决于子进程的行为,尤其是子进程在声明为__attribute__((destructor))的函数中写入的文件。waitpid调用返回后,这个文件存在且生成正确,这意味着段错误发生在另一个析构函数中,或者发生在我无法控制的某个地方。

2
状态11表示子进程收到了信号11,即SIGSEGV。非信号退出是传递给_exitexit或由main返回的值的低8位的256倍。如果您在像Linux这样具有strace的平台上,请使用它(带有-f标志)查看子进程是否由于对execv的错误调用或成功执行后而收到信号。 - rob mayoff
@robmayoff 您是正确的!我不知道状态变量的低字节保存了所生成进程的退出状态和信号标识。感谢您指出这一点! - Andreas Grapentin
我不明白的是,为什么通过execv调用会导致段错误。args.binary_invocation是什么样子的?它来自哪里?你创建了它吗? - Dabo
@Dabo 是的,args.binary_invocation 是一个以 NULL 结尾的 char 指针数组,包含子应用程序和其参数的名称。我已经验证该数组是正确的。 - Andreas Grapentin
1
@robmayoff,感谢您的评论,我找到了segfault原因 - 问题在于我的应用程序更改了子进程的环境,而这在我的独立测试中没有重现,这就是为什么segfault隐藏在exec环境之外的原因。我想给你点赞,因为你指导了我正确的方向。所以,如果你愿意把你的评论转换成答案,我将非常乐意接受 :) - Andreas Grapentin
1个回答

11

在 Unix 和 Linux 系统上,从 waitwaitpid(或任何其他 wait 变体)返回的状态具有以下结构:

bits   meaning

0-6    signal number that caused child to exit,
       or 0177 if child stopped / continued
       or zero if child exited without a signal

 7     1 if core dumped, else 0

8-15   low 8 bits of value passed to _exit/exit or returned by main,
       or signal that caused child to stop/continue

请注意,Posix并没有定义这些位,只是定义了宏,但至少在Linux、Mac OS X/iOS和Solaris中使用这些位的定义。此外,请注意,只有当您传递WUNTRACED标志时,waitpid才会返回停止事件,而只有当您传递WCONTINUED标志时,它才会返回继续事件。

因此,状态为11意味着子进程由于信号11而退出,该信号是SIGSEGV(再次强调,这不是Posix标准,而是惯例)。

要么您的程序向execv(其是execve或其他特定于内核的函数的C库封装)传递了无效参数,要么当您从shell或gdb运行它时,子进程运行方式与使用execv时不同。

如果您所在的系统支持strace,请在strace -f下运行(父)程序,以查看execv是否引起了信号。


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