Родительский процесс, создайте 2 дочерних процесса и отправьте данные с помощью каналов

Мне нужно создать два дочерних процесса и отправить данные от одного родителя двум, поэтому я использовал 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 {

        }
    }
}

Я действительно перепробовал все комбинации. Объявляем все трубы вместе, закрывать или не закрывать трубу. все, кроме ничего.

Большая часть работы после второго fork() должна выполняться только исходным (родительским) процессом. Вы должны гораздо быстрее разделить работу для второго дочернего процесса. Обратите внимание, в частности, что вы закрыли b[0] перед выполнением вызовов fdopen() для записи; Затем вы позже попытаетесь подключиться к fdopen(b[0], "r"), но уже слишком поздно. Переместите блок if (pid2 == 0) сразу после второго fork(). Проблемы могут быть, но они будут отличаться от ваших текущих проблем. Это показывает, почему рекомендуется проверять успешность системных вызовов. Если вы проверите последний fdopen(), вы поймете, что он не работает.

Jonathan Leffler 09.09.2018 20:43

Обратите внимание, что ваше утверждение «Я действительно перепробовал все комбинации… но ничего [не сработало]» является преувеличением - вы можете с полным основанием сказать «Я действительно пробовал довольно много комбинаций… но ничего [я пробовал] не сработало». Это было бы точно. Вы не пробовали комбинации все, потому что хотя бы одна из них сработала бы.

Jonathan Leffler 09.09.2018 23:00
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
514
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Этот код устраняет проблемы, указанные в мой комментарий, и некоторые случайные проблемы.

#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!

Другие вопросы по теме