Вы недостаточно закрываете файловые дескрипторы канала в дочерних элементах или какие-либо из них в родительском.
Правило большого пальца : если вы dup2()
на одном конце канала к стандартному вводу или стандартному выводу закройте оба исходных дескриптора файла, возвращенных pipe()
, как можно скорее.В частности, вы должны закрыть их перед использованием любого из семейства функций exec*()
.
Правило также применяется, если вы дублируете дескрипторы с dup()
или fcntl()
с F_DUPFD
Вот рабочий код, который правильно закрывает трубы.Я удалил компонент пути команд, поскольку (а) они были неправильны для моей машины, и (б) нет смысла использовать execvp()
, если вы указали путь к команде.Я сообщаю о большинстве ошибок (но я не проверяю результаты вызовов read()
и write()
. Я также сообщаю о статусах выхода детей. Дети сообщают и выходят, если им не удается выполнить команду. Ваш кодс 'save_stdin` и т. д. еще больше сбивало с толку, потому что переменные были неинициализированы в родительском объекте, где выполнялась последовательность:
dup2(save_stdin, 0);
dup2(save_stdout, 1);
close(save_stdin);
close(save_stdout);
. Ни вы, ни я не знаем, что делают эти два вызова dup2()
, нобыло вероятно, что они потерпели неудачу или что они сделали что-то странное с вашими стандартными каналами ввода / вывода.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUF_SIZE 100
int main(void)
{
pid_t pid1, pid2;
int fd[2];
char *argv1[] = { "ps", "-j", NULL }; // Remove PATH because using execvp()
char *argv2[] = { "more", NULL }; // Remove PATH because using execvp()
char prompt[] = "Press enter: ";
char buffer[BUF_SIZE];
write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);
ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
buffer[readIn - 1] = '\0';
printf("readIn: %zd\n", readIn);
if (pipe(fd) < 0)
{
fprintf(stderr, "failed to create pipe\n");
exit(EXIT_FAILURE);
}
if ((pid1 = fork()) < 0)
{
fprintf(stderr, "failed to fork - 1\n");
exit(EXIT_FAILURE);
}
if (pid1 == 0) // child
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execvp(argv1[0], argv1);
fprintf(stderr, "failed to execute %s\n", argv1[0]);
exit(EXIT_FAILURE);
}
if ((pid2 = fork()) < 0)
{
fprintf(stderr, "failed to fork - 2\n");
exit(EXIT_FAILURE);
}
if (pid2 == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execvp(argv2[0], argv2);
fprintf(stderr, "failed to execute %s\n", argv2[0]);
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
(int)getpid(), corpse, (unsigned)status);
return 0;
}
Программа была pipe41
(с небольшим редактированием на выходе, чтобы удалить то, что на самом деле неВам не нужно знать, что я делал в других окнах):
$ ./pipe41
Press enter:
readIn: 1
USER PID PPID PGID SESS JOBC STAT TT TIME COMMAND
jonathanleffler 6618 6617 6618 0 1 S s000 0:00.11 -bash
jonathanleffler 6629 6628 6629 0 1 S+ s001 0:00.04 -bash
jonathanleffler 6660 6645 6660 0 1 S+ s002 0:00.04 -bash
jonathanleffler 6716 6695 6716 0 1 S+ s003 0:00.04 -bash
jonathanleffler 6776 6746 6776 0 1 S+ s004 0:00.04 -bash
jonathanleffler 6800 6771 6800 0 1 S+ s005 0:00.04 -bash
jonathanleffler 7487 7486 7487 0 1 S s006 0:00.04 -bash
jonathanleffler 9558 9557 9558 0 1 S s007 0:00.06 -bash
jonathanleffler 10375 9558 10375 0 1 S+ s007 0:00.01 ./pipe41
jonathanleffler 10377 10375 10375 0 1 S+ s007 0:00.00 more
(END)10375: child 10376 exited with status 0x0000
10375: child 10377 exited with status 0x0000
$