「Linux」- 文件系统(学习笔记)

  CREATED BY JENKINSBOT

磁盘,为系统提供了最基本的持久化存储;
文件系统,则在磁盘的基础上,提供了一个用来管理文件的树状结构

索引节点和目录项

Linux 文件系统为每个文件都分配两个数据结构:

索引节点(index node,inode)

记录文件的元信息,比如 inode 编号、文件大小、访问权限、修改日期、数据的位置等等
索引节点和文件对应,它跟文件内容一样,都会被持久化存储到磁盘中,同样占用磁盘空间

目录项(directory entry,dentry)

用来记录:文件的名字索引节点指针与其他目录项的关联关系
多个关联的目录项,就构成了文件系统的目录结构

不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。

两者之间的关系

索引节点是每个文件的唯一标志,而目录项维护的正是文件系统的树状结构。

目录项和索引节点的关系是多对一,可以简单理解为,一个文件可以存在与多个目录中(我们经常听说的硬链接,就是同个文件存在于不同目录中)。

文件数据读写的最小单位(逻辑块)

磁盘读写的最小单位是扇区,只有 512B 大小。每次都读写这么小的单位,效率一定很低。

文件系统又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB(也就是由连续的 8 个扇区组成)。

超级块(Superblock)

用来记录文件系统整体的状态,如索引节点和逻辑块的使用情况等。

文件系统格式化时的三个存储区域

存储在磁盘中的持久化数据,因此在格式化时需要处理:
1)超级块,存储整个文件系统的状态。
2)索引节点区,用来存储索引节点。
3)数据块区,则用来存储文件数据。

虚拟文件系统(VFS, Virtual File System)

为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层,也就是虚拟文件系统 VFS(Virtual File System)

VFS 定义了一组所有文件系统都支持的数据结构和标准接口,用户进程和内核中的其他子系统,只需要跟 VFS 提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节。

Userspace => VFS => tmpfs / nfs / fs

文件系统,要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件。我们平时使用 ls 命令看到的文件系统,实际上是 VFS,而不是 xfs、ext4 等等文件系统。

文件系统 I/O

VFS 提供一组标准的文件访问接口。这些接口以系统调用的方式,提供给应用程序使用。比如 open() 打开文件,read() 读取文件,write() 写入文件。也就是说,我们操作的是 VFS 而不是 实际的文件系统(ext4、xfs、……)

缓冲与非缓冲 I/O(是否利用标准库缓存)

缓冲 I/O,是指利用标准库缓存来加速文件的访问,而标准库内部再通过系统调度访问文件。

非缓冲 I/O,是指直接通过系统调用来访问文件,不再经过标准库缓存。

注意,这里的缓冲是由 标准库内部实现的缓冲。比如,有些程序遇到换行时才真正输出,而换行前的内容,其实就是被标准库暂时缓存。

但是,它们最终还是要经过系统调用来访问文件,系统调用后,还会通过页缓存,来减少磁盘的 I/O 操作。

直接与非直接 I/O(是否利用操作系统的页缓存)

直接 I/O,是指跳过操作系统的页缓存,直接跟文件系统交互来访问文件。比如,指定 O_DIRECT 标志。

非直接 I/O,文件读写时,先要经过系统的页缓存,然后再由内核或额外的系统调用,真正写入磁盘。

直接 I/O、非直接 I/O,本质上还是和文件系统交互。但是,在某些数据库等场景中,会跳过文件系统直接读写磁盘,也就是我们通常所说的裸 I/O

阻塞与非阻塞 I/O(应用程序是否阻塞自身运行)

阻塞 I/O,是指应用程序执行 I/O 操作后,如果没有获得响应,就会阻塞当前线程,自然就不能执行其他任务。

非阻塞 I/O,是指应用程序执行 I/O 操作后,不会阻塞当前的线程,可以继续执行其他的任务。随后再通过 轮询事件通知 的形式,获取调用的结果。比如 访问管道或者网络套接字时,设置 O_NONBLOCK 标志,就表示用非阻塞方式访问。

同步与异步 I/O(是否等待响应结果)

同步 I/O,是指应用程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获得 I/O 响应。

异步 I/O,是指应用程序执行 I/O 操作后,不用等待完成和完成后的响应,而是继续执行就可以。等到这次 I/O 完成后,响应会用事件通知的方式,告诉应用程序

在操作文件时,如果你设置了 O_SYNC 或者 O_DSYNC 标志,就代表同步 I/O。如果设置了 O_DSYNC,就要等文件数据写入磁盘后,才能返回;而 O_SYNC,则是在 O_DSYNC 基础上,要求文件元数据也要写入磁盘后,才能返回。

# 注意事项 #

”阻塞 / 非阻塞“和”同步 / 异步“是两个不同角度的 I/O 划分方式,它们描述的对象也不同:
1)阻塞 / 非阻塞,针对的是 I/O 调用者(即应用程序);
2)同步 / 异步,针对的是 I/O 执行者(即系统);

观测文件系统的性能

查看文件系统容量

# df /dev/sda1

# df -h /dev/sda1 // 加上 -h 选项,以获得更好的可读性

# df -i /dev/sda1  // 查看索引节点的使用情况

// 如果 inode 全部使用,则会出现“df 显示剩余空间充足,但是空间不足”的问题

// 索引节点的容量,(也就是 Inode 个数)是在格式化磁盘时设定好的,一般由格式化工具自动生成。当你发现索引节点空间不足,但磁盘空间充足时,很可能就是过多小文件导致的。

观察目录项和索引节点缓存

核使用 Slab 机制,管理目录项和索引节点的缓存。/proc/meminfo 只给出了 Slab 的整体大小,具体到每一种 Slab 缓存,还要查看 /proc/slabinfo 这个文件。

# cat /proc/slabinfo | grep -E '^#|dentry|inode'

// dentry 行表示目录项缓存
// inode_cache 行,表示 VFS 索引节点缓存
// 其余的则是各种文件系统的索引节点缓存

// /proc/slabinfo 的列比较多,具体含义你可以查询  man slabinfo

在实际性能分析中,我们更常使用 slabtop 来找到占用内存最多的缓存类型:

# slabtop
 Active / Total Objects (% used)    : 1149991 / 1422582 (80.8%)
 Active / Total Slabs (% used)      : 37781 / 37781 (100.0%)
 Active / Total Caches (% used)     : 110 / 141 (78.0%)
 Active / Total Size (% used)       : 278331.67K / 357378.34K (77.9%)
 Minimum / Average / Maximum Object : 0.01K / 0.25K / 23.19K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
193011 180385  93%    0.20K   4949       39     39592K vm_area_struct
191373 101342  52%    0.10K   4907       39     19628K buffer_head
146944 133042  90%    0.06K   2296       64      9184K anon_vma_chain
100352  92760  92%    0.03K    784      128      3136K kmalloc-32
 89880  60916  67%    0.19K   4280       21     17120K dentry
 89824  71806  79%    0.57K   3208       28     51328K radix_tree_node
 75532  67981  90%    0.09K   1642       46      6568K anon_vma
 61184  50791  83%    0.25K   1912       32     15296K filp

// 从这个结果可以看到,在系统中 radix_tree_node 占用最多的 Slab 缓存

实验探究:命令 find / -name file-name 对缓存的影响

使用 slabtop 可以看到缓存的变换:
1)清空缓存:echo 3 > /proc/sys/vm/drop_caches ; sync
2)运行 slabtop -s c 命令
3)执行 find / -name test 命令
4)观察 slabtop 输出

我们可以猜测到 ext4 proc dentry 会升高,如果使用网络文件系统,那么网络文件系统的缓存也会升高。

参考文献

23 | 基础篇:Linux 文件系统是怎么工作的?
32 | 答疑(四):阻塞、非阻塞 I/O 与同步、异步 I/O 的区别和联系