waitpid() 函数返回 ERROR (-1),为什么?

问题描述

我正在用 C 编写一个类似 Linux shell 的程序。

除其他外,我正在实施两个内置命令:jobs,history.jobs 中,我打印当前工作命令的列表(在后台)。 在 history 中,我打印了迄今为止所有命令历史记录的列表,为每个命令指定它是 RUNNING 还是 DONE。

为了实现这两者,我的想法是有一个命令列表,将命令名称映射到它们的 PID。调用作业/历史命令后,我会遍历它们,检查哪些正在运行或已完成,然后进行相应的打印。

我在网上读到函数waitpid(pid,&status,WNOHANG),可以从“PID”检测进程是否仍在运行或完成,而无需停止进程。 它运行良好,除了这个:

当程序处于活动状态时,函数会返回它。 当程序完成时,我第一次调用它返回完成,从那时起,如果再次使用相同的 PID 调用,它返回 -1(错误)。

例如,它看起来像这样:(& 象征背景命令)

$ sleep 3 &
$ jobs
sleep ALIVE 
$ jobs  (withing the 3 seconds)
sleep ALIVE
$ jobs (after 3 seconds)
sleep DONE
$ jobs 
sleep ERROR
$ jobs 
sleep ERROR
....

此外,这些不受我之前或之后可能执行的其他命令调用的影响,似乎上述行为与其他命令无关。

我在网上阅读了 waitpid 可能返回 -1 的各种原因,但在我的案例中我无法确定原因。此外,我尝试寻找如何理解 waitpid 错误类型,但还是没有成功。

我的问题是:

  1. 您认为为什么会发生这种行为
  2. 如果您有解决方案(理想的情况是让它一直返回 DONE)
  3. 如果您对如何实施 jobs/history 命令有更好的了解,请接受

这个问题的一个解决方案是,一旦我得到“DONE”,我就将命令签名为 DONE,并且在打印之前不再对其执行 waitid。这将解决问题,但我仍然不知道为什么会发生这种情况

解决方法

您应该熟悉在 Unix 环境中如何处理子进程。特别是阅读Zombie processes

当一个进程死亡时,它会进入“僵尸”状态,因此它的 PID 仍然保留并且唯一标识现在死亡的进程。僵尸进程上的成功 wait 会释放进程描述符及其 PID。因此,在同一 PID 上对 wait 的后续调用将失败,因为没有更多进程具有该 PID(除非为新进程分配了相同的 PID,在这种情况下等待它会是一个逻辑错误)。

您应该重构您的程序,以便如果 wait 成功并报告进程为 DONE,您将该信息记录在您自己的数据结构中,并且永远不要调用 wait再次PID。

为了比较,一旦一个进程完成,bourne shell 最后一次报告它,然后将它从作业列表中删除:

$ sleep 10 &
$ jobs
[1] + Running                 sleep 10
$ jobs
[1] + Running                 sleep 10
$ jobs
[1]   Done                    sleep 10
$ jobs
$