8.1 术语
1.文件系统,一种把数据组织成文件和目录的存储方式,提供了基于文件的存取接口。
2.文件系统缓存,主存的一块区域,用来缓存文件系统的内容,可能包含各种数据和元数据。
3.逻辑IO,由应用程序发给文件系统的IO
4.物理IO,由文件系统直接发给磁盘的IO
5.inode,一个索引节点是一种含有文件系统对象元数据的数据结构,其中有访问权限,时间戳,以及数据指针。
6.vfs,虚拟文件系统,一个为了抽象与支持不同文件系统类型的内核接口。
7.卷管理器,灵活管理物理存储设备的软件,在设备上创建虚拟卷提供操作系统使用。
8.2 模型
8.2.1 文件系统接口
8.2.2 文件系统缓存
8.2.3 二级缓存
8.3 概念
8.3.1 文件系统延时
文件系统延时是文件系统性能一项主要的指标,指的是一个文件系统逻辑请求从开始到结束的时间。它包括了消耗在文件系统,内核磁盘IO子系统以及等待磁盘设备---物理IO的时间。
有些条件下应用程序不受文件系统的直接影响,比如非阻塞IO,或者一个IO由一个异步线程发起。
8.3.2 缓存
文件系统启动后会使用主存当缓存以提高性能。这对应用程序是透明的。
8.3.3 随机与顺序I/O
一连串的文件系统逻辑IO,按照每个IO的文件偏移量,可以分为随机IO与顺序IO。顺序Io里每个IO都开始于上一个IO结束的地址,随机IO则找不出IO之间的关系,偏移量随机变化。
8.3.4 预取
预取是文件系统解决这个问题的通常做法。通过检查当前和上一个IO的文件偏移量,可以检测出当前是否顺序读父子啊,并且做出预测,在应用程序请求前像磁盘发出读命令,以填充
文件系统缓存。这样如果应用程序真的发出了读请求,就会命中缓存。下面例子中,缓存一开始是空的:
1.上一个应用程序对某个文件调用 read(),把控制器交给内核。
2.文件系统发起磁盘读操作
3.将上一次文件偏移量指针和当前地址进行对比,如果发现是顺序的,文件系统就会发起额外的读请求
4.第一次的读取结束返回,内核把数据和控制权还给应用程序
5.额外的读请求也结束返回,并填进缓存,以备将来应用程序的读取
8.3.5 预读
预取一直也被认为是预读。最近,linux采用了 "预读"这个词作为一个系统调用,readahead(2),允许应用程序显式的预热文件系统缓存。
8.3.6 写回缓存
写回缓存广泛的应用于文件系统,用来提高性能。它的原理是,当数据写入主存后,就认为写入已经结束并返回,之后再异步的把数据刷入磁盘。文件系统写入"脏"数据的
过程称为刷新。例子如下:
1.应用程序发起一个文件的write()请求,把控制权交给内核
2.数据从应用程序地址空间复制到内核空间
3.write()系统调用被内核视为已经结束,并把控制权交还给应用程序。
4.一段时间后,一个异步的内核任务定位到要写入的数据,并发起磁盘的写请求
这期间牺牲了可靠性,基于DRAM的主存是不可靠的,"脏"数据会在断电的情况下丢失。并且,数据可能被非完整的写入,这样磁盘上的数据就是在一种破坏的状态。如果文件系统的元数据
遭到破坏,更糟糕的是,如果损坏蔓延到了应用程序的读写的文件内容,那业务就会收到严重冲击。
8.3.7 同步写
同步写完成的标志是,所有的数据以及必要的文件系统元数据被完整的写入到了永久存储介质中。由于包含了磁盘IO的延时,所以肯定比异步慢。同步写有两种形式:
1.单次同步写
当使用 O_SYNC 标志,或者其他变体,如O_DSYNC和O_RSYNC打开一个文件后,这个文件的写IO即为同步。
2.一组已写IO的同步提交
一个应用程序可能在检查点使用fsync()系统调用,同步提交之前异步写入数据,通过合并同步写提供性能。
8.3.8 裸I/O 和直接I/O
应用程序可能会用到其他的IO种类如下:
1.裸IO
绕过了整个文件系统,直接发给磁盘地址。有些应用程序使用了裸IO(特别是数据库),因为他们能比文件系统更好的缓存自己的数据。其缺点在于难以管理,
即不能使用常用文件系统工具执行备份/恢复和监控。
2.直接IO
允许应用程序绕过缓存使用文件系统。这有点像同步写(但缺少O_SYNC选项提供的保证),而且在读取时也能用。它没有裸IO那么直接,文件系统仍然会把文件地址
映射到磁盘地址,IO可能会被文件系统重新调整大小以适应文件系统的磁盘上的块大小。
直接IO可用于备份文件系统的应用程序,防止只读一次的数据污染文件系统缓存。裸IO和直接IO还可以用于哪些在进程堆里自建缓存的应用系统,避免了双重缓存的问题。
8.3.9 非阻塞I/O
一般而言,文件系统IO要么立即结束,要么需要等待。如果需要等待,应用程序线程会被阻塞并让出cpu,在等待期间给其他线程执行的机会。虽然被阻塞的线程不能执行其他工作,但
问题不大,多线程的应用程序会在有些线程被阻塞的情况下创建额外的线程来执行任务。
在某些情况下,非阻塞IO正合适,因为可以避免线程创建带来的额外性能和资源开销。
8.3.10 内存映射文件
对于某些应用程序的负载,可以通过把文件映射到进程地址空间,并直接存取内存地址的方法来提高文件系统IO性能。这样可以避免调用read()和write()存取文件数据时产生的系统
调用和上下文切换开销。如果内核直接支持复制文件数据缓冲到进程地址空间,那么还能防止复制数据两次。
内存映射通过系统调用mmap()创建,通过munmap()销毁。
8.3.11 元数据
如果说数据对应了文件和目录的内容,那么元数据则对应了有关它们的信息。元数据可能是通过文件系统接口(POSIX)读出的信息,也可能是文件系统实现磁盘布局所需的信息。前者
被称为逻辑元数据,后者被称为物理元数据。
逻辑元数据:
逻辑元数据是用户(应用程序)读取或者写入的信息。
物理元数据:
为了记录文件系统的所有信息,有一部分元数据与磁盘布局有关,这就是物理元数据。物理元数据的类型依赖于文件系统类型,可能包括了超级块,inode,数据块指针以及空闲表。
8.3.12 逻辑I/O vs.物理I/O
应用程序向文件系统发起的IO(逻辑IO)与磁盘IO(物理IO)可能并不相称。
8.3.13 操作并非不平等
8.3.14 特殊文件系统
文件系统的目的通常是持久到存储数据,但有些特殊的文件系统也有着其他用途,比如临时文件(/tmp),内核设备路径(/dev)和统计信息(/proc)。
8.3.15 访问时间戳
许多文件系统支持访问时间戳,可以记录下每个文件和目录被访问读取的时间。这会造成读取文件时更新元数据,读取变成了消耗磁盘IO资源的写负载。
8.3.16 容量
8.4 架构
从系统调用直接调用磁盘子系统的是裸IO,穿过VFS和文件系统的是文件系统IO,包括绕过了文件系统缓存的直接IO。
8.4.1 文件系统I/O 栈
8.4.2 VFS
vfs(虚拟文件系统接口)给不同类型的文件系统提供了一个通用的接口。
8.4.3 文件系统缓存
缓冲区高速缓存
页缓存 :页缓存缓存了虚拟内存的页面,包括文件系统的页面,提升了文件和目录的性能。文件系统使用的内存脏页由内核线程写回磁盘上,pdflush。页面因下面原因被写回磁盘:
1.过了一段时间(30s)
2.调用了 sync(),fsync()或者 msync()等系统调用
3.过多的脏页面
4.页缓存内没有可用的页面
目录项缓存
记录了从目录项到 vfs inode的映射关系。提升了路径名查找性能。
inode 缓存
这个缓存的对象是 vfs inode,每个都描述了文件系统一个对象的属性。这些属性可以通过 stat() 系统调用获得,并被操作系统频繁访问。例如在打开文件时检查权限,或者修改更新
时间戳。
8.4.4 文件系统特性
块和区段
基于块的文件系统把数据存储在固定大小的块里,被存储的元数据块里的指针所引用。
日志
文件系统的日志记录了文件系统的更改,这样在宕机时,能原子的回访更改。
写时复制
写时复制的文件系统从不覆写当前使用中的块,而是按照以下的步骤完成写入:
1.把数据写到一个新块(新的拷贝)
2.更新指引执行新块
3.把老块放到空闲链表中
擦洗
8.4.5 文件系统种类
FFS
伯克利快速文件系统(FFS)通过把磁盘划分为多个柱面组以提高性能。
UFS
ext3
ext4
zfs
btrfs
8.4.6 卷和池
文件系统一直以来建立在一块磁盘或者一个磁盘分区上。卷和池使文件系统可以建立在多块磁盘上,并可以使用不用的RAID策略。
卷把多块磁盘组合成一块虚拟磁盘,在此之上建立文件系统。在整块磁盘上建立文件系统时,卷能够隔离负载,降低竞争,缓和性能问题。
卷管理软件包括linux的逻辑卷管理器(LVM)。
池存储把多块磁盘放到一个存储池里,池上可以建立多个文件系统。池存储比卷存储更灵活,文件系统可以增长或者缩小而不牵涉下面的设备。
8.5 方法
8.5.1 磁盘分析
以前通常的策略是关注磁盘性能而忽略文件系统,这假定了IO瓶颈在磁盘。
8.5.2 延时分析
延时分析从测量文件系统操作的延时开始。这应该包括所有的对象操作,而不限于IO。
操作延时 = 时刻(完成操作) - 时刻(发起操作)
文件系统延时分析的目标层:
1.应用系统
2.系统调用接口
3.VFS
4.直接在文件系统上
8.5.3 负载特征归纳
下面是文件系统负载需要归纳的几个基本属性:
1.操作频率和操作类型
2.文件IO吞吐量
3.文件IO大小
4.读写比例
5.同步写比例
6.文件随机和连续访问比例
8.5.4 性能监控
文件系统性能指标是:
1.操作频率
2.操作延时
8.5.5 事件跟踪
8.5.6 静态性能调优
8.5.7 缓存调优
内核和文件系统会使用多种缓存,包括缓冲区高速缓存,目录缓存,inode缓存和文件系统页缓存。
8.5.8 负载分离
例如,让日志文件系统和数据库文件拥有单独的文件系统和缓存,能提高数据库性能。
8.5.9 内存文件系统
另一个通过配置提高性能的方法是使用内存文件系统。文件内容放在内存里,这样能够提高响应速度。
/tmp
8.5.10 微型基准测试
8.6 分析
8.6.1 vfsstat
8.6.2 fsstat
8.6.3 strace、truss
8.6.4 DTrace
8.6.5 SystemTap
8.6.6 LatencyTOP
8.6.7 free
8.6.8 top
8.6.9 vmstat
8.6.10 sar
8.6.11 slabtop
8.6.12 mdb ::kmastat
8.6.13 fcachestat
8.6.14 /proc/meminfo
8.6.15 mdb ::memstat
8.6.16 kstat
8.6.17 其他工具
8.6.18 可视化
8.7 实验
8.7.1 Ad Hoc
8.7.2 微型基准测试工具
8.7.3 缓存写回
8.8 调优
8.8.1 应用程序调用
8.8.2 ext3
8.8.3 ZFS