深入理解文件系统(二)

个人主页:小则又沐风

个人专栏:<数据结构>

<竞赛专栏>

<C语言>
<C++>

<Linux>

座右铭

悟已往之不谏,知来者之可追

目录

前言

"块"的概念

分区的概念

[inode : 文件属性的"身份证"](#inode : 文件属性的"身份证")

相关的inode的属性

ext2⽂件系统

[Super Block超级块](#Super Block超级块)

[块组描述符(Group Descriptor Table)](#块组描述符(Group Descriptor Table))

[块位图(Block Bitmap)](#块位图(Block Bitmap))

[inode位图(Inode Bitmap)](#inode位图(Inode Bitmap))

[i节点表(Inode Table)](#i节点表(Inode Table))

[Data Block](#Data Block)

简单阐述文件的创建过程

inode和datablock映射

路径解析(包括路径缓存)

挂载


前言

在上一篇的学习中,我们简单的了解了文件是怎么在磁盘中存储的,我们简单的知道了,文件的存储逻辑结构就是一个三维数组.具体名称就是LBA地址.

我们在之前只是了解一下硬件的知识,今天我们来了解一下文件系统中的软件的部分

"块"的概念

磁盘读写最小的单位就是扇区,在通常情况下,一个扇区的大小是512字节的,可以知道的是,如果在读取信息的时候是按照一个一个扇区进行读取的话,这样的效率难免太过于低下,所以这时候就引入了块的概念

在实际的情况下,操作系统在读取硬盘数据的时候,并不是按照一个一个扇区读取的方式进行读取的,

而是采取了更加高效的方法------多个扇区一起读取.(在通常的情况下,读取的时候是8个扇区进行读取的,也就是4kb)

这时候我们就称8个扇区为一个"块"

因为在之前的讲解中,我们认识到的是我们可以把磁盘看作一个长长的一维数组,所以我们得到一个LBA地址,是可以计算出来这个地址所对应的块区的

LBA//8 就是对应的块区

知道块号:LAB=块号*8+n.(n是块内第⼏个扇区)

分区的概念

什么是分区呢????

其实我们在使用我们的电脑的时候我们就已经使用了分区了

因为电脑中的C,D,E,盘就是分区

但是在Linux下是怎么进行分区的呢?

在磁盘的物理结构中,有一个天然的结构,他是由多个扇区组成的,他就是柱面

因为一个柱面是有多个扇区的,也可以利用柱面来区分扇区,所以在通常的情况下,使用柱面作为分区的最小的单位,

inode : 文件属性的"身份证"

为什么称作inode时文件属性的身份证呢?

在我们的认知中文件的组成是:文件属性+文件内容

那么显而易见的就是inode是和文件的属性相关的

相关的inode的属性
  • 一个文件对应一个唯一的inode
  • inode中没有存储文件的文件名

但是平时在使用文件的时候我们都是用的是文件名,而不是inode这是因为文件名和inode建立了连接(建立连接的媒介是目录---目录也是文件)

  • inode的⼤⼩⼀般是128字节或者256,我们后⾯统⼀128字节
  • 任何⽂件的内容⼤⼩可以不同,但是属性⼤⼩⼀定是相同的

来查看一下inode

cpp 复制代码
struct ext2_inode {
    __le16 i_mode;        // 文件权限(如0644)
    __le16 i_uid;         // 所有者ID
    __le32 i_size;        // 文件大小(字节)
    __le32 i_atime;       // 最后访问时间
    __le32 i_mtime;       // 最后修改时间(内容)
    __le32 i_ctime;       // 最后变更时间(属性)
    __le16 i_gid;         // 所属组ID
    __le16 i_links_count; // 硬链接数
    __le32 i_blocks;      // 占用块数
    __le32 i_block[15];   // 块映射指针(12直接+1一级间接+1二级间接+1三级间接)
};

怎么查看文件的inode

ls -li

现在我们来简单的做一个总结

读取的基本的单位是块.每一个分区中是包含着很多的块的

块是怎么进行组织的?

inode是文件属性的身份证,拥有一个文件的inode就可以知道他的相关的属性,那么具体是怎么使用这个inode来访问到文件的内容的?

解释上述的疑问需要我们简单的了解一下Ext系列文件系统

就以Ext2文件系统为例

ext2⽂件系统

在这个的文件系统中的核心就是块组(Block Group)结构.

具体的就是在一个分区中,我们把相同数量的块分为成一个个的组,然后对一个个的组进行管理的话,管理的成本就会相对的降低,然后将这个管理的方式推广到一个个的分区的话,就实现了对文件系统的管理

在一个块组中包含的结构都有什么呢?

Super Block超级块

超级块是记录在这个分区中的信息的

比如:

  • 块的大小
  • inode的大小
  • inode空闲的数量
  • 块的闲置数量,以及块的总数量
  • 最近挂载时间、最近写入时间、最近磁盘检查时间

在每一个块组中,都包含了一个超级块的结构,看起来是冗余的,但是这是在对超级块的信息进行备份.

块组描述符(Group Descriptor Table)

在这个结构中存储的就是有关当前块组的信息了,关键的信息就是当前的块组的块位图的起始地址,

inode位图的起始地址,inode表的起始地址,空闲的inode和数据块还有多少个等等

一个块组就有一个相应的块组描述符

cpp 复制代码
块组0: | 超级块 | GDT | 块位图 | inode位图 | inode表 | 数据块 |
块组1: | 超级块备份 | GDT备份 | 块位图 | inode位图 | inode表 | 数据块 |
块组3: | 超级块备份 | GDT备份 | 块位图 | inode位图 | inode表 | 数据块 |
块组5: | 超级块备份 | GDT备份 | 块位图 | inode位图 | inode表 | 数据块 |
块组7: | 超级块备份 | GDT备份 | 块位图 | inode位图 | inode表 | 数据块 |
...

块位图(Block Bitmap)

在这里面就是记录那些块是没有进行使用,那些块被使用了

具体的实现我们可以联想一下在进程调度中的位图

每一个块是有一个对应的比特位的,这个比特位是0就是空闲,1就是被占用了

inode位图(Inode Bitmap)

相同的是这个inode位图是记录哪一个的inode号是空闲或者是使用的

i节点表(Inode Table)

在前面的认识中我们知道的是inode实际上就是一个结构体,在这个表中存储的就是一个个的inode结构体,这一个个的结构体就是一个个的文件的属性.

我们可以依靠我们在GDP中得到的Inode Table的起始地址,然后拿着inode号也就是偏移号.

访问我们inode对应文件的属性

文件的属性中是包含着文件数据存储的位置的,我们可以拿着这个数据的地址,就可以对文件内容进行访问了

Data Block

这个是块组中的最大的部分了,在这个区域中存储的是文件的内容

  • 对于普通文件是存储文件的内容
  • 对于目录文件存储的就是文件名和inode的对应关系

简单阐述文件的创建过程

依靠我们上述的知识,我们就来实战一下(一个文件的创建过程)

首先就是给文件进行分配inode号

通过查看一个分区中的超级块查看是否有空余的inode和块

在合适的分区中,遍历inode位图,找出第一个空闲的inode号,将这个inode号置为1

然后在inode表中的inode结构体中填写将要创建文件的属性(文件的权限,拥有者,创建时间等等)

然后在属性表中文件所占用的块的数目进行初始化为0

遍历块位图找到空闲的块,将文件的内容写入到块中,并在位图置为1

在inode结构体中建立块映射,更新占用块的个数

在所处的目录中建立文件名和inode的映射

这样的看起来文件存储的时候并不是连续的,是依靠inode这个结构体实现对不连续的数据块

变成有序的,那么我们删除一个文件就是把一个文件属性中对于文件的数据块的映射关系进行了删除,然后我们对相应的数据块对应的位图中的位置置为0

inode和datablock映射

在这个图中我们看到的是inode的映射的方式是有三种的

分别就是图中的

直接映射

一级映射

二级映射

三级映射

对于较小的文件,文件的所需要的数据块较少,可以直接进行映射,这样会更加的高效

但是对于大文件的话,就需要对文件需要的数据块的数量来进行挑选相应的映射的方式了

就以一级映射为例子,首先第一次的映射我们得到的是一个类似目录的结构,在进行映射的话就和直接映射是一样的了,这样的操作就可以关联到更多的数据块了

路径解析(包括路径缓存)

平常的使用中,我们使用的是文件名,并不是inode,对文件名和inode进行映射的媒介是目录

假设我们现在所位于的路径是/home/study/7.9/text.cc

我们是怎么找到text.cc的inode呢?

答案就是在目录7.9中查找相关的映射

但是问题是我们怎么找到7.9这个目录的内容呢?

也就是说我们想要找到当前目录中文件名和inode的对应关系的,我们需要找到上级目录中此目录的inode.

这时候我们就明白了我们再平常的使用中我们必须带上路径就是因为这个.

因为根目录的inode号是固定的所以我们在根目录中找到了home的inode号,在home中找到了study的inode号........

这样递归的查找

但是这样我们每次打开一个文件就需要这样不停的查找的话,难免会有点浪费资源了,所以操作系统中会记录我们在之前查找的历史,并建立一个缓存

所以我们在日常使用的时候我们会发现第一次打开文件的时候会相对的慢一点,但是之后再次打开的时候就会快了,这是因为记录下来这个路径的缓存了

挂载

我们现在的知识储备是可以支持我们在一个分区中横行霸道的,但是有一个问题就是我们怎么知道所处于的分区呢???

因为在每一个分区中inode号是唯一的,所以我们不能拿着正确的inode号走错了分区

但是因为对目录进行了挂载,这样的话我们的根目录会在对应的分区建立起联系了,这时候再去用inode的话就会对应到相应分区的了

相关推荐
HEJOO92 小时前
深入理解 Java volatile 关键字:原理、用法与常见误区
java·开发语言·spring
曹牧2 小时前
Java:no content to map due to end of input
java·开发语言
子非鱼a2 小时前
【WEB】multiSQL
前端
阿维的博客日记2 小时前
Example 类全场景实战指南
java·mybatis
两点王爷2 小时前
SpringBoot 项目集成 GeoServer 源码,实现地图服务发布
java·spring boot·后端
Javatutouhouduan3 小时前
Java如何速通性能优化难题?
java·java面试·jvm调优·后端开发·java程序员·java八股文·java性能优化
lemon_sjdk3 小时前
DOM 节点
java·前端·javascript
nvd113 小时前
深入现代 Web 鉴权架构:网关统一代理 (Forward-Auth) vs 前端持有 JWT 的终极选型与边缘同域实践
前端·架构·状态模式
leoZ2313 小时前
2026-09-09-springboot-cloud-deploy-pitfalls
java·前端·javascript·vue.js·人工智能·spring boot·后端
YWL3 小时前
OpenLayers弹窗Overlay深度实战
前端·vue·openlayers