Завершение процесса чтения при закрытии FIFO-файла

Я написал простую пару программ чтения-записи. Writer создает/открывает FIFO-файл и постоянно записывает в него строку. Читатель просто читает его и записывает в стандартный вывод. Читатель делает это только 10 раз, а затем выходит. Удивительно (для меня), что писатель почти сразу же уходит. Он не просто выходит из цикла записи, он как бы выпрыгивает из него, я могу сказать это, не видя финального «до свидания» на экране. Я мог бы принять такое поведение, но я до сих пор не могу понять, почему. Может ли кто-нибудь поделиться со мной своими знаниями?

/* writer code */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    char msg [] = "Leo_Tolstoy";

    size_t len = strlen("Leo_Tolstoy");

    if (mkfifo ("myfifo", 0600) != 0) {
        perror ("creating fifo");
    }
    int fd;
    if ( (fd = open ("myfifo", O_WRONLY)) == -1) {
        perror ("opening fifo");
        exit (1);
    }
    while (1)
    {
        int r = write (fd, msg, len);
        if (r == -1)
            perror ("writing");
        sleep(1);
    }
    printf ("byebye\n");
    close (fd);
    return 0;
}
/* reader code */
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>

int main()
{
    char buf[50];

    printf ("bef opening\n");
    int fd = open ("myfifo", O_RDONLY);
    if (fd == -1) {
        perror ("opening fifo");
        exit (1);
    }

    printf ("bef reading\n");
    int cnt=0;
    while (cnt < 10)
    {
        int r = read (fd, buf, 50);
        if (r == 0)
            break;
        if (r == -1)
            perror ("reading");
        write (1, buf, r);
        cnt++;
    }
//  close (fd);
    return 0;
}

person daniel.kish    schedule 23.08.2016    source источник
comment
Итак, С или С++. Я бы сказал C, но это ваш выбор. Пожалуйста, выберите один язык, а не 2 (похоже, ваш вопрос не относится к разным языкам).   -  person Rakete1111    schedule 23.08.2016
comment
Нет языка C/C++, но есть два разных языка C и C++. Выберите один из них.   -  person too honest for this site    schedule 23.08.2016
comment
Что говорит отладчик?   -  person too honest for this site    schedule 23.08.2016
comment
Я выбрал С). Я не использовал отладчик, это позор, но я не знаю, как использовать gdb, и у меня сейчас нет установленной IDE (это просто виртуальная машина с Ubuntu 14.04)   -  person daniel.kish    schedule 23.08.2016
comment
if (mkfifo ("myfifo", 0600) != 0) использует восьмеричное значение, 384 десятичное, если это уместно.   -  person Weather Vane    schedule 23.08.2016
comment
Я искренне признаю, что не знаю, что именно означает 0600, но это должно работать, поскольку все, кого я читал, используют это значение. И да, я знаю, что это восьмеричное значение.   -  person daniel.kish    schedule 23.08.2016
comment
Вы сами не понимаете код, который вы написали?   -  person iRove    schedule 23.08.2016
comment
@daniel.kish man 2 umask. должны сообщить вам об актуальности этого значения.   -  person WhozCraig    schedule 23.08.2016
comment
Этот цикл while в читателе выглядит так, как если бы он был лучше цикла for. Это не решает вашу проблему, но делает код более понятным.   -  person Riley    schedule 23.08.2016
comment
относительно 0600 это разрешения для узла FIFO. там написано octal, owner=read/write, group=no permissions, world=no permissions   -  person user3629249    schedule 25.08.2016
comment
в коде записи эта строка: int r = write (fd, msg, len); не совсем корректна. функция: write() возвращает ssize_t, а не int   -  person user3629249    schedule 25.08.2016
comment
когда вызов mkfifo() терпит неудачу, нет причин продолжать выполнение кода после вывода сообщения об ошибке, поэтому за вызовом perror() должен следовать вызов exit( EXIT_FAILURE );   -  person user3629249    schedule 25.08.2016
comment
вызов mkfifo() завершится ошибкой с кодом ошибки EEXIST, когда fifo уже существует. Настоятельно рекомендуем 1) проверить наличие ошибок 2) удалить файл fifo после завершения связи   -  person user3629249    schedule 25.08.2016
comment
в коде считывателя отсутствует оператор: #include <unistd.h>, поэтому функции: read() и write() не определены   -  person user3629249    schedule 25.08.2016
comment
когда эта строка оценивается как «истина» if (r == -1), затем следует все вызовы perror(), для этого требуется следующий оператор: exit( EXIT_FAILURE );   -  person user3629249    schedule 25.08.2016
comment
в fifo нет понятия записей, это просто поток байтов, поэтому код должен реализовать проверку того, что была прочитана полная запись, а если нет, попытаться прочитать остальную часть записи. И код должен вызвать close(fd);, когда закончит чтение fifo   -  person user3629249    schedule 25.08.2016
comment
для простоты чтения и понимания 1) используйте осмысленные имена переменных. r НЕ является осмысленным именем переменной. 2) отдельные блоки кода (for, if, else, while, do...while, switch, case, default) через пустую строку   -  person user3629249    schedule 25.08.2016
comment
при компиляции всегда включайте все предупреждения, а затем исправьте эти предупреждения. (для gcc при минимальном использовании: -Wall -Wextra -pedantic также использую: -Wconversion -std=gnu99 )   -  person user3629249    schedule 25.08.2016
comment
код содержит несколько «магических» чисел. «магические» числа — это числа без основы. в данном случае 10 и 50. Настоятельно рекомендуем использовать оператор enum или оператор #define, чтобы дать этим «магическим» числам осмысленные имена, а затем использовать эти осмысленные имена во всем коде.   -  person user3629249    schedule 25.08.2016
comment
в современном C, когда функция main() не имеет оператора return 0;` в конце, компилятор автоматически вызовет возврат 0, поэтому больше нет необходимости иметь оператор return 0; в конце   -  person user3629249    schedule 25.08.2016


Ответы (1)


При выходе (после 10 итераций) модуль записи получает SIGPIPE из-за закрытия конца чтения. Таким образом, выполняется действие по умолчанию для сигнала SIGPIPE, которое завершает программу. Вот почему вы не видите, что окончательный printf() не выполняется.

Вместо этого вы можете проигнорировать (SIG_IGN) сигнал SIGPIPE в модуле записи, вызвав sigaction(), а затем самостоятельно обработать ошибку записи.

person P.P    schedule 23.08.2016
comment
Хорошо, теперь я указал обработчик сигнала для SIGPIPE, и все работает. Спасибо. - person daniel.kish; 23.08.2016