为啥打开 mkfifo 管道时我的程序会挂起?

Posted

技术标签:

【中文标题】为啥打开 mkfifo 管道时我的程序会挂起?【英文标题】:Why does my program hang when opening a mkfifo-ed pipe?为什么打开 mkfifo 管道时我的程序会挂起? 【发布时间】:2011-12-14 16:17:22 【问题描述】:

我使用 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+"(请参阅this question for the differences 的答案)。

此程序将正确写入 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_NONBLOCKopen 阻塞,直到另一个进程打开 FIFO 进行写入。这是将fopen 与模式参数"r" 一起使用时的行为。

只写,没有O_NONBLOCKopen 阻塞,直到另一个进程打开 FIFO 进行读取。这是使用带有模式参数"w"fopen 时的行为。

只读,O_NONBLOCK:open 立即返回。

只写,O_NONBLOCKopen 返回错误,errno 设置为 ENXIO,除非另一个进程打开 FIFO 以供读取。

来自 W. Richard Stevens 的“UNIX 环境中的高级编程”的信息。

打开一个 FIFO 用于读取写入

在 Linux 中也可以在同一进程中打开 FIFO 进行读取和写入。 Linux FIFO man page 声明:

在 Linux 下,打开一个 FIFO 进行读写都会成功 阻塞和非阻塞模式。 POSIX 未定义此行为。 这可用于在没有读取器的情况下打开 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】:

进程阻塞,直到管道的另一端打开。

【讨论】:

以上是关于为啥打开 mkfifo 管道时我的程序会挂起?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的自托管 NancyFX 应用程序在通过 HTTPS 运行时会挂起?

为啥通过内存流上传 JSON 时 BlobClient.UploadAsync 会挂起?

为啥 bash 在写入命名管道时关闭?

为啥 mkfifo'ed 管道仅在 ~25 秒后更新?

SerialPort有时会挂起

两个进程之间使用的命名管道有啥问题?