当前位置: 首页 > news >正文

《Linux 内核设计与实现》13. 虚拟文件系统

通用文件接口

VFS 使得可以直接使用 open()、read()、write() 这样的系统调用而无需考虑具体文件系统和实际物理介质。

好处:新的文件系统和新类型的存储介质需要挂载时,程序无需重写,甚至无需重新编译。

VFS 将各种不同的文件系统抽象后采用统一的方式进行操作。

image-20230427193920113

文件系统抽象层

可以使用这种通用接口对所有类型的文件系统进行操作,是因为内核在它的底层文件系统接口上建立了一个抽象层。

为了支持多文件系统,VFS 提供了一个通用文件系统模型,该模型囊括了任何文件系统的常用功能集和行为。

VFS 抽象层之所以能衔接各种各样的文件系统,是因为它定义了所有文件系统都支持的、基本的、概念上的接口和数据结构。

文件系统需要和 VFS 所定义的概念保持一致(其实就是规则、约束)。文件系统提供 VFS 所期望的抽象接口和数据结构,这样内核就可以和任何文件系统协同工作。

在内核中,在文件系统之外的其它部分,并不需要了解其文件系统内部细节:

ret = write(fd, buf, len); // 系统调用
// 其具体实现:sys_write

系统调用是通用 VFS 接口,提供给用户使用。系统调用的具体实现由文件系统中的具体文件实现,实现的是 VFS 所需要的 sys_write() 方法。

image-20230427193850774

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:

image-20230427200942704

对超级块的操作,位于: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

索引节点对象

索引节点对象包含了内核在操作文件或目录时需要的全部信息。

image-20230427203843419

一个索引节点代表文件系统中的一个文件,它也可以是设备或管道这样的特殊文件。但索引节点只有当文件被访问时,才能在内存中创建。

索引节点操作

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);
};

目录项对象

image-20230427211519800

目录项对象没有对应的磁盘数据结构,VFS 根据字符串形式的路径在内存中创建它,因此目录项对象并非真正保存在磁盘上。可以看到该结构体也没有磁盘相关的域。

目录项状态

状态有三:被使用、未被使用、负状态。

**被使用:**一个被使用的目录项对应一个有效的索引节点(即 d_inode 指向相应的索引节点)并且表面该对象存在一个或多个使用者(d_count 为正)。一个目录项处于被使用状态,意味着它正被 VFS 使用并且指向有效的数据,因此不能被丢弃。

未被使用: 一个未被使用的目录项对应一个有效的索引节点(d_inode 指向一个索引节点),但是应指明 VFS 当前并未使用它(d_count 为 0)。该目录项对象仍然指向一个有效对象,而且被保留在缓存中以便需要时再使用它。以后若再需要它,就不必重新创建。若要回收内存,也可以撤销未使用的目录项。

负状态: 其实就是无效目录项,一个负状态的目录项没有对应的有效索引节点(d_inode 为 NULL),因为索引节点已被删除,或路径不在正确,但目录项仍然可以保留,以便快速解析以后的路径查询。

目录项对象释放后也可以保存到 slab 对象缓存中。此时,任何 VFS 或文件系统代码都没有指向该目录项对象的有效引用。

目录项缓存

内核将目录项对象缓存在目录项缓存中。

目录项缓存包括三个主要部分:

image-20230427214346521

目录项操作

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() 系统调用撤销。

image-20230428101619847

文件操作

/** 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; } 没有换行&#xff0c;不然会格式错误 第二题 #include<stdio.h>int main() {int a;s…...

JMeter学习第一、二、三天

首先&#xff0c;我们来了解一下到底什么是接口测试与性能测试&#xff1a; 接口测试 定义 接口测试主要关注系统组件之间的交互&#xff0c;确保各个接口按预期工作。这包括验证传递的数据、数据格式、调用的频率和其他与接口调用相关的任何限制。 目的 确保系统的各个组件可…...

常用的分布式ID解决方案原理解析

目录 前言 一&#xff1a;分布式ID的使用场景 二&#xff1a;分布式ID设计的技术指标 三&#xff1a;常见的分布式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 || ""…...

分布式主键算法

目录 一、引言二、常见算法介绍雪花算法&#xff08;Snowflake Algorithm&#xff09;特性详解优势劣势 UUID&#xff08;Universally Unique Identifier&#xff09;特性详解优势劣势 数据库自增主键特性详解优势劣势 分布式数据库的序列&#xff08;Sequence&#xff09;特性…...

暴力破解及验证码安全

1.暴力破解注意事项 1、破解前一定要有一个有郊的字典&#xff08;Top100 TOP2000 csdn QQ 163等密码&#xff09; https://www.bugku.com/mima/ 密码生成器 2、判断用户是否设置了复杂的密码 在注册页面注册一个,用简单密码看是否可以注册成功 3、网站是…...

程序无法启动,提示“找不到msvcp140.dll”或“msvcp140.dll缺失报错”解决方法

大家好&#xff01;今天我来给大家分享一下msvcp140.dll丢失的解决方法。我们都知道&#xff0c;在运行一些软件或游戏时&#xff0c;经常会遇到“找不到msvcp140.dll”的错误提示&#xff0c;这会让我们非常苦恼。那么&#xff0c;这个问题该怎么解决呢&#xff1f;下面我将为…...

【Python查找算法】二分查找、线性查找、哈希查找

目录 1 二分查找算法 2 线性查找算法 3 哈希查找算法 1 二分查找算法 二分查找&#xff08;Binary Search&#xff09;是一种用于在有序数据集合中查找特定元素的高效算法。它的工作原理基于将数据集合分成两半&#xff0c;然后逐步缩小搜索范围&#xff0c;直到找到目标元素…...

【MySQL实战45讲-基础篇】

基础篇 基础架构 MySQL的基本架构示意图&#xff1a;MySQL可以分为Server层和存储引擎层两部分。 Server层包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖MySQL的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学和加密函…...

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)) 持久代&#xff1a; JVM用持久带&#xff08;Permanent Space&#xff09;实现方法…...

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…...

掌动智能:性能压力测试的重要性

采用性能压力测试可以帮助企业预估系统容量、提升用户体验以及降低风险和成本。在软件开发过程中&#xff0c;将性能压力测试纳入测试策略的重要一环&#xff0c;将为企业的成功和用户满意度打下坚实的基础。 性能压力测试的重要性&#xff1a; 一、发现性能瓶颈 性能压力测试能…...

kafka日志文件详解及生产常见问题总结

一、kafka的log日志梳理 日志文件是kafka根目录下的config/server.properties文件&#xff0c;配置log.dirs/usr/local/kafka/kafka-logs&#xff0c;kafka一部分数据包含当前Broker节点的消息数据(在Kafka中称为Log日志)&#xff0c;称为无状态数据&#xff0c;另外一部分存在…...

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"&#xff1a; 指名工程名、使用的JDK版本等信息。如下所示&#xff1a; 接着创建Java类&#xff1a; 2 编写代码 public class HelloWorld {public static void main(S…...

【1++的Linux】之进程(五)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;什么是进程替换二&#xff0c;替换函数三&#xff0c;实现我们自己的shell 一&#xff0c;什么是进程替换 我们创建出来进程是要其做事情的&#xff0c;它可…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...