6

我需要能够:

  1. fork 一个进程并使其成为 execvp(我这样做了)
  2. 检查子进程execvp是否成功(不知道如何)
  3. 检查子进程是否完成(有问题)

我正在分叉一个进程,我没有任何方法可以检查孩子的 execvp 是否有效。如果它失败了,我需要能够知道它失败了。目前我正在使用

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

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

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

编辑:为了解释更多:
我正在为家庭作业构建自己的终端。我需要输入一个命令字符串,比如说“ls”,然后我必须执行命令。
孩子分叉后,孩子调用 execvp 以执行命令(在我解析字符串之后),父母需要检查命令末尾是否有'&'。
如果命令末尾不存在符号“&”,则父级需要等待子级执行。

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

4

2 回答 2

6

#2 的一个常见解决方案是在 fork() 之前打开一个管道,然后在 exec 之后的子进程中写入它。在父级中,成功读取意味着 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。

于 2012-11-20T21:37:25.090 回答
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()在程序中的某个时刻调用子进程,无论&避免使子进程处于僵尸状态,

注意:当您使用它时,WNOHANG它意味着waitpid()如果没有进程更改其状态,它将立即返回,即它不会阻塞,我假设您知道这一点,否则使用wait()或调用waitpid()作为主循环的一部分。

于 2012-11-20T21:32:37.987 回答