easyfs 简易文件系统
easyfs
- easyfs 简易文件系统
- 文件系统
- 虚拟文件系统 VFS
- 简易文件系统 easyfs
- 磁盘布局
- 超级块
- easyfs 文件系统结构
- 磁盘上的索引结构
- 索引节点
- `Inode` 和 `DiskInode` 之间的关系
- 举例说明读取文件的过程( `/hello` )
- 参考文档
easyfs 简易文件系统
文件系统
常规文件和目录实际都是保存在持久存储设备中的。持久存储设备仅支持以扇区为单位的随即读写,这和我们通常认识的通过路径即可索引到文件进行操作的用户视角有很大的不同。负责两者转换的便是 文件系统 。
文件系统负责将逻辑上的目录树结构映射到持久存储器上,决定设备上的每个扇区各应存储哪些内容;同时,文件系统页可以从持久存储设备还原出逻辑上的目录树结构。
虚拟文件系统 VFS
一个计算机系统中存在多个持久存储设备,每个存储设备上面的数据可以以不同文件系统格式存储的;所以为了能够向用户提供统一的界面和对这些设备进行统一的管理,内核中增加了一层 虚拟文件系统 VFS 。
虚拟文件系统规定了逻辑上的目录结构的通用格式以及相关操作的抽象接口。不同的底层文件系统均实现虚拟文件系统要求的那些抽象接口,并挂在到系统中,就可以实现对这些持久存储设备上的不同文件系统的统一管理。
简易文件系统 easyfs
该文件系统只是为了完整展示文件系统的工作原理。
- 扁平化:仅存在一个目录
/
。在索引一个文件时,直接使用文件名而不关心其路径; - 权限控制:全程只有单用户,不区分用户和用户组概念。
- 不记录文件访问/修改的时间辍。
磁盘的布局体现在磁盘上各个扇区的内容上,而解析磁盘布局得到的逻辑目录树结构则是通过内存上的数据结构来访问的——这表示文件系统涉及到同时对磁盘和对内存的访问:
- 对内存直接通过一条指令即可直接读写内存相应的位置;
- 操作磁盘则需要使用软件的方式向磁盘发出请求来间接进行对写;
我们需要特别注意哪些数据结构是存放在磁盘上的,哪些时存储在内存中的——这样才不会引起还乱。
easyfs
本身划分为不同的层次,形成层次花模块设计构架:
- 磁盘块设备接口层:定义了以块大小为单位对磁盘块设备进行对写的
trait
接口 - 块缓存层:在内存中缓存磁盘块的数据,避免频繁读写磁盘;
- 磁盘数据结构层:磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理;
- 磁盘块管理器层:合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构,以及创建/打开文件系统相关处理和磁盘块的分配和回收处理;
- 索引点层:管理索引节点(文件控制块)数据结构,并实现文件创建/打开/读写等成员函数来支持文件操作相关的系统调用的处理;
块设备接口层
在 easyfs
最底层声明了一个块设备的抽象接口 BlockDevice
:
pub trait BlockDevice: Send + Sync + Any {fn read_block(&self, block_id: usize, buf: &mut [u8]);fn write_block(&self, block_id: usize, buf: &mut [u8]);
}
它实现了两个抽象方法:
read_block
可以将编号为block_id
的块从磁盘读入内存中的缓冲区buf
;write_block
可以将内存中的缓冲区buf
中的数据写入磁盘编号为block_id
的块;
因为块设备仅支持以块为单位进行随即读写,这两个抽象方法向上提供了统一的接口方法;但具体实现需要由块设备驱动实现。
easyfs
的块缓存层会调用这两个方法进行块缓存的管理。
Tips:
块与扇区
扇区(Sector)是块设备随即读写的大小单位,通常扇区为 512 字节。
块(Block) 是文件系统存储文件时的大小单位,每个块的大小等同于一个或多个扇区。
在 easyfs 中块大小和扇区同为 512 字节。
块缓存层
由于操作系统频繁读写磁盘会极大降低性能,因此先通过 read_block
将一个块上的数据从磁盘督导内存缓冲区中,这个缓冲区中的内容是可以直接读写的。如果对于缓冲区中的内容进行了修改,那么后续还需要通过 write_block
将缓冲区中的内容写回到磁盘块中。
从性能上考虑,要尽可能降低实际块读写( read/write_block
)的次数,因为每一次调用都会产生大量的开销。要做到这一点,关键在于对块进行读写合并操作。
统一管理块缓冲区:
- 当要读写一个块的时候,首先区全局管理器中查看这个块是否已经被缓存到内存中;
- 在一段连续时间内对于一个块进行的所有操作均是在同一个固定的缓冲区中进行的,从而解决同步性问题;
- 全局管理器会尽可能将更多的块操作合并起来,并在必要的时机发起真正的块读写;
pub const BLOCK_SZ: usize = 512;
pub struct BlockCache{cache: [u8; BLOCK_SZ],block_id: usize,block_device: Arc<dyn BlockDevice>,modified: bool,
}
cache
是一个 512 字节的数组,表示位于内存中的缓冲区 —— 对应于一个磁盘块的大小;
block_id
记录了这个块缓存来自于磁盘中的块的编号;
block_device
保留一个底层块设备的引用时的可以和它打交道;
modified
记录自从这个块缓存从磁盘在如内存之后,它是否被修改过;
每个 BlockCache
保存一个扇区的数据。
磁盘数据结构层
磁盘上的超级块、位图、索引节点、数据块、目录项等核心数据结构和相关处理
磁盘块管理器层
合并了上述核心数据结构和磁盘布局所形成的磁盘文件系统数据结构,以及创建/打开文件系统的相关处理和磁盘块的分配和回收处理
索引点层
管理索引节点(即文件控制块)数据结构,并实现文件创建/文件打开/文件读写等成员函数来向上支持文件操作相关的系统调用的处理
磁盘布局
如何将一个逻辑上的文件目录树结构映射到磁盘上,决定磁盘上的每个块应该存储文件相关的哪些数据。为了更容易进行管理和更新,将磁盘上的数据组织位若干种不同的磁盘上数据结构,并合理安排它们在磁盘中的位置。
easyfs的磁盘布局:
超级块
#[repr(c)]
pub struct SuperBlock{magic: u32,pub total_blocks: u32,pub inode_bitmap_blocks: u32,pub inode_area_blocks: u32,pub data_bitmap_blocks: u32,pub data_area_blocks: u32,
}
magic
用于验证文件系统合法性的魔数;
total_block
文件系统管理的总块数, 不一定等于磁盘的总块数;
inode_bitmap_blocks
索引块位图区域的长度——索引块位图区用于表示索引块的分配情况;
inode_area_blocks
索引块区域——该区域被索引位图块管理;
data_bitmap_blocks
数据块位图区域的长度——数据块位图区用于表示数据块的分配情况;
data_area_blocks
数据块区域——该区域被数据块位图管理;
超级块是文件系统使用的数据,表示文件系统在磁盘上的布局情况。
- 索引节点位图区域:索引位图的实际保存区域;
- 索引节点区域:索引节点的实际保存区域;
- 数据块位图区域:数据块位图的实际保存区域;
- 数据块区域:数据块的实际保存区域;
通过超级块可以得到文件系统的整体情况:
easyfs 文件系统结构
pub struct EasyFileSystem{pub block_device: Arc<dyn BlockDevice>,pub inode_bitmap: Bitmap,pub data_bitmap: Bitmap,inode_area_start_block: u32,data_area_start_block: u32,
}
impl EasyFileSystem {/// Open a block device as a filesystempub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {// read SuperBlockget_block_cache(0, Arc::clone(&block_device)).lock().read(0, |super_block: &SuperBlock| {assert!(super_block.is_valid(), "Error loading EFS!");let inode_total_blocks =super_block.inode_bitmap_blocks + super_block.inode_area_blocks;let efs = Self {block_device,inode_bitmap: Bitmap::new(1, super_block.inode_bitmap_blocks as usize),data_bitmap: Bitmap::new((1 + inode_total_blocks) as usize,super_block.data_bitmap_blocks as usize,),inode_area_start_block: 1 + super_block.inode_bitmap_blocks,data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,};Arc::new(Mutex::new(efs))})}
}
open
函数可以获取一个磁盘上文件系统。从函数中的内容可以看出:
- 索引位图区是超级块上从 1 到长度为
inode_bitmap_blocks
的位置的内容; - 数据位图区是
inode_total_blocks
下一个磁盘块开始长度为data_bitmap_blocks
的位置的内容; - 索引区域开始位置为索引位图区的下一个磁盘块位置;
- 数据区域开始位置为数据位图区的下一个磁盘位置;
这正好与磁盘布局相对应。
磁盘上的索引结构
#[repr(C)]
pub struct DiskInode {pub size: u32,pub direct: [u32; INODE_DIRECT_COUNT],pub indirect1: u32,pub indirect2: u32,type_: DiskInodeType,
}#[derive(PartialEq)]
pub enum DiskInodeType {File,Directory,
}
size
表示文件/目录内容的字节数;type_
表示索引节点的类型DiskInodeType
,目前仅支持文件File
和目录Directory
两种类型;direct/indirect1/indirect2
都是存储文件内容/目录内容的数据块的索引,这也是索引节点名字的由来:- 当文件很小的时候,只需用到直接索引,
direct
数组中最多可以指向INODE_DIRECT_COUNT
个数据块,当取值为 28 的时候,通过直接索引可以找到 14KiB 的内容。 - 当文件比较大的时候,不仅直接索引的
direct
数组装满,还需要用到一级间接索引indirect1
。它指向一个一级索引块,这个块也位于磁盘布局的数据块区域中。这个一级索引块中的每个u32
都用来指向数据块区域中一个保存该文件内容的数据块,因此,最多能够索引 512 / 4 = 128 512/4=128 512/4=128 个数据块,对应 64 K i B 64KiB 64KiB 的内容。 - 当文件大小超过直接索引和一级索引支持的容量上限 78 K i B 78KiB 78KiB 的时候,就需要用到二级间接索引
indirect2
。它指向一个位于数据块区域中的二级索引块。二级索引块中的每个u32
指向一个不同的一级索引块,这些一级索引块也位于数据块区域中。因此,通过二级间接索引最多能够索引 128 × 64 K i B = 8 M i B 128×64KiB=8MiB 128×64KiB=8MiB 的内容;
索引节点
easyfs
实现了磁盘布局并能够将所有块有效管理起来。但是对于使用者,往往不关心磁盘布局的实现,而更多的时关注目录树中逻辑上的文件和目录。 Inode
索引节点就是实现这个目的而存在的。
pub struct Inode {block_id: usize,block_offset: usize,fs: Arc<Mutex<EasyFileSystem>>,block_device: Arc<dyn BlockDevice>,
}
block_id
是记录磁盘id
的 ;block_offset
是数据在某个磁盘块中的偏移量;fs
是一个指向easyfs
的指针;block_device
代表哪一个磁盘设备;
Inode
和 DiskInode
之间的关系
Inode
是存放在内存中的记录文件索引节点信息的数据结构,对应了磁盘上的 DiskInode
结构体的位置;
DiskInode
是磁盘中固定块的数据结构,它是磁盘上每个数据存储位置的索引,对应实际数据存放于物理的块的管理;
从超级块中得到的文件系统信息和索引的 inode_id
可以得到 Inode
数据结构体。
I n o d e . b l o c k _ i d = s u p e r _ b l o c k . i n o d e _ a r e a _ s t a r t _ b l o c k + i n o d e _ i d / i n o d e s _ p e r _ b l o c k Inode.block\_id\ = super\_block.inode\_area\_start\_block\ + inode\_id\ /\ inodes\_per\_block Inode.block_id =super_block.inode_area_start_block +inode_id / inodes_per_block
I n o d e . b l o c k _ o f f s e t = ( i n o d e _ i d % i n o d e s _ p e r _ b l o c k ) a s u s i z e ∗ i n o d e _ s i z e Inode.block\_offset\ =\ (inode\_id\ \%\ inodes\_per\_block)\ as\ usize\ *\ inode\_size Inode.block_offset = (inode_id % inodes_per_block) as usize ∗ inode_size
根据 Inode
结构体中的信息可以得到其数据块对应的磁盘索引的位置。
根据读出的磁盘位置索引 DiskInode
可以根据其中的 direct
、 indirect1
和 indirect2
三个字段获取数据块的实际存储位置。
举例说明读取文件的过程( /hello
)
- 读取根目录内容,获取
hello
的inode
信息,获取hello
的数据索引块号; - 根据数据索引块号找到对应的数据实际存储的物理块号;
- 读取数据信息;
参考文档
《文件系统接口》
《简易文件系统 easy-fs》
相关文章:

easyfs 简易文件系统
easyfs easyfs 简易文件系统文件系统虚拟文件系统 VFS简易文件系统 easyfs磁盘布局超级块 easyfs 文件系统结构磁盘上的索引结构索引节点Inode 和 DiskInode 之间的关系举例说明读取文件的过程( /hello ) 参考文档 easyfs 简易文件系统 文件系统 常规文…...
【架构论文-1】面向服务架构(SOA)
【摘要】 本文以我参加公司的“生产线数字孪生”项目为例,论述了“面向服务架构设计及其应用”。该项目的目标是构建某车企的数字孪生平台,在虚拟场景中能够仿真还原真实产线的动作和节拍,实现虚实联动,从而提前规避问题ÿ…...

刚刚!更新宁德时代社招Verify测评语言理解数字推理SHL题库、网盘资料、高分答案
宁德时代社招入职的Verify测评主要分为两大块:语言理解和数字推理。语言理解部分包括阅读理解、逻辑填空和语句排序,要求在17分钟内完成30题。数字推理部分包括数字序列、数学问题解决和图表分析,同样要求在17分钟内完成18题。这些测评题目旨…...

C++笔记---智能指针
1. 什么是智能指针 1.1 RALL设计思想 RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种资源管理类的设计思想,广泛应用于C等支持对象导向编程的语言中。它的核心思想是将资源的管理与对象的生命周期紧密绑定…...
CentOS 7系统中更改YUM源为阿里云的镜像源
引言 更换阿里的镜像源可以带来诸多好处,包括提高下载速度、提升稳定性、同步更新、简化配置、节省带宽资源以及增强系统安全性等。因此,对于使用CentOS系统的用户来说,更换阿里的镜像源是一个值得考虑的选择。 1.备份yum源 mv /etc/yum.r…...

Python酷库之旅-第三方库Pandas(206)
目录 一、用法精讲 961、pandas.IntervalIndex.mid属性 961-1、语法 961-2、参数 961-3、功能 961-4、返回值 961-5、说明 961-6、用法 961-6-1、数据准备 961-6-2、代码示例 961-6-3、结果输出 962、pandas.IntervalIndex.length属性 962-1、语法 962-2、参数 …...

3.4CQU数学实验???
meshgrid 是一个用于生成网格点坐标的函数。它常用于在二维或三维空间中创建坐标网格,用于可视化和数据处理。 在二维情况下,meshgrid 函数接受两个一维数组作为输入,并返回两个二维数组,这两个数组中的元素分别表示了所有可能的…...

Linux(CentOS)开放端口/关闭端口
一、普通用户使用 sudo 操作,开放/关闭端口,80 1、检查端口是否开放 sudo firewall-cmd --zonepublic --query-port80/tcp 2、开放端口 sudo firewall-cmd --zonepublic --add-port80/tcp --permanent 3、重新加载(开放或关闭端口后都需…...
GreenDao适配AGP8.7+
升级配置 工具版本Android StudioLadybug 2024.2.1 Path2AGP8.7.2KPG1.8.21GGP3.3.1明细 classpath "com.android.tools.build:gradle:$agp_version"classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kgp_version"classpath "org.greenrobot:g…...
【前端】Typescript从入门到进阶
以下是 TypeScript 的常用知识点总结,涵盖了从基础到入门的内容,并配有代码示例: 1. TypeScript 基础 1.1 安装和配置 安装 TypeScript 并初始化配置文件: npm install -g typescript tsc --init 1.2 基本类型 TypeScript 提供…...

在 RHEL 8 | CentOS Linux release 8.5.2111上安装 Zabbix 6
1. 备份YUM源文件 cd /etc/yum.repos.d/ mkdir bak mv C* ./bak/ wget -O /etc/yum.repos.d/CentOS-Linux-BaseOS.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo yum clean all yum makecache2. 将 SELinux 设置为宽容模式,如下所示。 sudo s…...
光纤HDMI线怎么连接回音壁?
第一步:准备HDMI线、光纤线(TOSLINK线)、视频源设备、回音壁 第二步:连接HDMI线,找到视频源设备上的HDMI输出口,将HDMI线的一端插入这个接口,再把HDMI线的另一端插入回音壁的HDMI输入口。注意检…...
屏幕后期处理
1、屏幕后期处理效果 屏幕后期处理效果( Screen Post-Processing Effects)是一种在渲染管线的最后阶段应用的视觉效果,允许在场景渲染完成后对最终图像进行各种调整和效果处理,从而增强视觉体验 常见的屏幕后期处理效果有&#x…...
K8资源之endpoint资源EP资源
1 endpoint资源概述 endpoint资源在K8S中用来表s示vc与后端 Pod 之间的连接关系的对象。当创建svc时,svc根据标签是否相同或svc名字是否和ep名字相同,把svc和ip关联上。 删除svc时,会自动的删除同名的ep资源。 2 ep资源和svc的关联测试 […...

微软日志丢失事件敲响安全警钟
NEWS | 事件回顾 最近,全球最大的软件公司之一——微软,遭遇了一场罕见的日志丢失危机。据报告,从9月2日至9月19日,持续长达两周的时间里,微软的多项核心云服务,包括身份验证平台Microsoft Entra、安全信息…...

Qt生成应用程序exe
1. 将工程用MinGW编译器在release模式下编译,生成可执行文件XXX.exe,新建一个文件夹如:F:\Setup\minGW,把exe文件放到这个目录下。 2. 将该编译器的bin文件添加到PATH环境变量里:bin文件路径为:D:\Qt\Qt5.…...
C#中的HttpContent、HttpClientHandle、HttpWebRequest
C#中的HttpContent 在C#中,HttpContent 是 System.Net.Http 命名空间下的一个类,它是 HttpClient 类用来发送和接收HTTP内容的基础。HttpContent 表示HTTP请求或响应的正文内容,并且可以序列化和反序列化数据。 HttpContent 是一个抽象类&a…...
23.网工入门篇--------介绍一下园区网典型组网架构及案例实践
园区网典型组网架构主要分为小型、中型、大型三种类型,以下是详细介绍及相关案例实践: 小型园区网: 架构特点: 用户规模:适用于接入用户数量较少的场景,一般支持几个至几十个用户。覆盖范围:仅限…...

QT鼠标事件
QT鼠标事件 1.概述 这篇文章介绍如何使用事件和获取事件的信号 2.创建项目 创建一个widget类型项目,在widget.ui文件中添加一个label控件 然后在项目名称上右键选择Add new... 添加文件,选择 C Class 自定义类名Mylabel,选择基类Base …...

Ubuntu 的 ROS 操作系统turtlebot3环境搭建
引言 本文介绍如何在Ubuntu系统中为TurtleBot3配置ROS环境,包括安装和配置ROS Noetic的步骤,为PC端控制TurtleBot3提供操作指南。 安装和配置的过程分为PC设置、系统安装、依赖安装等部分,并在最后进行网络配置,确保PC端能够顺利…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...

实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...