【问题标题】:execve fails - ignores exit statement [duplicate]execve 失败 - 忽略退出语句 [重复]
【发布时间】:2018-10-22 19:25:09
【问题描述】:

fgetsexecve 混合在一起时遇到一个奇怪的问题。

我有一个从文件中读取行并执行它们的函数,或者通过它自己的函数解析它们,或者使用execve。为了简单起见,我的测试文件只有外部函数:

echo whoowhee
lx
ls
dir

第二个命令lx 不存在,所以此时execve 应该会失败。

这是我从文件中读取的代码:

  while(fgets(line, 1024, testfile) != NULL){
    if(strlen(line) < 3){continue;}
    if(line[0] == '#'){continue;}

    printf("%s%s", value("PROMPT"), line); // Emulates the prompt

    //Terminates line at right place to simulate input
    line[strlen(line)-2] = '\0';

    execute(line);

    Var *prompt = retrieveVar("PROMPT");
    sprintf(prompt->value, "< Executing script... - [%s] > $ ", value("EXITCODE"));

    lineNo++; 
  }

这是我的 fork-exec 块:

  if(pid == 0){ // Child

    // For loop for using all paths
    for(int i = 0; i < pathn; i++){
      args[0] = paths[i];

      // Executes command path[i] with arguments args with environment envp
      execve(paths[i], args, envp);
    }

    perror("execve");
    exit(0);
  }
  else if(pid > 0){ //Parent
    current_pid = pid;

    if(setpgid(pid, pid) != 0) perror("setpid");

    // Waits if background flag not activated.
    if(BG == 0){
      // WUNTRACED used to stop waiting when suspended
      waitpid(current_pid, &status, WUNTRACED);

        if(WIFEXITED(status)){
          setExitcode(WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status)){
          printf("Process received SIGNAL %d\n", WTERMSIG(status));
        }
    }
  }
  else{
    perror("fork()");
  }

由于execve 需要程序的绝对路径,paths 是程序应该存在的可能路径数组。BG 表示进程是否应该在后台执行,value("EXITCODE") 是程序的退出代码进程,execute 是执行该行的函数。

现在,这是我运行测试文件时的输出,代码原样:

<Welcome to Eggshell> $ echo whoowhee
whoowhee
< Executing script... - [0] > $ lx
execve: No such file or directory
< Executing script... - [0] > $ ls
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt

如您所见,dir 运行了两次。

问题不止于此,如果我在文本文件中添加另一个无意义的命令:

echo whoowhee
lx
onetimesoneistwo
ls
dir

如果我尝试运行该函数,它最终会陷入无限循环。不仅如此,这一次它还会一遍又一遍地运行整个文件!

但是,如果我从fork-exec 位中删除exit,然后重新运行该函数,就会出现这样的结果:

<Welcome to Eggshell> $ echo whoowhee
whoowhee
< Executing script... - [0] > $ lx
execve: No such file or directory
< Executing script... - [0] > $ onetimesoneistwo
execve: No such file or directory
< Executing script... - [0] > $ ls
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ ls
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ onetimesoneistwo
execve: No such file or directory
< Executing script... - [0] > $ ls
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ ls
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt
< Executing script... - [0] > $ dir
add-on  codecov.yml documentation  eggshell.c  LICENSE  Makefile        README.md  switch.sh      val.log
ci  createfile.txt  eggshell       eggshell.h  main.c   Makefile-Clang  src        testinput.txt

令人惊讶的是,它不是无限循环!当然onetimesoneistwo 之后的每个命令都运行了 4 次,onetimesoneistwo 本身也运行了两次,但至少它是一些东西。

我猜execve 失败会导致分叉子进程永远不会终止,但是为什么exit 会导致无限循环,而之后没有exit 重复所有指令呢?

有趣的故事是,如果我正常运行程序,自己提供输入而不是从文件中获取输入,则不会发生同样的事情,所以我的猜测是我的源函数或我的fork-exec 代码。

【问题讨论】:

  • kill(SIGINT, getpid()) 替换exit 似乎停止了无限循环,但下一行仍然运行两次。然而,之后的每一行只运行一次。似乎是一种情况的破解,同时也没有解决整个问题......
  • paths 数组究竟包含什么?它是当前PATH 环境变量上的目录名称列表吗?如果是这样,您在尝试execve() 之前是否需要将paths[i] 和命令名称格式化为args[0]
  • execve 适用于实际命令。它包含来自PATH 的所有目录名称的列表,并附有命令名称。因此,如果命令是ls,则来自PATH 变量的所有路径都添加了/ls
  • 这是一种奇怪的方法——创建大量格式化的字符串,即使其中只有一个有用。大多数人仅在必要时才安排进行连接,而不仅仅是以防万一。有用;这不是显而易见的方法。您应该确保args[1] 也是一个空指针(假设命令没有参数;命令的最后一个参数之后必须有一个空指针)。
  • 老实说,我事先一次性完成了所有操作,以便可以将它们全部组合到一个函数中。因此,当我进入实际的执行块时,我会减少不相关的代码使其复杂化。至于空指针,这是有道理的。我不确定它是否相同,但如果没有参数,args[1] 甚至根本没有初始化。调试器说是0x0,所以我猜是空指针?

标签: c fork fgets execve


【解决方案1】:

至少对我来说,解决方案似乎是使用_exit 而不是exit。如果execve 失败,使用前者会立即杀死孩子,而不是产生奇怪的无限循环效果。

虽然我仍然不知道为什么exit 会导致无限循环,以及为什么_exitexit 不起作用的地方工作...

更新:效果不佳。解决问题的方法是在循环之前使用fgets,并将所有行存储在字符串数组缓冲区中。然后,在下一个循环中,我只需从数组中逐行读取。由于exit 正在重置文件指针,因此解决方案是不再在循环中使用文件指针。

【讨论】:

  • 我认为您遇到了Unwanted child processes being created while file readingWhy does forking my process cause the fie to be read infinitely? 中突出显示的问题 奇怪的是,这是本周第三次出现此问题,也是 9+ 中的第三次我在 SO 上的时间。
  • 至少我的问题没有我想象的那么可笑,但是该死的,真的那么晦涩难懂吗?有道理为什么我找不到任何东西......
  • IMO,是的——它确实是那么晦涩难懂。突然间,它似乎在折磨着人们。我不记得以前见过它,即使在 Linux 上也是如此,但我听说它已经存在了一段时间。我没有在 Mac 上见过它,也没有在 Solaris、HP-UX、AIX(也不是 DG-UX、SCO、Xenix、Dynix ......)上见过它,我记得。不知道最近发生了什么变化让它突然出现。您应该考虑您的子进程是否应该关闭父进程正在读取的文件——执行的命令将对该文件做什么。进行关闭可以避免出现的问题。
  • 我会说这只是一个巧合,但也许filesfork-exec 的组合花了这么长时间才发生。发生在我身上是因为一个项目要创建一个很小的shell
猜你喜欢
  • 1970-01-01
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
  • 2012-09-18
  • 1970-01-01
  • 2017-09-13
相关资源
最近更新 更多