【问题标题】:POSIX POLLOUT and blocking file descriptorsPOSIX POLLOUT 和阻塞文件描述符
【发布时间】:2016-02-05 02:16:43
【问题描述】:

根据the POSIX poll docsPOLLOUT 标志表示“可以写入正常数据而不会阻塞。”。但是有多少数据?有没有固定的保证,或者有什么办法可以知道有多少缓冲空间?

我正在写管道和套接字。一般来说,我的程序不会得到EINTR,因为所有预期的信号都是通过signalfd/kqueue处理的;据我所知,这意味着我可以期待write 一直阻塞,直到所有请求的数据都传输完毕。

我不确定我是否想要这个。理想情况下,我只想写入与缓冲区空间一样多的数据,以便代码可以尽快返回轮询循环而不会阻塞。 (如果还有数据要写,我的代码可以在同一个 FD 上再次检查POLLOUT,并在空间可用时再次执行相同的操作。)

有什么办法可以让这个工作顺利进行吗? poll 似乎可以很好地从阻塞管道/套接字 FD 中读取:当设置 POLLIN 标志时,您读取到缓冲区,并且您可以在没有阻塞的情况下获得尽可能多的数据。但它开始看起来有点不方便写作!我错过了什么吗?

或者这一切只是想告诉我使用O_NONBLOCK

我正在开发 OS X 和 Linux。

【问题讨论】:

  • 它告诉你使用非阻塞模式。

标签: sockets pipe posix polling


【解决方案1】:

从表面上看,您最多可以写入 1 个字节。

切换到非阻塞模式也无济于事。当然,您不会阻塞,但您必须弄清楚如何处理非阻塞写入未写入的任何数据。无论文件描述符的阻塞模式如何,poll() 都会为您提供相同的结果。

您可以尝试几种替代方案,异步 IO 或 ZeroMQ 等框架。

异步 IO

asynchronous IO,它实际上归结为让一堆后台线程为您编写。这很好,但是您必须管理数据生命周期/所有权问题。在被告知异步写入完成之前,您必须将正在写入的任何内容留在原处并且​​保持不变。至少可以说,这可能很繁琐。

框架

另一个选项是消息框架,例如ZeroMQ。还有其他的 - nanomsg(由编写 ZeroMQ 的同一个人编写)、DDS、Corba(如果你敢的话)、来自 ESA 的 TASTE 等。

直接替换

这些都有不同的品质,但 ZeroMQ 和 nanomsg 尤其被设计为将代码从使用标准套接字和管道 API 转换为使用它们自己的(尤其是 nanomsg)最不痛苦的方式。

消息,而不是流

所有这些都是面向消息的,而不是面向流的,它们都很好地为您管理通过某种连接传递数据的痛苦业务,并解决了数据生命周期问题。

底层传输

ZeroMQ 和 nanomsg 可以跨管道、套接字、共享内存等工作。它们也可以在它们的 poll() 等效项中包含普通文件描述符,以便在代码等待输入时使用(例如,如果你有fd 用于监听的串行端口)。

与异步 IO 的比较

此类框架与异步io的区别如下。使用框架,连接另一端的软件也必须使用相同的框架,否则它们无法通信。有了aio,另一端仍然可以使用普通的同步套接字函数调用,因为它本质上仍然只是一个流连接。

这些框架都比 aio 更不透明。谁知道框架内进行了多少数据复制?这通常无关紧要,假设您的 RAM 比您的网络快得多。

0MQ/nanomsg 对比其他

ZeroMQ/nanomsg 与 DDS/Corba/TASTE 之类的区别在于后者还包含序列化。它们允许您以独立于平台的描述语言指定消息结构,该语言可以“编译”为您使用的任何源代码语言(C、C#、JAVA 等)。这很自然,有点像声明 C 结构。这使您可以在连接的另一端拥有完全不同的平台和编程语言,并且他们仍然会说话。 DDS 和 Corba 为此使用 IDL,TASTE 使用 ASN.1(在所有可能的方面都非常出色),但 TASTE 有点难以使用。

您可以使用 ZeroMQ/nanomsg 来实现相同的效果,例如,使用 Google 协议缓冲区或 ASN.1(或任何 many available)来序列化数据,并使用 ZeroMQ/nanomsg 来传输由序列化器生成的字节流作为消息。

【讨论】:

    【解决方案2】:

    唯一的保证是你至少可以写1个字节;但是,通常这会更多,因为大多数操作系统都很聪明,可以避免silly window syndrome

    我不确定我是否想要这个。理想情况下,我只想写尽可能多的数据 由于缓冲区中有空间,因此代码可以返回 尽快轮询循环而不阻塞。

    然后使用 O_NONBLOCK。对套接字的 write() 将返回写入内部缓冲区的字节数。如果您尝试写入 10,000 个字节并且 write() 返回 2,000,那么您还剩下 8,000 个字节。它需要更多的内部管理(嗯,一个指针和一个整数),但它是保持内核缓冲区满的最有效方法。

    请注意,如果您已完成写入,请从 events 标志中清除该位,否则您的 poll() 调用将继续返回“写入更多数据!”。

    【讨论】:

    • 写入是缓冲的,因此写入一个字节与愚蠢的窗口综合症没有任何关系。
    • 不,相反:愚蠢的窗口综合症可能会导致您只能发送 1 个字节的 write()。抱歉,如果不清楚。
    猜你喜欢
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多