Linux 通过 VFS 建立了一个抽象层,并在之上提供了通用的文件系统模型,使得 Linux 能够支持多种文件系统。
文件系统的原理
文件系统的管理方式,可以分为 bitmap 和 tree 两大类。
在支持大容量文件系统时,bitmap 存在问题:
- bitmap 本身占用空间太大,占用内存空间,并且回写磁盘更慢,对于掉电故障更不友好。
- 如果已分配空间较为零散,那么后续新的分配需要遍历很多的位。并且产生较多的随机写。
Linux VFS
索引节点,即 index node, inode,存储了文件的元信息。Unix中,文件和目录其实都是文件。
装载点即 mount pointer,Unix 把每个文件系统 mount 到一个特定的 mount 点上,称为命名空间 namespace。
VFS 为了封装这些抽象使用了四个主要的对象类型:
- 超级块对象,代表一个具体的已安装文件系统
- 索引节点对象,代表一个具体文件
- 目录项对象,代表一个目录项
注意目录项不等同于目录,Linux 中目录属于文件。
这里的目录项描述了一个路径的性质,例如说在基于路径名的查找时需要访问整个路径,所以将路径名从文件中抽象出来简化了过程。 - 文件对象,代表一个由进程打开的文件
以 write()
调用为例,它首先经过了 VFS 层中的 sys_write()
函数,接着调用具体文件系统的实现,以写入物理介质。
超级块
Linux 中使用了 struct super_block
来描述超级块,使用 struct super_block.super_operations
来定义这个超级块中的所有行为。
inode
struct inode
结构体在文件被访问时在内存中创建,它可以描述一个管道、块设备或者字符设备。
1 | union { |
目录项
目录项有三种状态
- 被使用。这个状态的目录的
d_inode
是一个有效的节点,并且d_count
为正,表示正在被VFS访问。 - 未被使用。这个状态的目录的
d_count
为0,表示这个对象没有被使用,但是仍然在中缓存以便将来使用。 - 负状态。此时
d_inode
为NULL
,表示inode已经删除,或者路径已经不再合法,缓存这样的节点可以方便查询,即使是对一个不合法的节点。
和进程相关的结构
文件描述符 fd 在进程中以 struct file_struct
定义。
Page Cache
O_DIRECT 表示 IO 不使用 Buffer,
O_SYNC:以同步IO方式打开文件。
ZFS
ZFS 支持下面的内容:
- COW
- Snapshot
- 数据完整性验证和自动修复
- RAID-Z
- 最大 16 EiB 的文件大小
- 最大 256 ZiB 的存储
IO 调度
Linus 电梯
- 合并相邻扇区的请求
- 出现驻留时间过长的请求时插入尾部
- 尝试插入扫描序列中的对应顺序位置
- 如果不存在插入尾部
最终期限调度
对某个磁盘扇区上的繁重操作会导致其他扇区的饥饿。
写-饥饿-读问题。这是因为写操作常常是异步的,在内核有空时提交,而读操作则用户进程必须阻塞等待,所以是同步的。而读写操作都涉及到访问inode,因此这里存在竞争关系,能够料想写操作的饥饿能够导致读请求的饥饿。为了解决这个问题我们引入了最后期限调度,但是如我们所想的那样,这种控制延迟在一定程度内的算法一定会降低吞吐量。
磁盘相关性能测试工具介绍
/proc/diskstats
诸如 iostat,node_exporter 之类的都是基于读取该指标实现的。
- Field 1 – # of reads completed
This is the total number of reads completed successfully. - Field 2 – # of reads merged, field 6 – # of writes merged
相邻的 read 和 write 会被 merge 起来读写。比如两个相邻的 4k 会被合并成一个 8k 的,然后被算作一次 io。通过这个可以了解 merge 的频率。
iostat
- rareq-sz/wareq-sz/dareq-sz
read/write/discard 请求的平均大小,kb 计算。 - r_await/w_await/d_await
read/write/discard 的处理耗时,包含队列中的等待时间,以及真正的处理时间。毫秒计算。 - f_await
The average time (in milliseconds) for flush requests issued to the device to be served. The block layer combines flush requests and executes at most one at a time. Thus flush operations could be twice as long: Wait for current flush request, then execute it, then wait for the next one. - aqu-sz
队列的平均长度。 - %rrqm/%wrqm/%drqm
The percentage of read/write/discard requests merged together before being sent to the device. - rrqm/s wrqm/s drqm/s
The number of read/write/discard requests merged per second that were queued to the device. - r/s w/s d/s f/s
The number (after merges) of read/write/discard/flush requests completed per second for the device. - rkB/s wkB/s
等于rareq-sz * r/s
和wareq-sz * w/s
。