异步 IO 中实际发生了什么

What actually happens in asynchronous IO

我一直在阅读为什么异步 IO 比同步 IO 好,这是因为在异步 IO 中,您的程序可以保持 运行ning,而在同步 IO 中,您会被阻塞直到操作完成.
我不理解这句话,因为内核使用同步 IO(例如 write())将数据写入磁盘 - 它不会自行发生。内核确实需要 CPU 时间才能完成。
所以在异步 IO 中,它也需要它,这可能会导致上下文从我的应用程序切换到内核。所以它并不是真正的阻塞,但是cpu个周期确实需要运行这个操作。


同步IO示例:

异步IO示例:

The kernel do need CPU time in order to do it.

Is that correct?.

差不多,是的。

Is the difference between those two that we assume disk access is slow ... in a-sync IO the time you wait for it to be written to disk can be used to continue doing application processing, and the kernel part of writing it to disk is small?

完全正确。

Let's say I have an application that all it does is get info and write it into files. Is there any benefit for using a-sync IO instead of sync IO?

取决于很多因素。应用程序如何“获取信息”? CPU 密集吗?它使用与写入相同的IO吗?它是同时处理多个请求的服务吗?有多少同时连接?性能首先重要吗?在某些情况下:是的,使用异步 IO 可能会有很大的好处。在其他一些情况下,您可能会通过在单独的线程中使用同步 IO 来获得大部分好处。在其他情况下,单线程同步 IO 就足够了。

上下文切换在任何情况下都是必要的。内核总是在它自己的上下文中工作。因此,同步访问不会节省处理器时间。 通常,写入不需要大量的处理器工作。限制因素是磁盘响应。问题是我们是否会等待此响应来完成我们的工作。

Let's say I have an application that all it does is get info and write it into files. Is there any benefit for using a-sync IO instead of sync IO?

如果您实施同步访问,您的顺序如下:

  1. 获取信息
  2. 写入信息
  3. 转到 1.

因此,在 write() 完成之前您无法获取信息。让信息供应商与您写入的磁盘一样慢。在这种情况下,程序将比异步程序慢两倍。 如果信息提供者迫不及待地在你写的时候保存信息,你写的时候就会丢失部分信息。此类信息源的示例可以是用于快速处理的传感器。在这种情况下,您应该同步读取传感器并异步保存获取的值。

您的理解有一部分是对的,但是您使用哪些工具取决于您喜欢哪种编程模型,并不能确定您的程序是否会冻结等待 I/O 操作完成。对于特定的、专门的、负载非常高的应用程序,某些模型的效率略有提高,但除非您处于这种情况,否则您应该选择可以轻松编写和维护程序的模型,并让它成为可移植到您和您的用户关心的系统,而不是那些被宣传为高性能的系统。

传统上,有两种不阻塞的方法I/O:

  1. 将您的程序构建为事件循环,在一组文件描述符上执行 select(如今 pollselect 已过时且存在严重缺陷)可能已准备好读取输入或接受输出。这需要为您尚未准备好处理的部分输入和您尚未能够写出的待处理输出保持某种显式状态。

  2. 将 I/O 分隔到单独的执行上下文中。从历史上看,Unixy 方法是单独的进程,当您有其他原因想要单独的进程(特权隔离等)时,这仍然有意义,但更现代的方法是使用线程。每个 I/O 通道都有一个单独的执行上下文,你可以只使用正常的阻塞 read/write (或者甚至是缓冲的 stdio 函数),任何部分输入或未完成的输出状态都会为你保留 隐含地在其执行上下文的调用框架stack/local变量中。

请注意,在上述两个选项中,只有后者有助于解决因磁盘访问缓慢而导致的停顿,因为根据 select/[=11=,常规文件始终“准备好”输入和输出].

现在有一种趋势,可能主要是由于像 JavaScript 这样的语言,转向第三种方法,即“异步模型”,甚至处理程序回调。我发现与上述任何一种方法相比,它更难使用,需要更多的样板代码,也更难推理,但很多人喜欢它。如果你想使用它,最好使用一个库来抽象你提到的 Linux 主义(io_uring 等),这样你的程序可以 运行 在其他系统上和不依赖于最新的 Linux 时尚。

现在回答您的特定问题:

Let's say I have an application that all it does is get info and write it into files. Is there any benefit for using a-sync IO instead of sync IO?

如果您的应用程序具有单一输入源(无交互性)和单一输出,例如像大多数 unix 命令一样,无论哪种 programmind 模型(事件循环、线程、异步回调等),任何类型的异步 I/O 绝对没有任何好处。最简单高效的就是读写。

I do not understand this saying because using sync IO (such as write()) the kernel writes the data to the disk - it doesn't happen by itself. The kernel do need CPU time in order to do it.

没有。大多数现代设备都能够自行传输数据 to/from RAM(使用 DMA 或总线控制)。

举个例子; CPU 可能会告诉磁盘控制器“将 4 个扇区读入地址为 0x12345000 的 RAM”,然后 CPU 可以在磁盘控制器进行传输时做任何它喜欢的事情(并且会被 IRQ 中断)当磁盘控制器完成数据传输时从磁盘控制器。

但是;对于现代系统(你可以有任意数量的进程都想同时使用同一个设备)设备驱动程序必须维护一个挂起操作列表。在这种情况下(负载下);当设备生成一个 IRQ 表明它完成了一个操作时,设备驱动程序通过告诉设备开始下一个“挂起的操作”来响应。这样,设备几乎不会闲置等待被要求开始下一个操作(更好的设备利用率),并且 CPU 几乎所有时间都在做其他事情(在 IRQ 之间)。

当然硬件通常更先进(例如本身有一个内部操作队列,所以驱动程序可以告诉它做多个事情并且它可以在完成前一个操作后立即开始下一个操作);并且驱动程序通常更高级(例如,具有“IO 优先级”以确保首先完成更重要的事情,而不是仅仅具有一个简单的待处理操作的 FIFO 队列)。

Let's say I have an application that all it does is get info and write it into files. Is there any benefit for using a-sync IO instead of sync IO?

假设您从设备 A 获取信息(而 CPU 和设备 B 处于空闲状态);然后稍微处理该信息(当 deviceA 和 deviceB 空闲时);然后将结果写入 deviceB(而 deviceA 和 CPU 处于空闲状态)。你可以看到大多数硬件大部分时间什么都不做(利用率低)。

带异步IO;当 deviceA 正在获取下一条信息时,CPU 可以处理当前信息,而 deviceB 正在写入前一条信息。在理想条件下(没有速度不匹配),您可以实现 100% 的利用率(deviceA、CPU 和 deviceB 从不空闲);并且即使存在速度不匹配(例如,deviceB 需要等待 CPU 来完成当前片段的处理),任何空闲时间都会被最小化(并且利用率尽可能最大化)。

另一种选择是使用多个任务 - 例如一个任务从 deviceA 同步获取数据,并在读取数据时通知另一个任务;第二个任务等待数据到达并处理它,并在处理数据时通知另一个任务;然后是第三个任务,等待数据处理完毕并将其同步写入 deviceB。供使用;这实际上与使用异步 IO 相同(实际上它可以被认为是“异步 IO 的仿真”)。问题是你已经添加了一堆额外的开销来管理和同步多个任务(更多的 RAM 花在状态和堆栈上,任务切换,锁争用,......);并使代码更复杂,更难维护。

异步IO并不比同步IO好。反之亦然。

问题是哪个更适合您的用例。

同步 IO 通常更易于编码,但异步 IO 可以以更复杂的代码为代价带来更好的吞吐量和响应能力。

我从来没有从仅用于文件访问的异步 IO 中受益,但某些应用程序可能从中受益。

访问“慢速”IO 的应用程序(如网络或终端)受益最大。使用异步 IO 可以让他们在等待 IO 完成的同时做有用的工作。这可能意味着能够为更多客户端提供服务或保持应用程序响应用户。

(“慢”只是意味着 IO 操作完成的时间是无限的,它可能永远不会完成,例如,在等待用户按下回车键或网络客户端发送命令时)

归根结底,异步IO并没有少干活,只是时间分配不同,减少了空闲等待。