Из кода, похоже, проблема XY (правка: этот раздел перенесен на передний план из-за комментария, подтверждающего это).Если цель состоит в том, чтобы получить статус выхода ребенка, то для этого есть значение, которое wait
возвращает , и трубы не требуются:
int stat;
wait(&stat);
Прочтите руководствоwait
, чтобы понять, как это читать.Значение stat
можно проверить следующим образом:
- WEXITSTATUS (stat) - если WIFEXITED (stat)! = 0, то это младшие 8 битов дочернего вызова на
exit(N)
иливозвращаемое значение от main
.Он может работать правильно без проверки WIFEXITED, но в стандарте это не указано. - WTERMSIG (stat) - если WIFSIGNALED (stat)! = 0, то это номер сигнала, который вызвал выход из процесса (например,11 - ошибка сегментации).Он может работать правильно без проверки WIFSIGNALED, но в стандарте это не указано.
В коде есть несколько ошибок.См. Добавленные комментарии:
void Command::fork_helper() {
// File descriptors here: 0=stdin, 1=stdout, 2=stderr
//(and 3..N opened in the program, could also be none).
bool return_type_child = true;
int fd[2];
pipe(fd);
// File descriptors here: 0=stdin, 1=stdout, 2=stderr
//(and 3..N opened in the program, could also be none).
// N+1=fd[0] data exhaust of the pipe
// N+2=fd[1] data intake of the pipe
pid_t child;
char *const argv[] = {"zf","-la", nullptr};
child = fork();
if (child > 0) {
// This code is executed in the parent.
wait(NULL); // wait for the child to complete.
Этот wait
является потенциальным тупиком: если дочерний элемент записывает достаточно данных в канал (обычно в килобайтах) , запись блокируется и ожидаетдля родителя, чтобы прочитать трубу.Ожидание родителя (NULL) ожидает завершения дочернего процесса, который ожидает, пока родитель прочитает канал.Это, вероятно, не влияет на рассматриваемый код, но это проблематично.
close(0);
close(fd[1]);
dup(fd[0]);
// File descriptors here: 0=new stdin=data exhaust of the pipe
// 1=stdout, 2=stderr
// (and 3..N opened in the program, could also be none).
// N+1=fd[0] data exhaust of the pipe (stdin is now a duplicate)
Это проблематично, поскольку:
- код только что потерял исходный stdin.
- Труба никогда не закрывается.Вы должны явно закрыть fd [0], не закрывать (0) и не дублировать fd [0].
- Хорошая идея - избегать дублирующих дескрипторов, за исключением наличия stderr дубликата stdout.
.
bool return_type_parent = read(fd[0], &return_type_child, sizeof(return_
this->return_type = return_type_parent;
}
else if (child == 0) {
// this code runs in the child.
close(fd[0]);
close(1);
dup(fd[1]);
// File descriptors here: 0=stdin, 1=new stdout=pipe intake, 2=stderr
//(and 3..N opened in the program, could also be none).
// N+2=fd[1] pipe intake (new stdout is a duplicate)
Это проблематично, поскольку в канал поступает два дублированных потока данных.В этом случае это не критично, так как они закрываются автоматически по окончании процесса, но это плохая практика.Это плохая практика, так как только закрытие всех входных патрубков сигнализирует END-OF-FILE в выхлопную трубу.Закрытие одного входа, но не другого, не сигнализирует об окончании файла.Опять же, в вашем случае это не вызывает проблем, так как выход ребенка закрывает все приемники.
execvp(argv[0], argv);
Код под строкой выше никогда не достигается, если только execvp
не завершился ошибкой.Сбой execvp
возможен только в том случае, если файл не существует или у вызывающей стороны нет разрешения на его выполнение.Если исполняемый файл начинает выполняться и завершается ошибкой позже (возможно, даже если ему не удается прочитать совместно используемую библиотеку), то все равно execvp
сам по себе завершается успешно и никогда не возвращается.Это связано с тем, что execvp
заменяет исполняемый файл, и следующий код больше не находится в памяти, когда execvp
начинает запускать другую программу.
this->return_type = false;
return_type_child = false;
write(1,&return_type_child,sizeof(return_type_child));
}
return;
}