在linux上缓冲异步文件I / O.

时间:2011-04-14 13:36:37

标签: linux asynchronous io linux-kernel aio

我正在寻找在linux上进行异步文件I / O的最有效方法。

POSIX glibc实现在userland中使用线程。

本机aio内核api仅适用于无缓冲操作,内核补丁用于添加对缓冲操作的支持,但是那些> 3岁,似乎没有人关心将它们集成到主线中。

我发现了许多允许异步I / O的其他想法,概念和补丁,尽管其中大多数都是在> 3岁的文章中。在今天的内核中真正可用的是什么呢?我已经读过有关servlet,acalls,内核线程的内容以及我现在甚至都不记得的更多内容。

在今天的内核中进行缓冲异步文件输入/输出的最有效方法是什么?

4 个答案:

答案 0 :(得分:35)

除非你想编写自己的IO线程池,否则glibc实现是一个可接受的解决方案。对于完全在用户区运行的东西,它实际上效果非常好。

根据我的经验,内核实现根本不适用于缓冲IO(尽管我见过其他人说的相反!)。如果你想通过DMA读取大量数据,这很好,但是如果你打算利用缓冲区缓存,当然会花费大量时间。
另请注意,内核AIO调用实际上可能会阻塞。存在一个有限大小的命令缓冲区,大型读取被分解为几个较小的读取缓冲区。队列满后,异步命令同步运行。惊喜。我在一两年前遇到过这个问题而无法找到解释。问周围给了我“当然,这就是它的工作方式”答案 从我的理解来看,支持缓冲aio的“官方”兴趣也不是非常好,尽管多年来似乎可以使用多种工作解决方案。我读过的一些论点是“你不想使用缓冲区”和“没有人需要”和“大多数人甚至不使用epoll”。好吧......好吧。

直到最近,能够通过完成的异步操作发出epoll信号是另一个问题,但与此同时,这通过eventfd工作得很好。

请注意,glibc实现实际上会在__aio_enqueue_request内按需生成线程。它可能没什么大不了的,因为产生线程不再非常昂贵,但是人们应该意识到这一点。如果您对启动异步操作的理解是“立即返回”,则该假设可能不正确,因为它可能首先产生一些线程。

修改
作为旁注,在Windows下,存在与glibc AIO实现中非常相似的情况,其中“立即返回”假设排队异步操作不正确。
如果您想要读取的所有数据都在缓冲区缓存中,Windows将决定它将改为同步运行请求,因为它无论如何都会立即完成。这是有据可查的,而且听起来也很棒。除非有几兆字节需要复制,或者如果另一个线程有页面错误或同时进行IO(因此竞争锁定),“立即”可能是一个令人惊讶的长时间 - 我已经看到“立即”时间为2 -5毫秒。这在大多数情况下都没有问题,但是例如在16.66ms帧时间的约束下,您可能不希望在随机时间内阻塞5ms。因此,天真的假设“可以从我的渲染线程做异步IO没问题,因为异步不会阻塞”是有缺陷的。

答案 1 :(得分:3)

材料看起来很旧 - 好吧,它 旧 - 因为它已经存在了很长时间,虽然并非琐碎,但是很容易理解。你可以解决的解决方案发表在W. Richard Stevens的精湛无与伦比的书中(读作“圣经”)。这本书是一本清晰,简洁,完整的稀有宝藏:每一页都给出了真实而直接的价值:

Advanced Programming in the UNIX Environment

另外两个,也是史蒂文斯,是他的 Unix网络编程集合的前两卷:

Volume 1: The Sockets Networking API (与Fenner和Rudoff合作)
Volume 2: Interprocess Communications

我无法想象没有这三本基础书籍;当我找到一个没有听说过他们的人时,我很沮丧。

更多史蒂文的书,同样珍贵:

TCP/IP Illustrated, Vol. 1: The Protocols

答案 2 :(得分:2)

我不认为异步文件I / O的Linux内核实现真的可用,除非你也使用O_DIRECT,抱歉。

这里有关于当前世界状况的更多信息:https://github.com/littledan/linux-aio。它曾于2012年由曾在Google工作过的人更新过。

答案 3 :(得分:1)

(2021) 如果您的 Linux 内核足够新(至少 5.1 但更新的内核带来了改进),那么 io_uring will be "the most efficient way to do asynchronous file input/output" *。这适用于缓冲和直接 I/O!

在 Kernel Recipes 2019 视频“通过 io_uring 实现更快的 IO”中,io_uring 作者 Jens Axboe demonstrates buffered I/O via io_uring finishing in almost half the time of synchronous buffered I/O。正如@Marenz 指出的那样,除非您想要用户空间线程 io_uring 是执行缓冲异步 I/O 的唯一方式,因为 Linux AIO (aka libaio/io_submit()) doesn't have the ability to always do buffered asynchronous I/O...

此外,在“现代存储非常快”一文中。 Glauber Costa demonstrates how careful use of io_uring with asynchronous direct I/O can improve throughput compared to using io_uring for asynchronous buffered I/O 在傲腾设备上。它要求 Glauber 有一个用户空间预读实现(如果没有它,缓冲 I/O 显然是赢家),但改进令人印象深刻。


* 这个答案的上下文显然与存储有关(毕竟提到了缓冲这个词)。对于网络 I/O,io_uring 在后来的内核中得到了稳步改进,以至于它可以与 epoll() 之类的东西进行交易,如果它继续下去,总有一天它会在所有情况下都相等或更好。< /p>