【问题标题】:Handling errors from execvp()处理来自 execvp() 的错误
【发布时间】:2013-11-19 05:21:21
【问题描述】:

我对如何处理来自execvp() 的错误有点困惑。到目前为止,我的代码如下所示:

int pid = fork();
if (pid < 0) {
    // handle error.
}
else if (pid == 0) {
    int status = execvp(myCommand,myArgumentVector); // status should be -1 because execvp
                                                     // only returns when an error occurs
    // We only reach this point as a result of failure from execvp
    exit(/* What goes here? */);
}
else {
    int status;
    int waitedForPid = waitpid(pid,&status,0);
    //...
}

我试图解决三种情况:

  1. myCommand,myArgumentVector 有效且命令执行正确。
  2. myCommand,myArgumentVector 是有效参数,但在执行 myCommand 时出现问题。
  3. myCommand,myArgumentVector 是无效参数(例如,myCommand 找不到),execvp() 调用失败。

我主要担心的是父进程将拥有正确处理子进程错误所需的所有信息,但我不完全确定该怎么做。

在第一种情况下,程序可能以退出状态 0 结束。这意味着如果我要在宏中调用 WIFEXITED(status),我应该得到 true。我认为这应该可以正常工作。

在第二种情况下,程序可能以非 0 的退出状态结束。这意味着如果我要调用 WEXITSTATUS(status),我应该得到子调用 myCommand 的具体退出状态(请告知如果这是不正确的)。

第三种情况让我很困惑。因此,如果execvp() 失败,则错误将存储在全局变量errno 中。但是这个全局变量只能从子进程访问;父母作为一个完全独立的过程,我认为看不到它。这是否意味着我应该打电话给exit(errno)?还是我应该在这里做点别的事情?另外,如果我打电话给exit(errno),我怎样才能从父级的status 中获取errno 的值?

我的掌握仍然有点薄弱,所以我正在寻找的是确认或纠正我对如何处理这三种情况的理解。

【问题讨论】:

  • -1EXIT_FAILURE 应该足够了。
  • 但我想保留errno的值...
  • 那为什么不退出(-errno)呢?可能需要投射它,但这种形式的东西可能会起作用......
  • 这有什么帮助?我不明白退出代码值与 waitpid 分配的整数状态之间的关系。

标签: c unix fork wait execvp


【解决方案1】:

这是我尝试过的简单代码。

if(fork() == 0){
   //do child stuff here
   execvp(cmd,arguments); /*since you want to return errno to parent
                            do a simple exit call with the errno*/
   exit(errno);
}
else{                
    //parent stuff
    int status;
    wait(&status);       /*you made a exit call in child you 
                           need to wait on exit status of child*/
    if(WIFEXITED(status))
         printf("child exited with = %d\n",WEXITSTATUS(status));
                              //you should see the errno here
}

【讨论】:

  • 这是在cmd无效并导致execvp失败的情况下测试的?这是否意味着如果我exit(errno) 可以通过WEXITSTATUS(status) 访问errno 的值?
  • 如果子进程以exit_exit 正常终止,WIFEXITED 宏将返回非零值。
  • 谢谢,但不是我问的。
  • 好的。如果WIFEXITED的状态为真,WEXITED(status)宏从子进程返回exit状态值的低8位。
  • 是 errno 存储的地方吗?如果孩子运行exit(errno),status 的值究竟是什么,分解成不同的位组件?
【解决方案2】:

在情况 1 中,execvp() 不会返回。返回给父进程的状态将是子进程的退出状态——它提供给exit() 或它从main() 返回的内容,或者可能是子进程死于信号,在这种情况下退出状态为不同但可以检测到(WIFSIGNALED 等)。请注意,这意味着状态不必为零。

对于案例 2,您对案例 2 的想法并不完全清楚(对我而言)。如果命令启动但拒绝调用它的选项,则实际上是案例 1,但退出状态的可能性为零应该很小(尽管已知程序会在错误时退出状态为 0)。或者,找不到该命令,或者找到该命令但无法执行,在这种情况下,execvp() 返回并且您遇到情况 3。

在情况 3 中,execvp() 调用失败。你知道,因为它会返回;成功的execvp() 永远不会返回。测试execvp()的返回值没有意义;它返回的事实意味着它失败了。您可以从errno 的设置中看出它失败的原因。 POSIX 使用 126 和 127 的退出状态——例如,参见 xargssystem()。您可以查看来自execvp() 的错误代码,以确定何时应该返回这些值或其他一些非零值。

【讨论】:

  • 案例 2 的示例类似于命令 cd foobar,其中 foobar 不是有效目录。 exec 找到命令cd 并运行它(参数有效),但随后在执行cd 程序时出现错误。案例 3 的示例类似于 foobar,其中 foobar 根本不是有效程序,因此实际的 execvp 调用本身失败。
  • 基本上我对#3 的疑惑是,如果 execvp 失败,我怎样才能让父母知道原因?我应该从孩子那里退出 errno 吗?
  • 嗯...好吧,在某种程度上你可以找到一个命令cd(它是一个内置的shell,有很好的理由,但在Mac OS X上有一个/usr/bin/cd ),您的案例 2 示例实际上是案例 1:命令已执行但执行失败。 execvp() 不返回;执行的命令以某些(非零)状态退出。案例 3 很容易理解;您可以识别它,因为execvp() 可能会返回 ENOENT。在情况 3 中,您以适当的非零退出状态退出。我的大部分代码都会打印一条错误消息,但以状态 1 退出,即通用的“出现问题”状态。
【解决方案3】:

在第三种情况下,errno 也可以从父级访问,因此您可以退出(errno)。 但是,这不是最好的做法,因为 errno 的值可能会在您退出时发生变化。

如果在 exec() 和 exit() 调用之间有代码,为了更确定不会丢失 errno,请将 errno 分配给 int:

execvp(<args>);

int errcode=errno;

/* other code */

exit(errcode);

至于您的其他问题,退出状态不能直接与 errno 相提并论,无论如何,您不应该尝试从除 errno 之外的任何东西(如上)中检索 errno。

本文档可能会有所帮助: http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html

【讨论】:

  • 所以说我用 execvp 产生的 errno 值做exit(errcode)。现在在父 waitpid(pid,&amp;status,0) 中已经为 status 变量分配了一些值。该值是否等于errcode?或者如果不是,它有什么关系?
  • 另外,当您说 errno 可以从父级访问时,您的意思是仅通过执行 exit(errcode),对吗?还是跨进程共享全局变量...?
  • 它们在 bash 系统调用中无关。一个 exec 调用可能有多个错误,errno 会多次更改,最终的 errno 不必与退出状态相关。从返回值的范围可以看出这一点——状态为 8 位,errno 为 int 类型的可修改左值。当 execvp 终止时(可能使用 exit() 或等效项,它不会返回 exit(errno))。所以状态与errno不对应。
【解决方案4】:

这是我的代码: 它工作正常。子状态在 waitpid 中返回,因此它告诉子进程是否成功执行。 //声明一个进程id变量 pid_t pid, ret_pid;

//fork a child process is assigned 
//to the process id
pid=fork();

DFG_STATUS("Forking and executing process in dfgrunufesimulator %d \n", pid);

//code to show that the fork failed
//if the process id is less than 0
if(pid<0)
{
    return DFG_FAILURE;
}
else if(pid==0)
{
    //this statement creates a specified child process
    exec_ret = execvp(res[0],res);  //child process

DFG_STATUS("Process failed ALERT exec_ret = %d\n", exec_ret);
exit(errno);

}
//code that exits only once a child 
//process has been completed
else
{
  ret_pid = waitpid(pid, &status, 0);
  if ( ret_pid == -1) 
  { 
    perror("waitpid"); 
    return DFG_FAILURE; 
  }

   DFG_STATUS("ret_pid = %d, pid = %d, child status = %d\n",ret_pid,pid,status);


  if (WIFEXITED(status)) {
        DFG_STATUS("child exited, status=%d\n", WEXITSTATUS(status));


    } else if (WIFSIGNALED(status)) {
        DFG_STATUS("child killed (signal %d)\n", WTERMSIG(status));


    } else if (WIFSTOPPED(status)) {
        DFG_STATUS("child stopped (signal %d)\n", WSTOPSIG(status));


#ifdef WIFCONTINUED     /* Not all implementations support this */
    } else if (WIFCONTINUED(status)) {
        DFG_STATUS("child continued\n");
#endif
    } else {    /* Non-standard case -- may never happen */
        DFG_STATUS("Unexpected status (0x%x)\n", status);
    }

  if(status != 0) /* Child process failed to execute */
    return DFG_FAILURE;

  result = DFG_SUCCESS;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-07
    • 2010-11-11
    • 2017-05-16
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    • 2017-03-18
    • 1970-01-01
    相关资源
    最近更新 更多