news 2026/2/7 6:02:12

[Linux]学习笔记系列 -- [fs]filesystems

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[Linux]学习笔记系列 -- [fs]filesystems

title: filesystems
categories:

  • linux
  • fs
    tags:
  • linux
  • fs
    abbrlink: 2089198e
    date: 2025-10-03 09:01:49

https://github.com/wdfk-prog/linux-study

文章目录

  • fs/ VFS - 虚拟文件系统(Virtual Filesystem) 内核统一的文件系统抽象层
      • 历史与背景
        • 这项技术是为了解决什么特定问题而诞生的?
        • 它的发展经历了哪些重要的里程碑或版本迭代?
        • 目前该技术的社区活跃度和主流应用情况如何?
      • 核心原理与设计
        • 它的核心工作原理是什么?
        • 它的主要优势体现在哪些方面?
        • 它存在哪些已知的劣s势、局限性或在特定场景下的不适用性?
      • 使用场景
        • 在哪些具体的业务或技术场景下,它是首选解决方案?
        • 是否有不推荐使用该技术的场景?为什么?
      • 对比分析
        • 请将其 与 其他相似技术 进行详细对比。
  • fs/filesystems.c
    • find_filesystem 查找文件系统
    • register_filesystem 注册新的文件系统
    • get_filesystem 获取文件系统的引用
    • /proc/filesystems 接口创建与展示 proc_filesystems_init
      • 实现原理分析
      • 代码分析

fs/ VFS - 虚拟文件系统(Virtual Filesystem) 内核统一的文件系统抽象层

历史与背景

这项技术是为了解决什么特定问题而诞生的?

虚拟文件系统(Virtual Filesystem Switch, VFS)是Linux内核最核心、最强大的子系统之一。它的诞生是为了解决一个根本性的问题:如何让应用程序以一种统一的方式来访问各种不同类型的文件系统

在VFS出现之前,操作系统如果想支持一种新的文件系统(例如,从Minix文件系统切换到ext文件系统),可能需要重写大量与文件操作相关的代码。应用程序也可能会与特定的文件系统实现产生耦合。VFS通过创建一个通用的抽象层来解决这个问题:

  1. 对应用程序的统一接口:无论底层是ext4、XFS、Btrfs、NFS(网络文件系统),还是一个USB U盘上的FAT32,应用程序都使用同样标准的系统调用(open,read,write,close,stat等)来操作文件。应用程序完全不需要知道底层文件系统的具体类型和实现细节。
  2. 对文件系统驱动的统一接口:VFS定义了一套标准的“插件”接口。任何想要被Linux内核支持的文件系统,只需要实现这套接口(即实现一系列定义好的回调函数),就可以无缝地“挂载”到VFS中,从而被所有应用程序使用。

简单来说,VFS就像一个“转换插头”,它将各种不同形状的“插座”(具体的文件系统实现)转换成一个标准的“插口”,供所有“电器”(应用程序)使用。

它的发展经历了哪些重要的里程碑或版本迭代?

VFS的概念源于早期的Unix系统,Linux继承并极大地发展了这一思想。

  • 四大核心对象的建立:VFS的核心设计围绕四个主要的对象(数据结构)展开,这些对象的稳定和完善是VFS发展的基石:
    • 超级块(Superblock): 代表一个已挂载的文件系统实例。
    • 索引节点(Inode): 代表一个具体的文件或目录。
    • 目录项(Dentry): 代表一个目录中的一个条目(即文件名与其inode的链接),是路径的组成部分。
    • 文件(File): 代表一个进程打开的文件实例(由open()调用创建)。
  • dcache(目录项缓存)的引入:这是Linux VFS一个巨大的性能优化里程碑。每次查找文件都需要从根目录开始逐级解析路径,这是一个非常耗时的操作。dcache将路径名到inode的解析结果缓存起来,极大地加速了后续对同一路径的访问。
  • 与Page Cache的深度融合:VFS与内存管理子系统中的页缓存(Page Cache)紧密协作。对文件的读写操作实际上是在操作内存中的页缓存,由内核的后台线程负责将“脏”页写回底层存储设备,这极大地提高了I/O性能。
目前该技术的社区活跃度和主流应用情况如何?

VFS是Linux内核的绝对核心,其稳定性和性能对整个系统至关重要。它不是一个经常添加新奇功能的领域,而是作为所有I/O操作的基石被持续地进行性能优化和精细维护。

  • 主流应用
    • 所有文件操作:系统中的每一次文件读、写、创建、删除,都必须经过VFS层。
    • 支持多样化的文件系统:从传统的磁盘文件系统(ext4, XFS, Btrfs),到网络文件系统(NFS, CIFS),再到各种伪文件系统(procfs, sysfs, tmpfs),它们能够共存并协同工作,完全得益于VFS的抽象。

核心原理与设计

它的核心工作原理是什么?

VFS的核心是通过面向对象的思想,使用上面提到的四个核心对象来抽象所有文件系统的共同特征。

  1. 超级块对象 (struct super_block):当一个文件系统被mount时,内核会读取该文件系统在磁盘上的“超级块”,并在内存中创建一个super_block对象。这个对象包含了该文件系统的元信息(如块大小、总空间等)和一组操作函数指针(struct super_operations),例如,如何分配一个新的inode或如何将整个文件系统同步到磁盘。它代表一个已挂载的文件系统
  2. 索引节点对象 (struct inode):当一个文件第一次被访问时,内核会读取它在磁盘上的“inode”,并在内存中创建一个inode对象。这个对象包含了文件的元数据(权限、大小、所有者、时间戳等)和两组关键的操作函数指针:struct inode_operations(例如,如何创建一个新文件、如何创建一个符号链接)和struct file_operations(例如,如何读/写这个文件)。它代表一个文件实体
  3. 目录项对象 (struct dentry):当内核解析一个路径(如/home/user/file.txt)时,它会为路径的每一个组成部分(home,user,file.txt)创建一个dentry对象。dentry的核心作用是将一个文件名与一个inode链接起来,并维护目录的父子关系。这些dentry对象被缓存在dcache中。它代表一个路径组件
  4. 文件对象 (struct file):当一个进程调用open()时,内核会创建一个file对象。这个对象代表一个打开的文件实例。它最重要的成员是f_pos(当前读写位置),以及一个指向对应inodef_opfile_operations)的指针。不同的进程打开同一个文件会得到不同的file对象,但它们都指向同一个inode对象。

调用流程示例:read(fd, ...)

  1. 内核通过文件描述符fd在当前进程的打开文件表中找到对应的struct file对象。
  2. 通过file对象找到其f_op指针,即file_operations
  3. 调用f_op中的.read.read_iter函数,并将file对象(其中包含了f_pos)作为参数传入。
  4. 这个.read函数是由具体的文件系统驱动(如ext4)实现的。ext4的read函数会根据f_pos和要读取的长度,计算出需要读取哪些磁盘块,然后通过块设备层去读取数据。
它的主要优势体现在哪些方面?
  • 极高的抽象性和可移植性:应用程序和大部分内核代码都无需关心底层文件系统的细节。
  • 代码复用:通用的逻辑(如权限检查、路径解析)都在VFS层实现,避免了每个文件系统驱动重复实现。
  • 性能优化:通过dcache和inode缓存,极大地减少了对物理设备的访问次数。
  • 灵活性:能够轻松地支持各种非传统的文件系统,甚至是用户空间的文件系统(FUSE)。
它存在哪些已知的劣s势、局限性或在特定场景下的不适用性?
  • 性能开销:VFS的抽象层会带来一些微小的性能开销。
  • 功能集的限制(“最小公分母”):VFS的标准接口只能支持大多数文件系统都具备的通用功能。对于某些高级文件系统(如Btrfs, ZFS)提供的独特功能(如子卷、快照),应用程序需要通过特定的ioctl系统调用来访问,这在一定程度上绕过了VFS的统一抽象。

使用场景

在哪些具体的业务或技术场景下,它是首选解决方案?

这不是一个可选的方案,而是Linux/Unix系统中进行任何文件操作的唯一框架

  • 所有常规文件操作ls,cp,mv,rm, 文本编辑,编译代码等。
  • 挂载不同介质:挂载硬盘、U盘、光盘、网络共享目录等。
  • 与内核交互:通过/proc/sys等伪文件系统,以读写文件的方式来查看和修改内核状态。
是否有不推荐使用该技术的场景?为什么?

不能说“不推荐使用VFS”,但存在一些场景需要绕过VFS的文件系统层

  • 裸设备访问(Raw Device Access):一些高性能的数据库(如Oracle)为了实现自己的缓存和I/O调度策略,会选择直接打开块设备文件(如/dev/sda1),绕过文件系统的逻辑,直接对磁盘块进行读写。但这仍然需要经过VFS的块设备处理部分。

对比分析

请将其 与 其他相似技术 进行详细对比。

最好的对比是理解VFS在整个Linux I/O栈中所处的位置。

层次名称核心功能典型数据单元
应用层用户程序 (e.g.,cp,bash)发起逻辑I/O请求。用户缓冲区 (char array)。
系统调用接口Glibc / Syscall Interface将用户请求转换为内核请求。open(),read(),write()
VFS (虚拟文件系统)VFS Core (fs/)提供统一的文件系统抽象。管理inode,dentry,file对象。struct inode,struct dentry,struct file
具体文件系统层Concrete FS (e.g., ext4, xfs)实现VFS接口。管理自己的元数据(如块位图),将文件逻辑块号映射为物理块号。文件系统特定的元数据(如ext4_extent)。
块设备层Block Layer (block/)提供对块设备的通用访问接口,进行I/O调度和合并。struct bio(Block I/O request)。
设备驱动层Device Driver (drivers/scsi, drivers/nvme)实现块层接口。知道如何与具体的硬件控制器(如SATA, NVMe)通信。硬件特定的命令 (如SCSI CDB)。
硬件层物理设备物理上执行读写操作。磁盘扇区。

fs/filesystems.c

find_filesystem 查找文件系统

staticstructfile_system_type**find_filesystem(constchar*name,unsignedlen){structfile_system_type**p;for(p=&file_systems;*p;p=&(*p)->next)if(strncmp((*p)->name,name,len)==0&&!(*p)->name[len])break;returnp;}

register_filesystem 注册新的文件系统

/** * register_filesystem - 注册新的文件系统 * @fs:文件系统结构 * * 将传递给内核可识别的文件系统列表的文件系统添加 mount 和其他 syscall。成功时返回 0,错误时返回负 errno 代码。 * * 传递的 &struct file_system_type 链接到内核结构中,在取消注册文件系统之前不得释放。 */intregister_filesystem(structfile_system_type*fs){intres=0;structfile_system_type**p;/* 如果文件系统结构包含参数(fs->parameters),调用 fs_validate_description 验证参数的合法性 */if(fs->parameters&&!fs_validate_description(fs->name,fs->parameters))return-EINVAL;/* 检查文件系统名称中是否包含非法字符(如 .) */BUG_ON(strchr(fs->name,'.'));/* 如果文件系统结构的 next 字段非空,表示该文件系统已经被注册过,返回 -EBUSY */if(fs->next)return-EBUSY;write_lock(&file_systems_lock);/* 调用 find_filesystem 查找文件系统名称是否已存在 */p=find_filesystem(fs->name,strlen(fs->name));if(*p)res=-EBUSY;else/* 将file_systems->next 挂载为 fs */*p=fs;write_unlock(&file_systems_lock);returnres;}

get_filesystem 获取文件系统的引用

/* WARNING: This can be used only if we _already_ own a reference */structfile_system_type*get_filesystem(structfile_system_type*fs){__module_get(fs->owner);returnfs;}

/proc/filesystems 接口创建与展示 proc_filesystems_init

本代码片段展示了内核如何在/proc虚拟文件系统中创建一个名为filesystems的文件,并通过这个文件向用户空间展示当前内核支持的所有已注册文件系统类型。它由两部分组成:proc_filesystems_init在内核初始化时创建/proc/filesystems文件,并将其与一个回调函数filesystems_proc_show关联;当用户读取该文件时,filesystems_proc_show函数被调用,遍历内核中维护的文件系统链表,并将每个文件系统的名称及其一个重要属性打印出来。

实现原理分析

此机制是内核通过procfs向用户空间提供信息的一个典型范例。其核心原理是利用 VFS(虚拟文件系统)层维护的一个全局链表和procfs的回调机制。

  1. 全局文件系统链表 (file_systems):

    • 内核中有一个全局的、以NULL结尾的单向链表,其头指针为file_systems。每当一个文件系统模块被加载(通过register_filesystem),一个新的file_system_type结构体就会被添加到这个链表的头部。
    • 这个file_system_type结构体包含了文件系统的元数据,如名称 (name) 和一系列标志位 (fs_flags)。
  2. Procfs 文件创建 (proc_create_single):

    • proc_filesystems_init函数在内核启动时被module_init机制调用。
    • 它调用proc_create_single,这是一个简化的procfs接口,用于创建一个只读的、内容由单个回调函数生成的/proc文件。 它将文件名 “filesystems” 与filesystems_proc_show函数绑定。当有进程尝试读取/proc/filesystems时,内核会调用filesystems_proc_show
  3. 信息展示 (filesystems_proc_show):

    • 安全遍历: 此函数首先调用read_lock(&file_systems_lock)来获取一个读锁。file_systems_lock是一个读写锁(rw_lock),用于保护file_systems链表。获取读锁允许多个读者同时安全地遍历链表,但会阻止任何写者(例如,正在加载或卸载文件系统模块的任务)修改链表,从而防止了竞态条件。
    • 信息提取与格式化: 函数通过一个while循环遍历链表。对于每一个file_system_type节点:
      a. 它检查tmp->fs_flags & FS_REQUIRES_DEVFS_REQUIRES_DEV是一个标志位,用于标识该文件系统是否需要一个块设备作为其物理存储后端(如 ext4、FAT32)。 如果该文件系统不需要块设备(例如,像procfs,sysfs这样的虚拟文件系统),条件为假,函数会打印 “nodev”。
      b. 它打印文件系统的注册名称tmp->name
    • 释放锁: 遍历完成后,调用read_unlock(&file_systems_lock)释放读锁,允许写者继续操作。

代码分析

/** * @brief filesystems_proc_show - 为 /proc/filesystems 生成内容的函数。 * @param m: 指向 seq_file 结构体的指针,用于向 proc 文件写入数据。 * @param v: 一个迭代器指针 (在此处未使用)。 * @return int: 始终返回0,表示成功。 */staticintfilesystems_proc_show(structseq_file*m,void*v){structfile_system_type*tmp;/// < 用于遍历文件系统链表的临时指针。// 获取读锁,以安全地遍历全局文件系统链表 file_systems。read_lock(&file_systems_lock);// 将 tmp 指针初始化为链表的头部。tmp=file_systems;// 循环遍历链表,直到链表末尾 (NULL)。while(tmp){// 使用 seq_printf 向 proc 文件输出格式化的字符串。seq_printf(m,"%s\t%s\n",// 检查文件系统的 fs_flags 标志位。// 如果 FS_REQUIRES_DEV 标志位未设置(表示该文件系统不需要块设备),// 则打印 "nodev";否则打印空字符串。(tmp->fs_flags&FS_REQUIRES_DEV)?"":"nodev",// 打印文件系统的名称。tmp->name);// 移动到链表中的下一个文件系统类型。tmp=tmp->next;}// 释放之前获取的读锁。read_unlock(&file_systems_lock);return0;}/** * @brief proc_filesystems_init - /proc/filesystems 的初始化函数。 * @return int: 始终返回0,表示成功。 */staticint__initproc_filesystems_init(void){// 创建一个名为 "filesystems" 的 proc 文件,权限为0(只读)。// 此文件没有父目录(在/proc/的根目录下),// 其内容由 filesystems_proc_show 函数生成。proc_create_single("filesystems",0,NULL,filesystems_proc_show);return0;}// 将 proc_filesystems_init 函数注册为内核的一个初始化模块。module_init(proc_filesystems_init);
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/2 19:47:14

写了这么多年Java,这几个神仙技巧你用过吗?

如果你也是从 public static void main(String[] args) 和 System.out.println() 开始Java生涯的&#xff0c;那也是Java老油条了。在日常的业务开发中&#xff0c;我们每天都在写着增删改查的逻辑&#xff0c;有时候会觉得Java有点笨重&#xff0c;语法有点啰嗦。 但其实&…

作者头像 李华
网站建设 2026/2/5 14:46:38

为什么你的Open-AutoGLM Web地址无法加载?深度排查与修复方案

第一章&#xff1a;Open-AutoGLM Web地址无法加载的现象分析当访问 Open-AutoGLM 的 Web 地址时&#xff0c;部分用户反馈页面长时间无响应或直接显示空白&#xff0c;该现象可能由多种因素共同导致。为系统性排查问题&#xff0c;需从网络、服务端与客户端三个维度进行深入分析…

作者头像 李华
网站建设 2026/2/4 1:08:38

科研假设生成器:帮助科学家提出可验证的新猜想

科研假设生成器&#xff1a;帮助科学家提出可验证的新猜想 在现代科研的战场上&#xff0c;信息不再是稀缺资源——它已经泛滥成灾。每年全球发表的学术论文超过300万篇&#xff0c;仅PubMed收录的生命科学类文献就以每天数千条的速度增长。一个研究者穷尽一生也无法读完自己领…

作者头像 李华
网站建设 2026/2/5 8:49:22

为什么顶尖开发者都在关注Open-AutoGLM开源代码?真相令人震惊

第一章&#xff1a;Open-AutoGLM开源代码地址Open-AutoGLM 是一个面向自动化自然语言生成与理解任务的开源框架&#xff0c;由深度学习与大模型研究团队联合发布。该项目旨在降低大语言模型在实际业务场景中的应用门槛&#xff0c;支持模型微调、推理优化、任务自动化编排等功能…

作者头像 李华
网站建设 2026/2/3 20:31:25

智能论文撰写:2025年10个支持LaTeX模板的AI网站横向对比

工具对比排名 工具名称 核心优势 支持LaTeX 适用场景 aibiye AIGC率降个位数&#xff0c;兼容知网规则 是 AI痕迹强处理 aicheck 学术改写优化&#xff0c;语义保留佳 是 格式统一化 askpaper 降重降AI一体&#xff0c;20分钟快速响应 是 初稿优化 秒篇 人类特…

作者头像 李华
网站建设 2026/2/5 23:49:17

Anything-LLM镜像:打造个人AI文档助手的终极解决方案

Anything-LLM镜像&#xff1a;打造个人AI文档助手的终极解决方案 在信息爆炸的时代&#xff0c;我们每天都在与越来越多的文档打交道——技术手册、研究论文、项目报告、会议纪要……但真正能被“记住”并随时调用的知识却少之又少。更令人沮丧的是&#xff0c;当你试图向一个大…

作者头像 李华