Мне нужно создать два дочерних процесса и отправить данные от одного родителя двум, поэтому я использовал pipe
.
Если я использую только 1 дочерний процесс и 1 канал, все будет отлично работать с fdopen
, fscanf
и fprintf
.
Кроме того, если я создаю 2 канала и отправляю данные одному процессу, все равно работает отлично.
Но если я создам второй процесс и попытаюсь читать со второго pipe
, ничего не произойдет.
Например:
int main() {
pid_t pid1, pid2;
int a[2];
pipe(a);
pid1 = fork();
if (pid1 == 0) {
char x,y;
FILE *stream;
stream = fdopen(a[0],"r");
fscanf(stream,"%c",&x);
printf("%c\n", x);
close(a[1]);
close(a[0]);
} else {
int b[2];
pipe(b);
pid2 = fork();
FILE *stream1, *stream2;
close(a[0]);
close(b[0]);
stream1 = fdopen(a[1],"w");
stream2 = fdopen(b[1],"w");
fprintf(stream1, "yo bella zio\n");
fprintf(stream2, "como estas\n");
fflush(stream1);
fflush(stream2);
close(a[1]);
close(b[1]);
waitpid (pid1, NULL, 0);
waitpid (pid2, NULL, 0);
if (pid2 == 0) {
FILE *stream;
close(b[1]);
close(a[1]);
close(a[0]);
stream = fdopen(b[0],"r");
fscanf(stream,"%c",&x);
printf("%c\n", x);
} else {
}
}
}
Я действительно перепробовал все комбинации. Объявляем все трубы вместе, закрывать или не закрывать трубу. все, кроме ничего.
Обратите внимание, что ваше утверждение «Я действительно перепробовал все комбинации… но ничего [не сработало]» является преувеличением - вы можете с полным основанием сказать «Я действительно пробовал довольно много комбинаций… но ничего [я пробовал] не сработало». Это было бы точно. Вы не пробовали комбинации все, потому что хотя бы одна из них сработала бы.
Этот код устраняет проблемы, указанные в мой комментарий, и некоторые случайные проблемы.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
pid_t pid1, pid2;
int a[2];
int b[2];
pipe(a);
pid1 = fork();
if (pid1 < 0)
{
fprintf(stderr, "failed to fork child 1 (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid1 == 0)
{
close(a[1]); // Must be closed before the loop
FILE *stream = fdopen(a[0], "r");
if (stream == NULL)
{
fprintf(stderr, "failed to create stream for reading (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
int c;
while ((c = getc(stream)) != EOF)
putchar(c);
//char x;
//fscanf(stream, "%c", &x);
//printf("%c\n", x);
//close(a[0]); -- Bad idea once you've used fdopen() on the descriptor
printf("Child 1 done\n");
exit(0);
}
else
{
pipe(b);
pid2 = fork();
if (pid2 < 0)
{
fprintf(stderr, "failed to fork child 2 (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
else if (pid2 == 0)
{
close(b[1]);
close(a[1]);
close(a[0]);
FILE *stream = fdopen(b[0], "r");
if (stream == NULL)
{
fprintf(stderr, "failed to create stream for reading (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
int c;
while ((c = getc(stream)) != EOF)
putchar(c);
//char x;
//fscanf(stream, "%c", &x);
//printf("%c\n", x);
printf("Child 2 done\n");
exit(0);
}
}
close(a[0]);
close(b[0]);
FILE *stream1 = fdopen(a[1], "w");
if (stream1 == NULL)
{
fprintf(stderr, "failed to create stream for writing (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
FILE *stream2 = fdopen(b[1], "w");
if (stream2 == NULL)
{
fprintf(stderr, "failed to create stream for writing (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stream1, "yo bella zio\n");
fprintf(stream2, "como estas\n");
fflush(stream1); // Not necessary because fclose flushes the stream
fflush(stream2); // Not necessary because fclose flushes the stream
fclose(stream1); // Necessary because child won't get EOF until this is closed
fclose(stream2); // Necessary because child won't get EOF until this is closed
//close(a[1]); -- bad idea once you've used fdopen() on the descriptor
//close(b[1]); -- bad idea once you've used fdopen() on the descriptor
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
printf("All done!\n");
return 0;
}
Обратите внимание, что я изменил дочерние процессы так, чтобы (а) они явно выходили из блока кода и (б) превратили их тело в цикл, чтобы все отправленные данные были напечатаны. Это потребовало от меня переместить close(a[1])
в первого ребенка; в противном случае цикл не завершается, потому что операционная система видит, что у дочернего элемента 1 дескриптор открыт для записи.
При выполнении на Mac под управлением macOS 10.13.6 High Sierra (GCC 8.2.0 в качестве компилятора) я получаю вывод:
yo bella zio
Child 1 done
como estas
Child 2 done
All done!
Большая часть работы после второго
fork()
должна выполняться только исходным (родительским) процессом. Вы должны гораздо быстрее разделить работу для второго дочернего процесса. Обратите внимание, в частности, что вы закрылиb[0]
перед выполнением вызововfdopen()
для записи; Затем вы позже попытаетесь подключиться кfdopen(b[0], "r")
, но уже слишком поздно. Переместите блокif (pid2 == 0)
сразу после второгоfork()
. Проблемы могут быть, но они будут отличаться от ваших текущих проблем. Это показывает, почему рекомендуется проверять успешность системных вызовов. Если вы проверите последнийfdopen()
, вы поймете, что он не работает.