Linux虚拟文件系统

发布时间:2026/9/4 14:22:04
Linux虚拟文件系统 参考深入分析LINUX内核源码深入分析Linux内核源码 (kerneltravel.net)作为一个最著名的自由软件Linux 确实名不虚传几乎处处体现了“自由”你可以编译适合自己系统要求的内核或者轻松添加别人开发的新的模块。只要你有实力你还可以自己写一个新的 Linux 支持的文件系统。写一个新的文件系统虽然是一个“耸人听闻”的 事但 Linux 确实有这样一个特点就是可以很方便地支持别的操作系统的文件系统比如 Windows 的文件系统就被 Linux 所支持。Linux 不仅支持多种文件系统而且还支持这些文件系统相互之间进行访问这一切都要归功于神奇的虚拟文件系统。概述虚拟文件系统又称虚拟文件系统转换Virual Filesystem Switch 简称 VFS。说它虚拟是因为它所有的数据结构都是在运行以后才建立并在卸载时删除而在磁盘上并没有存储这些数据结构。如果只有 VFS系统是无法工作的因为它的这些数据结构不能凭空而来只有与实际的文件系统如 Ext2、Minix、MSDOS、VFAT 等相结合才能开始工作所以 VFS 并不是一个真正的文件系统。与 VFS 相对应我们称 Ext2、Minix、MSDOS 等为具体文件系统。1虚拟文件系统的作用在第一章 Linux 内核结构一节中我们把 VFS 称为内核的一个子系统其他子系统只与 VFS 打交道而并不与具体文件系统发生联系。对具体文件系统来说VFS 是一个管理者而对内核的其他子系统来说VFS 是它们与具体文件系统的一个接口整个 Linux 中文件系统的逻辑关系如图 8.1 所示。VFS 提供一个统一的接口实际上就是 file_operatoin 数据结构稍后介绍一个具体文件系统要想被 Linux 支持就必须按照这个接口编写自己的操作函数而将自己的细节对内核其他子系统隐藏起来。因而对内核其他子系统以及运行在操作系统之上的用户程序而言所有的文件系统都是一样的。实际上要支持一个新的文件系统主要任务就是编写这些接口函数。 概括说来VFS 主要有以下几个作用。1对具体文件系统的数据结构进行抽象以一种统一的数据结构进行管理。2接受用户层的系统调用例如 write、open、stat、link 等。3支持多种具体文件系统之间相互访问。4接受内核其他子系统的操作请求特别是内存管理子系统。通过 VFSLinux 可以支持很多种具体文件系统表 8.1 是 Linux 支持的部分具体文件系统。2VFS 所处理的系统调用表 8.2 列出 VFS 的系统调用这些系统调用涉及文件系统、常规文件、目录及符号链接。 另外还有少数几个由 VFS 处理的其他系统调用诸如 ioperm( )、ioctl( )、pipe( )和 mknod( )涉及设备文件和管道文件有些内容在下一章进行讨论。由 VFS 处理的最后一组系统调用诸如 socket( )、connect( )、bind( )和 protocols( )属于套接字系统调用并用于实现网络功能。前面我们已经提到VFS 是应用程序和具体的文件系统之间的一个层。不过在某些情况下一个文件操作可能由 VFS 本身去执行无需调用下一层程序。例如当某个进程关闭一个打开的文件时并不需要涉及磁盘上的相应文件因此VFS 只需释放对应的文件对象。 类似地如果系统调用 lseek()修改一个文件指针而这个文件指针指向有关打开的文件与进程交互的一个属性那么 VFS 只需修改对应的文件对象而不必访问磁盘上的文件因此 无需调用具体的文件系统子程序。从某种意义上说可以把 VFS 看成“通用”文件系统它在必要时依赖某种具体的文件系统。VFS 中的数据结构虚拟文件系统所隐含的主要思想在于引入了一个通用的文件模型这个模型能够表示所有支持的文件系统。该模型严格遵守传统 UNIX 文件系统提供的文件模型。你可以把通用文件模型看作是面向对象的在这里对象是一个软件结构其中既定义了数据结构也定义了其上的操作方法。出于效率的考虑Linux 的编码并未采用面向对象的程序设计语言比如 C。因此对象作为数据结构来实现数据结构中指向函数的域就对应于对象的方法。 通用文件模型由下列对象类型组成。• 超级块superblock对象存放系统中已安装文件系统的有关信息。对于基于磁盘的文件系统这类对象通常对应于存放在磁盘上的文件系统控制块也就是说每个文件系统都有一个超级块对象。• 索引节点inode对象存放关于具体文件的一般信息。对于基于磁盘的文件系统 这类对象通常对应于存放在磁盘上的文件控制块FCB也就是说每个文件都有一个索引 节点对象。每个索引节点对象都有一个索引节点号这个号唯一地标识某个文件系统中的指 定文件。• 目录项dentry对象存放目录项与对应文件进行链接的信息。VFS 把每个目录看作一个由若干子目录和文件组成的常规文件。例如在查找路径名/tmp/test 时内核为根 目录“/”创建一个目录项对象为根目录下的 tmp 项创建一个第 2 级目录项对象为/tmp 目录下的 test 项创建一个第 3 级目录项对象。• 文件file对象存放打开文件与进程之间进行交互的有关信息。这类信息仅当进程访问文件期间存在于内存中。下面我们讨论超级块、索引节点、目录项及文件的数据结构它们的共同特点有两个• 充分考虑到对多种具体文件系统的兼容性• 是“虚”的也就是说只能存在于内存。这正体现了 VFS 的特点在下面的描述中读者也许能体会到以上特点。内容太多具体参考开头连接的pdf。暂略linux中文件系统的监听机制inotifyinotify是Linux 内核提供的文件系统事件监控机制用来监听文件 / 目录发生的变化创建、删除、修改、重命名、权限变更等。替代老旧的 dnotify用户态不需要不停轮询stat()内核主动推送事件效率很高。核心原理应用调用系统调用创建一个 inotify 实例得到一个文件描述符 fd。对要监控的文件 / 目录添加 watch监控项指定关心哪些事件。文件系统发生对应操作时内核把事件放到 inotify 的缓冲区队列。用户程序read()读取这个 fd拿到事件可以配合select/poll/epoll做异步监听不阻塞。用完 close (fd) 销毁实例。✅ 内核态通知不是轮询 statCPU 开销远高于自己循环 ls、stat。重要系统调用C APIinotify_init()/inotify_init1(flags)创建 inotify 实例返回 fdinotify_init1可以设置IN_NONBLOCK非阻塞。int inotify_add_watch(int fd, const char *path, uint32_t mask)添加监控路径 事件掩码 mask返回 watch 描述符 wd。⚠️监控目录只会通知目录内子文件事件不会自动递归子目录inotify 本身不支持递归需要代码自己遍历子目录逐个 add_watch。int inotify_rm_watch(int fd, int wd)移除监控项。read(fd, buf, buf_len)读取事件返回一堆struct inotify_event结构体。struct inotify_eventstruct inotify_event { int wd; // watch id uint32_t mask; // 事件类型掩码 uint32_t cookie; // 重命名时配对 old/new 事件 uint32_t len; // name字段有效长度 char name[]; // 发生事件的文件名监控目录时才有 };常用 mask 事件标志mask含义IN_CREATE文件 / 目录被创建IN_DELETE文件 / 目录被删除IN_MODIFY文件内容修改writeIN_ATTRIB属性变化 (chmod,chown, 时间戳)IN_CLOSE_WRITE打开写的文件被关闭写完IN_MOVED_FROM移出目录IN_MOVED_TO移入目录 / 重命名进来IN_DELETE_SELF被监控本身这个文件删除IN_MOVE_SELF被监控对象自身被重命名IN_ALL_EVENTS全部事件重点IN_MODIFY每次 write 都会触发会多次事件如果你想知道文件写完优先监听IN_CLOSE_WRITE。关键坑点面试高频不支持递归监控只监控你 add_watch 的那一级目录。新建子目录不会自动监控程序收到IN_CREATE发现新建是目录需要手动inotify_add_watch再加进去。事件队列溢出IN_Q_OVERFLOW事件爆发太快内核缓冲区满会产生溢出事件丢失部分事件。可以调整但缓冲区有上限。监控目录 vs 监控文件区别watch 文件事件发生在该文件name字段为空。watch 目录目录里面文件发生变化事件到来name存子文件名字。目录本身修改属性是 IN_ATTRIB子文件修改才报子文件事件。文件被删除后 watch 自动失效文件被重命名watch 也会丢失。不监听软链接本身inotify 跟随 symlink监控的是真实文件。NFS 网络文件系统inotify 在 NFS 客户端无效事件只能在 NFS 服务端产生。本地文件系统 ext4/xfs/btrfs 没问题。用户层工具inotifywait/inotifywatch工具包inotify-toolsshell 直接用不用写 C 代码。示例 shell# 监听/tmp目录创建、删除、关闭写 inotifywait -m /tmp -e create -e delete -e close_write-mmonitor 模式持续监听。上层封装库C直接系统调用Pythonpyinotify、watchdogGofsnotify底层封装 inotifyfsnotify 做了递归封装屏蔽底层 inotify 递归麻烦。inotify vs 自己轮询 stat轮询每秒 stat用户态不停扫描CPU 高有时间延迟。inotify内核主动推送空闲几乎 0CPU事件实时。和ubus 的联系OpenWrtOpenWrt 很多守护进程比如文件配置监控底层就是 inotify而不是 sleep 轮询触发文件变化之后再通过 ubus 对外抛出事件。这就是为什么优先 ubus subscribe而不是轮询 ubus底层思想和 inotify 一致事件驱动代替轮询。简单对比 fanotifyinotify监控文件事件轻量不能读取文件内容。fanotify更新一代可以拦截文件访问、读取文件内容用于杀毒软件权限要求高。