Почему программа зависает от общения ребенка с родителем? - PullRequest
0 голосов
/ 04 апреля 2019

Я пытаюсь понять, почему моя программа зависает. Родитель отправляет вводные данные файл, который он читает в дочернюю программу, и дочерняя программа отправит результат своего вычисления обратно своему родителю. Однако у меня проблемы с отправкой сообщения через второй канал. Родитель кажется зависает при чтении из трубы.

Из других постов, которые я прочитал, похоже, указывается, что родитель должен ждать, пока ребенок закончит, используя wait или waitpid (что в моем случае оба из них не решают мою проблему).

Я добавил уведомление о том, что ни РОДИТЕЛЬ, ни РЕБЕНОК не заканчиваются. Может кто-нибудь объяснить мне, почему это происходит?

Почему это не работает?

int main(int argc,char** argv) {
    char buffer[1];
    int i;

    int fd1[2]; int fd2[2];
    pipe(fd1); pipe(fd2);
    pid_t pid;

    // FIRST PROCESS.
    // -------------------
    pid = fork();
    if(pid == 0) {
        int cnt;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);

        for (i = 0; i < 2; i++) {
            close(fd1[i]);
            close(fd2[i]);
        }

        while(read(STDIN_FILENO, buffer,  sizeof(buffer)) > 0) {
            fprintf(stderr, "( %s )", buffer);
            cnt = cnt + *buffer - 48;
        }

        write(STDOUT_FILENO, &cnt, sizeof(cnt));
        exit(0);
    }


    // PARENT.
    // ------------------------
    int file = open(argv[1], O_RDONLY);

    // READ THE FILE.
    while(read(file, buffer, 1) > 0) {
        if (48 <= *buffer && *buffer <= 57) {
            // PIPE TO CHILD.
            write(fd1[1], buffer, 1);
        } 
    }

    // WAIT FOR CHILD TO FINISH SENDING BACK.

    // int status = 0;
    // waitpid(pid, &status, 0);
    // THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.

    // **** THIS IS THE PART WHERE IT DOESN'T WORK.
    while(read(fd2[0], buffer, 1) > 0) {
        fprintf(stderr, "RESULT : %s", buffer);
    }

    // CLOSING PIPES
    for (i = 0; i < 2; i++) {
        close(fd1[i]);
        close(fd2[i]);
    }

    close(file);
    exit(0);
}

1 Ответ

2 голосов
/ 04 апреля 2019

Вы недостаточно закрываете достаточно файловых дескрипторов в родительском элементе.

Правило большого пальца : Если вы 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
$
...