【Linux内核系列】:文件系统

🔥 本文专栏:Linux

🌸作者主页:努力努力再努力wz



★★★ 本文前置知识:

文件系统初识

那么在我们此前关于文件的学习中,我们学习的都是进程与打开的文件之间的关系,以及打开的文件如何进行管理,那么我们文件有打开的文件,那么同样也就相应有未被打开的文件,所以本篇文章将围绕为未被打开的文件,来揭开它神秘的面纱,看看我们操作系统是如何管理未被打开的文件的,那么废话不多说,就让我们进入正文的学习!

1.引入

那么我们知道我们的文件的元数据是存储在外部设备当中,所以要认识未被打开的文件,那么第一步我们得先认识存储该文件的介质或者说设备,而目前我们存储的文件的外部设备就是SSD固态硬盘以及HDD机械硬盘,那么这两种存储设备各有各的优势,那么目前我们大众接触到最多的也就是固态硬盘SSD,那么它内部构造是由晶体管所构成,而我们的文件本质就是一个二进制序列,那么SSD就通过内部的晶体管的电子的状态来表示二进制的0和1,那么通过电信号来访问各个晶体管的电子的状态从而获得二进制的数据,所以它的访问速度是极其快速,但是由于其应用场景实在家用以及办公用的便携式笔记本电脑当中,意味着我们的SSD固态硬盘所占的体积不能过大,所以通过集成电路的技术使其具有较小的体积并且同时保证了其不错的容量,那么必定意味着SSD的制作成本相比于机械磁盘就要高昂一点

那么对于机械磁盘HDD来说,那么它的应用场景则是一些不需要移动的笨重的台式机或者互联网公司的服务器后端数据的存储设备,而对于HDD来说,它的优点就是容量大,并且造价成本便宜,所以这就是为什么大型的互联网公司的服务器后端选择磁盘作为数据的存储设备,但是由于其是机械运动来定位数据,那么必然其访问的效率不如SSD固态硬盘,并且其不能够应用在便携式电脑当中,但是由于HDD在互联网公司的广泛引用,鉴于其重要性,所以我们今天便要着重来研究我们的磁盘的硬件结构

2.磁盘的物理结构以及原理

那么我们的磁盘是由盘片以及磁头以及高转速的马达等部件所构成,那么在这些构成磁盘的部件当中,其中最为重要的那便是磁头与盘片,磁头则是用来定位我们在磁盘当中存储的数据,而我们的在磁盘中存储的所有数据则是保存在盘片上,所以一个磁盘的容量就由盘片的数量以及盘片的存储密度等所决定。

磁盘的盘片有两面,盘片的两面分别有一层磁性的物质,那么我们知道我们的文件的各种元数据的最终形态其实就是一个二进制序列,而二进制序列无非就是由0和1所构成,所以我们如何在盘片上表示出二进制的0和1呢?

那么盘片表面上的磁性物质,我们可以看成有数十亿甚至百亿的磁性颗粒所组成,那么其中对于这每一个磁性的颗粒来说,那么它是具有磁性的,那么一旦具有磁性,那么它便具有特定的磁级指向南或者北,所以我们就可以根据每一个颗粒该磁极的指向的两个状态来分别表示二进制的0和1,那么具体的实现的途径,则是我们磁头通过放电会在其周围产生一个磁场,那么利用该磁场来磁化磁性颗粒使其产生特定的磁极,从而写入二进制序列,那么这就是磁盘写入数据的一个原理

而读取数据则是根据我们的磁头感受到读取位置的磁场变化从而磁生电,转换成电信号,那么利用该电信号得到特定的二进制序列,那么这就是利用我们高中物理的电磁学的原理

那么我们知道我们的数据是存储在我们的磁盘的盘片上,那么我们对于磁盘读取则是依靠磁头以及盘片的机械运动来定位数据的,那么我们磁盘的读取速度的效率则取决于该磁盘定位速度的机械运动的快慢,那么读取越快意味着磁盘定位一个数据所做的机械运动越少,那么我们希望磁盘所做的机械运动尽量的少,那么我们就需要将我们一个文件的数据集合尽可能存放在磁盘的盘片的相邻的物理位置处,这样就能够减少机械的运动了

那么我们知道了如何在磁盘中存储数据,那么我们要在磁盘中读取我们需要的目标数据那么肯定就得有该目标数据在我们的磁盘中的具体位置,所以为了表示一个数据在磁盘中的具体位置,那么我们此时便引入了一个磁道的概念

那么我们对于一个盘片来说,那么它的形状是一个圆形,那么我们以该盘片的中心为圆心,然后往外划分一圈一圈不同的半径的圆,那么该圆的圆弧就是磁道,其中每一个相邻的磁道之间的空间,我们还可以在进行进一步的划分,我们将圆心与每一个不同半径上的圆的圆弧处上的点形成一条条连线,那么该连线将我们的相邻两个磁道之间会划分成一个一个的扇形区域,那么该扇形区域区域我们称之为扇区,那么它就是我们磁盘存储数据的一个最基本单元,那么一般我们磁盘不只有一个盘片,那么每一个盘片都有两个盘面,那么每一面都能够存储数据,那么我们每一个盘面都对应有一个磁头用来定位,其中每一个盘面的划分磁道的数量是相同的,并且对于同一个盘片来说,每个相邻磁道之间的扇区的数量也是一致的,而我们知道越靠近外侧,那么相邻磁道之间的空隙也就越大,越靠近圆心位置处,相邻磁道之间的空隙越小,所以为了达到每一个相邻磁道之间的扇区数量相等,那么我们每一个磁道之间的划分的疏密以及磁道之间的存储密度就不同

所以我们要定位一个扇区的话,那么我们首先磁头就得来回摆动,定位确定是在哪个磁道上,然后我们的盘片通过高速马达的转轴的带动在转动定位到目标扇区

而我们知道我们的盘片是高速转动并且磁头也在摆动,那么也许有的小伙伴就有疑问,那么万一我的磁头刮花了盘片怎么办,盘片上存储着各种数据,那么刮花了岂不是造成了数据的缺失,那么注意虽然看起来磁头是和盘片是挨着的,但是他们其实并没有接触,他们中间有一个间隙,那么一旦盘片高速转动,那么中间的空气会托举着磁头,使其悬浮,应用了空气动力学的原理,所以我们的磁盘的工艺设计其实是非常巧妙的!并且磁盘内部不能有灰尘,一旦有了灰尘,那么灰尘是个颗粒状的固体,那么随着盘片的高速转动,那么其会刮花盘片,所以博主在这里警惕各位读者,如果自己好奇来拆开磁盘来研究,那么对不起,你的磁盘就报废了

而我们知道盘片之间是沿着一个转轴来垂直摆放的,那么不同盘面上的相同半径的磁道在立体的空间当中的集合就称之为一个柱面,所以我们定位,我们只需要确定三个坐标即可,即使它在对应哪个磁头,哪个磁道以及哪个扇区,那么这三个坐标我们也要一个专业术语就是CHS位置

3.磁盘的逻辑结构

那么我们知道磁盘的物理构造之后,那么我们在操作系统层面上怎么来理解描述磁盘这样的物理结构呢

那么我们知道有一个叫做磁带的东西,想必各位读者在小学的时候播放英语教材会用到,那么我们一圈一圈弯曲的磁带,那么我们可以将其整体拉直,那么其磁带上的所有数据就是按照这个拉直过后的磁带呈线性排列

而同理这里对于我们的磁盘来说,我们不同盘面上的磁道就可以理解为一圈一圈的磁带,那么我们也可以将其给拉直,那么我们磁盘上所有的数据就可以按照一个一维的线性数组来表示,那么为了区分磁盘上的不同的扇区,那么我们会给扇区分配一个唯一的编号,那么我们这个一维数组中的每一个元素就是一个扇区,那么该扇区的数组下标就是该扇区的编号,也就是LAB地址,而我们要将扇区的逻辑结构的编号转换成实际物理层面上的坐标,那么也就是确定该扇区的磁头以及磁道和磁道的第几个扇区的话

那么现在有了扇区的编号,那么如何来转换呢?

那么假设每一个盘面上有m个扇区,每个盘面有k个磁道,每个磁道有q个扇区,假设此时我们的扇区的数组下标为i,那么首先确定其在哪个盘面上,那么我们就可以利用公式:

L=i/m

H=i%m

那么如果余数H不为0的话,那么意味着当前处于第L+1盘面处,接着在计算所处的磁道:

O=H/(k*q)

P=H%(k*q)

余数P不为0的话,那么意味着当前处于该盘面的第O+1磁道处,而P就是该磁道的第P个扇区位置

那么我们就可以根据该公式来实现逻辑地址到物理地址的转换,那么我们获取到地址之后,CPU会交给磁盘的寄存器,那么磁盘有4个寄存器,分别是命令寄存器以及地址寄存器和数据寄存器和数据寄存器以及状态寄存器,它们4个的作用就是分别用来记录当前是往磁盘进行读还是写和一个读写的地址以及写入的数据和以及当前写入是否成功

4.如何管理未被打开的文件

那么我们知道了我们操作系统已经有一个逻辑结构也就是一维的线性数组来表示我们整个磁盘中的数据也就是所有扇区,但是我们的扇区的存储的数据量的大小通常是512个字节,那么我们扇区是磁盘的基本存储单元,而我们上文就说过磁盘访问的效率取决于机械运动的次数,况且我们现在的一个文件动不动就是几个甚至上百个GB,那么我们以扇区为单位一次一次的读取的话,那么效率太慢了

所以我们的操作系统以及其采取的文件系统就对此有所优化,那么我们知道此前我们整个文件的逻辑结构是一个一维的线性数组,那么此时对于磁盘访问一个数据的基本单位不再是一个扇区,而是几个扇区的集合作为该文件系统的存储数据的基本单位也称之为逻辑块,比如8个扇区也就是4kb的数据量,那么这样就能提升访问效率,那么原本磁盘的逻辑结构此时在该文件系统的新的视角下便有了不同,那么该一维的线性数组的长度没有发生变化,但是其元素不在是之前的扇区,而是一个一个逻辑块,那么该逻辑块就是由几个扇区所组成,那么以前该一维的线性数组的元素个数假设是1000个,那么现在在该文件系统下比如EXT4,那么逻辑块是由8个扇区所组成,那么个数意味着就是250个,那么每一个逻辑块就有了新的编号也就是数组的下标

而我们的磁盘的容量极大,可以达到800GB甚至1TB,那么管理这么庞大的数据,那么我们操作系统采取的管理策略就是分区

那么按照不同的文件系统,那么可能将这800个G或者1TB的数据给划分成不同的区,那么假设以EXT4文件系统为例,那么其会划分成4个区,那么每一个区此时的容量就是200个G,但是即使划分成不同的区,那么管理的数据量可能还是极大,所以对于每一个区我们又进行·更细致的划分,将其划分成了不同的组,那么我们管理好这整个文件系统,就是管理好这每一个组以及每一个区即可

那么其中对于这每一个分区来说,那么操作系统管理的方式就是我们再熟悉不过的先描述,再组织了,那么它会为其定义一个struct partition结构体,那么每一个结构体记录了每一个区的起始位置以及结束位置和该区的分组的数量等等属性,其中第一个分区的开头会有一个boot block分组,那么该分组就是记录了操作系统开机的有关信息,并且其他区也有该分组的备份

而对于每一个区的组来说,那么它也有对应的一个结构体来描述,其中就包含超级块以及块表和innode表以及数据块等

c 复制代码
// 示例结构体定义,用于描述文件系统中的一个区组
struct FileSystemGroup {
    // 超级块(或超级段)
    struct SuperBlock {
        int magicNumber;       // 文件系统魔数,用于识别文件系统类型
        int blockSize;         // 逻辑块大小(字节)
        int totalBlocks;       // 区组内总逻辑块数
        int freeBlocks;        // 区组内空闲逻辑块数
        int totalInodes;       // 区组内总inode数
        int freeInodes;        // 区组内空闲inode数
        // 其他元数据...
    } superBlock;

    // 块表(位图)
    // 假设每个位代表一个逻辑块,1表示已使用,0表示空闲
    // 这里简化为一个数组表示,实际中可能使用更紧凑的位图存储
    unsigned char blockBitmap[/* 根据区组大小动态分配 */];

    // inode表
    // 每个inode对应一个文件或目录
    struct Inode {
        int type;              // 文件类型(普通文件、目录等)
        int permissions;       // 文件权限
        int owner;             // 文件所有者
        int size;              // 文件大小(字节)
        int lastModified;      // 最后修改时间戳
        // 指向数据块的指针数组
        int dataBlockIndices[/* 根据文件大小动态分配,可能包含多级索引 */];
    } inodes[/* 根据区组大小动态分配 */];

    // 数据块表(或数据块区域)
    // 实际存储文件内容的逻辑块,这里简化为一个指针数组表示
    // 每个指针指向一个数据块,数据块可能分布在磁盘的不同位置
    void* dataBlocks[/* 根据区组大小动态分配 */];
};

那么其中这4个属性,我们知道它们本质上也是数据,那么必然就在相应的逻辑块中存放,而我们同一个分组的逻辑块一般是在物理内存的同一个区域,并且其中的属性也是按照特定的顺序来排列,比如超级快块一般在该分组的逻辑块的第一个位置,然后再是块表和inode表依次向后排列,那么这取决于特定的文件系统实现,并且不同的文件系统对于每一个分组所占据的逻辑块的数量也是不同的

​ 那么我来详细介绍一下该组的各个不同的属性:

  • 超级块::超级块记录了文件系统的类型、大小、块大小、inode数量、空闲块和inode的数量等关键信息。它还包括文件系统的挂载时间、最后写入时间等时间戳信息

  • 块表:采取的是位图的实现方式,那么其中每一个二进制位的状态用来表示每一个逻辑块是否被使用

  • inode表:那么每一个文件都会对应有一个innode结构体用来存储该文件的属性其中就包括权限等字段,其中innode表还会记录一个数组,那么该数组一般长度为15,那么该数组则是记录该文件对应的文件内容的数据的逻辑块,那么每一个数组元素的内容就是逻辑块的索引,那么数组前12个位置则是直接索引,那么他们指向的逻辑块就是直接存储的是文件的内容,而之后的元素则是二级以及三级索引,那么所谓二级索引则是该指向的逻辑块存储的内容不是有效的文件内容的数据,而是指向其他保存文件内容数据的逻辑块,那么这样就能扩展文件的体积,所以innode表则是记录每一个innode所对应的逻辑块的位置,用位图来实现

  • data block:那么除了超级块以及快表和inode和inode表所占据的逻辑块,那么其余就是存储文件内容的数据块,所以我们发现Linux下文件的属性以及内容是分开存储的


那么这就是我们操作系统管理未打开文件的方式,采取的是分治以及先描述,再组织的策略

结语

那么这就是本篇文章关于文件系统的全部内容了,那么下一期文章将是软硬件链接,那么下一期博客就是我们Linux文件系统的一个收尾啦,那么文件系统学写完之后,我们便打开了语言与操作系统之间的隔阂,那么我会持续更新,希望你多多管制,如果我的文章有帮组到你的话,还请多多三连加关注哦,你的支持,就是我创作的最大动力!

相关推荐
zym大哥大2 分钟前
Linux信号之捕捉信号
linux
liuyunluoxiao4 分钟前
进程(下)【Linux操作系统】
linux
LK_074 分钟前
【蓝桥杯—单片机】第十五届省赛真题代码题解析 | 思路整理
c语言·笔记·单片机·蓝桥杯·学习方法
什么半岛铁盒11 分钟前
【Linux系统】进程优先级:进程间的权力游戏
linux·运维·游戏
pe7er19 分钟前
Mac mini作为服务器每天定时开、关机设置
服务器·macos·程序员
skywalk816334 分钟前
Windows10下docker desktop命令行操作指南(大部分也适用于Linux)
运维·docker·容器
Ares-Wang36 分钟前
Linux》》Ubuntu apt 常用命令汇总,Linux 文件目录结构 修改root 密码 查看系统版本
linux·运维·ubuntu
小慧102442 分钟前
Ubuntu开荒
linux·运维·ubuntu
怪兽也会哭哭1 小时前
部署项目至服务器:响应时间太长,无法访问此页面?
运维·服务器·学习笔记
hkNaruto1 小时前
【AI】单台10卡4090 openEuler服务器离线部署kasm workspace 提供简单的GPU云服务 虚拟化桌面
linux·运维·ai