Как и многие другие, я пытаюсь смоделировать оболочку.Я правильно использовал execvp
в строке, исходящей от пользователя.Строка анализируется и генерируется массив строк (каждое слово имеет свой массив, разбитый на символ space
), включая NULL
в самом конце.
Когда я нахожу последнее слововведенный пользователем &
, я установил флаг, чтобы уведомить свою оболочку о том, что команда должна выполняться в фоновом режиме, позволяя пользователю сразу ввести другую команду.Команда "background-execute" видит, что ее &
заменяется символом NULL
в массиве строк, переданных в execvp
.
Как я пытался использовать pthread
чтобы запустить процесс в фоновом режиме, но он действует несколько странно: команда, переданная в execvp
через функцию потока, требует от меня два раза нажать ENTER
после отправки команды.
Вот мой упрощенныйmain
функция, которая должна имитировать оболочку:
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) будет:
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
Aи тогда мне действительно нужно снова нажать 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
(При этом последнем выполнении я также получаю следы своей функции, которая запрашивает и анализирует ввод пользователя, ноэто казалось неуместным, поэтому я абстрагировал всю эту часть.)
Итак, мои вопросы :
- Как получается, что созданный поток ожидает секунду
ENTER
перед запускомexecvp
?(thr_func
прекращает выполнение run_shell
и ожидает вторую ENTER
прямо перед инструкцией pid = fork();
) - Есть ли у меня правильный подход для решения проблемы?(Попытка выполнить команду оболочки в фоновом режиме.)