Skip to content

Latest commit

 

History

History
44 lines (32 loc) · 5.05 KB

4.Direct-IO.md

File metadata and controls

44 lines (32 loc) · 5.05 KB

Direct I/O 即直接 I/O。其名字中的"直接"二字用于区分使用 page cache 机制的缓存 I/O。"直接"在这里还有另一层语义:其他所有技术中,数据至少需要在内核空间存储一份,但是在 Direct I/O 技术中,数据直接存储在用户空间中,绕过了内核。

直接与非直接 I/O

磁盘 I/O 是非常慢的,所以 Linux 内核为了减少磁盘 I/O 次数,在系统调用后,会把用户数据拷贝到内核中缓存起来,这个内核缓存空间也就是「页缓存」,只有当缓存满足某些条件的时候,才发起磁盘 I/O 的请求。

根据「是否利用操作系统的缓存」,可以把文件I/O分为:

  • 直接 I/O
    不会发生内核缓存和用户程序之间数据复制,而是直接经过文件系统访问磁盘
  • 非直接 I/O
    读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存,再由内核决定何时写磁盘

备注:如果调用open()函数时,指定了 O_DIRECT 标志,则表示使用直接 I/O; 否则默认是非直接 I/O。

如果用了非直接 I/O 进行写数据操作,内核什么情况下才会把缓存数据写入到磁盘?

  • 在调用 write 的最后,当发现内核缓存的数据太多的时候,内核会把数据写到磁盘上
  • 用户主动调用 sync,内核缓存会刷到磁盘上
  • 当内存十分紧张,无法再分配页面时,也会把内核缓存的数据刷到磁盘上
  • 内核缓存的数据的缓存时间超过某个时间时,也会把数据刷到磁盘上

Direct I/O 的读写非常有特点:

  • Write 操作:由于其不使用 page cache,所以其进行写文件,如果返回成功,数据就真的落盘了(不考虑磁盘自带的缓存);
  • Read 操作:由于其不使用 page cache,每次读操作是真的从磁盘中读取,不会从文件系统的缓存中读取。

事实上,即使 Direct I/O 还是可能需要使用操作系统的 fsync 系统调用。为什么?
这是因为虽然文件的数据本身没有使用任何缓存,但是文件的元数据仍然需要缓存,包括 VFS 中的 inode cache 和 dentry cache 等。

在部分操作系统中,在 Direct I/O 模式下进行 write 系统调用能够确保文件数据落盘,但是文件元数据不一定落盘。如果在此类操作系统上,那么还需要执行一次 fsync 系统调用确保文件元数据也落盘。否则,可能会导致文件异常、元数据缺失等情况。MySQL 的 O_DIRECT 与 O_DIRECT_NO_FSYNC 配置是一个具体案例[9]。

Direct I/O 的优缺点:

(1)优点
Linux 中的直接 I/O 技术省略掉缓存 I/O 技术中操作系统内核缓冲区的使用,数据直接在应用程序地址空间和磁盘之间进行传输,从而使得自缓存应用程序可以省略掉复杂的系统级别的缓存结构,而执行程序自己定义的数据读写管理,从而降低系统级别的管理对应用程序访问数据的影响。
与其他零拷贝技术一样,避免了内核空间到用户空间的数据拷贝,如果要传输的数据量很大,使用直接 I/O 的方式进行数据传输,而不需要操作系统内核地址空间拷贝数据操作的参与,这将会大大提高性能。 (2)缺点
由于设备之间的数据传输是通过 DMA 完成的,因此用户空间的数据缓冲区内存页必须进行 page pinning(页锁定),这是为了防止其物理页框地址被交换到磁盘或者被移动到新的地址而导致 DMA 去拷贝数据的时候在指定的地址找不到内存页从而引发缺页错误,而页锁定的开销并不比 CPU 拷贝小,所以为了避免频繁的页锁定系统调用,应用程序必须分配和注册一个持久的内存池,用于数据缓冲。
如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘进行加载,这种直接加载会非常缓慢。

谁会使用 Direct I/O?

  • 自缓存应用程序 page cache 是 Linux 为所有应用提供的缓存机制,但是有些应用太特殊了,page cache 影响了数据对特性的追求。在应用层引入直接 I/O 需要应用层自己管理,这虽然带来了额外的系统复杂性,但是却大幅提升了I/O性能和缓存命中率; IBM[5]的一篇文章指出,自缓存应用程序( self-caching applications)可以选择使用 Direct I/O。

对于自缓存应用程序来说,它会有它自己的数据缓存机制,它会将数据缓存在应用程序地址空间,这类应用程序完全不需要使用操作系统内核中的高速缓冲存储器,这类应用程序就被称作是自缓存应用程序( self-caching applications )。当有读操作时,首先读取应用层的缓存数据,如果没有,那么就通过 Direct I/O 直接通过磁盘 I/O 来读取数据。自缓存应用程序对要操作的数据的语义了如指掌,所以它可以采用更加高效的缓存替换算法。

  • 目前 Linux 上的异步 IO 库,其依赖于文件使用 O_DIRECT 模式打开,它们通常一起配合使用。