Linux,waitpid,WNOHANG和僵尸

时间:2012-11-20 21:24:42

标签: c++ c fork wait waitpid

我需要能够:

  1. 分叉一个进程并使其成为execvp(我做到了)
  2. 检查子进程execvp是否成功(不知道如何)
  3. 检查子进程是否已完成(有问题)
  4. 我正在寻求一个过程,我无论如何都无法检查孩子的execvp是否有效。如果失败了,我需要知道它失败了。目前我正在使用

    -1 != waitpid( pid, &status, WNOHANG )
    

    但似乎如果pid进程的execv失败,waitpid不会返回-1。

    我该怎么检查?我读了waitpid手册页,但我不清楚;也许我的英语不够好。

    编辑:为了解释更多:
     我正在为家庭作业建立自己的终端。我需要输入一个命令字符串,让我们说“ls”,然后我必须执行命令 在子forks之后,子进程调用execvp来执行命令(在我解析字符串之后),并且父进程需要检查是否有'&'在命令的最后是否。
    如果标志'&'在命令末尾不存在,那么父母需要等待孩子执行。

    所以我需要知道execvp是否失败。如果它没有失败,则父使用waitpid等待子进程完成执行。如果失败,那么父母将不会等待孩子。

2 个答案:

答案 0 :(得分:5)

#2的常见解决方案是在fork()之前打开一个管道,然后在exec之后的子代中写入它。在父母中,成功阅读意味着执行失败;不成功的读取意味着exec成功并且写入从未发生过。

// ignoring all errors except from execvp...
int execpipe[2];
pipe(execpipe);
fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC);
if(fork() == 0)
{
    close(execpipe[0]);
    execvp(...); // on success, never returns
    write(execpipe[1], &errno, sizeof(errno));
    // doesn't matter what you exit with
    _exit(0);
}
else
{
    close(execpipe[1]);
    int childErrno;
    if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno))
    {
        // exec failed, now we have the child's errno value
        // e.g. ENOENT
    }
}

这让父母可以明确地知道exec是否成功,并且作为副产品,如果不成功则是errno值。

如果exec成功,子进程可能仍然会失败并带有退出代码,并且使用WEXITSTATUS宏检查状态也会给出这个条件。

注意:使用waitpid标记调用WNOHANG是非阻塞的,您可能需要轮询该过程,直到返回有效的pid为止。

答案 1 :(得分:4)

exec调用如果成功则不应该返回,因为它将当前进程映像替换为另一个,所以如果这样做则意味着发生了错误:

execvp(...);
/* exec failed and you should exit the
   child process here with an error */
exit(errno);

要让父进程知道exec是否失败,您应该阅读子进程的状态:

waitpid(pid, &status, WNOHANG);

然后使用手册页中的WEXITSTATUS(status)宏:

  

WEXITSTATUS(status)返回子项的退出状态。 :此   由状态参数的最低有效8位组成   在对exit(3)或_exit(2)的调用中指定的子项或作为main()

中的return语句的参数

注意最后一个语句表示如果exec成功并运行命令,您将获得该命令的main()函数的退出状态,换句话说您无法可靠地告诉失败的exec和失败的命令之间的区别,所以这取决于这对您是否重要。

另一个问题:

  

如果标志'&'在命令结束时不存在   父母需要等待孩子执行。

您需要在程序中的某个时刻调用子进程的wait(),无论&是什么,以避免将子进程留在 zombie 状态,< / p>

注意:使用WNOHANG时,如果没有进程更改其状态,即waitpid()将立即返回,即它不会阻塞,我认为您知道,否则使用{{1}或者将wait()作为主循环的一部分。