草庐IT

c - Linux内核如何处理异步I/O(AIO)请求?

coder 2023-06-16 原文

我正在编写一个C程序,通过直接从原始块设备文件读取来从SSD驱动器读取数据。

我正在尝试Linux AIO(我正在谈论Linux AIO API,即linuxaio.h提供的功能,例如io_submit(...)等,而不是POSIX AIO API)。我使用O_DIRECT标志打开块设备文件,并确保写入缓冲区的内容与块大小对齐。

我注意到Linux AIO的速度比使用带有O_DIRECT标志的同步IO快得多。

最让我惊讶的是,使用Linux AIO发出许多随机的,每个数KB的小型随机读取所获得的吞吐量,甚至比使用同步I/O和O_DIRECT进行大量的(顺序)读取几个MB所获得的吞吐量要高得多。 。

因此,我想知道:Linux AIO为何比同步I/O更好呢?使用AIO时内核会做什么?内核是否执行请求重新排序?与使用同步I/O相比,使用Linux AIO是否会导致更大的CPU利用率?

在此先多谢

最佳答案

简短答案:
AIO实现很可能是“更快”的,因为它并行提交多个IO,而同步实现在运行中有零个或一个I/O。它与写入内存或内核I/O路径无关,而后者具有同步I/O的额外开销。

您可以使用iostat -x -d进行检查。1.查看avgqu-sz(平均队列大小=运行中I/O的平均数量)和%util(利用率=设备至少拥有时间的百分比)向其发出一个I/O)。

长答案:

  • 在谈论I/O时,“更快”的概念很棘手。 “更快”是否意味着更高的带宽?还是更低的延迟?还是给定请求大小的带宽?还是在给定队列深度下的延迟?还是延迟,带宽,请求大小,队列深度以及许多其他参数或工作负载的组合?我在这里假设您正在考虑吞吐量/带宽,但是,请记住,存储设备的性能不是一个单一维度的指标。
  • SSD是高度并行的设备。 SSD由许多闪存芯片组成,每个芯片都有多个可以独立读取/写入的管芯。 SSD充分利用了这一优势,可以并行执行许多I/O,而响应时间却没有明显增加。因此,就吞吐量而言,SSD看到多少个并行I/O至关重要。
  • 让我们了解当线程提交同步I/O时会发生什么:a)线程花费一些CPU周期来准备I/O请求(生成数据,计算偏移量,将数据复制到缓冲区等),b)系统调用执行(例如pread()),执行传递到内核空间和线程块,c)I/O请求由内核处理并遍历各个内核I/O层,d)I/O请求是提交给设备并遍历互连模块(例如PCIe),e)SSD固件处理I/O请求,f)将实际的读取命令发送到适当的闪存芯片,g)SSD Controller 等待数据,h)SSD Controller 从闪存芯片获取数据并通过互连发送。此时,数据离开SSD,而e-a)阶段则相反。
  • 如您所见,同步I/O进程正在使用SSD播放请求乒乓球。在上述许多阶段中,实际上没有从闪存芯片中读取任何数据。最重要的是,尽管您的SSD可以并行处理数十至数百个请求,但它在任何给定时刻最多只能看到一个请求。因此,吞吐量非常非常低,因为您实际上并未真正使用SSD。
  • 异步I/O有两种帮助方式:a)允许进程并行提交多个I/O请求(SSD有足够的工作来保持繁忙),b)允许在各个处理阶段对I/O进行流水线化(因此将阶段延迟与吞吐量脱钩)。
  • 之所以看到异步I/O比同步I/O快,是因为您比较了苹果和橘子。同步吞吐量是在给定的请求大小,低队列深度且没有流水线的情况下。异步吞吐量是在不同的请求大小,更高的队列深度以及流水线的情况下进行的。您看到的数字不可比。
  • 大多数I/O密集型应用程序(即,大多数应用程序,例如数据库,Web服务器等)都有许多执行同步I/O的线程。尽管每个线程可以在任何给定的时间最多提交一个I/O,但是内核和SSD设备会看到许多可以并行处理的I/O请求。多个同步I/O请求与多个异步I/O请求具有相同的好处。

    异步和同步I/O之间的主要区别在于I/O和处理调度的方式以及编程模型。如果操作正确,异步和同步I/O都可以从存储设备中压缩相同的IOPS/吞吐量。
  • 关于c - Linux内核如何处理异步I/O(AIO)请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28999765/

    有关c - Linux内核如何处理异步I/O(AIO)请求?的更多相关文章

    1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    2. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

      在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

    3. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

      我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

    4. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

      rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

    5. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

      我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

    6. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

      在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

    7. Ruby - 如何处理子类意外覆盖父类(super class)私有(private)字段的问题? - 2

      假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案

    8. ruby - HTTP 请求中的用户代理,Ruby - 2

      我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)

    9. ruby-on-rails - 获取并发布相同匹配项的请求 - 2

      在我的路线文件中我有:match'graphs/(:id(/:action))'=>'graphs#(:action)'如果是GET请求(工作)或POST请求(不工作),我想匹配它我知道我可以使用以下方法在资源中声明POST请求:post'/'=>:show,:on=>:member但是我怎样才能为比赛做到这一点呢?谢谢。 最佳答案 如果你同时想要POST和GETmatch'graphs/(:id(/:action))'=>'graphs#(:action)',:via=>[:get,:post]编辑默认值可以设置如下match'g

    10. ruby-on-rails - 我如何处理 View 中的 nils? - 2

      我设置了以下模型:classContact:no_freq?validates_presence_of:freq,:if=>:no_band?protecteddefno_freq?freq.nil?enddefno_band?band.nil?endendclassBand当我在我的新View中输入频率时,如果输入了频率,则不允许指定波段。这在我的其他观点中造成了问题,因为band现在为零。我如何允许不指定band并在我的index和showView中显示为空,然后在editView中允许在以后指定一个。通过执行以下操作,我已经能够让我的索引显示空白:contact.band&&co

    随机推荐