为啥只读打开命名管道块?

Posted

技术标签:

【中文标题】为啥只读打开命名管道块?【英文标题】:Why does a read-only open of a named pipe block?为什么只读打开命名管道块? 【发布时间】:2011-08-12 13:00:31 【问题描述】:

在使用 Python 处理各种风格的 UNIX(Linux、FreeBSD 和 MacOS X)下的命名管道 (FIFO) 时,我注意到了一些奇怪的地方。第一个,也许是最烦人的是尝试打开一个空/空闲 FIFO 只读将阻塞(除非我使用 os.O_NONBLOCK 和较低级别的 os.open() 调用)。但是,如果我打开它进行读/写,那么我不会被阻塞。

例子:

f = open('./myfifo', 'r')               # Blocks unless data is already in the pipe
f = os.open('./myfifo', os.O_RDONLY)    # ditto

# Contrast to:
f = open('./myfifo', 'w+')                           # does NOT block
f = os.open('./myfifo', os.O_RDWR)                   # ditto
f = os.open('./myfifo', os.O_RDONLY|os.O_NONBLOCK)   # ditto

我只是好奇为什么。为什么 open 调用阻塞而不是一些后续的读取操作?

我还注意到,非阻塞文件描述符在 Python 中会表现出不同的行为。在我使用os.open()os.O_NONBLOCK 进行初始打开操作的情况下,如果文件描述符上的数据尚未准备好,os.read() 似乎会返回一个空字符串。但是,如果我使用 fcntl.fcnt(f.fileno(), fcntl.F_SETFL, fcntl.GETFL | os.O_NONBLOCK),那么 os.read 会引发异常 (errno.EWOULDBLOCK)

我的os.open() 示例中没有设置普通open() 设置的其他标志吗?它们有何不同?为什么?

【问题讨论】:

我建议删除 Python 引用并可能更改此问题的标题。我在处理 Python/C++ 混合项目时碰巧遇到了这个问题,但这绝对不是 Python 的错。就我而言,我试图在 C++ 中创建一个 fifo,然后从 Python 连接到它。自从 C++ 代码挂起后,我从未走到那一步。 要在尝试打开文件之前检查文件是否为 fifo(命名管道),(例如,如果您需要排除它)您可以通过以下方式确定:***.com/questions/8558884/… 第三个语句是错误的(至少在 python3.7 中):open('myfifo', 'w+') 会引发错误:“文件不可搜索”。只有最后 2 个语句有效。 【参考方案1】:

这就是它的定义方式。来自 open() 函数的 Open Group 页面

O_NONBLOCK

    When opening a FIFO with O_RDONLY or O_WRONLY set: If O_NONBLOCK is
    set:

        An open() for reading only will return without delay. An open()
        for writing only will return an error if no process currently
        has the file open for reading.

    If O_NONBLOCK is clear:

        An open() for reading only will block the calling thread until a
        thread opens the file for writing. An open() for writing only
        will block the calling thread until a thread opens the file for
        reading.

【讨论】:

这正是我所希望的答案(可以说我懒得去追捕自己)。这似乎是一个特别重载 open() 语义的例子,它对 FIFO 来说不同于任何其他类型的文件描述符。我很想从这个问题中删除 Python 标签,因为我怀疑它根本不是 Python 特定的;这恰好是我提供一个我所说的例子的最简单的方法。 是的,Jim Dennis,你不是唯一一个被抛出循环的人。对我来说,这种行为根本不是预期的。我不明白有人如何预测这种行为。在这里,我试图通过仅将管道打开为 O_WRONLY 来编写干净的代码,却发现它挂起(因此我发现自己在这里。)是的,您应该始终阅读文档,但正如 Jim 指出的那样,这似乎改变了语义open 用于特定类的文件描述符。一点也不像 Unix。

以上是关于为啥只读打开命名管道块?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 (ubuntu 11.10 bash 4.2.10, x86_64 ) 命名管道会堵塞?

为啥命名管道是本地的?

为啥在没有人阅读后继续写入命名管道?

为啥这个命名管道不打印发送的行?

为啥 os.path.exists() 会阻止 Windows 命名管道连接?

为啥线程在进程间通信期间会破坏命名管道?