【问题标题】:execvp/fork -- how to catch unsuccessful executions?execvp/fork——如何捕捉不成功的执行?
【发布时间】:2012-11-22 11:57:03
【问题描述】:

现在我正在编写一个必须执行子进程的 C 程序。我不会同时执行多个子进程或任何事情,所以这相当简单。我肯定成功地执行了内置的 shell 程序(即 cat 和 echo 之类的东西),但我还需要能够判断这些程序之一何时无法成功执行。我正在尝试使用以下简化代码:

int returnStatus; // The return status of the child process.
pid_t pid = fork();

if (pid == -1) // error with forking.
{
   // Not really important for this question.
}
else if (pid == 0) // We're in the child process.
{
   execvp(programName, programNameAndCommandsArray); // vars declared above fork().

   // If this code executes the execution has failed.
   exit(127); // This exit code was taken from a exec tutorial -- why 127?
}
else // We're in the parent process.
{
   wait(&returnStatus); // Wait for the child process to exit.

   if (returnStatus == -1) // The child process execution failed.
   {
      // Log an error of execution.
   }
}

例如,如果我尝试执行 rm fileThatDoesntExist.txt,我会认为这是一个失败,因为该文件不存在。我怎样才能做到这一点?此外,虽然 execvp() 调用成功执行了内置的 shell 程序,但它不会执行可执行文件当前目录中的程序(即运行此代码的程序);为了让它运行当前目录中的程序,我还需要做些什么吗?

谢谢!

【问题讨论】:

标签: c exec fork execvp


【解决方案1】:

这是一个非常优雅的解决方案的经典问题。在分叉之前,在父节点中创建一个pipe。在fork 之后,父级应关闭管道的写入端,并阻止从读取端尝试read。孩子应该关闭阅读端并使用fcntl为写作端设置close-on-exec标志。

现在,如果child调用execvp成功,管道的写端会关闭,没有数据,而parent中的read会返回0。如果child调用execvp失败,写错误管道中的代码,并且父级中的read 将返回非零值,已读取父级要处理的错误代码。

【讨论】:

  • 感谢您的评论,它确实让我走上了正轨。经过更多研究后,我有点意识到我只关心捕获子进程的 stderr 输出。我想我会检查 stderr 是否为空,这足以确定执行中存在问题。但是,我无法将这个想法转化为代码。它会涉及调用 dup() 吗?
【解决方案2】:

wait(2) 给你的不仅仅是子进程的退出状态。为了得到真正的退出状态,你需要使用WIFEXITED()宏来测试孩子是否正常退出(而不是通过信号等异常退出),然后使用WEXITSTATUS()宏来得到真正的退出状态:

wait(&status);
if(WIFEXITED(status))
{
    if(WEXITSTATUS(status) == 0)
    {
        // Program succeeded
    }
    else
    {
        // Program failed but exited normally
    }
}
else
{
    // Program exited abnormally
}

为了让execvp(3) 在当前目录中运行程序,您需要将当前目录添加到您的$PATH 环境中(通常不是一个好主意),或者将完整路径传递给它,例如使用./myprogram 而不仅仅是myprogram

【讨论】:

  • 这个信息都是正确的,但不能告诉你execvp是否成功。
【解决方案3】:

在故障检测方面,如果 exec() 函数将当前进程替换为新进程,则当前进程消失;执行的程序是否决定它被要求做的事情是成功还是失败并不重要。但是,fork 之前的父进程可以发现子进程的退出代码,该代码可能包含命令成功/失败信息。

在查找可执行文件方面, execvp() 复制了 shell 的操作,搜索当前路径。如果在工作目录中找不到可执行文件,则该目录可能不在搜索路径中(或者文件实际上不是可执行文件)。您可以尝试通过完整路径名指定它们。

如果你只是想运行一个命令并等待结果,你可能想使用 system() 函数来为你处理这个,而不是用 fork/exec 自己构建它。

【讨论】:

  • 很好的信息,但不足以让父级区分 execvp 失败和以非零退出状态退出的外部程序。
  • @R - 如果使用 execvp(),子进程继续运行原始程序的唯一方法是如果 execvp 失败,此时您可以通知父进程。如果使用 system() 可能需要进行一些调查才能弄清楚如何区分故障。
  • 通知父母是棘手的部分。我的回答说明了如何做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 1970-01-01
  • 2010-12-07
  • 2012-12-16
  • 2019-12-13
  • 1970-01-01
相关资源
最近更新 更多