execve 失败 - 忽略退出语句
execve fails - ignores exit statement
我在将 fgets
和 execve
混合在一起时遇到了一个奇怪的问题。
我有一个从文件中读取行并执行它们的函数,可以通过它自己的函数解析它们,也可以使用 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] = '[=11=]';
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
运行s 之后 4 次,onetimesoneistwo
本身 运行s 两次,但至少它是一些东西。
我猜测 execve
失败会导致分叉的 child 永远不会终止,但是为什么 exit
会导致无限循环而没有 exit
重复之后的所有说明?
有趣的是,如果我 运行 正常运行程序,我自己提供输入而不是从文件中获取输入,那么同样的事情就不会发生,所以我猜这两者都存在严重错误我的源函数,或者我的 fork-exec 代码。
解决方案,至少对我来说,似乎是使用 _exit
而不是 exit
。如果 execve
失败,使用前者会立即杀死 child,而不是产生奇怪的 无限循环 效果。
尽管我仍然不知道为什么 exit
会导致无限循环,以及为什么 _exit
在 exit
不工作的地方工作...
更新:效果不佳。解决问题的方法是在出现之前的循环中使用 fgets
,并将所有行存储在字符串数组缓冲区中。然后,在下一个循环中,我只是从数组中逐行读取。由于 exit
正在重置文件指针,因此解决方案是不再在循环中使用文件指针。
我在将 fgets
和 execve
混合在一起时遇到了一个奇怪的问题。
我有一个从文件中读取行并执行它们的函数,可以通过它自己的函数解析它们,也可以使用 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] = '[=11=]';
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
运行s 之后 4 次,onetimesoneistwo
本身 运行s 两次,但至少它是一些东西。
我猜测 execve
失败会导致分叉的 child 永远不会终止,但是为什么 exit
会导致无限循环而没有 exit
重复之后的所有说明?
有趣的是,如果我 运行 正常运行程序,我自己提供输入而不是从文件中获取输入,那么同样的事情就不会发生,所以我猜这两者都存在严重错误我的源函数,或者我的 fork-exec 代码。
解决方案,至少对我来说,似乎是使用 _exit
而不是 exit
。如果 execve
失败,使用前者会立即杀死 child,而不是产生奇怪的 无限循环 效果。
尽管我仍然不知道为什么 exit
会导致无限循环,以及为什么 _exit
在 exit
不工作的地方工作...
更新:效果不佳。解决问题的方法是在出现之前的循环中使用 fgets
,并将所有行存储在字符串数组缓冲区中。然后,在下一个循环中,我只是从数组中逐行读取。由于 exit
正在重置文件指针,因此解决方案是不再在循环中使用文件指针。