具有poll()的命名pipe道上的O_RDWR

我已经经历了多种不同的命名pipe道客户端/服务器实现,但其中大部分使用读/写阻塞默认值。

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

在所有的研究之后,我认为在O_RDWR模式下打开pipe道是防止在没有打开作者的情况下在pipe道上无限次数的EOF事件的唯一方法。

这样pipe道的两端都closures,其他客户端也可以打开可写入端。 要回应我会使用单独的pipe道…

我的问题是,虽然我发现了一些使用O_RDWR标志的例子,但open()手册页将该标志描述为分配给FIFO时正在使用。 ( http://linux.die.net/man/3/open )

但是如何在不使用O_RDWR的情况下在pipe道上使用poll()呢? 你认为“O_RDWR”是一个合法的方式来打开pipe道?

Solutions Collecting From Web of "具有poll()的命名pipe道上的O_RDWR"

首先是一些预备:

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

  • read()返回值0意味着EOF – 另一端已经关闭了它的连接。 这对应于(通常,但不是所有的操作系​​统) poll()返回POLLHUP 。 在尝试read()之前,您可能需要检查POLLHUP ,但是这并不是绝对必要的,因为read()在写入方关闭后保证返回0
  • 如果在O_RDONLY | O_NONBLOCK连接之前调用read() ,并且您有O_RDONLY | O_NONBLOCK O_RDONLY | O_NONBLOCK ,你会得到EOF( read()返回0 ),你已经注意到了。 但是,如果在调用read()之前使用poll()等待POLLIN事件,它将等待writer连接,而不会产生EOF。
  • read()返回值-1通常意味着错误。 但是,如果errno == EAGAIN ,这意味着现在没有更多的数据可用,并且您没有阻塞,所以如果其他设备需要处理,则可以返回poll() 。 如果errno == EINTR ,则read()在读取任何数据之前中断,并且可以返回poll()或者直接再次调用read()

现在,对于Linux:

  • 如果你用O_RDONLY在阅读面O_RDONLY ,那么:
    • open()会阻塞,直到打开相应的writer。
    • poll()将在数据准备好读取或发生EOF时给出POLLIN revent。
    • read()将会阻塞,直到read()到请求的字节数,连接被关闭(返回0),被信号中断,或者发生一些致命的IO错误。 这种阻塞排除了使用poll()的目的,这就是poll()几乎总是与O_NONBLOCK 。 在超时后,可以使用alarm()read()唤醒,但这太复杂了。
    • 如果作者关闭,读者将收到poll() POLLHUPread()将在无限期后返回0 。 在这一点上,读者必须关闭它的文件夹并重新打开它。
  • 如果您用O_RDONLY | O_NONBLOCK打开阅读面 O_RDONLY | O_NONBLOCK ,然后:
    • open()不会被阻塞。
    • poll()将在数据准备好读取或发生EOF时给出POLLIN revent。 poll()也会阻塞,直到一个写者可用,如果没有的话。
    • 读取所有当前可用的数据后,如果连接仍处于打开状态,则read()将返回-1并设置errno == EAGAIN如果连接已关闭(EOF) 或尚未由作者打开,则返回0 。 当errno == EAGAIN ,这意味着是时候返回到poll() ,因为连接是打开的,但是没有更多的数据。 当errno == EINTRread()没有读取任何字节,并被信号中断,因此可以重新启动。
    • 如果作者关闭,读者将收到一个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在读取侧打开 O_RDWR | O_NONBLOCK ,然后:
    • open()不会被阻塞。
    • poll()会在数据准备好被读取时给予POLLIN revent。 但是,EOF将不会在命名管道上引起POLLINPOLLHUP
    • 读取所有当前可用的数据后, read()将返回-1并设置errno == EAGAIN 。 这是返回到poll()等待更多数据的时间,可能来自其他流。
    • 如果作者关闭,读者不会失去读取命名管道的能力,如果其他作家打开命名管道。 连接是持久的。

正如你所关心的,在管道中使用O_RDWR并不是标准的,POSIX或其他地方的。

但是,由于这个问题似乎经常出现,所以Linux上最好的方法是使“弹性命名管道”即使在一方关闭的情况下仍然存在,并且不会导致POLLHUP或者为read()返回0 ,使用O_RDWR | O_NONBLOCK O_RDWR | O_NONBLOCK

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

  1. (便携式。)没有poll() ,并与一个单一的管道:

    • open(pipe, O_RDONLY);
    • 主循环:
      • read()尽可能多的数据,可能循环在read()调用。
        • 如果read() == -1errno == EINTR ,则再次read()
        • 如果read() == 0 ,则连接关闭,并且所有数据都已收到。
  2. (Portable)使用poll() ,并期望管道,甚至命名管道,只打开一次,一旦它们关闭,必须由读写器重新打开,建立一个新的管道:

    • open(pipe, O_RDONLY | O_NONBLOCK);
    • 主循环:
      • poll()用于POLLIN事件,可能一次在多个管道上。 (注意:这可以防止在作者连接之前, read()获得多个EOF。)
      • read()尽可能多的数据,可能循环在read()调用。
        • 如果read() == -1errno == EAGAIN ,则返回到poll()步骤。
        • 如果read() == -1errno == EINTR ,则再次read()
        • 如果read() == 0 ,则连接关闭,并且必须终止,或关闭并重新打开管道。
  3. (非可移植,特定于Linux)使用poll() ,并期望命名管道永不终止,并且可以多次连接和断开连接:

    • open(pipe, O_RDWR | O_NONBLOCK);
    • 主循环:
      • poll()用于POLLIN事件,可能一次在多个管道上。
      • read()尽可能多的数据,可能循环在read()调用。
        • 如果read() == -1errno == EAGAIN ,则返回到poll()步骤。
        • 如果read() == -1errno == EINTR ,则再次read()
        • 如果read() == 0 ,则有些错误 – 在命名管道上不应该发生O_RDWR ,而只能使用O_RDONLY或未命名的管道。 它表示一个必须关闭并重新打开的封闭管道。 如果在同一个poll()事件处理循环中混合使用了命名管道和未命名管道,则可能仍然需要处理这种情况。

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

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