【发布时间】:2019-06-27 18:22:27
【问题描述】:
像许多其他人一样,我正在尝试模拟一个外壳。我已经在来自用户的字符串上正确使用了execvp。解析字符串并生成字符串数组(每个单词都有自己的数组,在space 字符上拆分),包括最后的NULL。
当我发现用户输入的最后一个单词是& 时,我设置了一个标志来通知我的shell 该命令将在后台执行,同时让用户立即输入另一个命令。 “后台执行”命令将其 & 替换为传递给 execvp 的字符串数组中的 NULL 字符。
事实上,我一直在尝试使用pthread 在后台运行该进程,但它的行为有些奇怪:通过线程函数传递给execvp 的命令需要我按两次@ 987654330@发送命令后。
这是我简化的main 函数,它是模拟一个shell:
int main (void) {
fprintf (stdout, "%% ");
bool running = true;
while(running) {
/* Ask for an instruction and parses it. */
char** args = query_and_split_input();
/* Executing the commands. */
if (args == NULL) { // error while reading input
running = false;
} else {
printf("shell processing new command\n");
int count = count_words(args);
split_line* line = form_split_line(args, count);
Expression* ast = parse_line(line, 0, line->size - 1);
if(line->thread_flag) {
pthread_t cmd_thr;
/* Setting up the content of the thread. */
thread_data_t thr_data;
thr_data.ast = *ast;
thr_data.line = *line;
/* Executing the thread. */
int thr_err;
if ((thr_err = pthread_create(&cmd_thr, NULL, thr_func, &thr_data))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", thr_err);
return EXIT_FAILURE;
}
printf("thread has been created.\n");
} else {
run_shell(args);
}
free(line);
printf("done running shell on one command\n");
}
}
/* We're all done here. See you! */
printf("Bye!\n");
exit (0);
}
这是我的线程的功能:
void *thr_func(void *arg) {
thread_data_t *data = (thread_data_t *)arg;
data->line.content[data->line.size-1] = NULL; // to replace the trailing '&' from the command
run_shell(data->line.content);
printf("thread should have ran the command\n");
pthread_exit(NULL);
}
以及运行命令的实际行:
void run_shell(char** args) {
/* Forking. */
int status;
pid_t pid; /* Right here, the created THREAD somehow awaits a second 'ENTER' before going on and executing the next instruction that forks the process. This is the subject of my first question. */
pid = fork();
if (pid < 0) {
fprintf(stderr, "fork failed");
} else if (pid == 0) { // child
printf("Child executing the command.\n");
/* Executing the commands. */
execvp(args[0], args);
/* Child process failed. */
printf("execvp didn't finish properly: running exit on child process\n");
exit(-1);
} else { // back in parent
waitpid(-1, &status, 0); // wait for child to finish
if (WIFEXITED(status)) { printf("OK: Child exited with exit status %d.\n", WEXITSTATUS(status)); }
else { printf("ERROR: Child has not terminated correctly. Status is: %d\n", status); }
free(args);
printf("Terminating parent of the child.\n");
}
}
所以基本上,作为一个例子,run_shell(args) 接收的是["echo","bob","is","great",NULL](在顺序执行的情况下)或["echo","bob","is","great",NULL,NULL](在后台执行的命令的情况下)。
我留下了printf 的痕迹,因为它可能有助于您了解执行流程。
如果我输入echo bob is great,输出(printf traces)是:
shell processing new command
Child executing the command.
bob is great
OK: Child exited with exit status 0.
Terminating parent of the child.
done running shell on one command
但是,如果我输入echo bob is great &,则输出为:
shell processing new command
thread has been created.
done running shell on one command
然后我实际上需要再次按ENTER 才能获得以下输出:
Child executing the command.
bob is great
OK: Child exited with exit status 0.
Terminating parent of the child.
thread should have ran the command
(在最后一次执行中,我还获得了查询和解析用户输入的函数的痕迹,但这似乎无关紧要,因此我将整个部分抽象出来。)
所以我的问题是:
- 创建的线程怎么会在运行
execvp之前等待第二个ENTER? (thr_func停止执行run_shell并在pid = fork();指令之前等待第二个ENTER) - 我是否有解决手头问题的正确方法? (尝试在后台执行 shell 命令。)
【问题讨论】:
-
@wildplasser 我不确定我理解你的意思。事实上,当我检测到
&时,我会尝试自己调用一个子shell,因为execxxx()确实不会做这样的事情。这就是为什么我一直在尝试首先创建一个pthread,然后从那里开始fork(),然后在其中创建execvp。 -
@wildplasser 我不认为我们可以使用
/bin/sh来进行赋值,因为它会自动完成赋值本身所需的大部分单词(解析,创建抽象语法树,在需要时重定向输出等)。 -
不要将线程与 fork 结合使用。您可以在不启动新线程的情况下分叉。分叉线程代码很复杂。
-
@rici 但如果我使用
fork而不是pthread,我不需要使用wait,这正好阻止我的主进程允许用户输入新命令在等待分叉进程完成时? -
执行此操作的传统方法是在 SIGCHLD 信号处理程序中进行等待。