根据POSIX轮询文档,POLLOUT
标志表示"正常数据可以在不阻塞的情况下写入"。但是有多少数据呢?是否有任何固定保证,或者有什么方法可以找出有多少缓冲区空间?
我正在写管道和套接字。通常,我的程序不会EINTR
,因为所有预期的信号都是通过signalfd
/kqueue
处理的;据我所知,这意味着我可以期望write
始终阻止,直到所有请求的数据都被传输完成。
不确定我是否想要这个。理想情况下,我只想写入缓冲区中空间尽可能多的数据,以便代码可以尽快返回轮询循环而不会阻塞。(如果还有数据要写,我的代码可以再次检查同一 FD 上的POLLOUT
,并在空间可用时再次执行相同的操作。
有没有办法使这项工作做好? poll
似乎很好地从阻塞管道/套接字 FD 读取:设置 POLLIN
标志时,您读取缓冲区,并且您可以获得尽可能多的可用数据而不会阻塞。但是它开始看起来有点不方便写作!我错过了什么吗?
还是这一切都只是想告诉我使用O_NONBLOCK
?
我正在开发OS X和Linux。
从表面上看,您保证最多可以写入 1 个字节。
切换到非阻塞模式也无助于改善情况。当然,您不会阻塞,但您必须弄清楚如何处理非阻塞写入未写入的任何数据。poll(( 会给你相同的结果,而不管文件描述符的阻塞模式如何。
您可以尝试几种替代方案,异步 IO 或 ZeroMQ 等框架。
异步 IO
有异步 IO,它有效地归结为让一堆后台线程为您编写。这很好,但是您必须管理数据生命周期/所有权问题。您必须将正在写入的内容保留在原地,直到您被告知异步写入已完成。至少可以说,这可能很繁琐。
框架
另一种选择是消息框架,如ZeroMQ。还有其他的 - nanomsg(由做ZeroMQ的同一个人编写(,DDS,Corba(如果你敢的话(,ESA的TASTE等。
直接更换
这些都有不同的品质,但特别是ZeroMQ和nanomsg被设计为将代码从使用标准套接字和管道API转换为使用自己的(特别是nanomsg(的最不痛苦的方式。
消息,而不是流
所有这些都是面向消息的,而不是面向流的,它们都很好地为您管理通过某种连接传递数据的痛苦业务,并解决了数据生命周期问题。
基础传输
ZeroMQ和nanomsg将跨管道,套接字,共享内存等工作。它们还可以在代码等待输入时将普通文件描述符合并到它们的 poll(( 等效项中(例如,如果您有串行端口的 fd 可以侦听,则很有用(。
与异步 IO 的比较
此类框架和异步io之间的区别如下。对于框架,连接另一端的软件也必须使用相同的框架,否则它们将无法通信。使用 aio,另一端仍然可以使用普通的同步套接字函数调用,因为从根本上说,它仍然只是一个流连接。
这些框架都比 aio 更不透明。谁知道框架内部有多少数据复制?这通常无关紧要,假设您的 RAM 比您的网络快得多。
0MQ/纳米msg 与其余的
ZeroMQ/nanomsg 与 DDS/Corba/TASTE 等产品之间的区别在于后者也包含序列化。它们允许您使用独立于平台的描述语言指定消息结构,该语言被"编译"为您正在使用的任何源代码语言(C,C#,JAVA等(。这是非常自然的,有点像声明 C 结构。这允许您在连接的另一端拥有完全不同的平台和编程语言,它们仍然会说话。DDS和Corba使用IDL,TASTE使用ASN.1(在各方面都更胜一筹(,但TASTE有点难以上手。
你可以通过使用Google协议缓冲区或ASN.1(或许多可用方法中的任何一个(来序列化数据,并使用ZeroMQ/nanomsg将序列化器生成的字节流作为消息传输,从而使用ZeroMQ/nanomsg达到相同的效果。
唯一的保证是你可以写至少 1 个字节;但是,通常这会更多,因为大多数操作系统都很聪明,可以避免愚蠢的窗口综合症。
我不确定我是否想要这个。理想情况下,我只想写入尽可能多的数据 因为缓冲区中有空间,以便代码可以返回到 轮询循环尽快无阻塞。
然后使用O_NONBLOCK。对套接字的 write(( 将返回写入内部缓冲区的字节数。如果您尝试写入 10,000 字节并且 write(( 返回 2,000,则您还剩下 8,000 字节。它需要更多的内部内务管理(嗯,一个指针和一个整数(,但这是保持内核缓冲区已满的最有效方法。
请注意,如果您已完成写入,请清除events
标志中的位,否则您的 poll(( 调用将继续返回"写入更多数据!