考虑一个受CPU限制的应用程序,但也有高性能的I / O要求。
我正在将Linux文件I / O与Windows进行比较,我看不出epoll将如何帮助Linux程序。内核会告诉我文件描述符“已准备好读取”,但我仍然需要调用阻塞read()来获取我的数据,如果我想读取兆字节,那么很明显它会阻塞。
在Windows上,我可以创建一个设置了OVERLAPPED的文件句柄,然后使用非阻塞I / O,并在I / O完成时收到通知,并使用该完成功能中的数据。我不需要花费任何应用程序级别的挂钟时间等待数据,这意味着我可以将我的线程数精确调整到我的内核数量,并获得100%有效的CPU利用率。
如果我必须在Linux上模拟异步I / O,那么我必须分配一些线程来执行此操作,并且这些线程将花费一些时间来做CPU事情,并且很多时间阻塞我/ O,加上来自/来自这些线程的消息将产生开销。因此,我将过度订阅或未充分利用我的CPU核心。
我把mmap()+ madvise()(WILLNEED)视为“穷人的异步I / O”,但它仍然无法完全通过,因为我无法收到通知 - - 我必须“猜测”,如果我猜“错误”,我将最终阻止内存访问,等待数据来自磁盘。
Linux似乎在io_submit中启动了异步I / O,它似乎也有一个用户空间POSIX aio实现,但它已经有一段时间了,我知道没有人会担保这些用于关键,高性能应用的系统。
Windows模型大致如下:
步骤1/2通常作为单一事物完成。步骤3/4通常使用工作线程池完成,而不是(必要)与发出I / O相同的线程。这个模型有点类似于boost :: asio提供的模型,除了boost :: asio实际上不会给你异步的基于块的(磁盘)I / O.
Linux中epoll的不同之处在于,在步骤4中,还没有I / O发生 - 它会在步骤4之后提升步骤1,如果你确切知道你需要的话,那就是“向后”。
编写了大量的嵌入式,桌面和服务器操作系统之后,我可以说这种异步I / O模型对于某些类型的程序来说非常自然。它还具有非常高的吞吐量和低开销。我认为这是Linux I / O模型在API级别上仍然存在的真正缺点之一。
答案 0 :(得分:15)
Peter Teoh间接指出的真正答案是基于io_setup()和io_submit()。 具体来说,Peter指示的“aio_”函数是基于线程的glibc用户级仿真的一部分,这不是一种有效的实现。 真正的答案是:
io_submit(2)
io_setup(2)
io_cancel(2)
io_destroy(2)
io_getevents(2)
请注意,2012 - 08年的手册页说,这个实现尚未成熟到可以取代glibc用户空间模拟的程度:
http://man7.org/linux/man-pages/man7/aio.7.html
这个实现还没有成熟到POSIX的地步 可以使用内核完全重新实现AIO实现 系统调用。
因此,根据我发现的最新内核文档,Linux还没有一个成熟的,基于内核的异步I / O模型。而且,如果我假设记录的模型实际上已经成熟,那么它仍然不支持recv()vs read()意义上的部分I / O.
答案 1 :(得分:7)
(2019)如果您使用的是5.1 or above kernel,则可以将io_uring
interface用于类似文件的I / O并获得出色的异步操作。
与现有的libaio
/ KAIO接口io_uring
相比,具有以下优点:
recvmsg()
/ sendmsg()
,请参阅在io_uring.c's git history中提及支持一词的消息)与glibc的POSIX aio相比,io_uring
具有以下优点:
io_uring
当然可以! "Efficient IO with io_uring"文档会定期更新,并且会详细介绍io_uring
的好处和用法。 "What's new with io_uring"文档描述了自io_uring
以来添加的新功能。还有io_uring
作者Jens Axboe的"Faster IO through io_uring" videoed presentation。
关于“在recv()
与read()
的意义上支持部分I / O”:patch went into the 5.3 kernel that will automatically retry io_uring
short reads。一个尚待解决的补丁(我猜它将在5.4内核中出现)对该行为进行了进一步的调整,并only automatically takes care of short reads when working with "regular" files when the request isn't REQ_F_NOWAIT
(似乎可以通过REQ_F_NOWAIT
或通过打开IOCB_NOWAIT
O_NONBLOCK
的文件)。因此,看起来您也可以从recv()
获得io_uring
样式的“简短” I / O行为。
很显然,在撰写本文时(2019年),io_uring
接口是一个非常新的接口,但希望它将为Linux带来一个更好的基于异步文件的I / O故事。
答案 2 :(得分:6)
如下所述:
http://code.google.com/p/kernel/wiki/AIOUserGuide
在这里:
http://www.ibm.com/developerworks/library/l-async/
Linux确实在内核级提供异步块I / O,API如下:
aio_read Request an asynchronous read operation
aio_error Check the status of an asynchronous request
aio_return Get the return status of a completed asynchronous request
aio_write Request an asynchronous operation
aio_suspend Suspend the calling process until one or more asynchronous requests have completed (or failed)
aio_cancel Cancel an asynchronous I/O request
lio_listio Initiate a list of I/O operations
如果您询问这些API的用户是谁,那么它就是内核本身 - 这里只显示了一小部分:
./drivers/net/tun.c (for network tunnelling):
static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
./drivers/usb/gadget/inode.c:
ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
./net/socket.c (general socket programming):
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,
./mm/filemap.c (mmap of files):
generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
./mm/shmem.c:
static ssize_t shmem_file_aio_read(struct kiocb *iocb,
等
在用户空间级别,还有io_submit()etc API(来自glibc),但以下文章提供了使用glibc的替代方法:
http://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt
它直接实现了像io_setup()这样的函数的API作为直接系统调用(绕过glibc依赖关系),应该存在通过相同的“__NR_io_setup”签名的内核映射。在以下位置搜索内核源代码:
http://lxr.free-electrons.com/source/include/linux/syscalls.h#L474(URL适用于最新版本3.13)您将在内核中直接实现这些io _ *()API:
474 asmlinkage long sys_io_setup(unsigned nr_reqs, aio_context_t __user *ctx);
475 asmlinkage long sys_io_destroy(aio_context_t ctx);
476 asmlinkage long sys_io_getevents(aio_context_t ctx_id,
481 asmlinkage long sys_io_submit(aio_context_t, long,
483 asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,
glibc的更高版本应该使用“syscall()”来调用sys_io_setup(),但是没有最新版本的glibc,如果你使用具有这些功能的更高版本的内核,你可以自己进行这些调用。 “sys_io_setup()”。
当然,异步I / O还有其他用户空间选项(例如,使用信号?):
http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf
或perhap:
What is the status of POSIX asynchronous I/O (AIO)?
“io_submit”和glibc中的朋友仍然没有(请参阅io_submit联机帮助页),我已在Ubuntu 14.04中验证过,但此API是特定于Linux的。
libuv,libev和libevent等其他人也是异步API:
http://nikhilm.github.io/uvbook/filesystem.html#reading-writing-files
http://software.schmorp.de/pkg/libev.html
所有这些API都旨在通过BSD,Linux,MacOSX甚至Windows进行移植。
就性能而言,我没有看到任何数字,但由于其轻量化,怀疑libuv可能是最快的?
答案 3 :(得分:1)