文件系统考古4:如何支持多个文件系统

news2024/11/18 15:34:21

Steve Kleiman 在 1986 年撰写了《Vnodes: An Architecture for Multiple File System Types in Sun UNIX》一文。这篇论文幅较短,大部分内容是数据结构的列举,以及 C 语言结构之间相互指向的图表。

Steve Kleiman是分布式文件系统领域的专家,在 Sun Microsystem 工作了多年,曾参与开发 Sun Network File System(NFS)等项目,为分布式文件系统领域做出了重要贡献。

Kleiman 希望在 Unix 中能够拥有多个文件系统,并希望这些文件系统能够共享接口和内存。具体而言,他希望设计一个能够提供以下功能的架构:

  • 一个可以支持多个实现的通用接口;
  • 支持 BSD FFS,以及两个远程文件系统 NFS 和 RFS,还有特定的非 Unix 文件系统,如MS-DOS;
  • 接口定义的操作需要是原子性的。

并且,能够在不影响性能的情况下动态地处理内存和数据结构,支持重入(reentrant) 和多核,并且具有一定面向对象进行编程的特性。

重入(reentrant) 是指程序或子程序在尚未完成上一次调用之前,可以再次被调用且不会出错或发生冲突。

两个抽象概念

Steven 研究了文件系统的各种操作,决定将他们抽象为两个概念

  • vfs,虚拟文件系统,代表文件系统
  • vnode,虚拟 inode,代表文件

vfs,虚拟文件系统,它提供统一的接口,使操作系统可以以一致的方式访问不同的文件系统,无论是本地文件系统还是网络文件系统。

vnode,虚拟 inode, 表示一个文件,每个文件都有一个相关联的索引节点,其中包含了文件的元数据(如文件权限、所有者、大小等)以及指向文件数据存储位置的指针。

采用了 C++风格(实际使用 C 语言),每一个类型会匹配一个虚函数表,通过虚函数表,系统在运行时根据对象的实际类型来调用适当的虚函数,实现动态绑定:

  • 对于 vfs 类型,其虚函数表 struct vfsops,包含了一系列的函数指针,用来执行诸如 mount、unmount、sync 和 vget 等操作。在论文的后面,会解释这些函数的原型和功能;
  • 对于 vnode 类型也是类似的,其虚函数表 struct vnodeops,包含 open、rdwr 和 close 等函数,还有create、unlink 和 rename 等函数。一些函数是针对特定的文件类型的,比如 readlink、mkdir、readdir 和 rmdir。

通过 vfs 对象来进行跟踪实际的挂载,其虚函数表 struct vfsops 指向适用于该特定子树的文件系统操作。

类似地,vnode 实例用来进行跟踪打开的文件。它包含 struct *vnodeops 指针,作为 vfs 的一部分,有指针 struct *vfs 指向文件系统实例。

vfs 和 vnode 这两个结构体都需要一些用于存储特定实现数据的字段(如“子类私有字段”)。他们都以 caddr_t ...data 指针结尾。这些私有数据并不是 vfs 和 vnode 的一部分,而是位于其他位置,并通过指针进行引用。

Vnodes 实操

在论文中,有一整页的内容专门用于展示各种相互指向的结构。乍一看可能会感到困惑,但一旦追踪下来,就会发现它非常直观和优雅。

Kleiman 详细解释了如何使用 lookuppn() 函数来解释事物的工作原理,该函数替代了传统 Unix 中的 namei() 函数。类似于 namei() ,这个函数接受一个路径,并返回表示该路径所代表的 vnode 的 struct vnode 指针。

路径遍历始于根 vnode 或当前进程的当前目录 vnode,具体取决于路径的第一个字符是否为/。

然后,这个函数会依次取出路径的每一个子项,并调用当前 vnode 的 lookup 函数,它接受一个路径子项和一个假设是目录的当前 vnode,并返回代表那个子项的 vnode。

当一个目录是个挂载点,它的 vfsmountedhere 会被设置为一个指向 struct vfs 的指针。lookuppn 函数会跟随这个指针,并调用 vfs 的根函数,以获取该文件系统的根 vnode,替换当前正在处理的 vnode。

反过来也是可能的:当解析父目录("… ")时,如果当前 vnode 的 “flags” 字段中设置了根标志,我们会跟随 vfsmountedhere 指针从当前 vnode 到 vfs。然后,我们可以使用该 vfs 中的 vnodecovered 字段来获取上层文件系统的 vnode。

无论如何,在成功完成后,会返回一个 struct vnode 指针,即所使用的路径。

新增的系统调用

为了使系统高效地运行,需要添加一些新的系统调用来完善接口。

在 Unix 的历史中,我们看到引入了 statsfs 和 fstatsfs ,通过这两个函数可以获得与用户空间中的文件系统进行交互的接口。getdirentries 函数可以让用户一次性获取多个目录条目(取决于提供的缓冲区大小),这大大加快了远程文件系统的目录读取速度。

在 Linux 系统中

通过查看 Linux 内核源代码,我们可以找到 Kleiman 设计的总体结构,尽管 Linux 内核的复杂性和丰富性掩盖了其中大部分内容。Linux 内核拥有丰富的文件系统类型,并且还添加了许多在 40 年前的 BSD 中不存在的功能。因此,我们可以找到更多的数据结构和系统调用,它们被用于实现命名空间、配额、属性、只读模式、目录名称缓存等功能。

文件

如果你仔细观察,原始的结构仍然可以找到:Linux 内存中的文件相关结构分为两部分,一个是已打开的文件,它是一个带有当前位置的 inode;另一个是 inode,它代表整个文件。

我们可以在此处找到[文件对象](](https://github.com/torvalds/linux/blob/v6.3/include/linux/fs.h#L942C3-L981),struct file 的实例。在文件的所有其他内容中,最值得注意的是一个字段 loff_t f_pos,它表示文件当前位置距离文件起始位置的偏移量(以字节为单位)。

文件的类是通过一个虚函数表来定义。我们可以找到一个指针 struct file_operations *f_op 。它展示了文件可以执行的所有操作,其中最常见的是打开(open)、关闭(close)、定位(lseek)、读取(read)和写入(write)。

文件还包含指向 inode 的指针,即 struct inode *f_inode

索引节点

对于不需要偏移量的文件操作,它们是针对整个文件进行的,定义为 struct inode *

查看此处的定义。我们可以看到这里还有其他的定义,40 年前的 BSD 中没有类似的定义,比如 ACL(访问控制列表)和属性(attributes)。

我们发现 inode 的类通过虚函数表来定义,即 struct inode_operations *i_op。同样的,这其中很多函数涉及新特性,比如 ACL(访问控制列表)和扩展属性,但我们也会找到我们期望的功能,比如链接(link)、删除(unlink)、重命名(rename)等。

Inode 还包含一个指向文件系统的指针,即 struct super_block *i_sb

超级块

挂载点用 struct super_block 来表示,在此处查看其定义。同样地,它有 struct super_operations *s_op 定义的各个操作,在此处查看其定义。

支持的文件系统不再有限,可以通过内核模块动态地添加新的文件系统,通过数据结构 struct file_system_type 来表示,它只有一个用于创建 superblock 的工厂函数 mount。

小结

Unix 发生了变化。它的运行时变得更加复杂,增加了许多新的功能,并增加了系统调用。系统变得更有结构。

但是,由 Steve Kleiman 和 Bill Joy(BSD 操作系统的共同创始人之一) 构思的原始设计和数据结构仍然存在,在当前的 Linux 系统中仍然可以找到,虽然已经过去了40年。

回顾

  • 文件系统考古1 :1974-Unix V7 File System
  • 文件系统考古2:1984 - BSD Fast Filing System
  • 文件系统考古 3:1994 - The SGI XFS Filesystem

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/726907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

selenium 浏览器托管

selenium 浏览器托管,是启动一个浏览器,调试代码,可以运行当前调试代码,不用从启动浏览器开始从头执行 在谷歌浏览器chrome.exe 目录中打开cmd 输入下面目录,启动器浏览器 chrome.exe --remote-debugging-port9222 -…

cesium 控件 风格 白色 亮色 模式 组件变白

在要白色风格的控件的任意父级的class加上cesium-lighter即可, 如图黄色圈出来的

redis实现伪集群

主从复制配置 主从复制时只需要配置从库即可,其默认为主库模式. 打开 redis 客户端登录,使用命令 info replication 查看. 主从复制可以搭建真集群,也可以搭建伪集群 真集群就是有多台主机,每台主机 安装一个 redis.伪集群就是在一台主机上复制多份配置,修改其端口,运行多个…

ja3指纹

问题: 请求失效,带上抓包软件却可以->检测ja3浏览器指纹 方法,python 安装curl_cffi库 pip install curl_cffi 查看浏览器指纹 https://tls.browserleaks.com/json python 原生request 缺少两个指纹 使用curl_cffi之后 可以看到结果…

【计算机网络】第三章 数据链路层(可靠传输)

文章目录 第三章 数据链路层3.4 可靠传输3.4.1 可靠传输的基本概念3.4.2 可靠传输的实现机制——停止-等待协议3.4.2 可靠传输的实现机制——回退N桢协议3.4.3 可靠传输的实现机制——选择重传协议3.4.4 可靠传输的意义 第三章 数据链路层 3.4 可靠传输 可靠传输是指在数据通…

Java中可以使用哪些系统架构?怎样选择?

架构,又名软件架构,是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。 Java企业级的应用根据业务的复杂程度,通常使用的系统架构有应用架构、垂直应用架构、面向服务的架构(Service-Oriented Architectu…

在外远程访问NAS威联通(QNAP) - 免费内网穿透

文章目录 前言1. 威联通安装cpolar内网穿透2. 内网穿透2.1 创建隧道2.2 测试公网远程访问 3. 配置固定二级子域名3.1 保留二级子域名3.2 配置二级子域名 4. 使用固定二级子域名远程访问 转载自cpolar极点云文章:无需公网IP,在外远程访问NAS威联通QNAP【内…

1688按关键字搜索工厂数据,商品详情页数据的采集

item_search_factory-按关键字搜索工厂数据 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)​注册​Key获取appsecret和appkey;​secretString是调用密钥api_nameString是API接口名称(包括在请求地址中…

认知网络原理

目录 一、网络发展史 1.1、独立模式 1.2、网络互连 1.3、局域网 二、网络通信 2.1、IP地址 2.2、端口号 三、认识协议 3.1、五元组 3.2、协议分层 3.3、分层的作用 3.4、OSI七层模型 3.5、TCP/IP五层(或四层)模型 3.6、网络设备所在分…

【快应用】快应用学习之页面周期函数onBackPress无法触发?

【关键词】 onBackPress、退出提示 【问题背景】 在学习和调试快应用的过程中&#xff0c;我在子页面中的onBackPress()函数中定制了退出的一个弹框提醒&#xff0c;将它作为组件引入父页面中&#xff0c;弹框却无法触发&#xff1f; 问题代码如下&#xff1a; 子页面 <t…

集合处理常用Stream流

集合处理常用Stream流 1、Stream API介绍2、List集合常用Stream方法 stream流经常使用&#xff0c;但是遇到一些流操作时&#xff0c;会一下想不到用哪种&#xff0c;这里总结一下&#xff0c;方便自己或者读者查找 1、Stream API介绍 Stream API是Java 8引入的一项重要特性&a…

B. Nastya and Scoreboard(剪枝记忆化搜索)

Problem - 1340B - Codeforces Denis,在买了花和糖果之后&#xff08;你将在下一个任务中了解这个故事&#xff09;&#xff0c;与Nastya约会&#xff0c;向她表白要成为一对。现在&#xff0c;他们坐在咖啡馆里&#xff0c;最后... Denis请她答应在一起&#xff0c;但是... Na…

文盘 Rust -- tokio 绑定 cpu 实践

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢&#xff1f;这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

第48步 深度学习图像识别:RegNet建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;RegNet RegNet (Regulated Networks) 是一种由 Facebook AI 的研究者们在 2020 年提出的神经网络架构&#xff0c;旨在探索网络架构设计的各种可能性&#xff0c;并找出最优的网络设计规则。RegNet 的核心理…

vue3ts分离头部变量单独一个ts

这里要说下 博主 vue^3.2.47 typescript^5.0.2 群有小伙伴说之前版本是不支持&#xff0c;所以你对下版本是不是比博主的版本低。 主要是页面代码太多&#xff0c;你看下面 然后博主就想着组合式开发 怎么把页面变干净点&#xff1a; 1、vue的就处理逻辑 2、ts就单纯定义…

vc++ 弹出打开、保存对话框功能(COM组件使用详解)

文章目录 前言一、原理二、代码封装三、使用示例C/C++实战入门到精通 https://blog.csdn.net/weixin_50964512/article/details/125710864 前言 很多应用都会有选择打开文件、或者选择保存文件的功能,这种情况下一般都是弹出一个对话框让用户自己选择。 这并不需要自己…

如何使用Scrum工具进行敏捷项目管理?

敏捷开发是一种轻量级的软件开发方式。 敏捷是一种通过创造变化和响应变化在不确定和混乱的环境中取得成功的能力。 敏捷开发是为了快速响应市场变化、通过自组织、跨职能团队 运用适合他们自身环境的实践进行演进得出解决方案。 所有符合敏捷宣言和敏捷开发十二项原则的方…

单片机要这样保护临界区

目录 一、概述 二、临界区保护测试场景 三、临界区保护三种实现 1、入门做法 2、改进做法 3、终极做法 四、附录---PRIMASK寄存器设置函数在各 IDE 下实现 一、概述 今天给大家分享的是Cortex-M裸机环境下&#xff0c;临界区保护的三种实现。 搞嵌入式玩过 RTOS 的小伙…

程序设计-编程题

CISP-PTE-编程题 2014 #include <iostream> using namespace std;double H(int n,double x) {if (n > 1)return 2 * x*H(n - 1, x) - 2 * (n - 1)*H(n - 2, x);if (n 0)return 1;if (n 1)return 2 * x; }int main() {int n;double x,result;cin >> n;cin >…