在系统I/O性能调优中,有时候文件系统的性能比磁盘性能更为重要。文件系统通过缓存、缓冲、异步I/O扥手段实现了对磁盘I/O的优化。
模型及概念
- 缓存
文件系统启动之后会使用主存(RAM)当缓存以提高性能,对应用程序透明。文件系统缓存主要包括页缓存、文件系统主存、文件系统二级缓存、目录缓存、inode缓存、设备缓存、块设备缓存。 - 随机I/O与顺序I/O
顺序I/O指每个I/O都始于上一个I/O结束的地址,反之则为随机I/O。基于磁盘性能特征,文件系统试图在磁盘上顺序连续地存放文件数据,以减少随机I/O数目。当系统碎片化时,顺序的逻辑I/O会被分解为随机的物理I/O。 - 预读
进行磁盘读取时,系统通过检查当前和上一个I/O的文件偏移量检查当前是否顺序读并作出预测,在应用程序请求前向磁盘发出读命令。当系统预测准确时就会命中缓存。一个典型的读场景:
- 应用发起文件read,控制前移交内核
- 文件系统发起磁盘读操作
- 文件指针地址比对,若为顺序读则向数据前方发起额外的读请求
- 第一次读取结束,读取数据填充缓存,控制权返回应用
写回缓存
写文件时,除非指定否则数据并不会直接落地磁盘,数据进入主存后就认为写入已结束并返回。之后系统异步地将脏数据写入磁盘:- 应用发起写请求
- 数据从该应用地址空间复制到内核空间
- 写请求返回成功
- 一段时间后,内核发起磁盘写
写回缓存有可能会导致数据丢失,甚至破坏文件系统元数据。文件系统默认采用写回缓存策略,在一些可靠性要求高的场景需要使用同步写绕过该机制。
- 同步写
- 单次同步写
当使用O_SYNC及其变体时,所有写入将强制同步落入磁盘 - 同步已提交内容
不指定同步写,但在适当的检查点调用fsync主动触发脏数据写入磁盘。
- 单次同步写
- 裸I/O和直接I/O
- 裸I/O
绕过整个文件系统,直接写入磁盘地址 - 直接I/O
- 裸I/O
架构
文件系统I/O栈的一般模型
markdown画这种图有点心累…
系统调用直接调用磁盘设备子系统的的过程为裸I/O,穿过VFS和文件系统的是文件系统I/O,包括绕过了文件系统缓存的直接I/O。
VFS
虚拟文件系统接口给不同各类型的文件系统提供一个通用接口。文件系统类型主要有UFS/ext3/ext4/HSFS/PCFS/ZFS/btrfs
文件系统缓存
- 缓冲区高速缓存
缓冲区高速缓存用于块设备接口和磁盘之间。linux缓冲区高速缓存寄存在页缓存中,其大小是动态的,可以从/proc中查看。 - 页缓存
页缓存缓存了虚拟内存的页面,包括文件系统的页面。文件系统使用的内存脏页面由内核线程写回到磁盘上。写回发生的条件主要包括:
- 经过一段时间(30s)
- 调用了sync、fsync或msync等系统调用
- 过多的脏页面
- 页缓存已耗尽
写回线程可通过系统性能工具以内核任务的形式观察到。
- inode缓存
inode用于记录文件文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性等元信息。
文件系统特性
- 块和区段
基于块的文件系统把数据存储在固定大小的块中,这种方式使得大文件需要大量的块指针和元数据块,而且会使得块的存在变得零散造成随机I/O。有些基于块的文件系统会采用连续摆放块、增大块大小的方案来避免该问题。 - 日志
- 写时复制
- 擦洗
检测坏盘,会严重影响性能。