15

我经历了各种不同的 linux 命名管道客户端/服务器实现,但它们中的大多数都在读/写时使用阻塞默认值。

因为我已经在使用 poll() 来检查其他标志,所以虽然通过 poll() 检查传入的 FIFO 数据也是一个好主意......

经过所有研究后,我认为以 O_RDWR 模式打开管道是在没有编写器打开管道时防止管道上无限数量的 EOF 事件的唯一方法。

这样管道的两端都是关闭的,其他客户端也可以打开可写端。为了回应我会使用单独的管道......

我的问题是,尽管我找到了一些使用 O_RDWR 标志的示例,但 open() 联机帮助页将该标志描述为在分配给 FIFO 时未定义。( http://linux.die.net/man/3/open )

但是你将如何在没有 O_RDWR 的管道上使用 poll() 呢?您认为“O_RDWR”是打开管道的合法方式吗???

4

3 回答 3

51

首先,一些准备工作:

使用O_NONBLOCKandpoll()是常见的做法——而不是相反。要成功工作,您需要确保正确处理所有状态poll()read()返回状态:

  • read()返回值的0意思是EOF——对方已经关闭了它的连接。这对应于(通常,但不是在所有操作系统上)poll()返回一个POLLHUPrevent。您可能想POLLHUP在尝试之前检查read(),但这不是绝对必要read()的,因为保证0在写入端关闭后返回。
  • 如果您read()在编写器连接之前调用,并且您已经连接,O_RDONLY | O_NONBLOCK您将重复获得 EOF (read()返回0),正如您所注意到的。但是,如果您在调用之前使用poll()等待事件,它将等待编写器连接,并且不会产生 EOF。POLLINread()
  • read()返回值-1通常意味着错误。但是,如果errno == EAGAIN,这仅仅意味着现在没有更多可用数据并且您没有阻止,因此您可以返回poll()以防其他设备需要处理。如果errno == EINTR, thenread()在读取任何数据之前被中断,您可以立即返回poll()或简单地read()再次调用。

现在,对于 Linux:

  • 如果您在阅读端打开O_RDONLY,则:
    • open()阻塞,直到有相应的写入器打开。
    • poll()POLLIN当数据准备好读取或发生 EOF 时,将给出一个revent。
    • read()将阻塞,直到读取请求的字节数、关闭连接(返回 0)、被信号中断或发生某些致命的 IO 错误。这种阻塞方式违背了 using 的目的poll(),这就是为什么poll()几乎总是与 with 一起使用O_NONBLOCK。您可以在超时后使用 analarm()来唤醒read(),但这过于复杂。
    • 如果作者关闭,那么读者将收到一个poll() POLLHUPrevent,然后read()0无限期地返回。此时,阅读器必须关闭其文件句柄并重新打开它。
  • 如果您在阅读端打开O_RDONLY | O_NONBLOCK,则:
    • open()不会阻塞。
    • poll()POLLIN当数据准备好读取或发生 EOF 时,将给出一个revent。poll()如果没有写入器,也会阻塞直到写入器可用。
    • 在读取所有当前可用的数据后,如果连接仍然打开,则read()返回 -1 并设置,或者如果连接已关闭 (EOF)或尚未由 writer打开,则返回。当 时,这意味着是时候返回 了,因为连接已打开但没有更多数据。当,尚未读取任何字节并被信号中断时,可以重新启动。errno == EAGAIN0errno == EAGAINpoll()errno == EINTRread()
    • 如果作者关闭,那么读者将收到一个poll() POLLHUPrevent,然后read()0无限期地返回。此时阅读器必须关闭其文件句柄并重新打开它。
  • (Linux 特定的:)如果您在阅读端打开O_RDWR,那么:
    • open()不会阻塞。
    • poll()POLLIN当数据准备好被读取时会给出一个revent。但是,对于命名管道,EOF 不会导致POLLINPOLLHUP恢复。
    • read()将阻塞,直到读取请求的字节数,被信号中断,或发生其他一些致命的 IO 错误。对于命名管道,它不会返回errno == EAGAIN,甚至不会0在 EOF 上返回。它只会坐在那里,直到读取到请求的确切字节数,或者直到它收到信号(在这种情况下,它将返回到目前为止读取的字节数,或者errno == EINTR如果到目前为止没有读取任何字节,则返回 -1 并设置) .
    • 如果 writer 关闭,如果另一个 writer 打开命名管道,reader 将不会失去读取命名管道的能力,但 reader 也不会收到任何通知。
  • (Linux 特定的:)如果您在阅读端打开O_RDWR | O_NONBLOCK,那么:
    • open()不会阻塞。
    • poll()POLLIN当数据准备好被读取时会给出一个revent。但是,EOF 不会在命名管道上引起POLLIN或恢复。POLLHUP
    • 读取所有当前可用的数据后,read()将返回-1并设置errno == EAGAIN. 这是返回poll()等待更多数据的时间,可能来自其他流。
    • 如果 writer 关闭,如果另一个 writer 打开命名管道,读者将不会失去读取命名管道的能力。连接是持久的。

正如您所关心的那样,使用O_RDWR管道不是标准的,POSIX 或其他地方。

然而,由于这个问题似乎经常出现,在 Linux 上制作“弹性命名管道”的最佳POLLHUP方法0read()使用O_RDWR | O_NONBLOCK.

我看到在 Linux 上处理命名管道的三种主要方法:

  1. (便携式。)没有poll(), 和有一个管道:

    • open(pipe, O_RDONLY);
    • 主循环:
      • read()尽可能多的数据,可能会循环read()调用。
        • 如果read() == -1errno == EINTRread()从头再来。
        • 如果read() == 0,则关闭连接,并且已接收到所有数据。

  2. (便携式。)使用poll(),并期望管道,即使是命名的管道,也只打开一次,一旦它们关闭,必须由读取器和写入器重新打开,从而建立一个新的管道:

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • 主循环:
      • poll()对于POLLIN事件,可能同时在多个管道上。(注意:这可以防止read()在编写器连接之前获得多个 EOF。)
      • read()尽可能多的数据,可能会循环read()调用。
        • 如果read() == -1errno == EAGAIN,则返回poll()步骤。
        • 如果read() == -1errno == EINTRread()从头再来。
        • 如果read() == 0,则连接已关闭,您必须终止或关闭并重新打开管道。

  3. (不可移植,特定于 Linux。)使用poll(),并期望命名管道永远不会终止,并且可以多次连接和断开连接:

    • open(pipe, O_RDWR | O_NONBLOCK);
    • 主循环:
      • poll()对于POLLIN事件,可能同时在多个管道上。
      • read()尽可能多的数据,可能会循环read()调用。
        • 如果read() == -1errno == EAGAIN,则返回poll()步骤。
        • 如果read() == -1errno == EINTRread()从头再来。
        • 如果read() == 0,则有问题——它不应该发生O_RDWR在命名管道上,而只发生在命名管道O_RDONLY或未命名管道上;它表示必须关闭并重新打开的封闭管道。如果在同一个poll()事件处理循环中混合命名和未命名管道,这种情况可能仍需要处理。
于 2013-06-29T19:42:18.973 回答
1

根据open(2)手册页,您可以通过O_RDONLY|O_NONBLOCKO_WRONLY|O_NONBLOCK避免open系统调用被阻止(errno == ENXIO在这种情况下您会得到)

正如我所评论的,还阅读了fifo(7)mkfifo(3)手册页。

于 2013-02-25T08:59:20.077 回答
0

Just keep an open O_WRONLY file descriptor in the reading process alongside the O_RDONLY one. This will achieve the same effect, ensuring that read() never returns end-of-file and that poll() and select() will block.

And it's 100% POSIX

于 2021-01-10T00:07:44.210 回答