execvp / fork - 如何捕获不成功的执行?

时间:2012-12-04 18:58:37

标签: c exec fork execvp

现在我正在编写一个必须执行子进程的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程序,但它不执行可执行文件的当前目录中的程序(即该代码在其中运行的程序);为了让它在当前目录中运行程序,我还需要做些什么吗?

谢谢!

3 个答案:

答案 0 :(得分:16)

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

现在,如果孩子成功调用execvp,管道的写入结束将关闭而没有数据,并且父项中的read将返回0.如果execvp失败,则孩子,将错误代码写入管道,并且父级中的read将返回非零,并已读取父级要处理的错误代码。

答案 1 :(得分:3)

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

答案 2 :(得分:0)

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

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

如果您只是想运行命令并等待结果,您可能希望使用system()函数为您处理此问题,而不是使用fork / exec自己构建它。