带有poll()的命名管道上的O_RDWR

时间:2013-02-24 18:30:58

标签: linux ipc pipe mkfifo

我经历了一系列不同的linux命名管道客户端/服务器实现,但大多数都使用了读/写的阻塞默认值。

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

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

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

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

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

3 个答案:

答案 0 :(得分:34)

首先,一些预赛:

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

  • read()返回值0表示EOF - 另一方已关闭其连接。这与poll()返回POLLHUP revent相对应(通常但不是在所有操作系统上)。在尝试POLLHUP之前,您可能需要检查read(),但这并非绝对必要,因为read()保证在写作方关闭后返回0
  • 如果您在作家已连接之前致电read(),并且您有O_RDONLY | O_NONBLOCK,那么您将获得EOF(read()重复0),因为您已经我注意到了。但是,如果在调用poll()之前使用POLLIN等待read()事件,它将等待编写者连接,而不是生成EOF。
  • read()返回值-1通常表示错误。但是,如果errno == EAGAIN,这只是意味着现在没有更多可用数据且您没有阻止,因此如果其他设备需要处理,您可以返回poll()。如果errno == EINTR,则read()在读取任何数据之前被中断,您可以返回poll()或立即再次致电read()

现在,对于Linux:

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

正如您所关注的那样,将O_RDWR与管道一起使用并非标准,POSIX或其他地方。

然而,由于这个问题似乎经常出现,所以Linux上制作"弹性命名管道的最佳方法是"即使一方关闭,而且POLLHUP不会导致0 read() O_RDWR | O_NONBLOCK,也会使用poll()

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

  1. (便携式。)没有open(pipe, O_RDONLY);,只有一个管道:

    • read()
    • 主循环:
      • read()根据需要提供尽可能多的数据,可能会在read() == -1次调用上循环播放。
        • 如果errno == EINTRread()read() == 0重新开始。
        • 如果poll(),则关闭连接,并且已收到所有数据。

  2. (Portable。)使用open(pipe, O_RDONLY | O_NONBLOCK);,并期望管道(甚至是命名管道)只打开一次,一旦它们关闭,必须由读写器重新打开,设置一个新的管道:

    • poll()
    • 主循环:
      • POLLIN用于read()个事件,可能同时在多个管道上。 (注意:这可以防止read()在连接编写器之前获得多个EOF。)
      • read()根据需要提供尽可能多的数据,可能会在read() == -1次调用上循环播放。
        • 如果errno == EAGAINpoll(),请返回read() == -1步骤。
        • 如果errno == EINTRread()read() == 0重新开始。
        • 如果poll(),连接已关闭,您必须终止或关闭并重新打开管道。

  3. (非便携式,特定于Linux。)使用open(pipe, O_RDWR | O_NONBLOCK);,期望命名管道永不终止,并且可能多次连接和断开连接:

    • poll()
    • 主循环:
      • POLLIN用于read()个事件,可能同时在多个管道上。
      • read()根据需要提供尽可能多的数据,可能会在read() == -1次调用上循环播放。
        • 如果errno == EAGAINpoll(),请返回read() == -1步骤。
        • 如果errno == EINTRread()read() == 0重新开始。
        • 如果O_RDWR出现问题,那么O_RDONLY就不应该在命名管道上发生,但只能使用poll()或未命名的管道;它表示一个必须关闭并重新打开的封闭管道。如果在同一个{{1}}事件处理循环中混合使用命名管道和未命名管道,则可能仍需要处理此案例。

答案 1 :(得分:1)

根据open(2)手册页,您可以传递O_RDONLY|O_NONBLOCKO_WRONLY|O_NONBLOCK以避免open系统调用被阻止(您将获得errno == ENXIO那个案例)

我评论时还阅读了fifo(7)mkfifo(3)手册页。

答案 2 :(得分:0)

在读取过程中保持一个打开的 O_WRONLY 文件描述符与 O_RDONLY 一个。这将实现相同的效果,确保 read() 永远不会返回文件结尾并且 poll() 和 select() 将阻塞。

这是 100% POSIX