Почему моя программа зависает при открытии канала mkfifo-ed?

Я использую mkfifo для создания именованного канала. Затем я использую следующую программу для ее открытия. Однако программа зависает на линии "fopen". Здесь что-то не так?

int main(int argc, char** argv) {
char* line = "hello, world!";
FILE* fp = fopen("/tmp/myFIFO", "rw");
fprintf(fp, line);
fclose(fp);
return 0;
}

Ответ 1

Попробуйте передать "w" в качестве режима fopen. "rw" не является аргументом действительного режима для fopen, и даже если бы это было так, вы, вероятно, не хотели бы читать и писать в FIFO в том же процессе (хотя это возможно, см. ниже).

В стороне, аргумент правильного режима для открытия файла для чтения и записи - это "r+" или "w+" (см. Ответы на этот вопрос для различий).

Эта программа будет правильно записывать в FIFO:

#include <stdio.h>
int main(int argc, char** argv) {
    FILE* fp = fopen("/tmp/myFIFO", "w");
    fprintf(fp, "Hello, world!\n");
    fclose(fp);
    return 0;
}

Обратите внимание, что fopen в указанной выше программе будет блокироваться до тех пор, пока FIFO не откроется для чтения. Когда он блокируется, запустите его в другом терминале:

$ cat /tmp/myFIFO
Hello, world!
$ 

Причина, по которой он блокируется, состоит в том, что fopen не передает O_NONBLOCK в open:

$ strace -P /tmp/myFIFO ./a.out
open("/tmp/myFIFO", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
...

Некоторые сведения о том, как открываются FIFO

Только для чтения, без O_NONBLOCK: open блокируется до тех пор, пока другой процесс не откроет FIFO для записи. Это поведение при использовании fopen с аргументом mode "r".

Только запись, без O_NONBLOCK: open блокирует, пока другой процесс не откроет FIFO для чтения. Это поведение при использовании fopen с аргументом mode "w".

Только для чтения, с O_NONBLOCK: open немедленно возвращается.

Только для записи с O_NONBLOCK: open возвращает ошибку с errno, установленную в ENXIO, если другой процесс не имеет открытого FIFO для чтения.

Информация из "Расширенного программирования в среде UNIX" У. Ричарда Стивенса.

Открытие FIFO для чтения и записи

Открытие FIFO для чтения и записи в рамках одного процесса также возможно с Linux. В странице руководства FIFO Linux указано:

В Linux, открытие FIFO для чтения и записи будет успешным как в блокирующий и неблокирующий режим. POSIX оставляет это поведение undefined. Это можно использовать для открытия FIFO для записи, пока нет читателей доступный. Процесс, который использует оба конца соединения для того, чтобы общаться с собой должно быть очень осторожным, чтобы избежать тупиков.

Здесь программа, которая записывает и считывает из одного и того же FIFO:

#include <stdio.h>
int main(int argc, const char *argv[]) {
    char buf[100] = {0};
    FILE* fp = fopen("/tmp/myFIFO", "r+");
    fprintf(fp, "Hello, world!\n");
    fgets(buf, sizeof(buf), fp);
    printf("%s", buf);
    fclose(fp);
    return 0;
}

Он не блокируется и немедленно возвращается:

$ gcc fifo.c && ./a.out 
Hello, world!

Обратите внимание, что это не переносимо и может не работать в операционных системах, кроме Linux.

Ответ 2

Процесс блокируется до тех пор, пока не откроется другой конец трубы.