如何正确等待execve完成?

时间:2016-01-06 03:26:21

标签: c linux shell

C源代码(已编译并运行Linux Centos 6.3)具有以下行:

execve(cmd, argv, envp);

execve没有返回,但我想修改代码以了解它何时完成。所以我这样做:

if (child = fork()) {
    waitpid(child, NULL, 0);
    /*now I know execve is finished*/
    exit(0);
}

execve(cmd, argv, envp);

当我这样做时,生成的程序可以在99%的时间内工作,但很少会出现奇怪的错误。

以上是否有问题?我希望上面的代码能像以前一样精确地运行(除了慢一点)。我是对的吗?

如果您想知道背景,修改后的代码为dash。在execve找出要运行的字符串之后,dash调用用于运行简单命令。当我如上所述精确修改(在等待之后甚至没有运行任何东西)并在修改的破折号下重新编译和运行程序时,大多数时候它们运行正常。但是,重新编译一个特定的内核模块称为" biosutility"给我这个错误

cc1: error: unrecognized command line option "-mfentry"

4 个答案:

答案 0 :(得分:1)

这是一种可能性。

实际上,

dash确实需要知道子进程何时终止。它必须收割孩子(通过wait)以避免用僵尸填充进程表,并且无论如何它关心进程的退出状态。

现在,它知道它启动的进程的PID是什么,并且当它执行wait时可以使用它来确定哪个进程终止,从而如何处理退出状态。

但是你正在做额外的fork。所以dash认为它开始了一些PID过程,比如368.但是你fork一个新的孩子,比如说PID 723.然后你wait为那个孩子,但是你忽略了状态代码。最后,您的流程已成功终止。那么dash注意到进程368成功终止。即使它没有。

现在假设dash实际上正在执行像

这样的脚本
do_something && do_something_else

程序员指定如果do_something_else失败,shell肯定不应该do_something。可怕的事情可能发生。或者至少是神秘的东西。然而,你隐藏了这种失败。所以dash高兴地点燃do_something_elseEtvoilà

嗯,这只是一个理论。我真的不知道,但它显示了可能发生的事情。

底线是dash有一些机制可以让它知道子进程何时完成,如果你想挂钩子进程的退出处理,你最好搞清楚该机制如何工作,以便您可以挂钩。试图添加自己的附加机制几乎肯定会以泪流满面。

答案 1 :(得分:1)

仔细阅读 <{3}}以及execve(2)fork(2)文档。 execve和&amp; fork非常棘手, waitpid(2)难以理解。我强烈建议您阅读fork(网上免费提供,但您可以购买纸质书籍),这些有几章

(不要害怕花几天阅读并理解这些系统调用,他们 棘手)

一些要点。

  • 每个 Advanced Linux Programming 都可能失败,您总是处理其失败,至少通过system call显示一些错误消息并立即显示perror(3) - 。

  • exit(3) 系统调用通常永远不会返回,因为在失败时只返回 (成功时,它不会返回,因为调用程序已经被替换掉了!)因此大多数调用它(以及类似的execve(2)函数)通常都是这样的:

    if (execve(cmd, argv, envp)) { perror (cmd); exit(127); };
    /* else branch cannot be reached! */
    

    习惯上在execve失败时使用类似127的奇怪退出代码(通常未使用,除了上面这样),并且通常你不能做任何其他事情。 在fork使用(几乎总是)时,您通常会在进程中调用execve

  • exec(3)系统调用成功返回两次 (一次在父进程中,一次在子进程中)。这很难理解,阅读我给出的参考资料。它仅在失败时返回一次。因此,始终保持fork 的结果,因此典型代码为:

     pid_t pid = fork ();
     if (pid<0) { // fork has failed 
        perror("fork"); exit(EXIT_FAILURE);
     }
     else if (pid==0) { // successful fork in the child process
       // very often you call execve in child, so you don't continue here.
       // example code:
       if (execve(cmd, argv, envp)) { perror (cmd); exit(127); };
       // not reached! 
    };
    // here pid is positive, we are in the parent and fork succeeded....
    /// do something sensible, at some point you need to call waitpid and use pid
    

建议:在某些程序中使用fork(2),或者尝试strace -f bash -c 'date; pwd'并研究输出。它提到了很多strace(1) ....

您的示例代码可能(有时)只需添加一些else

即可
// better code, but still wrong because of unhandled failures....
if ((child = fork())>0) { 
  waitpid(child, NULL, 0);
  /*now I know execve is finished*/
  exit(0);
}
/// missing handling of `fork`  failure!
else if (!child) {
   execve(cmd, argv, envp);
   /// missing handling of `execve` failure
}

但该代码仍然不正确,因为未处理失败。

答案 2 :(得分:0)

根据Rici的优秀评论和答案,我找到了问题的根本原因。

原始代码以退出的cmd退出。我改变了,总是以0退出。这就是代码行为不同的原因。

以下修复程序未显示错误:

int status;

if (child = fork()) {
        waitpid(child, &status, 0);
        /*now we know execve is finished*/
        if (WIFEXITED(status))
            exit(WEXITSTATUS(status));
        exit(1);
    }

execve(cmd, argv, envp);

答案 3 :(得分:-1)

对于这个问题:“上面有什么问题吗?”

关于此代码:

if (child = fork()) {
    waitpid(child, NULL, 0);
    /*now I know execve is finished*/
    exit(0);
}

execve(cmd, argv, envp);
  1. fork()函数有三种返回值:

    • -1表示发生了错误
    • =0表示fork()成功,子进程正在运行
    • >0表示fork()成功,父进程正在运行
  2. 需要遵循对execvp()的调用
  3. (对于罕见的呼叫失败的情况)

     perror( "execvp failed" );
     exit( EXIT_FAILURE );
    
    1. fork()的调用会返回pid_t
    2. 调用之后,代码需要类似于:(使用child作为pid变量)

      if( 0 > child )
      {
          perror( "fork failed");
          exit( EXIT_FAILURE );
      }
      
      else if( 0 == child )
      { // then child process
          execve(cmd, argv, envp);
          perror( "execvp failed" );
          exit( EXIT_FAILURE );
      }
      
      //else
      //{ // else parent process
      waitpid(child, NULL, 0);
      exit( EXIT_SUCCESS );
      

      关于第二个问题,关于错误消息:

      cc1: error: unrecognized command line option "-mfentry"
      

      单词:unrecognized拼写错误,因此这不是实际的错误消息。

      此错误消息与您对dash所做更改的问题无关。

      但是,dash不会直接调用任何编译操作,所以我怀疑这些问题完全不相关。

      建议查看biosutility实用程序的makefile,了解为什么将无效参数传递给cc1