《Linux 内核设计与实现》13. 虚拟文件系统
通用文件接口
VFS 使得可以直接使用 open()、read()、write() 这样的系统调用而无需考虑具体文件系统和实际物理介质。
好处:新的文件系统和新类型的存储介质需要挂载时,程序无需重写,甚至无需重新编译。
VFS 将各种不同的文件系统抽象后采用统一的方式进行操作。

文件系统抽象层
可以使用这种通用接口对所有类型的文件系统进行操作,是因为内核在它的底层文件系统接口上建立了一个抽象层。
为了支持多文件系统,VFS 提供了一个通用文件系统模型,该模型囊括了任何文件系统的常用功能集和行为。
VFS 抽象层之所以能衔接各种各样的文件系统,是因为它定义了所有文件系统都支持的、基本的、概念上的接口和数据结构。
文件系统需要和 VFS 所定义的概念保持一致(其实就是规则、约束)。文件系统提供 VFS 所期望的抽象接口和数据结构,这样内核就可以和任何文件系统协同工作。
在内核中,在文件系统之外的其它部分,并不需要了解其文件系统内部细节:
ret = write(fd, buf, len); // 系统调用
// 其具体实现:sys_write
系统调用是通用 VFS 接口,提供给用户使用。系统调用的具体实现由文件系统中的具体文件实现,实现的是 VFS 所需要的 sys_write() 方法。

Unix 文件系统
- 目录是特殊的文件,即目录文件。
- 根目录:
/ - 文件相关的信息,乘作为文件的元数据,这些元数据被存储到 inode 结构体中。
- 文件系统的相关信息,即文件系统的元数据,被存储在超级块中。
VFS 对象及其数据结构
VFS 采用面向对象思想来设计的。其对象使用结构体表示。
VFS 四个主要的对象类型:
- 超级块对象:代表一个具体的已安装文件系统。
- 索引节点对象:代表一个具体文件。
- 目录项对象:代表一个目录项,是路径的一个组成部分。
- 文件对象:代表由进程打开的文件。
每个对象中都包含一个操作对象,这些操作对象描述了内核针对主要对象可以使用的方法:
- super_operations 对象:包含内核针对特定文件系统所能调用的方法,比如 write_inode() 和 sync_fs() 等。
- inode_operations 对象:包含内核针对特定文件所能调用的方法,比如 create() 和 link() 等。
- dentry_operations 对象:包含内核针对特定目录所能调用的方法,比如 d_compare() 和 d_delete() 等。
- file_operations 对象:包含进程针对已打开文件所能调用的方法,比如 read() 和 write() 等。
并不止这些对象…
超级块对象
各种文件系统都必须实现超级块对象,该对象用于存储特定文件系统的信息,通常对应于存放在磁盘特定扇区中的文件系统超级块或文件系统控制块。其它并非基于磁盘的文件系统(如基于内存的文件系统 sysfs),它们会在使用现场创建超级块并将其保存到内存中。
超级块的结构体为 super_block,位于 include/linux/fs.h:

对超级块的操作,位于:fs/super.c
超级块对象通过 alloc_super() 函数创建并初始化。在文件系统安装时,文件系统会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。
超级块操作
超级块对象 super_block 中的 s_op 域指向超级块的操作函数表。
超级块的操作函数表用 super_operations 结构体表示,位于 include/linux/fs.h
struct super_operations {struct inode *(*alloc_inode)(struct super_block *sb);void (*destroy_inode)(struct inode *);void (*dirty_inode) (struct inode *);int (*write_inode) (struct inode *, struct writeback_control *wbc);void (*drop_inode) (struct inode *);void (*delete_inode) (struct inode *);void (*put_super) (struct super_block *);void (*write_super) (struct super_block *);int (*sync_fs)(struct super_block *sb, int wait);int (*freeze_fs) (struct super_block *);int (*unfreeze_fs) (struct super_block *);int (*statfs) (struct dentry *, struct kstatfs *);int (*remount_fs) (struct super_block *, int *, char *);void (*clear_inode) (struct inode *);void (*umount_begin) (struct super_block *);int (*show_options)(struct seq_file *, struct vfsmount *);int (*show_stats)(struct seq_file *, struct vfsmount *);
#ifdef CONFIG_QUOTAssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
#endifint (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
};
Tips:
sb->s_op->write_super(sb);
// 因为这不是对象,只是将其看成对象
// 结构体无 this 指向
// 因此只能手动传入 sb 对象,间接实现 this 指向 sb
索引节点对象
索引节点对象包含了内核在操作文件或目录时需要的全部信息。

一个索引节点代表文件系统中的一个文件,它也可以是设备或管道这样的特殊文件。但索引节点只有当文件被访问时,才能在内存中创建。
索引节点操作
struct inode_operations {int (*create) (struct inode *,struct dentry *,int, struct nameidata *);struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);int (*link) (struct dentry *,struct inode *,struct dentry *);int (*unlink) (struct inode *,struct dentry *);int (*symlink) (struct inode *,struct dentry *,const char *);int (*mkdir) (struct inode *,struct dentry *,int);int (*rmdir) (struct inode *,struct dentry *);int (*mknod) (struct inode *,struct dentry *,int,dev_t);int (*rename) (struct inode *, struct dentry *,struct inode *, struct dentry *);int (*readlink) (struct dentry *, char __user *,int);void * (*follow_link) (struct dentry *, struct nameidata *);void (*put_link) (struct dentry *, struct nameidata *, void *);void (*truncate) (struct inode *);int (*permission) (struct inode *, int);int (*check_acl)(struct inode *, int);int (*setattr) (struct dentry *, struct iattr *);int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);ssize_t (*listxattr) (struct dentry *, char *, size_t);int (*removexattr) (struct dentry *, const char *);void (*truncate_range)(struct inode *, loff_t, loff_t);long (*fallocate)(struct inode *inode, int mode, loff_t offset,loff_t len);int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,u64 len);
};
目录项对象

目录项对象没有对应的磁盘数据结构,VFS 根据字符串形式的路径在内存中创建它,因此目录项对象并非真正保存在磁盘上。可以看到该结构体也没有磁盘相关的域。
目录项状态
状态有三:被使用、未被使用、负状态。
**被使用:**一个被使用的目录项对应一个有效的索引节点(即 d_inode 指向相应的索引节点)并且表面该对象存在一个或多个使用者(d_count 为正)。一个目录项处于被使用状态,意味着它正被 VFS 使用并且指向有效的数据,因此不能被丢弃。
未被使用: 一个未被使用的目录项对应一个有效的索引节点(d_inode 指向一个索引节点),但是应指明 VFS 当前并未使用它(d_count 为 0)。该目录项对象仍然指向一个有效对象,而且被保留在缓存中以便需要时再使用它。以后若再需要它,就不必重新创建。若要回收内存,也可以撤销未使用的目录项。
负状态: 其实就是无效目录项,一个负状态的目录项没有对应的有效索引节点(d_inode 为 NULL),因为索引节点已被删除,或路径不在正确,但目录项仍然可以保留,以便快速解析以后的路径查询。
目录项对象释放后也可以保存到 slab 对象缓存中。此时,任何 VFS 或文件系统代码都没有指向该目录项对象的有效引用。
目录项缓存
内核将目录项对象缓存在目录项缓存中。
目录项缓存包括三个主要部分:

目录项操作
include\linux\dcache.h
struct dentry_operations {int (*d_revalidate)(struct dentry *, struct nameidata *);int (*d_hash) (struct dentry *, struct qstr *);int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);int (*d_delete)(struct dentry *);void (*d_release)(struct dentry *);void (*d_iput)(struct dentry *, struct inode *);char *(*d_dname)(struct dentry *, char *, int);
};
文件对象
VFS 的文件对象表示进程已打开的文件。文件对象是已打开的文件在内存中的表示。该对象(切记,不是具体的文件,即不是物理的,而只是存在于内存中的,因此 VFS 的文件对象没有对应的磁盘数据)由相应的 open() 系统调用创建,由 close() 系统调用撤销。

文件操作
/** NOTE:* read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl* can be called without the big kernel lock held in all filesystems.*/
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);
};
和文件系统相关的数据结构
内核定义了用来管理文件系统的数据结构。
file_system_type 用来描述各种特定文件系统类型:
缺图
每种文件系统,不管有多少个实例安装到系统中,还是根本就没有安装到系统中,都只有一个 file_system_type 结构。
当文件系统被安装时,将有一个 vfsmount 结构体在安装点被创建。该结构体用来代表文件系统的实例,即代表一个安装点。
缺图
vfsmount 结构体中维护的各种链表,用于理清文件系统和所有其它安装点间的关系。
vfsmount 结构保存了在安装时指定的标志信息,该信息存储在 mnt_flags 域中。
缺图
和进程相关的数据结构
系统中每个进程都有自己的一组打开的文件。file_struct、fs_strruct、namespace 结构体将 VFS和系统的进程紧密联系在一起。
file_struct 结构体,该结构体由进程描述符中的 files 目录项指向。所有与单个进程相关的信息都保护在其中:
include\linux\fdtable.h
缺图
fs_struct 结构体,该结构由进程描述符的 fs 域指向。它包含了文件系统和进程相关的信息。
include\linux\fs_struct.h
缺图
namespace 结构体,由进场描述符中的 mmt_namespace 域指向。它使得每个进程在系统中都看到唯一的安装文件系统,不仅是唯一的根目录,而且是唯一的文件系统层次结构。
缺图
相关文章:
《Linux 内核设计与实现》13. 虚拟文件系统
通用文件接口 VFS 使得可以直接使用 open()、read()、write() 这样的系统调用而无需考虑具体文件系统和实际物理介质。 好处:新的文件系统和新类型的存储介质需要挂载时,程序无需重写,甚至无需重新编译。 VFS 将各种不同的文件系统抽象后采…...
2021-06-09 51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次
缘由51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次_嵌入式-CSDN问答 #include "REG52.h" sbit K1 P1^0; sbit K2 P1^1; sbit LEDP0^0; void main() {unsigned char Xd0,ss0;unsigned int wei0;while(1){if(K10&&Xd0){ss3*2;…...
C/C++ 经典面试算法题
1.打印杨辉三角 1 #include <stdio.h>2 #include <string.h>3 4 int main()5 {6 int x;7 int a[100][100];8 printf("输入行数\n");9 scanf("%d",&x); 10 for(int i 0;i<x;i) 11 { 12 for(int j 0;…...
2023年下学期《C语言》作业0x02-分支 XTU OJ 1068 1069 1070 1071 1072
第一题 #include<stdio.h>int main() {int a;scanf("%d",&a);if(a>90&&a<100) printf("A");else printf("B");return 0; } 没有换行,不然会格式错误 第二题 #include<stdio.h>int main() {int a;s…...
JMeter学习第一、二、三天
首先,我们来了解一下到底什么是接口测试与性能测试: 接口测试 定义 接口测试主要关注系统组件之间的交互,确保各个接口按预期工作。这包括验证传递的数据、数据格式、调用的频率和其他与接口调用相关的任何限制。 目的 确保系统的各个组件可…...
常用的分布式ID解决方案原理解析
目录 前言 一:分布式ID的使用场景 二:分布式ID设计的技术指标 三:常见的分布式ID生成策略 3.1 UUID 3.2 数据库生成 3.3 数据库的多主模式 3.4 号段模式 3.5 雪花算法 前言 分布式ID的生成是分布式系统中非常核心的基础性模块&#…...
echarts3D地图打点
1、echarts地图打点加鼠标移上去显示文字 2、1-3和前面的一样echart3D地图 if (res.code 0) {const resData res.data || [];if (resData.length > 0) {for (var i 0; i < resData.length; i) {let arr new Array(2);arr[0] resData[i].longitude || ""…...
分布式主键算法
目录 一、引言二、常见算法介绍雪花算法(Snowflake Algorithm)特性详解优势劣势 UUID(Universally Unique Identifier)特性详解优势劣势 数据库自增主键特性详解优势劣势 分布式数据库的序列(Sequence)特性…...
暴力破解及验证码安全
1.暴力破解注意事项 1、破解前一定要有一个有郊的字典(Top100 TOP2000 csdn QQ 163等密码) https://www.bugku.com/mima/ 密码生成器 2、判断用户是否设置了复杂的密码 在注册页面注册一个,用简单密码看是否可以注册成功 3、网站是…...
程序无法启动,提示“找不到msvcp140.dll”或“msvcp140.dll缺失报错”解决方法
大家好!今天我来给大家分享一下msvcp140.dll丢失的解决方法。我们都知道,在运行一些软件或游戏时,经常会遇到“找不到msvcp140.dll”的错误提示,这会让我们非常苦恼。那么,这个问题该怎么解决呢?下面我将为…...
【Python查找算法】二分查找、线性查找、哈希查找
目录 1 二分查找算法 2 线性查找算法 3 哈希查找算法 1 二分查找算法 二分查找(Binary Search)是一种用于在有序数据集合中查找特定元素的高效算法。它的工作原理基于将数据集合分成两半,然后逐步缩小搜索范围,直到找到目标元素…...
【MySQL实战45讲-基础篇】
基础篇 基础架构 MySQL的基本架构示意图:MySQL可以分为Server层和存储引擎层两部分。 Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函…...
asp.net core中间件预防防止xss攻击
using System; using System.Text.Json; using System.Text.Json.Serialization;namespace CommonUtils {/// <summary>/// newtonsoft的转化器/// 防止xss攻击/// </summary>public class AntiXssNewtonsoftConverter : Newtonsoft.Json.JsonConverter<string&…...
jvm概述
1、JVM体系结构 2、JVM运行时数据区 3、JVM内存模型 JVM运行时内存 共享内存区 线程内存区 3.1、共享内存区 共享内存区 持久带(方法区 其他) 堆(Old Space Young Space(den S0 S1)) 持久代: JVM用持久带(Permanent Space)实现方法…...
C++简单上手helloworld 以及 vscode找不到文件的可能性原因
helloworld #include <iostream>int main() {std::cout << "hello world!" << std::endl;return 0; }输入输出小功能 #include <iostream> using namespace std; /* *主函数 *输出一条语句 */int main() {// 输出一条语句cout << &q…...
掌动智能:性能压力测试的重要性
采用性能压力测试可以帮助企业预估系统容量、提升用户体验以及降低风险和成本。在软件开发过程中,将性能压力测试纳入测试策略的重要一环,将为企业的成功和用户满意度打下坚实的基础。 性能压力测试的重要性: 一、发现性能瓶颈 性能压力测试能…...
kafka日志文件详解及生产常见问题总结
一、kafka的log日志梳理 日志文件是kafka根目录下的config/server.properties文件,配置log.dirs/usr/local/kafka/kafka-logs,kafka一部分数据包含当前Broker节点的消息数据(在Kafka中称为Log日志),称为无状态数据,另外一部分存在…...
Linux-Centos中配置docker
1.安装yum工具 yum install -y yum-utils 2.配置yam源头 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 3.安装docker yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 4. 查看d…...
IDEA-2023-jdk8 HelloWorld的实现
目录 1 新建Project - Class 2 编写代码 3 运行 1 新建Project - Class 选择"New Project": 指名工程名、使用的JDK版本等信息。如下所示: 接着创建Java类: 2 编写代码 public class HelloWorld {public static void main(S…...
【1++的Linux】之进程(五)
👍作者主页:进击的1 🤩 专栏链接:【1的Linux】 文章目录 一,什么是进程替换二,替换函数三,实现我们自己的shell 一,什么是进程替换 我们创建出来进程是要其做事情的,它可…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema,不需要复杂的查询,只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 :在几秒钟…...
Centos 7 服务器部署多网站
一、准备工作 安装 Apache bash sudo yum install httpd -y sudo systemctl start httpd sudo systemctl enable httpd创建网站目录 假设部署 2 个网站,目录结构如下: bash sudo mkdir -p /var/www/site1/html sudo mkdir -p /var/www/site2/html添加测试…...
(12)-Fiddler抓包-Fiddler设置IOS手机抓包
1.简介 Fiddler不但能截获各种浏览器发出的 HTTP 请求,也可以截获各种智能手机发出的HTTP/ HTTPS 请求。 Fiddler 能捕获Android 和 Windows Phone 等设备发出的 HTTP/HTTPS 请求。同理也可以截获iOS设备发出的请求,比如 iPhone、iPad 和 MacBook 等苹…...
前端异步编程全场景解读
前端异步编程是现代Web开发的核心,它解决了浏览器单线程执行带来的UI阻塞问题。以下从多个维度进行深度解析: 一、异步编程的核心概念 JavaScript的执行环境是单线程的,这意味着在同一时间只能执行一个任务。为了不阻塞主线程,J…...
