18 | Linux文件系统

252 阅读9分钟

1. 概述

我们将进入下一个重要模块——文件系统和磁盘的 I/O 性能。相比CPU和内存,这应该是在计算机的资源环节里最慢的,也是比较出现性能瓶颈的模块。再次搬出这张图回顾一下。

与 CPU 和内存一样,磁盘和文件系统的管理也是操作系统的核心功能。其中:

  • 磁盘:为系统提供最基本的持久化存储。
  • 文件系统:基于磁盘,提供文件管理的树状结构。

本节我们将重点了解 Linux 文件系统的工作原理,而磁盘的工作原理将在下一节中详细学习。

首先了解文件系统的基本结构:索引节点和目录项

  1. 文件系统的作用
    • 文件系统用于组织和管理存储设备上的文件。
    • 在 Linux 中,一切皆文件(普通文件、目录、块设备、套接字、管道等),都通过统一的文件系统管理。
  1. 索引节点(inode)
    • 作用:记录文件的元数据(如 inode 编号、文件大小、权限、修改日期、数据位置等)。
    • 特点
      • 每个文件对应一个唯一的 inode。
      • inode 持久化存储在磁盘中,占用磁盘空间。
  1. 目录项(dentry)
    • 作用:记录文件名、inode 指针,以及与其他目录项的关系。
    • 特点
      • 由内核维护,存储在内存中(称为目录项缓存)。
      • 通过多个目录项,可以为同一文件创建别名(如硬链接),这些目录项指向相同的 inode。
  1. inode 与 dentry 的关系
    • inode 是文件的唯一标志,dentry 则维护文件系统的树状结构。
    • 关系是多对一:一个文件可以有多个 dentry(别名),但它们指向同一个 inode。

文件数据的存储方式

  1. 磁盘的读写单位
    • 磁盘的最小读写单位是扇区(512B),但扇区大小过小,频繁读写会导致效率低下。
  1. 文件系统的优化
    • 文件系统将连续的扇区组成逻辑块(如 4KB,由 8 个扇区组成)。
    • 数据以逻辑块为最小单位进行读写,提升了磁盘 I/O 的效率。

总结:

  • inode 记录文件元数据,并存储在磁盘中。
  • dentry 记录文件名和目录结构,由内核维护的内存数据结构。
  • 文件数据通过逻辑块管理,优化了磁盘的读写效率。

注意事项:

  1. 目录项与索引节点的缓存
    • 目录项(dentry) :本质是一个内存缓存,由内核维护,用于加速文件名到 inode 的解析。
    • 索引节点(inode) :虽然存储在磁盘中,但为了加速文件访问,也会缓存到内存中(类似页缓存 Cache)。
  1. 文件系统格式化后的存储区域
    磁盘在格式化文件系统时,会划分为以下三个存储区域:
    • 超级块(Superblock) :存储整个文件系统的状态信息。
    • 索引节点区(inode table) :存储所有文件的索引节点。
    • 数据块区(data block) :存储文件的实际数据内容。

其次需要了解:虚拟文件系统(VFS)

  1. VFS 的作用
    • VFS(Virtual File System)是用户进程与底层文件系统之间的抽象层。
    • VFS 定义了一组统一的数据结构和标准接口,使用户进程和内核只需与 VFS 交互,而无需关心底层文件系统的具体实现。
  1. 文件系统的四大基本要素
    • 目录项(dentry) :记录文件名与 inode 的关联关系。
    • 索引节点(inode) :记录文件元数据(如大小、权限等)。
    • 逻辑块:磁盘上文件数据的基本存储单元。
    • 超级块(superblock) :记录整个文件系统的状态信息。

文件系统的分类

按照存储位置,文件系统分为以下三类:

  1. 基于磁盘的文件系统
    • 特点:将数据直接存储在本地挂载的磁盘中。
    • 常见文件系统:Ext4、XFS、OverlayFS 等。
    • 应用场景:用于持久化存储,如操作系统安装、磁盘分区等。
  1. 基于内存的文件系统
    • 特点:不占用磁盘空间,数据存储在内存中。
    • 常见文件系统
      • /proc 文件系统:提供内核数据的动态视图。
      • /sys 文件系统:导出内核对象到用户空间。
    • 应用场景:用于系统信息查看和内核交互。
  1. 网络文件系统
    • 特点:通过网络访问其他计算机上的数据。
    • 常见文件系统:NFS、SMB、iSCSI 等。
    • 应用场景:分布式存储、远程数据访问。

文件系统挂载

  1. 根目录挂载
    • 在安装系统时,先挂载根目录(/)。
  1. 子文件系统挂载
    • 在根目录下,将其他文件系统(如磁盘分区、/proc、/sys、NFS 等)挂载到 VFS 的目录树中某个子目录(挂载点)。

系统调用、VFS、缓存、文件系统以及块存储之间的关系

文件系统 I/O 涉及的主要内容如下:

  1. 文件系统挂载与访问
    文件系统挂载到挂载点后,应用程序通过挂载点访问文件系统。VFS(虚拟文件系统)提供了一组标准的文件访问接口,以系统调用方式供应用程序使用。常见的文件访问操作包括 open()read()write()
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

2. 文件 I/O 分类
根据不同的特性,文件 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 操作后,应用程序不会阻塞当前线程,可以继续执行其他任务,最终通过轮询或事件通知获取结果。设置 O_NONBLOCK 标志实现非阻塞访问。
    • 同步与异步 I/O
      • 同步 I/O:应用程序执行 I/O 操作后,必须等待整个 I/O 完成,才能继续其他操作。设置 O_SYNCO_DSYNC 标志,确保数据写入磁盘后才返回。
      • 异步 I/O:应用程序发起 I/O 操作后,不需等待其完成,而是继续执行,完成后通过事件通知(如 SIGIO)告知应用程序。设置 O_ASYNC 选项时为异步 I/O。

通过这些分类,应用程序可以根据需求选择不同的 I/O 操作方式,以优化性能或满足特定的应用场景需求。

2. 性能观测

对于文件系统来说,空间不足是个非常严重的问题,所以一般会先监控使用量。常见命令就是df

$ df -h /dev/sda1 
Filesystem      Size  Used Avail Use% Mounted on 
/dev/sda1        29G  3.1G   26G  11% / 

除了文件数据,还要监控索引文件,也就是我们常说的inode。

$ df -i /dev/sda1 
Filesystem      Inodes  IUsed   IFree IUse% Mounted on 
/dev/sda1      3870720 157460 3713260    5% / 

缓存使用量

$ cat /proc/meminfo | grep -E "SReclaimable|Cached" 
Cached:           748316 kB 
SwapCached:            0 kB 
SReclaimable:     179508 kB 

如何观察文件系统中的目录项和索引节点缓存?实际上,内核使用 Slab 机制,管理目录项和索引节点的缓存。/proc/meminfo 只给出了 Slab 的整体大小,具体到每一种 Slab 缓存,还要查看 /proc/slabinfo 这个文件。

$ cat /proc/slabinfo | grep -E '^#|dentry|inode' 
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail> 
xfs_inode              0      0    960   17    4 : tunables    0    0    0 : slabdata      0      0      0 
... 
ext4_inode_cache   32104  34590   1088   15    4 : tunables    0    0    0 : slabdata   2306   2306      0hugetlbfs_inode_cache     13     13    624   13    2 : tunables    0    0    0 : slabdata      1      1      0 
sock_inode_cache    1190   1242    704   23    4 : tunables    0    0    0 : slabdata     54     54      0 
shmem_inode_cache   1622   2139    712   23    4 : tunables    0    0    0 : slabdata     93     93      0 
proc_inode_cache    3560   4080    680   12    2 : tunables    0    0    0 : slabdata    340    340      0 
inode_cache        25172  25818    608   13    2 : tunables    0    0    0 : slabdata   1986   1986      0 
dentry             76050 121296    192   21    1 : tunables    0    0    0 : slabdata   5776   5776      0 

3. 小结

  1. 文件系统概念
    文件系统是对存储设备上的文件进行组织和管理的一种机制,确保文件的有效存储、访问和操作。文件系统不仅要处理文件存储的逻辑结构,还要保证数据的安全和一致性。
  2. 虚拟文件系统(VFS)
    Linux 提供了一层虚拟文件系统(VFS)来支持不同类型的文件系统。VFS 定义了一组所有文件系统都支持的数据结构和标准接口,这些接口为用户进程和内核中的其他子系统提供了统一的交互方式。通过 VFS,应用程序与实际文件系统的实现解耦,可以更加方便地支持多种不同的文件系统(如 ext4、NTFS、FAT 等)。
  3. 性能优化:缓存机制
    为了缓解慢速磁盘访问对性能的影响,Linux 文件系统使用了多种缓存机制:
    • 页缓存:缓存磁盘上的数据页,减少磁盘访问,提高性能。
    • 目录项缓存(dentry cache):缓存文件路径信息,避免每次访问都进行路径查找。
    • 索引节点缓存(inode cache):缓存文件的元数据(如文件权限、大小、位置等),加速文件的读取。
  1. 性能观测:容量与缓存
    在文件系统的性能观测中,容量和缓存是两个重要指标:
    • 容量:指磁盘的存储能力,衡量系统能够处理的数据量。
    • 缓存:指内存中存储的数据副本,通过缓存来减少对慢速磁盘的访问,提高系统的响应速度。

Linux 文件系统通过 VFS 提供统一的文件访问接口,同时采用多级缓存机制来优化磁盘访问性能。通过对容量和缓存的合理管理,可以有效地降低磁盘延迟对性能的影响。

参考文献:《Linux 性能优化实战》