专栏名称: OSC开源社区
OSChina 开源中国 官方微信账号
目录
相关文章推荐
码农翻身  ·  在内卷的修罗场,国产技术该站出来了 ·  10 小时前  
程序员的那些事  ·  65 ... ·  昨天  
51好读  ›  专栏  ›  OSC开源社区

Linux 下访问 I\/O 的各种方法,我们为 Scylla 选择了哪个?为什么?

OSC开源社区  · 公众号  · 程序员  · 2017-10-16 08:30

正文


当大多数服务器应用程序开发人员想到 I/O 时,首先他们会考虑网络 I/O,因为大多数资源可以通过网络进行访问:数据库,对象存储以及其他微服务。 然而,数据库的开发人员也必须考虑文件 I/O。 本文介绍了可用的选择及其权衡,以及为什么 Scylla 选择异步直接 I/O(AIO/DIO)作为其访问方法。


选择访问文件


一般来说,访问 Linux 服务器上的文件有四种选择:读/写,mmap,直接 I/O(DIO)读/写和异步直接 I/O (AIO/DIO)。


传统读/写


从一开始就使用的传统方法,就是使用 read(2)和 write(2)系统调用。而在最新的实现中,读系统调用(或其许多变体之一 -  pread,readv,preadv 等)要求内核读取文件的一部分并将数据复制到调用进程地址空间中。如果所有请求的数据都在页面缓存中,则内核会将其复制并立即返回;否则,它将安排磁盘将请求的数据读入页面缓存,阻止调用线程,并且当数据可用时,它将恢复线程并复制数据。另一方面,写入通常 (注解 1 )会将数据复制到页面缓存中;内核会将页面缓存写回磁盘一段时间。

Mmap


一种替代和更现代的方法是使用 mmap(2),系统调用将文件记录到应用程序地址空间中。 这会导致一部分地址空间直接引用到包含文件数据的页面缓存页面。 在此准备步骤之后,应用程序可以使用处理器的存储器读和写指令访问文件数据。 如果请求的数据恰好在缓存中,则内核将被完全忽略,并且以内存速度执行读取(或写入)。 如果发生高速缓存未命中,则会发生页错误,并且内核将活动线程置于休眠状态,以便读取该页面的数据。 当数据最终可用时,存储器管理单元被编程,使得新读取的数据可被线程访问,然后被唤醒。

Direct I/O (DIO)


传统的读/写和 mmap 都涉及内核页缓存,并将内核的 I/O 延迟调度。 当应用程序希望自己调度 I/O(至于原因我们稍后将解释),它可以使用 Direct I/O。 这涉及使用 O_DIRECT 标志打开文件; 进一步的活动将使用系统调用的正常读写序列,但是它们的行为已经改变:不是去访问缓存,而是直接访问磁盘,这意味着调用线程将无条件地进入休眠状态。 此外,磁盘控制器将直接将数据复制到用户空间,绕过内核。

异步 direct I/O (AIO/DIO)


Direct I/O 的一种重构,异步 Direct I/O 的行为与之非常类似,但不会使调用线程阻塞。相反,应用程序线程可使用 io_submit(2) 系统调用来调度 Direct I/O 操作,并且该线程不会阻塞;I/O 操作与正常线程并行运行。单独的系统调用 io_getevents(2) 用于等待并收集已完成的 I/O 操作的结果。和 DIO 一样,内核中的页面缓存会被绕过,磁盘控制器负责将数据直接复制到用户空间。

了解权衡


不同的访问方法都有各自的特点,在其他方面有所不同。 表1总结了这些特点,详细阐述如下。


缓存控制


读/写和 mmap 的缓存都是由内核负责。系统的大部分内存都被提供给页面缓存。内核决定哪些页面在内存不足时被删除,以及页面何时需要写回磁盘,并且控制预读。应用程序可以使用 madvise(2) 和 fadvise(2) 系统调用为内核提供一些指导。

让内核控制缓存的巨大优点是内核开发人员在数十年来投入巨大的精力来调整缓存使用的算法。这些算法由数千种不同的应用程序使用,通常是有效的。然而,缺点是这些算法是通用的,并没有调整到对应的应用程序级别。内核必须判断应用程序将如何运行,即使知道应用程序不同,但也无法帮助内核进行判断。这会导致页面被误删, I/O 排列出错,或预先读取的数据无法消耗。


复制和 MMU 活动


mmap 方法的优点之一是如果数据在缓存中,则内核将被完全绕过。内核不需要将数据从内核复制到用户空间并返回,因此在该活动上花费的处理器周期较少。这有利于主要存在于缓存中的负载(例如,如果存储大小与 RAM 大小的比率接近 1:1)。

然而,当数据不在缓存中时,mmap 的缺点就会显现。当存储大小与 RAM 大小的比例明显高于 1:1 时,通常会发生这种情况。引入缓存的每个页面都会导致另一个页面被逐出。这些页面必须先插入页面表,之后又从表格中删除;内核必须扫描页面表以隔离停止活动的页面,然后将其删除。另外,mmap 需要页面表的内存。在 x8 6处理器上,这需要映射文件大小的 0.2%。这似乎很低,但是如果应用程序的存储比率为 100:1,则 20% 的内存(0.2%* 100)会被用于页面表。


I/O 调度


让内核控制缓存(使用 mmap 和 读/写访问方法)的一个问题是应用程序丢失了 I/O 调度的控制。内核选择其认为合适的数据块,调度它进行写入或读取。这可能会导致以下问题:

  • 写入风暴:当内核调度大量写入时,磁盘将被占用很长时间,并影响读取延迟。







请到「今天看啥」查看全文