Jon*_*tte 25 linux posix asynchronous overlapped-io
考虑一个受CPU限制的应用程序,但也具有高性能I/O要求.
我正在将Linux文件I/O与Windows进行比较,我看不出epoll将如何帮助Linux程序.内核会告诉我文件描述符"准备好读取",但是我仍然需要调用阻塞read()来获取我的数据,如果我想读取兆字节,那么很明显它会阻塞.
在Windows上,我可以创建一个设置了OVERLAPPED的文件句柄,然后使用非阻塞I/O,并在I/O完成时收到通知,并使用该完成函数中的数据.我需要不花费应用程序级别的挂钟时间等待数据,这意味着我可以精确地将我的线程数调整为我的内核数量,并获得100%的高效CPU利用率.
如果我必须在Linux上模拟异步I/O,那么我必须分配一些线程来执行此操作,并且这些线程将花费一些时间来处理CPU事务,并且大量时间阻塞I/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级别上仍然存在的真正缺点之一.
Jon*_*tte 16
真正的答案是Peter Teoh间接指出的,它基于io_setup()和io_submit().具体来说,Peter指示的"aio_"函数是基于线程的glibc用户级仿真的一部分,这不是一种有效的实现.真正的答案是:
io_submit(2)
io_setup(2)
io_cancel(2)
io_destroy(2)
io_getevents(2)
Run Code Online (Sandbox Code Playgroud)
请注意,2012 - 08年的手册页说,这个实现尚未成熟到可以替换glibc用户空间模拟的程度:
http://man7.org/linux/man-pages/man7/aio.7.html
这个实现还没有成熟到可以使用内核系统调用完全重新实现POSIX AIO实现的程度.
因此,根据我发现的最新内核文档,Linux还没有一个成熟的,基于内核的异步I/O模型.而且,如果我假设所记录的模型实际上已经成熟,那么它仍然不支持recv()vs read()意义上的部分I/O.
如下所述:
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
Run Code Online (Sandbox Code Playgroud)
如果您询问这些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,
Run Code Online (Sandbox Code Playgroud)
等等
在用户空间级别,还有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,
Run Code Online (Sandbox Code Playgroud)
glibc的更高版本应该使用"syscall()"来调用sys_io_setup()是不必要的,但是没有最新版本的glibc,如果你使用具有这些"sys_io_setup"功能的后来内核,你可以自己进行这些调用. ()".
当然,异步I/O还有其他用户空间选项(例如,使用信号?):
http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf
或者:
"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可能是最快的?
https://ghc.haskell.org/trac/ghc/ticket/8400
(2019)如果您使用的是5.1或更高版本的内核,则可以将该io_uring接口用于类似文件的I / O并获得出色的异步操作。
与现有的libaio/ KAIO接口相比io_uring具有以下优点:
与glibc的POSIX aio相比,io_uring具有以下优点:
io_uring大多数情况下肯定可以!关于ioio_uring的好处和用法,“ 带有io_uring的高效IO”文档更加详细。还有一个拍录像,演示文稿“通过io_uring更快的IO”的io_uring作者延斯·克斯博。
重新“以recv()vs的方式支持部分I / O read()”:5.3内核中io_uring有一个补丁,它将自动重试短读取。一个尚待解决的补丁(我猜它将在5.4内核中出现)进一步调整了行为,并且在未请求时处理“常规”文件时仅自动处理短读REQ_F_NOWAIT(看起来您可以请求)REQ_F_NOWAIT通过IOCB_NOWAIT或通过打开文件O_NONBLOCK)。因此,看起来您也可以从中获得recv()样式“短” I / O行为io_uring。
显然,在编写该io_uring接口时,该接口是一个非常新的接口,但希望它将为Linux带来更好的基于异步文件的I / O故事。