【问题标题】:Program crashed for calling execvp to compile a program with error?程序因调用 execvp 编译错误程序而崩溃?
【发布时间】:2018-11-25 22:01:23
【问题描述】:

我使用 execvp 编译错误的程序。但是随后我的终端屏幕上会弹出错误消息,这不应该发生,因为如果 execvp 失败,它只会让孩子以退出状态返回。我不明白为什么我的终端实际上​​会显示错误消息?

我的命令数组:{gcc,studentcode.c,ourtest3.c,-o,ourtest3.x,NULL},在ourtest3.c中,我故意犯了几个错误。我的调用函数是这样的:

commands = {"gcc", "studentcode.c", "ourtest3.c", "-o", "ourtest3.x", NULL};
int compile_program(Char** commands) {
  pid_t pid;
  int status;

  pid = safe_fork();
    if (pid == 0) { /*Child*/
      if (execvp(commands[0], commands) < 0) {
    exit(0);
      }
    } else { /*Parent*/
      if (wait(&status) == -1) {
    return 0;
      }
      if (WIFEXITED(status)) {
    if (WEXITSTATUS(status) != 0) {
      return 1;
    }
      } else {
    return 0;
      }
    }
  return 1;
}

ourtest3.c 是这样的:

#include <stdio.h>
#include <assert.h>
#include "studentcode.h"

int main(void) {
  assert(product(2, 16) == 32

  printf("The student code in public07.studentcode.c works on its ");
  printf("third test!\n");

  return 0;
}

我的程序应该以返回值 0 正常结束,但是在我的终端窗口中,它显示了

ourtest3.c: In function 'main':
ourtest3.c:19:0: error: unterminated argument list invoking macro "assert"
ourtest3.c:13:3: error: 'assert' undeclared (first use in this function)
ourtest3.c:13:3: note: each undeclared identifier is reported only once for each function it appears in
ourtest3.c:13:3: error: expected ';' at end of input
ourtest3.c:13:3: error: expected declaration or statement at end of input

【问题讨论】:

  • 阻止消息出现在终端上的代码在哪里?我没有看到 fork 和 execvp 之间的任何代码可以设置错误消息的位置。
  • @DavidSchwartz 你是什么意思?我想如果execvp执行失败那么它会返回,然后我的子进程将退出
  • 是的,这一切都是真的。但是您的代码不会更改错误消息的位置。您需要在fork 之后和execvp 之前添加代码才能做到这一点。
  • 编译器在代码中发现错误与execvp 失败之间存在差异。编译器没有运行失败,如其错误报告所示。
  • @KaiweiLuo 如果要对执行环境进行任何更改,可以在fork 之后和exec* 之前进行。一个常见的更改是在您想要的位置设置 stdin、stdout 和 stderr。另一个常见的更改是close 任何您不希望新进程继承的文件描述符。看我的回答。您的execvp 呼叫正在成功,并且正在做它应该做的一切。编译器并没有失败,它正在按照预期的方式发现错误。

标签: c process c89 process-control


【解决方案1】:

如果你想改变一个进程的标准输入、标准输出和标准错误,你需要这样做。否则,它只是从其父级继承它们。在fork 和execvp 之前,您可能希望将open /dev/null 和dup 放在文件描述符0 和1 上。

这是一些没有错误检查的丑陋代码:

if (pid == 0) { /*Child*/

  /* Redirect stdin and stderr to /dev/null */
  int fd = open ("/dev/null", O_RDONLY);
  dup2(STDIN_FILENO, fd);
  dup2(STDERR_FILENO, fd);
  close(fd);

  if (execvp(commands[0], commands) < 0) {
_exit(0);
  }

如果您希望父级访问子级的输出,也可以将它们重定向到文件或管道。

注意对_exit 的调用。不要养成失败的孩子打电话给exit的习惯。过去,这导致了具有严重安全隐患的错误。想象一下,如果您的进程在exits 时有要做的事情(例如将缓冲区刷新到终端或网络连接)。通过在孩子中调用exit,您可以两次执行该操作。你可能认为你知道你没有这样的东西,但你不可能(通常)知道这一点,因为你不知道你的库可能在内部做什么。

【讨论】:

    猜你喜欢
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-07
    • 2014-04-02
    相关资源
    最近更新 更多