原力注入

Linux VFS (Virtual File System)简介

VFS 概述

Linux 支持多种不同文件系统,要实现这个目的,就要将对各种不同文件系统和管理纳 入到一个统一的框架中,让内核中的文件系统界面成为一条文件系统“总线”,使用户程序可以通过同一个文件系统操作界面,也就是同一组系统调用,对各种不同的文件系统(以及 文件)进行操作。

这样,就可以对用户程序隐去各种不同文件系统的细节,为用户程序提供 一个统一的、抽象的、虚拟的文件系统界面,这就是所谓“虚拟文件系统” - VFS(Virtual Filesystem Switch)。这个抽象界面主要由一组标准的、抽象的文件操作构成,以系统调用 的形式提供于用户程序,如read()、write()、lseek()等等。这样,用户程序就可以 把所有的文件都看作一致的、抽象的“VFS文件”,通过这些系统调用对文件进行操作,而 无需关心具体的文件属于什么文件系统以及具体文件系统的设计和实现。

一个示例:在我们使用C语言编写应用程序时,相信会经常使用到write()系统调用;就是向一个 文件中写入数据。write()函数的原型为 ssize_t write(int fd, const void *buf, size_t count); 在用户程序的write(f, &buf, len),向文件描述符为f的文件中,写入len个字节数据, 待写入的数据存放在buf中。下图为write()将数据写入硬件上的简易流程。我们看到首先通过虚拟文件系统VFS,然后根据不同文件系统的write()方法将数据写入物理设备上。

Image

站在真实 Linux 的视角来看:

Image

Image

虚拟文件系统支持的三种文件系统类型

  1. 1. 磁盘文件系统
    存储在本地物理介质上的文件系统,例如硬盘、U盘、CD-ROM 等。常见格式包括 ext4、XFS 等,专注于数据的持久化存储。

  2. 2. 网络文件系统
    存储在远程主机上的文件系统,通过网络协议进行访问,例如 NFS(Network File System)。适用于分布式存储或远程文件共享场景。

  3. 3. 特殊文件系统
    提供虚拟化或系统信息的映射,例如 /proc 文件系统。它们通常不直接存储数据,而是用于内核与用户空间的交互或存储运行时信息。

VFS 主要对象及其数据结构

虚拟文件系统通过四种核心数据结构组织和管理文件系统,这些数据结构为用户和进程提供了对文件和目录的抽象。

Image

1. Superblock(超级块)

  • • 功能:
    超级块(spuerblock)对象由各自的文件系统实现,用来存储文件系统的信息。这个对象对应为文件系统超级块或者文件系统控制块,它存储在磁盘特定的扇区上。不是基于磁盘的文件系统临时生成超级块,并保存在内存中。

    • • 文件系统的类型(ext4、XFS 等)。

    • • 文件系统的大小和块大小。

    • • 空闲 inode 和数据块的数量。

  • • 用途:

    • • 超级块与物理文件系统一一对应。

    • • 在挂载时初始化,帮助管理文件系统。

2. Inode(索引节点)

  • • 功能:
    索引节点对象包含了内核在操作文件或目录时需要的全部信息。对于 Unix 文件系统来说,这些信息可以从磁盘索引节点直接读入。如果一个文件系统没有索引节点,那么,不管这些相关信息在磁盘上是怎么存放的,文件系统都必须从中提取这些信息。包含:

    • • 文件权限、大小、所有者信息。

    • • 数据块指针(指向文件或目录的实际存储位置)。

  • • 特点:

    • • 每个文件或目录都对应一个 inode。

    • • 文件系统通过 inode 定位实际内容。

3. Dentry(目录项)

  • • 功能:
    为了方便查找,虚拟文件系统引入目录项的概念。每个 Dentry 代表路径中一个特定部分。对于“/bin/ls”、“/”、“bin”和“ls”都是目录项对象。前面是两个目录,最后一个是普通文件。在路径中, 包括普通文件在内,每一个部分都是目录项对象。目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一棵庞大的目录树。

  • • 特点:

    • • 用于路径解析(例如 /home/user/file.txt)。

    • • 提高性能:通过目录项缓存减少路径查找开销。

4. File(文件)

  • • 功能:
    虚拟文件系统最后一个主要对象是文件对象,文件对象表示进程已打开的文件。如果我们站在用户空间的角度考虑虚拟文件系统,文件对象会首先进入我们的视野。进程直接处理的是文件而不是超级块、索引节点或目录。文件对象包含我们非常熟悉的信息(如访问模式、当前偏移等), 文件操作和我们非常熟悉的系统调用read()和write()等也很类似。文件对象是已打开的文件在内存中的表示。该对象(不是物理文件)由相应的open()系统调用创建,由close()系统调用销毁,所有这些文件相关的调用实际上都是文件操作 表中定义的方法。

  • • 用途:

    • • 每个打开的文件都有对应的 file 对象。

    • • 提供进程与文件之间的交互接口。

VFS 与文件系统的关系

虚拟文件系统(VFS)通过抽象层为用户提供统一的文件访问接口,不依赖具体的文件系统实现。
VFS 和实际文件系统之间存在密切的联系,但两者在数据结构和存储方式上有显著差异。

VFS 中的 Inode 和 Dentry

  • • 存储位置:

    • • VFS: inode 和 dentry 存在于 内存中,由系统根据实际文件系统的信息动态生成。

    • • 实际文件系统: inode 和 dentry 存储在 物理磁盘上,作为文件系统元数据的一部分。

  • • 生成过程:

    • • 当系统需要访问文件时,从磁盘加载文件系统的 inode 和 dentry 信息。

    • • VFS 依据这些信息生成对应的内存对象(VFS 的 inode 和 dentry)。

  • • 特点:

    • • VFS 的 Inode 和 Dentry: 提供统一的文件表示方式,与具体文件系统无关。

    • • 实际文件系统的 Inode 和 Dentry: 存储文件的具体元信息,依赖于文件系统的格式(如 ext4、XFS)。

虚拟文件系统与实际文件系统的关系

  1. 1. 抽象与具体

  • • VFS 提供文件系统的通用接口,屏蔽底层文件系统的差异,例如:

    • • 无论使用 ext4、XFS 或 NFS,用户的文件访问方式一致。

  • • 实际文件系统实现存储的具体逻辑,例如:文件的物理布局和数据块管理。

  • 2. 动态生成

    • • VFS 的数据结构(如 inode 和 dentry)是内存中的临时对象,随访问需求而生成或释放。

    • • 实际文件系统的数据结构(如磁盘上的 inode 和 dentry)是持久化存储的,用于记录文件的元信息。

  • 3. 性能优化

    • • VFS 利用内存缓存(如 dentry 缓存)提高文件访问速度,减少磁盘 I/O。

    • • 实际文件系统负责底层数据的可靠存储。

    示例关系

    假设用户访问文件 /home/user/file.txt,VFS 与实际文件系统的协作过程如下:

    Image

    1. 1. 路径解析

    • • VFS 使用 dentry 缓存快速解析路径 /home/user/file.txt,找到对应的内存中 dentry 和 inode。

    • • 若缓存中不存在,VFS 从实际文件系统加载磁盘上的 inode 和 dentry 信息。

  • 2. 文件访问

    • • VFS 通过内存中的 inode 获取文件的逻辑块位置。

    • • 实际文件系统负责从磁盘中读取对应的数据块。

    总结

    VFS 是实际文件系统的抽象层,两者的 inode 和 dentry 数据结构密切相关但不等同:

    • • VFS 的数据结构: 存在于内存中,用于提供统一接口和提高性能。

    • • 实际文件系统的数据结构: 持久化于磁盘,用于存储文件的具体元信息。

    这种设计使得 Linux 能够灵活地支持多种文件系统,同时保证了访问性能和扩展性。

    VFS 接口与系统调用一览

    接口类型VFS 接口相关系统调用功能描述
    文件操作openopen, creat打开或创建文件。

    closeclose关闭文件。

    readread从文件中读取数据到用户空间。

    writewrite将数据从用户空间写入文件。

    fdatasyncfdatasync同步文件数据到存储设备,不同步元数据。

    fsyncfsync同步文件数据及元数据到存储设备。

    lseeklseek调整文件偏移量,用于随机访问文件内容。

    mmapmmap将文件内容映射到进程的虚拟内存空间。
    目录操作mkdirmkdir创建目录。

    rmdirrmdir删除目录。

    opendiropendir打开目录。

    readdirreaddir读取目录中的文件条目。

    closedirclosedir关闭目录。
    文件状态statstat, fstat, lstat获取文件状态信息(如权限、大小、时间戳等)。

    chmodchmod, fchmod修改文件权限。

    chownchown, fchown修改文件所有者。
    文件系统操作mountmount挂载文件系统。

    umountumount卸载文件系统。

    syncsync同步所有挂载文件系统的修改到磁盘。
    链接操作linklink创建文件的硬链接。

    unlinkunlink删除文件或删除链接。

    symlinksymlink创建文件的符号链接。

    readlinkreadlink读取符号链接指向的目标路径。
    设备操作ioctlioctl对设备文件进行控制操作。
    文件锁操作fcntlfcntl执行文件描述符操作(如加锁、非阻塞 I/O 等)。
    文件监控inotifyinotify_add_watch监控文件或目录的变化。

    说明

    • • VFS 接口:内核中定义的抽象接口,用于统一处理不同文件系统的操作。

    • • 相关系统调用:用户空间提供的接口,通常是通过 libc 库封装,调用 VFS 提供的功能。