【问题标题】:O_RDWR on named pipes with poll()使用 poll() 在命名管道上的 O_RDWR
【发布时间】:2013-02-09 21:27:19
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 您阅读过mkfifo(3)fifo(7) 手册页吗?而且您不会获得无限数量的 EOF 事件....您不能在一侧打开它O_RDONLY,在另一侧打开O_WRONLY(然后适当地打开poll)?而fifo(7) 明确表示“在阻塞和非阻塞模式下打开 FIFO 进行读写都会成功”
  • 是的,你是对的 fifo(7) 提到它在 Linux 下是可以的(尽管它不是标准化的 POSIX 行为)。这里的问题是我不能只用“O_RDONLY”打开读取端,用“O_WRONLY”打开写入端,因为默认情况下这会阻塞,这在我的用例中是不允许的。所以虽然我理论上可以用 O_RDONLY | 打开它O_NONBLOCKING 它不会阻塞,但由于可能没有写入器 EOF 事件将在 poll() 上抛出。所以我将不得不使用 O_RDWR 以避免在 open() 上的阻塞行为并能够使用 poll()...

标签: linux ipc pipe mkfifo


【解决方案1】:

首先,一些准备工作:

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

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

现在,对于 Linux:

  • 如果您在阅读端使用O_RDONLY 打开,则:
    • open() 将阻塞,直到有相应的写入程序打开。
    • 当数据准备好读取或发生 EOF 时,poll() 将发出 POLLIN 事件。
    • read() 将阻塞直到读取请求的字节数、关闭连接(返回 0)、被信号中断或发生某些致命的 IO 错误。这种阻塞方式违背了使用poll() 的目的,这就是为什么poll() 几乎总是与O_NONBLOCK 一起使用。您可以在超时后使用alarm()read() 中唤醒,但这过于复杂。
    • 如果作者关闭,那么读者将收到poll() POLLHUP revent 并且read() 将在之后无限期地返回0。此时,阅读器必须关闭其文件句柄并重新打开它。
  • 如果您在阅读端使用O_RDONLY | O_NONBLOCK 打开,则:
    • open() 不会阻止。
    • 当准备好读取数据或发生 EOF 时,poll() 将发出 POLLIN revent。 poll() 也将阻塞,直到写入器可用(如果没有写入器)。
    • 读取所有当前可用数据后,如果连接仍处于打开状态,read() 将返回 -1 并设置 errno == EAGAIN,如果连接关闭 (EOF),它将返回 0 或尚未由作者打开。当errno == EAGAIN 时,这意味着是时候返回poll(),因为连接已打开但没有更多数据。当errno == EINTRread()还没有读到字节,被信号打断了,可以重启。
    • 如果作者关闭,那么读者将收到一个poll()POLLHUP revent,然后read() 将无限期地返回0。此时阅读器必须关闭其文件句柄并重新打开它。
  • (Linux 专用:)如果您在阅读端使用O_RDWR 打开,则:
    • open() 不会阻止。
    • 当准备好读取数据时,poll() 将发出 POLLIN 事件。但是,对于命名管道,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 revents 或返回 0对于read(),就是使用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() 事件处理循环中混合使用命名管道和未命名管道,则可能仍需要处理这种情况。

【讨论】:

  • (2.) 拯救了我的一天 =) 谢谢!
  • 很好的答案!这完全揭开了有关管道的所有奇怪事物的神秘面纱
【解决方案2】:

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

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

【讨论】:

  • 谢谢,但在 linux 上,我猜“O_WRONLY|O_NONBLOCK”将不起作用...问题是我无法在打开时阻塞并想使用 poll() 所以当我使用 O_RDONLY|O_NONBLOCK 时不会阻塞,但 poll() 会一直触发 EOF 事件......所以“O_NONBLOCK”和 poll() 是一个糟糕的组合。因此,尽管它没有 POSIX 标准,但似乎我必须使用 O_RDWR。那么有没有办法在没有 O_RDWR 的非阻塞管道上使用 poll() ???
  • 我从未编写过代码,但手册页说O_WRONLY|O_NONBLOCK 应该可以在没有阻塞的情况下工作。你测试过吗?当然你不能用poll 换成open(因为poll 需要open 返回的文件描述符)
  • 非常感谢。我稍后会试试这个。我不打算公开投票。成功打开管道后,我将在读取时使用 POLLIN....
【解决方案3】:

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

而且它是 100% POSIX

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 2019-10-22
    • 2023-03-09
    • 1970-01-01
    • 2011-10-24
    相关资源
    最近更新 更多