【问题标题】:waitpid(pid, status, options) not always setting statuswaitpid(pid, status, options) 并不总是设置状态
【发布时间】:2013-05-13 16:03:18
【问题描述】:

我正在为一项任务复制外壳管道。我让管道全部工作(并且没有更改管道代码,因此已知它可以工作),但仍然需要在管道的某个步骤中发生执行失败的情况下终止管道。在实现该代码的某个时刻,管道开始出现意外行为。

长度为 2 的管道可以正常工作。 长度大于 2 的管道会以下列方式失败:

  1. 管道中的第一个命令执行
  2. 紧接着 - 通常在第一个命令完成执行之前 - 管道中的 final 命令终止
  3. 然后第二个命令将挂起,显然没有收到来自第一个命令的输入。

这是使用waitpid的部分代码(无调试代码):

for (int i = 0 ; i < num_cmds ; i++) {
  if (waitpid(cmd_pids[i], &cmd_status[i], NULL) != cmd_pids[i]) {
     printf("Error waiting for command %s\n", cmds[i]);
     perror("Error");
  }
  if ((cmd_status[i] > 0) && (cmd_status[i-1] == 0)) {
     killPipeline(SIGINT); //Kill pipeline; something failed
  }
  fflush(logfp);

}

现在这就是为什么我认为 waitpid 部分应该受到指责的症结所在:由于分配需要一些奇怪的控制流,我发现将 cmd_status 的值初始化为 -2 很方便。日志功能将子进程的返回状态打印到日志中,文件说第二个函数的退出状态为-2,这当然意味着它没有设置程序的存在状态。管道似乎根本没有等待这些程序执行。

输入“ls | grep pipe | wc”

输出:

You entered : list of pipe commands  ls | grep pipe | wc
Creating process ls
Creating process grep pipe
Creating process wc
Waiting for command ls
      0       0       0 //wc received no input, but somehow an EOF
Command ls finished
Waiting for command grep
^C  //Terminate grep because it's waiting for input from a terminated process
Error waiting for command grep
Error: Interrupted system call
Command grep finished
Waiting for command wc
Command wc finished

【问题讨论】:

  • 我认为在您的第二个 if 语句中, cmd_status[i-1] != 0 因为 0 表示这些进程尚未完成(更改状态)。

标签: c system-calls waitpid


【解决方案1】:

要从waitpid() 返回的值中提取通过进程传递给exit() 的状态,请使用宏WEXITSTATUS()

waitpid() 的失败测试也是错误的。 waitpid() return -1 on failure and in this case seterrno, which then would make sense to be interpreted by a call toperror()`。

perror() 读出errno 的值。因此,仅当已知已设置 errno 时,调用 perror() 才有意义。对perror("Error") 的调用不是这种情况,我在修改您的代码时删除了,如下所示。

for (int i = 0 ; i < num_cmds ; i++) 
{
  int status = -1;
  int result = waitpid(cmd_pids[i], &status, NULL);
  if (-1 == result)
  {
    perror("waitpid()"); /* added call to perror() here as errno is know to have been set */
  }
  else
  { 
    if (result != cmd_pids[i]) 
    {
      fprintf(stderr, "Error waiting for command '%s'\n", cmds[i]);
      /* removed call to perror() here as errno had not been set here. As this however should
         log an error, I modded the code to printf to stderr, but stdout. */
    }
    else
    {
      cmd_status[i] = WEXITSTATUS(status);

      if ((cmd_status[i] > 0) && ((0 == i) || (cmd_status[i-1] == 0))) /* This mod keeps   
        the app from dereferencing cmd_status in case i == 0, as this would lead to 
        cmd_status[-1], which is undefined beaviour. */
      {
        killPipeline(SIGINT); //Kill pipeline; something failed
      }
    }
  }
  ...

【讨论】:

  • 虽然我看到这里有很多小问题,我很感激您指出了这些问题,但这些更改(我已经实现)都没有真正解决奇怪的行为。
  • 实际上,我尝试从之前的提交开始并重新实现管道更改以重新开始。同样的行为,所以我不能再保证管道代码是正确的。
【解决方案2】:

向所有试图找出 waitpid 问题的人道歉,因为它们只是表面上的错误。实际的错误确实存在在我的管道代码中,我显然在此过程中破坏了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多