Вы недостаточно закрываете достаточно файловых дескрипторов в родительском элементе.
Правило большого пальца : Если вы dup2()
один конец канала кстандартный ввод или стандартный вывод, закройте оба исходных дескриптора файла, возвращенных pipe()
, как можно скорее.В частности, вы должны закрыть их перед использованием любого из семейства функций exec*()
.
Правило также применяется, если вы дублируете дескрипторы с dup()
или fcntl()
с F_DUPFD
Теперь ваш дочерний процесс полностью следует RoT.Но следствие для родительских процессов заключается в том, что им нужно закрыть неиспользуемые концы канала, и они должны закрыть конец записи канала, который они используют для подачи сигнала EOF на конец чтения этого канала.Вот где ваш код завершается ошибкой.
Возможно, перед чтением файла родительский процесс должен закрыть конец чтения канала, который он использует для записи дочернему элементу, и закрыть конец записи каналаиспользуется для чтения из дочернего элемента.
Затем, после прочтения всего файла, он должен закрыть конец записи канала для дочернего элемента, прежде чем перейти в цикл «чтение из дочернего элемента».Этот цикл никогда не завершается, потому что у родителя по-прежнему открыт конец записи канала, поэтому существует процесс, который может (но не может) записывать в канал.
Кроме того, поскольку дочерний объект записывает байтыцелое число на канал, родитель должен прочитать байты целого числа.Использование char buffer[1];
с форматом %s
бессмысленно;вам необходим нулевой терминатор для строки, и один буфер символов не может содержать как нулевой байт, так и любые данные.
Наряду с различными другими улучшениями (например, '0'
вместо 48
), вы можете получить:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
Если он хранится в файле pipe71.c
и скомпилирован, я получаю следующие результаты при его запуске:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$