问题描述
我正在用 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
错误类型,但还是没有成功。
我的问题是:
- 您认为为什么会发生这种行为
- 如果您有解决方案(理想的情况是让它一直返回 DONE)
- 如果您对如何实施 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
$