Linux和I/O完成端口?

47
使用Winsock,您可以将套接字或单独的I/O操作配置为“重叠”。这意味着执行I/O的调用会立即返回,而实际操作则由单独的工作线程异步完成。
Winsock还提供了“完成端口”。据我了解,完成端口充当句柄(套接字)的复用器。如果一个句柄没有处于I/O操作中,也就是说,如果它的所有I/O操作都已经完成,那么该句柄可以被分解。
那么,接下来我的问题是,Linux是否支持完成端口或套接字的异步I/O?

1
epoll 是 Linux 最快的服务器编程方法。需要注意的是,Linux 目前在互联网服务器市场上的份额比 Windows 更大。 - unixman83
2
@unixman83:我知道epoll。我只是好奇,因为Linux上提到了异步I/O支持,但我不确定是否特别支持套接字(BlackAura证实了)。顺便说一下,epoll并不总是最好的解决方案(请参见http://sheddingbikes.com/posts/1280829388.html)。 - someguy
完成端口不是用于多路复用套接字的。它们可以与单个套接字或文件句柄完美地配合使用。它们是一种数据并行机制。它们的目的是允许多个线程异步处理从套接字或文件接收到的数据。它们也被用于线程池实现,例如.NET的实现。 - Ron Inbar
7个回答

94

如果你正在寻找与IOCP完全相同的东西,那么你会失望,因为它并不存在。

Windows使用一种通知完成模型(因此有I / O 完成端口)。您异步启动某些操作,并在该操作完成时接收通知。

Linux应用程序(以及大多数其他类Unix系统)通常使用通知就绪模型。您会接收到一个通知,表明可以从套接字读取或写入而不会阻塞。然后,您执行I / O操作,这将不会阻塞。

使用此模型,您不需要异步I / O。数据立即被复制到/出套接字缓冲区。

这种模型的编程方式有点棘手,这就是为什么有像libevent这样的抽象库。它提供了更简单的编程模型,并抽象出所支持操作系统之间的实现差异。

在Windows中也有通知就绪模型(如select或WSAWaitForMultipleEvents),也许你之前看过它。它无法扩展到大量套接字,因此不适用于高性能网络应用程序。

不要被这种情况吓倒 - Windows和Linux是完全不同的操作系统。在一个系统上不良扩展的东西,在另一个系统上可能效果很好。这种方法在Linux上实际上非常有效,性能可与Windows上的IOCP相媲美。


4
只要你写入的数据超过了缓冲区的容量,任何同步输入/输出都会阻塞。如果你写入的数据已经被页面换出(paged out),那么你的进程将会在页面错误(page fault)上阻塞。请注意,我已经尽力使翻译简明扼要且易于理解,同时尽可能保留了原意。 - Gabe
@某人:所谓“无位掩码”,是指不使用类似于select中的FD_SET。此外,WSAAsyncSelect需要使用线程消息队列,而非窗口过程API。在消息循环中捕获这些消息并不通过DispatchMessage和窗口过程代码传递它们是完全可能的。 - Ben Voigt
...或者出现错误。GetQueuedCompletionStatus() 可以与 epoll_wait() 相比,而异步的 0 字节 WSARecv() 可以与带有 EPOLL_CTL_MOD|EPOLLIN|EPOLLET|EPOLLONESHOT 标志的 epoll_ctl() 相比。 - Vladislav Vaintroub
3
WSARecv()会安排I/O操作执行。当操作完成后,GetQueuedCompletionStatus()将返回。请注意就绪事件和完成事件之间的区别。 - someguy
我不认为我曾经否认过它。你需要明白,WSARecv() 0字节操作相当于“开始观察准备就绪”。在这种情况下,GetQueuedCompletionStatus表示“是的,可以从套接字读取一些内容”。这是边缘触发的准备就绪通知,与上面的epoll_ctl/epoll_wait示例完全等效。而且epoll不会进行完成通知。 - Vladislav Vaintroub
显示剩余6条评论

23

谢谢。你能提供一些示例代码吗?我之前研究过POSIX AIO,但是找不到与套接字相关的内容。 - someguy
我以前没有使用过POSIX AIO。我只知道它在各种论坛中被讨论过。个人而言,我运行基于BSD的系统,因此我使用Kqueue来实现异步IO。如果您需要并发IO,我建议尝试使用Boost或某些MPI实现。 - D.Shawley
1
我已经研究了POSIX AIO和Kernel AIO,两者都似乎暗示着对套接字没有真正的支持,这很遗憾。我会假设我所阅读的所有文档都已过时,因为Java 7显然将支持异步I/O,所以Linux必须拥有它,或者将拥有它,对吧?也许我会尝试看看Sun/Oracle迄今为止做了什么。 - someguy
3
看起来 POSIX AIO 并没有得到应有的重视。你可能想阅读一些关于“快速 UNIX 服务器”的参考资料(http://dank.qemfd.net/dankwiki/index.php/Fast_UNIX_Servers)。那里似乎有很多有用的信息。 - D.Shawley
4
“asynchronous I/O” 在大多数Unix系统上存在漏洞且支持不佳!请注意。 - unixman83
我被告知完成端口(IOCP)比epoll/select更快,但是Linux现在有io_uring,它是IOCP的Linux版本。 - Scott 混合理论

10

使用boost::asio。毫无疑问。它具有温和的学习曲线,但它是跨平台的,并且自动使用适用于您正在编译的系统的最佳方法。简单地说,没有理由不这样做。

我知道这并不完全回答了你的问题,但这是我能给出的最好建议。


6
我非常讨厌ASIO,因为它极度使用命名空间,但我不得不承认,它是最高质量的免费库,完全支持Windows和Linux。另一方面,Libevent对于Windows的支持效果很差,且不完善。 - unixman83

8
那么,接下来我的问题是... Linux是否支持完成端口或套接字的异步I/O?
关于套接字,在5.3及以上版本的内核中,Linux有类似于完成功能端口的东西,叫做"io_uring"(对于文件/块设备的"io_uring"支持出现在5.1内核中)。(详情请见此处)

1

可能有点晚了,但是这个答案是为了完整性。

截至2020年(v5.11),Linux引入了io_uring,其在语义上类似于Windows上的完成端口,但具有非常不同的API。您可以使用struct io_uring_sqe排队操作,如read()write(),并通过struct io_uring_cqe接收通知以获取完成时的状态。

它还严重依赖于struct iovec,因此建议熟悉该结构和相关的API。有一个名为liburing的小型仅限于Linux的库,可使访问内核API变得更加容易。


被接受的答案指出了io_uring,但感谢您提供的额外细节。对于那些不介意使用高级库的人来说,我相信libev支持io_uring。 - someguy

0

1
我认为libevent不支持异步I/O,甚至在链接页面上也明确说明它是用于非阻塞套接字的。我想以后会更深入地研究它。0MQ也很有趣,但我不认为它是我要找的东西。它似乎是一个消息传递库(类似于erlang actors?)。 - someguy
@someguy 异步 I/O 包括非阻塞套接字。 - jweyrich

0

1
可能更有用的是从提供的链接中引用和总结该页面,因为链接失效会发生,用户最终会点击它并发现 404。 - t0mm13b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接