Linux 的文件系统

Linux 通过 VFS 建立了一个抽象层,并在之上提供了通用的文件系统模型,使得 Linux 能够支持多种文件系统。

文件系统的原理

文件系统的管理方式,可以分为 bitmap 和 tree 两大类。

在支持大容量文件系统时,bitmap 存在问题:

  1. bitmap 本身占用空间太大,占用内存空间,并且回写磁盘更慢,对于掉电故障更不友好。
  2. 如果已分配空间较为零散,那么后续新的分配需要遍历很多的位。并且产生较多的随机写。

Linux VFS

索引节点,即 index node, inode,存储了文件的元信息。Unix中,文件和目录其实都是文件。
装载点即 mount pointer,Unix 把每个文件系统 mount 到一个特定的 mount 点上,称为命名空间 namespace。

VFS 为了封装这些抽象使用了四个主要的对象类型:

  1. 超级块对象,代表一个具体的已安装文件系统
  2. 索引节点对象,代表一个具体文件
  3. 目录项对象,代表一个目录项
    注意目录项不等同于目录,Linux 中目录属于文件。
    这里的目录项描述了一个路径的性质,例如说在基于路径名的查找时需要访问整个路径,所以将路径名从文件中抽象出来简化了过程。
  4. 文件对象,代表一个由进程打开的文件

write() 调用为例,它首先经过了 VFS 层中的 sys_write() 函数,接着调用具体文件系统的实现,以写入物理介质。

超级块

Linux 中使用了 struct super_block 来描述超级块,使用 struct super_block.super_operations 来定义这个超级块中的所有行为。

inode

struct inode 结构体在文件被访问时在内存中创建,它可以描述一个管道、块设备或者字符设备。

1
2
3
4
5
6
7
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
char *i_link;
unsigned i_dir_seq;
};

目录项

目录项有三种状态

  1. 被使用。这个状态的目录的d_inode是一个有效的节点,并且d_count为正,表示正在被VFS访问。
  2. 未被使用。这个状态的目录的d_count为0,表示这个对象没有被使用,但是仍然在中缓存以便将来使用。
  3. 负状态。此时d_inodeNULL,表示inode已经删除,或者路径已经不再合法,缓存这样的节点可以方便查询,即使是对一个不合法的节点。

和进程相关的结构

文件描述符 fd 在进程中以 struct file_struct 定义。

Page Cache

O_DIRECT 表示 IO 不使用 Buffer,
O_SYNC:以同步IO方式打开文件。

ZFS

ZFS 支持下面的内容:

  1. COW
  2. Snapshot
  3. 数据完整性验证和自动修复
  4. RAID-Z
  5. 最大 16 EiB 的文件大小
  6. 最大 256 ZiB 的存储

IO 调度

Linus 电梯

  1. 合并相邻扇区的请求
  2. 出现驻留时间过长的请求时插入尾部
  3. 尝试插入扫描序列中的对应顺序位置
  4. 如果不存在插入尾部

最终期限调度

对某个磁盘扇区上的繁重操作会导致其他扇区的饥饿。
写-饥饿-读问题。这是因为写操作常常是异步的,在内核有空时提交,而读操作则用户进程必须阻塞等待,所以是同步的。而读写操作都涉及到访问inode,因此这里存在竞争关系,能够料想写操作的饥饿能够导致读请求的饥饿。为了解决这个问题我们引入了最后期限调度,但是如我们所想的那样,这种控制延迟在一定程度内的算法一定会降低吞吐量。

Reference

  1. https://developer.aliyun.com/article/401890
    ZFS
  2. https://www.cnblogs.com/zl1991/p/10288291.html
    O_SYNC 和 O_DIRECT