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

【Linux】深入理解文件系统(超详细)

目录

一.磁盘

1-1 磁盘、服务器、机柜、机房

📌补充:

📌通常网络中用高低电平,磁盘中用磁化方向来表示。以下是具体说明:

📌如果有一块磁盘要进行销毁该怎么办?

1-2 磁盘存储结构

​编辑

📌 在磁盘角度,如何定位一个扇区?

📌指令:fdisk是 Linux 系统中用于磁盘分区的一个非常重要的命令行工具 

📌磁盘各个结构介绍:

📌 CHS寻址

1-3.磁盘的逻辑结构

📌磁道:

📌柱⾯:

 📌整盘:

📌整个磁盘不就是多张⼆维的扇区数组表(三维数组?)

📌有了抽象成数组这个概念,这样定位一个扇区还用什么CHS呢!我们瞬间就得到一个数组,这样每一个扇区就有了一个线性地址 (其实就是数组下标),这种地址叫做LBA。一个磁盘不是有很多面吗?

1-4.CHS && LBA地址

 二.引入文件系统

2-1.引入"块"概念

📌磁盘是以块为单位的一维数组:

📌补充知识:

2-2 引⼊"分区"概念

📌问题:一个磁盘如果有500G大小,OS将如何进行管理呢?知道总大小500G,就知道有多少个块,那么整个磁盘该怎么管理呢?

📌有了这种分区概念,我们就可以以分区的形式进行管理,只要把一个分区管理好了,就可以把一个分区的管理方式拷贝给其他分区,就可以把其他分区也管理好,也可以给分区设置不同的文件系统,因为分区之间就相互独立了,这样一个分区挂掉了,不会影响其他分区,保证整个磁盘的可用性,所以就分区了!!!

📌分区分好,此时还要把这个分区分成一个个组,从此往后,我们只需要管理好一个组,假设一个组10G,从今天开始,我们只需要把一个组管理好,对于这一个分区来说,我们就把这一个组的管理方式拷贝给其他组,就可以把每个组管理好,每个组管理好,分区不就管理好了,分区管理好了,不就把整个磁盘管理好了!!!

三.理解文件系统

3-1. Boot Block(启动块)

3-2.inode 

3-2-1.Inode Table (inode表)

📌查看文件信息:

📌如果有10000个文件,这个inode Table需要占用多少个块呢?

3-3.Data Blocks(数据块)

📌如何找文件呢?

📌 后面会讲文件块不够怎么办,要存大文件怎么办,根据inode怎么在这个inode表里找!!!

📌补充:

3-4.Bitmap(位图)

3-4-1.Inode Bitmap 

📌在这个块组里面一共有多少个inode,我怎么知道这个inode Table 里面有多少个inode被分配了,有多少个inode 没有被分配,如果要新建一个inode,重新在这个inode Table 里申请一块inode空间,该怎么申请,你怎么知道inode Table 里面有哪些被占用哪些没被占用?

📌但是磁盘最小访问单位是扇区,磁盘与OS交互是以4kb大小进行访问,怎么做到给inode BItmap置1呢?

3-4-2.Block Bitmap 

 📌有了上面的认识,所以今天我们要新建一个文件,并像这个文件写 "Hello Word" 字符串,目前应该怎么做?

📌那么删除一个文件怎么办呢?

📌 所以删除一个文件可以恢复吗?

📌文件误删最好怎么办?

📌 如果文件被删除了,inode编号怎么知道呢?

3-5.Group Descriptor Table(块组描述符)

📌一个分组一共是多大呀?这个分区里面一共有多少个inode,一共有多少个blocks,数据块的使用率是多少,一万个inode有多少被用了,block有多少个被用了?这个block只剩一个G,我要放两个G内容怎么办,要放在其他块组里面,怎么评判放不放的下呢?

3-6.Super Block(超级块)

📌超级块表示整个分区的整体情况,他是表示分区的情况,为什么会在分区的其中一个组里面呢?那是不是所有分区都会包含这一个超级块呢?

📌但是理论上,这个分区超级块是一个文件系统,在分区的最开始就应该是超级块,然后剩下的区域再进行分组,这样不是更优雅吗更清楚吗,但Linux并没有这样干,而是放在若干个组里面为什么要这样干呢?

四.准确理解文件系统 

4-1.关于Data Blocks,Inode Table

4-2-1.关于inode

4-2-2.关于block

4-2-3.我们有这些知识铺垫重新看待Block Bitmap 、Inode Bitmap 、Inode Table 、Data Blocks:

📌1.我们是如何分配一个inode的?

📌1.我们是如何分配一个block的呢?

📌3.有了上面正向的认识,我们再来反向进行理解下,如果inode:10004、block:100006,怎么找到在哪个组?怎么找到inode属性?怎么找到文件内容?

📌题外话: 

4-2-4.知道inode号,有一下四个问题:

📌1.如何查找一个文件?

📌2.如何删除一个文件

📌3.如何修改一个文件?

📌4.如何新增一个文件?

📌子问题1:inode和block究竟是怎么进行映射的?

 📌子问题2:凭什么拿到inode?我们访问的好像都是文件名啊?我们操作的都是文件名呀?

衍生一个问题:📌如何理解目录文件?

📌所以有上面知识现在能不能理解,我们每一个进程他都要有cwd?

4-2-5.dentry 缓存:

📌如果此时在当前路径下有touch test2.txt,刚才访问完test.txt文件,现在又要访问test2.txt,该怎么办?

📌现在问一个问题,Linux用不用保存路径,用不用把路径整体信息保存起来?

📌所以我的问题是:Linux要对对应的路径进行缓存,应该以什么样的数据结构来缓存我们刚刚说的路径呢?-------多叉树!!!!

 4-2-6.做一个小实验

1.#制作⼀个⼤的磁盘块,就当做⼀个分区

2.# 格式化写⼊⽂件系统 

3.# 建⽴空⽬录

​编辑 4.# 将分区挂载到指定的⽬录

 5.#使用分区

6.# 卸载分区

📌总结:

📌所以路径从哪来?

📌所以回答最开始问题,你怎么确认你在哪个分区里面?访问任何一个文件,都要有路径,路径的前缀不就可以指名在哪一个分区下面。我的前缀是 /目录 就找到是/dev/vda2分区下。​编辑

📌那最开始不是要路径解析吗,他不是要根据从根目录下解析吗?


📌前面内容都是围绕被打开的文件进行讲解,接下来将围绕没有被打开的文件进行讲解!!!

没有被打开的文件是放在磁盘这个硬件设备上的,打开一个文件前提是找到这个文件,找到文件要有路径,不仅仅一个文件要有路径,要保证Linux下所有文件都要有路径,为了更好的进行磁盘级文件管理,必须要有一个磁盘文件系统的东西,之前讲的内核级文件系统,二者其实是一回事,就是一个硬币有两面,站在内存和磁盘看其实就是一套东西,文件系统要做的就是各种相关的文件管理。

一.磁盘

任何人根据将Linux文件系统的人,如果不给你讲磁盘,文件系统讲的再好也只是空中楼阁,因为你的大脑思考中,无法建立在无法想象的结构之中。

先看看磁盘的结构:

 

1-1 磁盘、服务器、机柜、机房

  • 机械磁盘是计算机中唯⼀的⼀个机械设备
  • 磁盘--- 外设
  • 容量⼤,价格便宜 

📌补充

关于磁盘 --- 磁铁 

磁盘也只认二进制,磁盘上的二进制怎么理解呢?

因为磁盘光滑的面,我们可以把磁盘想象成上百亿的小磁铁构成,规定南北极,比如北极为1,南极为0,所有我们磁盘立马可以进行0 1保存

📌通常网络中用高低电平,磁盘中用磁化方向来表示。以下是具体说明:

网络数据传输

  • 在网络通信中,尤其是在有线网络中,数据通常以电信号的形式进行传输。常见的以太网标准中,就是利用电压的高低电平来表示二进制的 0 和 1。例如,在双绞线中,规定高电平代表一种逻辑状态(如 1),低电平代表另一种逻辑状态(如 0)。通过在传输线路上快速地切换高低电平,就可以实现数据的编码和传输。

磁盘数据存储

  • 磁盘存储数据的原理是基于磁性材料的磁化特性。当磁头对盘片上的磁性材料进行写入操作时,通过控制磁头线圈中的电流方向,产生不同方向的磁场,使盘片上的磁性材料按照特定的方向被磁化。通常规定一种磁化方向代表 1,另一种磁化方向代表 0。读取数据时,磁头检测盘片上磁性材料的磁化方向,将其转换为相应的电信号,从而还原出存储的数据。

 📌计算机只认二进制,这是一种宏观的说法,在不同的设备上,是由不同物理特性表现的,不同硬件,对二进制表达是不一样的!!!

📌如果有一块磁盘要进行销毁该怎么办

 不能直接进行破坏,因为可以恢复,让磁盘退磁,就可以,高温消磁!!!

1-2 磁盘存储结构

📌磁盘作为计算机中唯一的机械部件,通常由一个或多个旋转的盘片组成,每个盘片有两个面,每个面上有一层磁性材料。通过主轴连接,磁盘上附有磁头。数据存储的基本单位是扇区(Sector),传统上每个扇区大小为512字节,但近年来逐渐过渡到4096字节的大扇区,接下来讲解我们以512大小来说明。磁盘中转数可以达到每秒钟两万转。 

物理结构:

  1. 磁头是一面一个,左右摆动,两个整体移动的,有磁头停靠点
  2. 磁头和盘面不接触,所以物理上不适用于笔记本,开机状态移来移去,磁头刮花了磁盘,属于硬件问题
  3. 机械磁盘要在无尘环境下,灰尘落上去就像一座大山,可能会把数据都磨没了 

 磁头摆动的目的本质:定位磁道(柱面)

磁盘盘面旋转的本质:定位扇区

📌要想访问到数据,只要找到其中整个磁盘特定的盘面,然后找到特定的磁道通过盘片旋转定位到特定的扇区就可以访问数据!!!如果想往磁盘上特定的位置写入,本质就是把数据写到一个或多个扇区里面,哪怕只修改一个扇区中一个bit位,也要把对应的整个扇区读到内存里,把对应bit位重新设置好,再把512字节大小写回特定的扇区中,这时,我称之为在读写时是以512字节大小为单位。

📌 在磁盘角度,如何定位一个扇区?

  • 可以先定位磁头(header)(定位磁道和柱面的过程)
  • 确定磁头要访问哪⼀个柱⾯(磁道)(cylinder)
  •  盘片转动:定位扇区的过程(sector)
  • 三个参数:Cylinder(柱面)Header(磁头)Sector(扇区)==》CHS 寻址方式

文件=内容+属性,都是数据,在磁盘上无非就是占据哪几个扇区的问题,可以定位一个扇区,能不能定位多个扇区?

传动臂上的磁头是共进退的,不能同时定位多个扇区。

云服务器上,磁盘信息在: /dev/vea 上:

📌指令:fdisk是 Linux 系统中用于磁盘分区的一个非常重要的命令行工具 

常用选项

  • -l:列出所有磁盘的分区信息。例如,在终端中输入fdisk -l,会显示系统中所有磁盘的分区情况,包括磁盘设备名称、分区编号、分区大小等。
  • -u:以扇区为单位显示分区大小等信息,与-l选项一起使用时,能更直观地了解分区在磁盘上的物理分布。
  • 设备名:指定要操作的磁盘设备,如fdisk /dev/sda表示对/dev/sda磁盘进行分区操作。

📌磁盘各个结构介绍:

• 扇区是从磁盘读出和写⼊信息的最⼩单位,通常⼤⼩为 512 字节。

• 磁头(head)数:每个盘⽚⼀般有上下两⾯,分别对应1个磁头,共2个磁头

• 磁道(track)数:磁道是从盘⽚外圈往内圈编号0磁道,1磁道...,靠近主轴的同⼼圆⽤于停靠磁 头,不存储数据

• 柱⾯(cylinder)数:磁道构成柱⾯,数量上等同于磁道个数 • 扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同

• 圆盘(platter)数:就是盘⽚的数量

磁盘容量=磁头数 × 磁道(柱⾯)数 × 每道扇区数 × 每扇区字节数

• 细节:传动臂上的磁头是共进退的(这点⽐较重要,后⾯会说明)

 柱⾯(cylinder),磁头(head),扇区(sector),显然可以定位数据了,这就是数据定位(寻址)⽅ 式之⼀,CHS寻址⽅式。

📌 CHS寻址

对早期的磁盘⾮常有效,知道⽤哪个磁头,读取哪个柱⾯上的第⼏扇区就可以读到数据了。 但是CHS模式⽀持的硬盘容量有限,因为系统⽤8bit来存储磁头地址,⽤10bit来存储柱⾯地 址,⽤6bit来存储扇区地址,⽽⼀个扇区共有512Byte,这样使⽤CHS寻址⼀块硬盘最⼤容量 为256 * 1024 * 63 * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是 8.4GB) 。

1-3.磁盘的逻辑结构

📌我们可以把磁盘抽象成线性结构,就像磁带一样,磁带上⾯可以存储数据,我们可以把磁带“拉直”,形成线性结构。

那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在⼀起的磁带,那么磁盘的逻 辑存储结构我们也可以类似于:

这样每⼀个扇区,就有了⼀个线性地址(其实就是数组下标),这种地址叫做LBA!!!

柱⾯是⼀个逻辑上的概念,其实就是每⼀⾯上,相同半径的磁道逻辑上构成柱⾯。 所以,磁盘物理上分了很多⾯,但是在我们看来,逻辑上,磁盘整体是由“柱⾯”卷起来的。

所以,磁盘的真实情况是:

📌磁道:

某⼀盘⾯的某⼀个磁道展开:

即:⼀维数组

📌柱⾯:

整个磁盘所有盘⾯的同⼀个磁道,即柱⾯展开:

• 柱⾯上的每个磁道,扇区个数是⼀样的

• 这不就是⼆维数组吗

 📌整盘:

📌整个磁盘不就是多张⼆维的扇区数组表(三维数组?)

所以,寻址⼀个扇区:先找到哪⼀个柱⾯(Cylinder) ,在确定柱⾯内哪⼀个磁道(其实就是磁头位置, Head),在确定扇区(Sector),所以就有了CHS。

📌我们之前学过C/C++的数组,在我们看来,其实全部都是⼀维数组:

📌有了抽象成数组这个概念,这样定位一个扇区还用什么CHS呢!我们瞬间就得到一个数组,这样每一个扇区就有了一个线性地址 (其实就是数组下标),这种地址叫做LBA。一个磁盘不是有很多面吗?

一块磁盘中,磁盘盘面都一样,数组元素个数都一样!!

如果有6个面,无非就是6个数组 X 6,这样就有了非常大的数组。

细节:扇区下标是从1开始,因为扇区有规定,第一个扇区必须从1号开始!!!磁头和柱面可以从0开始!!!

有了这种逻辑抽象,所以从OS角度看待我们磁盘的时候,根本就不使用CHS,而直接使用LBA这样的概念,但是真正访问一个扇区的时候,必须得CHS,逻辑上简化可以看待为LBA,但是磁盘内部必须使用CHS!!!

📌来谈一谈,磁盘进行抽取LBA的一个真实过程
柱面再多在我眼里也就是一张二维数组,对OS来讲,只需要LBA,这意味着,OS只要访问磁盘,只需要知道磁盘的总容量和磁盘的单个扇区的大小,OS就能计算出一共有多少个扇区,一瞬间扇区所有地址OS都能知道了,所有对于OS来讲,只需要在启动读取时,读取总容量和扇区大小,多少个扇区就知道了,一旦知道有多少个扇区,那么就知道所有扇区的LBA地址了!!!

所以OS里可以实现搞一张位图,如果有一亿个扇区,这个位图就有一亿个bit位,为0表示该对应扇区没有被使用,为1表示该对应扇区被使用,这样不就可以对磁盘进行管理了吗!!! 

1-4.CHS && LBA地址

OS只需要使⽤LBA就可以了!!LBA地址转成CHS地址,CHS如何转换成为LBA地址。谁做啊??磁盘 ⾃⼰来做!固件(硬件电路,伺服系统)

CHS转成LBA:

  • 磁头数*每磁道扇区数 = 单个柱⾯的扇区总数
  • LBA = 柱⾯号C*单个柱⾯的扇区总数 + 磁头号H*每磁道扇区数 + 扇区号S - 1
  • 即:LBA = 柱⾯号C*(磁头数*每磁道扇区数) + 磁头号H*每磁道扇区数 + 扇区号S - 1
  • 扇区号通常是从1开始的,⽽在LBA中,地址是从0开始的
  • 柱⾯和磁道都是从0开始编号的
  • 总柱⾯,磁道个数,扇区总数等信息,在磁盘内部会⾃动维护,上层开机的时候,会获取到这些参 数。

LBA转成CHS:

  • 柱⾯号C = LBA // (磁头数*每磁道扇区数)【就是单个柱⾯的扇区总数】
  • 磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数
  • 扇区号S = (LBA % 每磁道扇区数) + 1
  • "//": 表⽰除取整

 📌所以:从此往后,在磁盘使⽤者看来,根本就不关⼼CHS地址,⽽是直接使⽤LBA地址,磁盘内部⾃⼰ 转换。所以: 从现在开始,磁盘就是⼀个 元素为扇区 的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。OS使⽤ 磁盘,就可以⽤⼀个数字访问磁盘扇区了。

 二.引入文件系统

2-1.引入"块"概念

 📌OS和磁盘进行交互的时候,是以扇区为基本单位,512字节,如果只以单个扇区来进行IO交互,数据量有些少,如果要扯到文件系统的话,OS规定,我们对应的OS,在文件系统层面上和我们的整个磁盘做IO交互时,一般是以 1kb 2kb 3kb 4kb 8kb等这些数据块为单位进行IO,在Linux中大多是以4kb大小的数据块进行访问,一个数据块是8个扇区大小构成的!!!  所以在OS内部,文件系统层面上,不管什么扇区,在OS看来都是4kb大小,在我们看来IO基本单位都是8个扇区大小,在文件系统层面上,把8个扇区称之为一个一个的数据块!!!

📌磁盘是以块为单位的一维数组

所以此时我们对应的块就有了块号的概念,知道其中一个块,能不能算出来这个块有多少个扇区呀?块号转换为8个LBA地址?

 知道块号,根据,块号*8+[1,8],就可以算出具体的数组下标(线性地址)(LBA地址)。

就比如我想用块号1,直接翻译成8个扇区,8个扇区经过转化把8个LBA地址交给磁盘内部,转换成8个CHA地址,此时我们不就可以把8个扇区都访问了吗!!!

我们就把磁盘抽象成以块为单位的一维数组,所以磁盘就有了块的概念,磁盘有多少块呢?

我们自己只要知道磁盘大小,我们就知道有多少块了,一个块知道了,根据块号*8+[1,8]

最终每一个块的LBA地址都知道了,每一个扇区的LBA地址都知道了!!!

📌补充知识:

其实内存也是被划分成4kb大小,文件本身也被划分成4kb大小,4kb构成的块,所以Linux下所有的文件,文本,二进制的图片,都是4kb大小,所有的数据跟集装箱一样,以标准的方式载入内存,内存以4kb为单位,所以我们内存管理时候,也会以4kb的方式进行管理,包括我们之前说的写实拷贝,也是以4kb大小进行写实拷贝!!! 

2-2 引⼊"分区"概念

📌问题:一个磁盘如果有500G大小,OS将如何进行管理呢?知道总大小500G,就知道有多少个块,那么整个磁盘该怎么管理呢?

如果是统一进行管理,今天是500G,那要是5000G,16TB呢,这么大的空间,你想管,那成本就太大了,对文件系统来讲,管理的难度就越大,就比如:一个国家不可能一个人进行管理,所以分成省和市,省长管理下面的市长,通过把省长管理起来,下面的市长也会自动被管理起来。

所以我们磁盘那么大,首先我们要做的,并不是直接对磁盘做管理,而是把磁盘拆分成一个个区域,线性地址划分区域。

以500G为例子,划分五个区域,每一个区域100G,先把磁盘拆分成若干个区域,这个动作我们叫做分区,以Windows观点来看,你可能会有⼀块磁盘并且将 它分区成C,D,E盘。那个C,D,E就是分区。

📌有了这种分区概念,我们就可以以分区的形式进行管理,只要把一个分区管理好了,就可以把一个分区的管理方式拷贝给其他分区,就可以把其他分区也管理好,也可以给分区设置不同的文件系统,因为分区之间就相互独立了,这样一个分区挂掉了,不会影响其他分区,保证整个磁盘的可用性,所以就分区了!!!

📌分区分好,此时还要把这个分区分成一个个组,从此往后,我们只需要管理好一个组,假设一个组10G,从今天开始,我们只需要把一个组管理好,对于这一个分区来说,我们就把这一个组的管理方式拷贝给其他组,就可以把每个组管理好,每个组管理好,分区不就管理好了,分区管理好了,不就把整个磁盘管理好了!!!

这种思想叫什么思想?-------这种思想我们称之为分治!!!

所以我们学习文件系统,我们核心思想就切换成如何把一个组管理好!!!

三.理解文件系统

3-1. Boot Block(启动块)

我们分组当中有一个区域叫做Boot Block,是一个单独的组,跟启动有关,一般就是磁盘当中的0号柱面,0号磁头的一号扇区,他里面会写我们开机当中需要使用的信息。

📌Boot Block(启动块)是计算机存储设备(如硬盘、固态硬盘)中用于启动操作系统的关键部分,以下是其详细介绍:

基本概念

  • Boot Block 是存储设备开头的一个特殊区域,通常包含了引导加载程序(Boot Loader)等关键信息,是计算机启动过程中的重要环节。不同的操作系统和存储设备可能会有不同的 Boot Block 结构和大小,但一般都在几百字节到几千字节之间。

主要作用

  • 引导系统启动:其核心作用是启动计算机的操作系统。当计算机通电后,硬件会首先读取 Boot Block 中的引导加载程序,引导加载程序负责初始化硬件、检测系统环境,并将操作系统的核心程序从存储设备加载到内存中,然后将控制权交给操作系统,从而启动整个系统。
  • 硬件初始化:在引导过程中,Boot Block 中的代码会对一些基本的硬件设备进行初始化,如设置 CPU 的工作模式、初始化内存控制器、检测硬盘等存储设备,确保硬件处于可正常工作的状态,为后续操作系统的加载和运行做好准备。
  • 系统引导管理:Boot Block 还可以提供系统引导管理功能,例如在多操作系统环境下,引导加载程序可以显示操作系统选择菜单,让用户选择要启动的操作系统。它还能记录系统的引导参数和配置信息,根据这些信息来正确地引导操作系统。

与其他组件的关系

  • 与 BIOS/UEFI 的关系:BIOS(基本输入输出系统)或 UEFI(统一可扩展固件接口)是计算机硬件与软件之间的桥梁,它们在计算机启动时首先运行,负责初始化硬件和查找可启动设备。找到可启动设备后,BIOS 或 UEFI 会将控制权交给该设备的 Boot Block,由 Boot Block 进一步完成操作系统的引导过程。
  • 与操作系统内核的关系:Boot Block 的最终目的是将操作系统内核加载到内存并启动它。引导加载程序在 Boot Block 中执行一系列初始化操作后,会根据预设的规则和配置信息,找到操作系统内核在存储设备中的位置,将其加载到内存中,并将控制权传递给内核,使内核能够开始运行并初始化整个操作系统。
  • 与文件系统的关系:虽然 Boot Block 本身不直接属于文件系统,但它与文件系统密切相关。引导加载程序需要从文件系统中读取操作系统内核和相关的启动配置文件等。在一些情况下,Boot Block 可能还会包含文件系统的驱动程序或相关信息,以便能够正确地访问存储设备上的文件系统,获取启动所需的文件。

存储与保护

  • 存储位置:Boot Block 通常位于存储设备的第一个扇区,也称为 MBR(Master Boot Record)扇区。对于使用 GPT(GUID Partition Table)分区方案的存储设备,除了 MBR 扇区外,还有一个 GPT 头和 GPT 分区表,其中 GPT 头中也包含了一些引导相关的信息。
  • 保护机制:由于 Boot Block 的重要性,计算机系统通常会采取多种保护措施来防止其被破坏。例如,硬件层面可能会提供写保护功能,防止恶意软件或误操作对 Boot Block 进行修改。操作系统也会提供一些工具和机制,如磁盘保护软件、系统修复工具等,用于检测和修复 Boot Block 可能出现的问题。

📌总结

Boot Block是磁盘上非常重要的一个区域,它包含了启动系统所需的关键信息。无论是传统的MBR还是现代的GPT,Boot Block都扮演着启动过程中的关键角色。

3-2.inode 

 📌补充知识:

  • 文件 = 内容 + 属性
  • 属性也是数据,在Linux中会以一个结构体的方式构建出来----inode
  • 一个文件一个inode,inode->文件属性数据的集合

一个inode大小一般是128字节,有的是256字节,跟系统有关,今天我们以128字节来讲!!!一个数据块4kb,一个数据块里面可以存4kb/128字节 = 32个inode

 📌在我们这个一个组中,可能存在100个文件或者1000个文件,就会存在100个inode或者1000个inode,文件系统就会把当前要新建的文件属性节点在内存中定义出来,把属性值一填,然后把inode节点写到磁盘中指定的inode Table中,刚才说过磁盘与OS进行IO交互时基本单位是4kb大小的,一个块有多少个inode呢?32个!!!在我们对应的分组中,每一个文件他的属性要保存到inode Table中,可是这个分区里有非常多的inode,所以有一个inode都要有自己的编号,

指令:ls -l -i 。选项可以看到一个文件的inode值,所以每一个文件都要有一个inode编号,彼此之间互不重复,inode编号就是inode节点编号,就像进程PCB保存自己的pid。

补充Linux中,文件名不在inode中保存!!!

3-2-1.Inode Table (inode表)

inode Table就是保存这个组里所有的inode属性集。 

📌Inode Table(inode 表)是 Linux 文件系统中的核心数据结构之一,用于存储文件和目录的元数据信息,以下是关于它的详细介绍:

基本概念

  • Inode 表由一系列的 inode 组成,每个 inode 都是一个数据结构,包含了文件或目录的各种属性和信息,如文件的权限、所有者、大小、创建时间、修改时间、指向文件数据块的指针等。
  • 操作系统为文件系统中的每个文件和目录都分配了一个唯一的 inode 编号,通过这个编号可以在 inode 表中快速定位到对应的 inode,从而获取文件或目录的相关信息。

主要作用

  • 文件定位与管理:文件系统通过 inode 表来管理文件的存储和访问。当用户或程序请求访问一个文件时,文件系统首先根据文件名在目录项中找到对应的 inode 编号,然后通过该编号在 inode 表中查找相应的 inode,进而获取文件的属性和数据块指针,以确定文件数据的存储位置,实现对文件的准确读写操作。
  • 权限控制:inode 中包含了文件的权限信息,如文件所有者的读、写、执行权限,所属组的权限以及其他用户的权限等。操作系统在用户访问文件时,会根据 inode 中的权限信息进行权限检查,以决定是否允许用户对文件进行相应的操作,从而保证文件系统的安全性和数据的完整性。
  • 文件属性记录:记录文件的各种属性,如文件的大小、创建时间、修改时间、访问时间等。这些信息对于用户了解文件的状态和历史以及系统进行文件管理和优化都非常重要。

与其他组件的关系

  • 与文件数据块:inode 表中的 inode 包含指向文件数据块的指针,通过这些指针,文件系统可以找到存储文件实际数据的数据块,从而实现文件数据的读写。
  • 与目录结构:目录在文件系统中也是一种特殊的文件,其 inode 包含了目录的属性信息以及指向目录下文件和子目录的 inode 编号等信息。目录项中存储了文件名和对应的 inode 编号,通过这种方式将文件名与 inode 表中的 inode 关联起来,形成了文件系统的目录结构。

性能影响

  • 查找效率:inode 表的组织和查找算法直接影响文件系统的查找效率。一般来说,采用高效的数据结构和查找算法(如哈希表、B 树等)可以快速定位到目标 inode,提高文件访问的速度。
  • 空间占用:inode 表需要占用一定的磁盘空间来存储 inode 信息。如果 inode 表过大,会浪费磁盘空间;如果过小,可能无法满足文件系统中大量文件和目录的管理需求,导致 inode 不足,影响文件的创建和管理。

📌查看文件信息:

stat指令是在类 Unix 系统(如 Linux、Mac OS 等)中用于查看文件或文件系统状态信息的命令,以下是其详细介绍: 

指令:stat [选项] 文件或目录

常用选项

  • -c:指定输出格式,可自定义显示的信息字段。
  • -f:显示文件系统的信息,而不是文件的信息。
  • -t:以简洁的表格形式显示信息。
📌如果有10000个文件,这个inode Table需要占用多少个块呢?

10000*128 / 4 / 1024

所以,有一张表inode Table 大小以块为单位4kb大小,每一个块能保存32个inode属性,现在一文件属性已经能保存起来,那么内容在哪里保存呢?所以Linux文件系统中还有个Data Blocks数据块区,一个分组里面绝大部分空间被 Data Blocks占用。

3-3.Data Blocks(数据块)

Data Blocks里也是被划分好的4kb大小的块

📌Data Blocks(数据块)是计算机存储系统中用于存储数据的基本单元,在文件系统、数据库管理系统等多种场景中都有重要应用,以下是关于它的详细介绍:

基本概念

  • 数据块是将连续的数据存储在存储介质上的一个固定大小的区域,通常是磁盘、固态硬盘等存储设备的读写基本单位。它的大小一般是固定的,常见的有 512 字节、4KB、8KB 等。

作用

  • 提高存储效率:将数据划分成固定大小的数据块进行存储,可以更好地利用存储介质的空间,减少碎片的产生。例如,在文件系统中,文件的数据会被分割成多个数据块存储,当文件大小不是数据块大小的整数倍时,最后一个数据块会被部分填充,这样可以避免因数据分散存储而浪费大量的存储空间。
  • 优化读写性能:存储设备以数据块为单位进行读写操作,能够提高读写的效率。因为存储设备在读写数据时,磁头或闪存芯片等硬件需要一定的时间来定位和传输数据,以数据块为单位进行读写可以减少磁头寻道或闪存芯片寻址的次数,从而加快数据的读写速度。例如,硬盘在读取数据时,一次读取一个数据块比逐个字节读取要快得多。
  • 方便数据管理:数据块为数据的管理提供了便利。在文件系统中,通过 inode 等数据结构可以记录文件所占用的数据块编号,操作系统可以方便地根据这些信息对文件进行读写、删除、复制等操作。在数据库中,数据块可以用于存储表数据、索引数据等,数据库管理系统可以通过数据块的编号和相关元信息来管理和访问数据。

与其他概念的关系

  • 与 inode 的关系:在文件系统中,inode 包含了文件的元信息,其中包括指向文件数据块的指针。通过 inode 中的指针,文件系统可以找到文件所占用的数据块,实现对文件数据的访问。inode 与数据块的这种关系是文件系统实现文件存储和管理的基础。
  • 与磁盘分区的关系:磁盘分区是将磁盘划分成多个逻辑区域,每个分区可以包含一个或多个文件系统。数据块是文件系统在磁盘分区上存储数据的基本单元,磁盘分区为数据块的存储提供了物理空间,文件系统在磁盘分区内对数据块进行管理和分配,以实现数据的存储和访问。

在不同系统中的应用

  • 文件系统:文件的数据被存储在数据块中,文件系统通过 inode 表等数据结构来记录文件与数据块的对应关系。当创建一个文件时,文件系统会为其分配若干个数据块来存储文件数据,并在 inode 中记录这些数据块的编号。当读取文件时,文件系统根据 inode 中的数据块编号依次读取相应的数据块,将数据组合起来返回给用户或应用程序。
  • 数据库管理系统:数据库中的数据也被划分成数据块进行存储。例如,Oracle 数据库中的数据块是数据库最小的 I/O 单元,每个数据块包含了一定数量的数据库记录。数据库管理系统通过数据块来管理数据的存储和访问,当执行查询、插入、更新等操作时,会以数据块为单位从磁盘读取数据到内存,或者将内存中的数据写入磁盘的数据块中。

📌假设有一个文件inode值为1000,此时在磁盘中申请一个128kb空间,然后把inode属性值往内存里的inode数据结构里一填,填完之后,把他刷新到磁盘的某个分区的某个分组的Inode Table中,然后属性集就有了,然后这个文件不是有内容吗,在Data Blocks中找到特定的块,比如8kb,就把文件内容保存到这两个块中,所以就把一个文件存完了。

结论:Linux下,文件属性和文件内容分别储存!!! 

📌如何找文件呢?

我们只需要拿到这个inode值,根据这个inode值找到Inode Table表中的位置,属性找到了,那内容怎么找到?inode结构体中还保存了一个int blodes[NUM];这个数组记录了文件内容保存在哪里,所以把属性和数据一定方式关联起来。

所以拿到 inode值,找到inode节点,再根据节点查找blocks[NUM]这张映射表,就能查找到文件块,这样属性和内容都有了!!! 

📌 后面会讲文件块不够怎么办,要存大文件怎么办,根据inode怎么在这个inode表里找!!!

我们怎么知道Inode Table一个有多少个inode,一个可以有多少个inode,已经有多少个inode被占用,有多少个inode没被占用, 在文件系统上怎么知道这样信息呢?数据块块号是怎么理解的,inode值怎么来的,为什么找文件通过inode就能找到

📌补充:

group里面有这么多区域都是固定的吗?

是固定的,每个组里面都要分成固定的区域

3-4.Bitmap(位图)

3-4-1.Inode Bitmap 

📌在这个块组里面一共有多少个inode,我怎么知道这个inode Table 里面有多少个inode被分配了,有多少个inode 没有被分配,如果要新建一个inode,重新在这个inode Table 里申请一块inode空间,该怎么申请,你怎么知道inode Table 里面有哪些被占用哪些没被占用?

所以在group里面有一张位图Inode Bitmap 只要有这个位图,我们就可以知道整个inode Table 的使用情况,inode Table里哪个位置被占用,对应的inode Bitmap 的位置就置为1,没有被占用就为0,这样就能知道有多少个inode Table里面位置被占用,多少个没被占用,如果要新建一个inode,就找为0的位置新建,如果是1,就可以进行查数据。

📌但是磁盘最小访问单位是扇区,磁盘与OS交互是以4kb大小进行访问,怎么做到给inode BItmap置1呢?

一个4kb就能够存我们的位图了,修改这个位图的话,只要把4kb这个位图拷贝到内存,进行修改,修改完,再以4kb大小写回到磁盘,原则上也就是哪怕只修改一个bit位,也要把4kb数据读到内存,修改完再写回去。

📌Inode Bitmap(inode 位图)是文件系统中用于管理 inode 资源的一种数据结构,其作用类似于一种索引地图,能够高效地跟踪和管理 inode 的使用状态。以下是关于它的详细介绍:

基本概念

  • Inode Bitmap 本质上是一个二进制位序列,其中的每一位对应文件系统中的一个 inode。通常,0 表示对应的 inode 未被使用,1 表示 inode 已被占用。例如,在一个有 1000 个 inode 的文件系统中,Inode Bitmap 可能是一个包含 1000 位的二进制序列,第 1 位对应 inode 1,第 2 位对应 inode 2,依此类推。

主要作用

  • inode 分配与回收:当创建一个新文件或目录时,文件系统需要为其分配一个空闲的 inode。此时,文件系统会遍历 Inode Bitmap,寻找值为 0 的位,找到后将其置为 1,表示该 inode 已被占用,并将该 inode 分配给新的文件或目录。当删除一个文件或目录时,文件系统会将对应的 inode 在 Inode Bitmap 中的位从 1 置为 0,标记该 inode 为空闲,以便后续再次被分配使用。
  • 快速查询:通过 Inode Bitmap,文件系统可以快速判断 inode 的使用状态,而无需遍历整个 inode 表。例如,要判断 inode 500 是否被占用,只需查看 Inode Bitmap 的第 500 位的值即可,大大提高了查询效率,有助于文件系统快速响应用户的文件操作请求。
  • 空间管理:Inode Bitmap 能够帮助文件系统有效地管理 inode 空间,避免 inode 的过度分配或浪费。通过跟踪 inode 的使用情况,文件系统可以根据实际需求合理地分配和回收 inode,确保 inode 资源的高效利用。

与其他组件的关系

  • 与 inode 表的关系:Inode Bitmap 是对 inode 表的一种辅助管理结构。inode 表存储了文件和目录的详细元信息,而 Inode Bitmap 则主要用于跟踪 inode 表中 inode 的使用状态,两者相互配合,共同实现文件系统对文件和目录的管理。
  • 与数据块的关系:Inode Bitmap 与数据块虽然在功能上有所不同,但它们都是文件系统实现数据存储和管理的重要组成部分。inode 通过指向数据块来存储文件的实际数据,Inode Bitmap 通过管理 inode 的使用状态,间接影响着数据块的分配和使用。例如,当一个新的 inode 被分配用于创建文件时,文件系统也会为该文件分配相应的数据块来存储文件数据。

存储与性能

  • 存储方式:Inode Bitmap 通常作为文件系统的元数据存储在磁盘的特定区域,与 inode 表、超级块等其他元数据一起构成文件系统的管理信息。为了提高可靠性和可恢复性,有些文件系统可能会对 Inode Bitmap 进行冗余存储或采用校验和等技术来检测和修复可能出现的错误。
  • 性能影响:Inode Bitmap 的设计和实现对文件系统的性能有重要影响。高效的 Inode Bitmap 结构和操作算法可以快速完成 inode 的分配、回收和查询等操作,减少文件系统的响应时间。相反,如果 Inode Bitmap 的结构复杂或操作效率低下,可能会导致文件创建、删除等操作的性能下降。

3-4-2.Block Bitmap 

那么Data Blocks也是按块分配的,那么哪些块没被占用,哪些块被占用了呢?所以有了

Block Bitmap,也是一张位图,也是用0 1表示哪些被用哪些没被用。

Block Bitmap(块位图)是文件系统中用于管理数据块资源的关键数据结构,其工作机制和作用与 Inode Bitmap 类似,但主要针对数据块进行管理。以下是关于它的详细介绍:

基本概念

  • Block Bitmap 是一个二进制位序列,其中每一位对应文件系统中的一个数据块。一般情况下,用 0 表示对应的数据块未被占用,处于空闲状态;用 1 表示数据块已被占用,正在存储数据。例如,在一个具有 4096 个数据块的文件系统中,Block Bitmap 是一个包含 4096 位的二进制序列,第 1 位对应数据块 1,第 2 位对应数据块 2,以此类推,通过这种方式对所有数据块的使用状态进行标记。

主要作用

  • 数据块分配与回收:在文件创建或文件扩展需要更多存储空间时,文件系统会借助 Block Bitmap 来寻找空闲的数据块。系统遍历 Block Bitmap,找到值为 0 的位,将其置为 1,代表该数据块已被分配使用,并将此数据块分配给相应的文件。当文件被删除或数据块不再被需要时,系统会把对应数据块在 Block Bitmap 中的位从 1 置为 0,表明该数据块已释放,可再次被分配。
  • 快速定位空闲块:Block Bitmap 能够让文件系统迅速确定哪些数据块是空闲的,哪些是已占用的。这在需要大量数据块操作的场景中,如文件系统的碎片整理、数据迁移等,能大大提高操作效率。比如,在进行碎片整理时,系统可以根据 Block Bitmap 快速找到连续的空闲数据块,以便将分散的数据集中存储,提高存储效率。
  • 空间管理与监控:通过 Block Bitmap,文件系统可以清晰地了解数据块的使用情况,实现对存储空间的有效管理和监控。文件系统可以根据 Block Bitmap 的信息,统计空闲数据块的数量,判断存储空间是否充足。当空闲数据块数量低于一定阈值时,系统可以发出警告或采取相应的措施,如提示用户清理磁盘空间、自动进行数据压缩等。

与其他组件的关系

  • 与 inode 的关系:inode 包含了文件的元信息,其中记录了文件所占用的数据块编号等信息。Block Bitmap 通过管理数据块的使用状态,为 inode 提供了数据块分配和回收的依据。当 inode 需要为文件分配数据块时,需要参考 Block Bitmap 来确定哪些数据块是可用的。
  • 与超级块的关系:超级块是文件系统的核心元数据,包含了文件系统的整体信息,如文件系统的大小、数据块大小、inode 数量、Block Bitmap 的位置等。超级块为 Block Bitmap 的定位和使用提供了全局的信息支持,文件系统在启动或进行各种操作时,需要从超级块中获取 Block Bitmap 的相关信息,以便正确地管理数据块。

存储与性能

  • 存储方式:Block Bitmap 通常作为文件系统元数据的一部分,存储在磁盘的特定区域。为了保证数据的可靠性和可恢复性,有些文件系统会对 Block Bitmap 进行冗余存储,将其复制到多个不同的位置。同时,为了提高数据的读写效率,Block Bitmap 的存储位置通常会被优化,以便快速地被访问和读取。
  • 性能影响:Block Bitmap 的性能直接影响文件系统的数据读写和存储效率。高效的 Block Bitmap 设计和操作算法能够快速完成数据块的分配和回收,减少文件系统的 I/O 操作次数,提高文件的读写速度。而低效的 Block Bitmap 管理可能导致数据块分配和回收的延迟,增加文件系统的碎片,降低整体性能。 

 📌有了上面的认识,所以今天我们要新建一个文件,并像这个文件写 "Hello Word" 字符串,目前应该怎么做?

新建一个文件,第一件事情,永远是先分配 inode,首先要在 Inode Bitmap 里去查位图里面某一个位置没有被使用,比如7号位,然后把该bit 位由0置为1,然后再 Inode Table 里面拿着7初始化我们节点编号7,然后把7号位对应的Inode table位置,文件属性往里面一写,Inode Table 填好后,要写的内容"Hello Word",发现一个块就足够,,然后再 Block Bitmap 查找位图,看哪个位置没有被用,比如8号位,然后在Data Blocks 申请一个块 8号位置,然后把字符写入这个位置,然后拿着8号位置,回到 Inode Bitmap 中找到 Inode Table 中7号位置inode属性中int blocks[NUM]这个数组,把8号位一填,这样文件属性数据都被保存了,两个位置的位图都置为1了。

所以最终我们只要知道inode编号就可以在这个组里找到某一个文件了,找一个文件只需要把inode编号拿到就好,Linux系统里找文件就是通过inode编号来找的

📌那么删除一个文件怎么办呢?

根本不需要把他的内容、属性、inode做清空,只需要在两个位图中,只需要把他inode编号和数据块所对应占有的bit位由1置为0,就相当于把文件给删除了。

补充:这个数据块是怎么找到的通过inode结构体中block[NUM]数组映射找到的。 

📌 所以删除一个文件可以恢复吗?

可以恢复,把文件删了,只要拿到文件的inode编号,只需要把Inode Bitemap对应位置由0置为1,然后查inode结构体里block[NUM]拿到存该文件内容的块号,再把块号对应的Block Bitmap位置也由0置为1,这个文件瞬间就被恢复了,所以Linux中删除一个文件,可以被恢复的,以前说删了不能恢复是为了防止误操作,即使想恢复也是有难度的,Windows中不是删除只是放到回收站中,文件系统都类似。

📌文件误删最好怎么办?

最好的做法就是什么也不做。所谓的删除在Linux中,本质是设置Inode block 对应位置无效,如果删除后,一顿操作,把inode block对应位置又利用起来,此时对应的原来数据就被覆盖了,一旦被占用,即使数据没有被覆盖,想恢复也恢复不了,数据也恢复不过来了,因为已经被别人占了,更别说被覆盖!!!

📌 如果文件被删除了,inode编号怎么知道呢?

其实,在做任何操作的时候,在我们系统日志里面,都会记录下你的操作,在CentOS或者Ubuntu中,删除一些文件,他一般会有记录,记录里面会保存文件名和inode,如果想恢复,拿到inode编号,用特殊工具恢复。

3-5.Group Descriptor Table(块组描述符)

📌一个分组一共是多大呀?这个分区里面一共有多少个inode,一共有多少个blocks,数据块的使用率是多少,一万个inode有多少被用了,block有多少个被用了?这个block只剩一个G,我要放两个G内容怎么办,要放在其他块组里面,怎么评判放不放的下呢?

所以每个分组里面有一个块组描述符Group Descriptor Table简称GDT,在看到我们描述符,我们就要想到进程PCB,GDT也是一个结构体,里面有很多信息,表明了一共有多少个block数据块,一共有多少个inode,使用率是多少,所以要有一个管理数据,有对Block Bitmap,Inode Bitmap,Inode Table,Data Blocks这些信息进行整体管理。

📌Group Descriptor Table(块组描述符表)是 Linux 文件系统中的一个关键数据结构,以下是其详细介绍:

定义与作用

  • 块组描述符表是用于描述文件系统中各个块组的信息的数据结构。在 Linux 文件系统中,为了便于管理和提高效率,会将磁盘空间划分为多个大小相等的块组(Block Group),每个块组都有自己的块组描述符,这些描述符的集合就构成了块组描述符表。
  • 它记录了每个块组的使用情况、状态等重要信息,帮助文件系统快速定位和管理文件和目录在磁盘上的存储位置,对文件的存储、读取和系统的磁盘空间管理等操作起着至关重要的作用。

存储位置与结构

  • 存储位置:通常位于文件系统的特定位置,一般在超级块之后。其具体位置和大小等信息在文件系统的超级块中有相关记录,以便文件系统在启动和操作过程中能够准确找到并读取它。
  • 结构内容:每个块组描述符包含了多个字段,用于描述块组的不同属性,常见的字段包括:
    • 块组编号:唯一标识每个块组,方便文件系统进行索引和管理。
    • 块位图块号:记录该块组中数据块使用情况的位图所在的块号,通过位图可以快速了解哪些数据块已被占用,哪些是空闲的。
    • inode 位图块号:指示该块组中 inode 使用情况的位图所在的块号,用于追踪 inode 的分配状态。
    • inode 表起始块号:表示该块组中 inode 表存储的起始块号,inode 表包含了文件和目录的元数据信息,通过这个字段可以快速定位到块组中的 inode 表。
    • 空闲块数:记录该块组中当前空闲的数据块数量,为文件系统的空间分配提供依据。
    • 空闲 inode 数:表示该块组中未被使用的 inode 数量,用于文件和目录的创建。
    • 已用目录数:统计该块组中已使用的目录数量,有助于文件系统对目录结构的管理和空间分配。

工作原理

  • 在文件系统进行文件创建、删除、读写等操作时,会首先访问块组描述符表。例如,当创建一个新文件时,文件系统会根据块组描述符表中各个块组的空闲 inode 数和空闲块数等信息,选择一个合适的块组,并在该块组中分配一个空闲的 inode 来存储文件的元数据,同时根据文件的大小,从块组中分配相应数量的空闲数据块来存储文件内容。在这个过程中,块组描述符表中的相关字段会被更新,如空闲 inode 数和空闲块数会相应减少,已用目录数可能会根据情况增加等。
  • 当删除文件时,文件系统会根据文件的 inode 信息找到对应的块组描述符,然后将该文件占用的 inode 和数据块标记为空闲,同时更新块组描述符表中的空闲 inode 数、空闲块数等字段,以便这些资源可以被其他文件再次使用。

与其他文件系统组件的关系

  • 与超级块的关系:超级块是文件系统的全局控制块,包含了文件系统的整体信息,如文件系统的类型、大小、块组描述符表的位置等。块组描述符表的信息是基于超级块中的全局信息进行细化和扩展,超级块为访问块组描述符表提供了必要的引导和基础信息。

  • 与 inode 的关系:inode 是存储文件和目录元数据的结构,每个文件和目录都有一个对应的 inode。块组描述符表通过 inode 位图块号和 inode 表起始块号等字段,与 inode 紧密相关,负责管理和追踪 inode 在各个块组中的分配和使用情况。

  • 与数据块的关系:数据块是实际存储文件内容的区域,块组描述符表通过块位图块号等字段来管理数据块的使用情况,记录哪些数据块是空闲的,哪些是被占用的,为文件的存储和读取提供数据块的分配和定位信息。

3-6.Super Block(超级块)

Super Block是Linux文件系统里特别常见的一个东西,叫做超级块,超级块表示的就是文件系统

所以今天对于整个磁盘里面会有各种分区,所以文件系统是以谁为单位的,是以分区为单位的,一共分区一套文件系统,不同的分区可以写不同的文件系统,分区和分区之间相互独立。

📌超级块表示整个分区的整体情况,他是表示分区的情况,为什么会在分区的其中一个组里面呢?那是不是所有分区都会包含这一个超级块呢?

答案是在我们整个分区里面,比如一百个分组,不需要给每一个分组都保存一个超级块,但也不是只要一个分组有超级块,在这一百个分组里,可以有两三个超级块,这两三个超级块数据是完全一样的。

📌但是理论上,这个分区超级块是一个文件系统,在分区的最开始就应该是超级块,然后剩下的区域再进行分组,这样不是更优雅吗更清楚吗,但Linux并没有这样干,而是放在若干个组里面为什么要这样干呢?

因为在一个分区里,如果一个超级块挂掉了,整个文件系统就全部都没了,如果直接挂了,整个分组情况摸不清楚了,整个分区也就摸不清楚了,所以这个超级块会打散到不同分组里面,不一定每个分组都有,因为要提高效率,所以一个超级块挂掉了,我们对应的文件系统就会去其他块组里面找超级块,然后给他覆盖回来,此时就能进行文件系统恢复了。

就比如,我们自己的电脑Windows,电脑用久了,有一天打开电脑,问是否要进行恢复,他就会给你检测,检测是在干什么,是在检测超级块情况,如果有错误,直接拿其他超级块来恢复他。

📌Super Block(超级块)是文件系统的关键组成部分,以下是关于它的详细介绍:

定义与作用

  • 超级块是文件系统的元数据结构,存储了文件系统的整体信息,对文件系统的管理和操作至关重要,就如同一个文件系统的 “总控中心”,记录着文件系统的基本属性、状态和布局等关键信息,操作系统通过读取超级块来了解文件系统的全貌,进而进行各种文件操作。

主要包含的信息

  • 文件系统标识信息:包含文件系统的类型,如 ext2、ext3、ext4 等,以及文件系统的 UUID(通用唯一识别码),用于在系统中唯一标识该文件系统,便于系统进行管理和区分不同的文件系统。
  • 块设备信息:记录了文件系统所在块设备的相关信息,如块设备的大小、块的数量等,让系统知道文件系统可使用的存储资源规模。
  • 块和 inode 参数:指定了文件系统中块的大小,常见的有 4KB 等,以及 inode 的总数和 inode 表的位置等,这些参数决定了文件系统中文件和目录的存储和管理方式。
  • 文件系统状态信息:表示文件系统是处于挂载、卸载、只读还是读写等状态,还可能包含文件系统是否已被正确卸载、是否存在错误等状态标志,帮助系统在启动或操作文件系统时判断其可用性和完整性。
  • 日志信息(针对支持日志的文件系统):如 ext3、ext4 等文件系统中,超级块会记录日志相关的信息,如日志块的位置、日志的当前状态等,确保文件系统在出现故障后能通过日志进行数据恢复和一致性检查。

存储位置与访问

  • 存储位置:超级块通常位于文件系统的固定位置,一般是块设备的开始部分,如第 0 块或第 1 块等,不同的文件系统可能有具体差异。
  • 访问方式:操作系统在挂载文件系统时,会将超级块的内容读取到内存中,建立起内存中的超级块数据结构,后续对文件系统的操作大多基于内存中的超级块副本进行,以提高访问效率。当对文件系统进行一些关键操作,如创建文件、删除文件等,需要修改超级块信息时,系统会先更新内存中的超级块,再适时将更新后的内容写回到磁盘上的超级块中,保证数据的一致性。

重要性与保护

  • 重要性:超级块包含了文件系统的核心信息,一旦超级块损坏,可能导致整个文件系统无法正常挂载和访问,文件系统中的数据将面临丢失或无法读取的风险。

  • 保护措施:为防止超级块损坏带来的严重后果,文件系统通常会采取一些保护措施,如创建超级块的备份,将备份超级块存储在不同的位置,在主超级块损坏时,系统可以尝试从备份超级块中恢复文件系统的信息。此外,文件系统的操作也有严格的规范和流程,以确保超级块的完整性和一致性。


📌所以在我们整个磁盘中,我们以块为单位,进行划分的话,其中我们所对应整个磁盘当中,每一个分区分完组后,我们就可以在分组当中直接把组内部的空间进行划分好,划分好后,我们对应整个磁盘空间就也都被充分划分好了,所有划分的都是固定的,一旦固定了,我不填充 Inode Table 和Data Block是,至少要把两个位图全部清0,往GDT写入这个组数据相关信息,一个组要做这个工作,所有分组都要做这个工作,也就是说就算这个分区没有一个我们自己的文件,也要进行写入文件系统信息,这个工作叫做格式化,把整个分区所有位图全部清0,写入新文件系统,新的GDT,原本文件就都被格式化了。

格式化的本质:写入新的文件系统!!!

四.准确深入理解文件系统 

整个分区里的分组里的inode个数,block个数都是固定的,也就是在这个组里面有多少个inode都是固定的,每个区根据一定的比例划分。

4-1.关于Data Blocks,Inode Table

📌有一个问题,固定了,data blocks用完了,inode table没用完,或者inode table用完了,data blocks没用完,这两种情况存在吗?

data blocks用完了,inode table没用完,这种情况是不存在的,inode只要有,data blocks不嫌多,data blocks要随着inode。

data blocks没用完,inode table用完了,这种情况是存在的,分配10000个inode,全部都是小文件,大量的小文件把inode编号占完了,无法创建新文件,但是有数据块的化,已存在的文件还能继续使用。

📌“data blocks 用完了,inode table 没用完” 不存在的原因

  • 文件存储本质:文件的存储在逻辑上可看作是 inode 与 data blocks 的关联组合。inode 是文件的 “信息管理者”,负责记录文件的各种属性和指向数据块的指针等,而 data blocks 是文件的 “数据仓库”,实实在在地存放文件的数据内容。当有文件要存储时,必然既需要 inode 来记录文件的基本信息,也需要 data blocks 来存放文件的实际数据。只要有可用的 inode,就意味着有文件需要存储或可以存储,那么就一定需要 data blocks 来存放文件数据,所以从文件存储的本质逻辑来讲,“data blocks 用完了,inode table 没用完” 这种情况不符合文件存储的基本要求,一般是不存在的。
  • 文件系统的分配策略:文件系统通常采用预分配或动态分配的策略来管理 inode 和 data blocks。预分配是在文件系统创建或文件创建初期,就根据一定的规则为文件预先分配一定数量的 inode 和 data blocks,以保证文件后续的存储和扩展有足够的资源。动态分配则是在文件需要扩展或新文件创建时,根据实际需求动态地分配 inode 和 data blocks。无论哪种分配方式,都是基于文件对 inode 和 data blocks 的实际需求来进行的,不会出现只大量剩余 inode 而 data blocks 却完全没有可用的情况。

📌“data blocks 没用完,inode table 用完了” 存在的原因

  • inode 的有限性与文件创建机制:文件系统中 inode 的数量在格式化时就确定了,是有限的资源。在创建文件时,系统首先要为文件分配一个 inode,用于记录文件的元信息。如果创建了大量小文件,每个小文件都要占用一个 inode,很快就会把 inode 耗尽。而小文件对 data blocks 的占用相对较少,所以就会出现 data blocks 还有剩余,但 inode table 已用完,无法再创建新文件的情况。
  • 文件系统对 inode 和 data blocks 的管理差异:文件系统对 inode 和 data blocks 的管理方式有所不同。inode 主要用于文件的索引和元数据管理,其分配和回收相对简单,只要有文件创建或删除,就会对应地分配或回收 inode。而 data blocks 的管理则更加复杂,涉及到数据的存储、碎片化处理等。系统会尽量高效地利用 data blocks,避免出现过多的碎片,这就导致在 inode 用完的情况下,data blocks 可能还存在大量连续或不连续的可用空间。

4-2-1.关于inode

  1. inode以分区为单位,一套inode,不是以磁盘为单位,也不是以组为单位分配的,也就是说,一个分区有1号indoe,其他分区也有1号inode,分区之间有重复的编号,单个分区内部没有重复编号
  2. inode在分配的时候,只需要确定每个组起始inode即可,这样每个组里面inode编号就是固定的了,这个值只需要保存在GDT中

4-2-2.关于block

  1. 块号也是统一编号的
  2. block分配的时候,也只需要确定起始block,只需要保存在GDT中


整个分组情况只需要把inode block分组分好了,那么基本上我们整个分组情况就划分好了,知道inode号和block号,两个位图大小也能算出确定,超级块与GDT是固定的数据结构,大小也是固定的,所以这个组所有东西都能确定了。

4-2-3.我们有这些知识铺垫重新看待Block Bitmap 、Inode Bitmap 、Inode Table 、Data Blocks:

在一个组里:

问题:

📌1.我们是如何分配一个inode的?

知道inode起始位置时,根据Inode Bitmap中对应位置(比如:000100,就是第三个位置),起始位置跟对应位置二者相加,就能确定inode编号(在第一个组里面就是4,如果第二个组起始位置为10000,第二个组就可能是10003)。所以inode分配就可以分配成全局的inode编号,并且在局部上,根据位图得到任何一个文件的inode,而且该inode在我们分区中具有唯一性!!!

📌1.我们是如何分配一个block的呢?

 Data Blocks也是有对应位图的,也知道每个组的起始位置,比如块号是0、1、2、3,其实是块号+起始的块号位置,如果块号是3,加上起始位置后,得到的值存在映射表block[NUM]这个数组里,所以知道这意味着什么吗?意味着在一个数组里,如果块大小不足了,就可以跨组进行储存,在block[NUM]数组里,存第二个组的块号+起始位置,这样就允许Linux中进行大文件的建立了,所以块号和inode都是全局建立的,如果该文件两个组都存内容,因为块号在一个区里是唯一的,通过block[NUM]映射表就可以找到两个组内的文件内容了。

📌3.有了上面正向的认识,我们再来反向进行理解下,如果inode:10004、block:100006,怎么找到在哪个组?怎么找到inode属性?怎么找到文件内容?

根据10004来确定在哪个组,因为GDT里存了每个组的起始位置,拿着块号也遍历GDT,知道在哪个组里,拿着inode编号减去改组的起始位置,就知道位图的对应的位置,所以根据位图就能找到inode table中inode属性的位置,从全局转成局部在进行查找了,拿着block块号也是一样,减去该组的起始位置,就知道位图对应的位置,然后去对应的data blocks中进行找到对应的数据块,就能找到内容了。

既然我们都能确定任意一个块,任意一个inode了,所以就能确定多个块,多个inode,多个组了,在文件系统里OS内部,想加载哪一个inode,找到这个indoe随便一个块都可以找到。 

📌题外话: 

以一个分区为例子,一个分区可能有成千上万的块分组,我要遍历总不能在磁盘上进行遍历,包括在一个里新建增删查改,这一系列工作都是OS进行完成,文件系统属于OS系统的一部分,这些新建增删查改本质不就是文件管理吗?文件管理首先要做的,超级块,GDT这样的源数据管理起来,可我们系统里面有那么多分区,就有那么多的超级块,每一个分区有那么多分组那么多GDT,所以我们要把分组管理起来OS如何进行管理的呢?------先描述在组织!!!

所以我在我的文件系统里只需要把所有的Super Block把他加载到内存里,构成struct SB结构体对象,以链表的形式管理起来,不就是把所有的分区管理起来了!!!把我们要用的GDT加载到内存,构成struct GDT结构体对象,以链表的形式组织起来,那么此时,把GDT这个链表和特定的一个struct sb关联起来,不就是分区有那么多分组吗,然后就可以把整个文件系统的管理转化成对链表的增删查改,所以上面所说的inode遍历查找所有操作,全部都是内存级的操作!!!

sb和GDT在保存的时候,是以4kb为单位操作,加载到内存也是以4kb大小加载到内存,整个Linux操作系统对文件系统来讲属性也罢、位图也罢、数据块也罢,都是4kb进行划分,所以最后在我们整个OS内部,对内存进行管理,最简单的方法就是把内存也以4kb划分,然后整个内存管理,就变成对4kb进行管理!!!

4-2-4.知道inode号,有一下四个问题:

📌1.如何查找一个文件?

根据inode确定在哪个组里,然后inode减去起始位置,查找位图,找到后在inode table里去找inode结构体位置,这样就找到文件属性了,就知道属性和数据块的映射关系,块号也有了,减去该组的块号起始位置,查位图,找到data block里面文件内容

📌2.如何删除一个文件

删除一个文件前提就是查找找到文件,然后inode bitmap中该文件对应位置由1置为0,,block bitmap对应的位置由1置为0,GDT中已经使用的inode个数就减减,属性再已更新,就完成删除

📌3.如何修改一个文件?

修改的前提也是查到到该文件,要么对属性做修改,要么对内容进行修改,更多的是对内容进行修改,如果要修改属性,查找文件,把文件indoe属性加载到内存,修改完后,再写回对应的位置;如果修改内容,把数据块加载到内存,文件不是在内核级缓冲区吗,内容加载到内核级缓冲区,改完后,再写回到磁盘中,此时就对内存对属性都能进行修改了。

📌4.如何新增一个文件?

新增一个文件,我们首先要知道在哪个组里面进行新增,这个工作,由文件系统自动给我们确认,遍历GDT去找,根据要新增的文件,基本信息,找到组,就要进行分配inode号,首先查找位图,得到一个局部的值偏移量,然后把该位置由0置为1,block bitmap对应位置也由0置为1,然后inode和块号都申请好,属性内容一填,最后给用户返回一个inode号,,返回的inode号不就是改组的起始inode+inode bitmap位图里面的偏移量,所以inode号就返回了,但是新增还不是特别清楚,稍后再说!!!

📌子问题1:inode和block究竟是怎么进行映射的?

int blocks[NUM]有15个块大小,前十二个是直接进行映射的,存的就是文件的内容,剩下三个存不同级别的索引,他指向一个块,不存文件数据,存其他块号,一个块号按4字节来算,一个块就可以存 4kb * 1024 / 4 = 1024个块号。这个一级索引就直接把一个块的大小扩展成能存1024个块的大小,就相当于一个块多了1024个4kb,就相当于能保存4MB的文件了二级索引也是不保存数据,也不存其他块号,保存其他索引的块号,一个一级索引4kb大小,二级索引里保存一级索引的块号,一个一级索引带来4MB大小的空间,二级索引就又扩大1024个4MB大小,也就是4GB大小三级索引以此类推1024个4GB大小,也就是扩大成4T大小了!!!

📌为什么这样存?

因为他想把inode设置成固定大小,有人说可以定义指针然后malloc,但是这是磁盘级别的,哪能malloc开辟空间,因为inode固定起来方便对文件属性做整体加载。

📌访问内容多还是属性多?

很多情况都是访问数据多,比如命令行上进行 ls 一下就 访问那么多文件属性,如果inode不是固定的大小,那加载到内存里,光 ls 一下,当前文件大多了,就会导致内存阻塞所以inode属性必须是固定大小的,这样就不会一次性把所有文件全加载到内存,只会加载需要的那一部分,而查文件内容,一定只查一个文件的内容,都是具体某一个文件,不像 ls 把全部属性都加载显示,保证系统的一个稳定性,把inode设置成固定大小,而 文件 = 内容+属性,属性固定大小,查我们内容只要在内容上动脑子,所以以这样的方法来扩展大小!!!而block是全局的,这时这索引爱指向哪指向哪,可以跨组,所以我们就可以存下特别大的文件,而且这种设计特别优秀,可以跨组存也可以不跨组存,跨组存无非就多一次查找,不跨组他在组内也可以消化这个文件。这意味着,他组大小也可以固定起来,文件不受组的的限制,可以跨组,他的文件大小可以自由设置,这在软件上不就是解耦合吗,把组的设计和文件存储又解耦合了,这就是Linux文件系统,设计特别优雅!!!

 📌子问题2:凭什么拿到inode?我们访问的好像都是文件名啊?我们操作的都是文件名呀?

前面补充说过,Linux中,文件名不在inode中保存!那他文件名保存在哪里?文件是有类型的:普通文件,目录文件。

衍生一个问题:📌如何理解目录文件?

目录文件也要有自己的inode + data blocks = 属性 + 内容;请问目录的内容是什么?他是文件有内容也就会有数据块,目录里数据块存什么呢?存文件名和inode的映射关系!!!!!

也就是说我们目录内容他存的是文件名和inode的映射关系,查一个文件只要找到当前目录,根据目录的inode号找到对应的数据块,然后根据文件名再进行映射找到文件的inode,这样就能拿到inode,这个映射关系是互相映射的!!!因为同一级目录不可能存在同门文件,所以文件名可以映射inode,inode可以映射文件名!!!

📌重新理解目录权限?rwx

所以我们对一个目录没有r权限的时候,查不了文件内容,因为对目录没有读权限,就无法读取目录的data blocks,系统不让读,读不到,就得不到文件名和inode的映射关系,拿不到inode怎么可能访问到文件。如果没有w权限,为什么就不能在该目录下新建文件呢?因为没有写权限就无法将文件名和inode的映射关系写到目录内容中,所以就不可以写了。所以没有x权限为什么进不去目录中,文件进不去的本质就是打不开!!!

📌为什么要把文件名和inode映射关系保存在目录中?为什么找文件要用inode呢?

如果没有inode,那么文件名在属性里面,查找的时候都是按文件名进行查文件的,文件名可长可短,而inode就是一个整数,所以比较整数比比较字符串有更高的效率!!!

指令:ll -ln 把能显示成数字的都显示成数字,我们这个用户名在Linux中就是一个数字,Linux认这个人也是拿数字认的,所以就有了UID,组id,用户id,在内核中比较都是拿数字比较。

指令:ll -li 显示inode。

📌ll 一下底层做了什么?

ll一下把当前目录在底层打开,然后遍历该目录的文件名和inode的映射关系,把所有的文件inode都拿到,一次在文件系统进行查找inode table里面的inode,把左侧属性全部罗列出来,和文件名进行拼接形成字符串,就瞬间全部解决了。

📌今天找到文件名就能找到inode,怎么找到文件名呀?

首先找到当前目录的indoe,可是当前目录也是文件!!!所以要依次进行路径逆向解析,找到最后就找到跟目录,根目录就是固定的,在文件系统中,根目录的文件名,inode编号,他的类型属性都是硬编码直接写到超级块中,所以递归递归就到出口了,所以找一个文件原则上,我们Linux系统根据当前对应的文件,根据路径解析,解析到根目录。其实实际上他是根据目录正向解析的

📌所以现在能不能理解为什么任何一个文件都要有路径?

因为没有路径就根本不可能找到这个文件的文件名和inode,映射关系建立不起来,所以必须要有全路径,才可以根据全路径在Linux系统内部进行路径解析,才能找到,你最终的文件名和inode映射关系,才能找到这个文件。

📌所以有上面知识现在能不能理解,我们每一个进程他都要有cwd?

之前说的打开文件:

这些open在调用的时候都是以进程为载体,因为Linux操作全是进程,所以有cwd,不管以上面什么方式打开文件,最终都可以得到。

结论:任何目标文件都有路径!!!

也就是说这就是为什么我们显示一个文件的时候,访问一个文件的时候,这个文件路径是谁给你的?-------进程!!!

📌进程自己执行ls进程也有路径吗?

有!!!所以Bach在创建时,就有自己的路径cwd,就是给进程准备,在进程里面做的任何操作全是进程操作,所以你进程内部保存了任何文件的cwd,保存了你要找的文件的cwd,所以不管怎么访问,通过绝对、相对路径访问,他都可以拼接成我们完整路径,为什么要拼接路径呢?---因为要进行路径解析方便我们进行查找!!

4-2-5.dentry 缓存:

所以我们最终访问任何一个文件的时候,我们最终就可以根据我已有的路径,

我们要访问打开这个文件的时候,所以首先在系统层面上,他进程就要根据文件名,结合当前的cwd,以拼接,拼成一个完整的路径,然后我们依次进行路径解析,从根目录开始,根目录是固定的,所以在系统里就加载到OS中,所以OS就知道根目录了,然后找home,然后打开根目录,根目录不是有自己的inode与data blocks,根目录里面有home目录名,然后根据字符串名字,在根目录的data blocks中找,找到home的data blocks,然后根据whb这个字符串,在home这个目录里面的data blocks里找,找到whb的data blocks,依次类推,就可以找到这个文件的inode,最后就找到文件,再操作。前面理解路径解析是逆向推到的,只是为了告诉需要解析,实际上找一个文件是要以绝对路径的方式从左向右进行路径解析的

dentry在文件系统中起着关键作用,主要体现在以下几个方面:

  • 文件路径查找:它是文件路径解析的重要环节,通过dentry的层次结构,文件系统可以快速地从根目录开始,沿着目录项链找到目标文件或目录的 inode。
  • 缓存机制:作为一种缓存机制,dentry缓存可以加快文件系统的访问速度。当文件或目录被访问时,其dentry结构会被加载到内存中的dentry缓存中。这样,下次再访问相同的文件或目录时,就可以直接从缓存中获取相关信息,而无需再次从磁盘读取,从而大大提高了文件系统的性能。
  • 文件系统操作关联dentry与文件系统的各种操作密切相关。例如,在打开文件时,文件系统会根据文件路径查找对应的dentry,然后通过dentry获取文件的 inode,进而进行文件的打开操作。在读写文件时,也会通过dentry来定位文件的 inode,以获取文件的物理存储位置等信息。

对打开文件的管理在很大程度上确实可以转换为对dentry缓存的管理,这主要包括以下几个方面:

  • 缓存命中与失效:当进程打开一个文件时,文件系统首先会在dentry缓存中查找对应的dentry。如果缓存命中,说明该文件的dentry已经在内存中,文件系统可以直接使用缓存中的信息进行后续操作。如果缓存未命中,文件系统则需要从磁盘读取文件的相关信息,并将其dentry结构加载到缓存中。
  • 缓存清理与回收:为了保证dentry缓存的有效性和系统内存的合理利用,需要对缓存进行清理和回收。当文件不再被使用或系统内存紧张时,文件系统会根据一定的策略,如最近最少使用(LRU)算法,将一些不常用的dentry从缓存中移除,释放其占用的内存空间。
  • 缓存一致性维护:在多进程或多线程环境下,可能会有多个进程同时访问同一个文件。为了保证数据的一致性,文件系统需要对dentry缓存进行一致性维护。例如,当一个进程对文件进行了修改,文件系统需要及时更新dentry缓存中的相关信息,确保其他进程读取到的是最新的数据。

📌如果此时在当前路径下有touch test2.txt,刚才访问完test.txt文件,现在又要访问test2.txt,该怎么办?

正常情况下,又从头向右进行路径解析,如果从头又开始路径解析,本就就是不断进行访问磁盘文件系统,Linux如果这样干,效率就太低了。所以注定了需要对路径结构进行缓存!!!怎么缓存稍后再说。

📌现在问一个问题,Linux用不用保存路径,用不用把路径整体信息保存起来?

Linux系统根本就不需要保存路径,在磁盘上就没有路径的概念,在磁盘系统上只有inode、data block这些,根本就没有路径,那路径谁维护的呢?路径由:1.你的进程提供的2.是由他对应的每一个文件data blocks提供的映射关系,就相当于他在data blocks里构建了一张磁盘级的一张简单的一个路径关系,一个多叉树的关系,但是这个跟文件系统没有半毛钱关系,所以在Linux系统里,你可以理解Linux系统里在磁盘上不需要维护任何路径关系,他只需要在自己的数据块里记录下来他这个目录里存的什么文件与inode映射关系就行了,所以像tree命令查看的树形结构在磁盘上不存在,即便存在,也是逻辑上的存在,在物理存储上,他只是文件名和inode的映射关系,在磁盘文件系统只有文件属性和数据块,只不过数据块里存的是其他文件对应的文件名和inode的映射关系,但是tree命令可以看到这个树形结构呀?

📌所以我的问题是:Linux要对对应的路径进行缓存,应该以什么样的数据结构来缓存我们刚刚说的路径呢?-------多叉树!!!!

所以在Linux系统中,他在OS加载的时候,需要把他根目录挂载,未来,用户首次使用比如home路径时,他在内存里结构体构建一个home路径的一个节点,链接到这个多叉树中,所以整个多叉树就是这样构建出来的,构建出来后,这个缓存一旦构建出来了,这个多叉树和我们磁盘级的文件系统什么关系?这个缓存只是磁盘级我的文件系统中众多文件中的一小部分,他会把之前磁盘上访问过的目录,构建成节点缓存起来,这个缓存起来后,紧接着没有访问的目录就不需要了,所以这个缓存就是一个内存级的缓存!!!这样就在我们内存就有一个多叉树的路径了,所以又回到上面问题:刚才访问完test.txt文件,现在又要访问test2.txt,该怎么办?所以第一次访问这个多叉树构建好了,第二次访问test2.txt文件不需要再访问磁盘了,只需要查缓存结构,这个缓存在内核数据结构叫 struct dentry ,是一个内核数据结构纯内存的,也就是维护的是在Linux内核中,帮助我们维护的一个dentry树,来进行路径缓存。

所以find可以查找特定路径下的特定文件,如果find是真的在磁盘上去找,什么叫真的去磁盘上找,说人话就是,根据路径去进行不断解析,在磁盘中访问磁盘,从根目录开始,这样效率太慢了,而事实上 find 首次查找会慢点,第二次查找就会快了,这时为什么,因为第二次查找时,路径结构已经被缓存起来了,而dentry结构可以未来间接找到任意一个目录的inode+内容,内容找到了我们就可以在内存级进行路径解析了。

dentry这个结构就是Linux中一个数据结构,包括inode也是,但是因为Linux系统有VFS虚拟文件系统,我们阿卡难道时候有许多属性看不到,但是可以看到宏观的结构, 其实我们对应的多叉树路径缓存,他是多叉树树状结构的,在Linux中,我们在整个Linux中最开始开机的时候,整个路径里只需要一个节点,必须把根目录给我们挂上,跟目录必须要知道,根目录知道了,整个Linux系统里,只要给我提供路径,任何路径都能给找到。

其实这个struct dentry ,之前说过,一个进程PCB,可以属于全局的链表,也可以属于某个队列中,一个结构体可以同时属于多种数据结构。所以一个struct dentry可以属于多叉树,又可以属于某个管理的链表!!!

dentry这里的缓存会不会变得越来越大?----struct dentry这里面也有自己的剔除算法,有的路径从来没使用过,那这个路径上的节点就会被释放掉,所以struct dentry中也有最近最少使用的字段,来统计使用的最少次数。

两个大问题:

 📌文件描述符和进程有什么关系?他们是怎么关联起来的?

下图是Linux2.6.26版本Linux内核源码,file里面有个path对象,在 Linux 文件系统中,每一个文件(包括普通文件、目录、设备文件等)都有一个对应的dentry(目录项)结构,也要把自己挂载到目录树里面,方便查找,对打开的文件管理就转换成对dentry缓存的管理。

dentry里面有指针指向我们的inode,在Linux里,也要把我们访问的所有inode加载到内存,inode依附于dentry,每一个dentry都会指向自己的inode,所以文件属性内容都能看见了。 

📌一个文件和文件系统真正产生关联,整体的认识应该是怎么样的?

我们所对应的磁盘他最终上面就会存在各种的文件系统,Linux系统里默认把根目录给我们在全局当中,会让我们找到最开始的dentry,然后当你想打开一个文件的时候,你的进程就必须提供一个路径/home/whb/test.txt,然后Linux系统就根据根目录查这个dentry,这个dentry目录打开后,再根据字符串比较找到home,然后再加载home、inode属性、内容,然后在home所对应的内容中去找whb,最终我们就形成一条路径,然后在whb这个目录里打开这个目录,打开数据块,发现test.txt的inode,最后就得到文件名和inode的映射关系,然后此时我们对应的dentry,根据当前路径,根据test.txt的inode,在磁盘中指定分区下,找我们对应的inode,加载进来我们inode就有了,在内核中就形成dentry结构,构成dentry节点,指向inode,然后是打开这个文件,他要给我们创建我们对应的struct file,再分配文件描述符,然后dentry里面的inode就指向文件的inode然后struct path里面dentry指向新建缓存的dentry。所以我们一个文件从磁盘中,包括这个磁盘不是还有内容吗,把文件加载到内核缓冲区中,所以操作表,内容表,属性表就都有了,所以这个文件就有了。

如果打开的是目录的话,比如cd进来这个目录,那这个目录也要被加载,同时更新一下进程的cwd。

所以OS内打开一个文件会涉及到创建内核数据结构,还要在这个磁盘中读取数据,加载数据到内存,构建映射关系,至少一个文件内容加载到缓冲区,至少一个文件inode加载进来,至少他路径关系要增加到系统内部的路径缓存中。

 4-2-6.做一个小实验

📌所以在一块磁盘中,在一个分区里面,我们能找到这个分区里面的任何一个文件的inode和block,我们就是可以通过路径解析,让我们找到任何一个文件,和他对应的内容,但是有没有想过一个问题,我们怎么知道我在哪一个分区里面呢?因为必须先确定一个分区,才有后面找到这个文件这些过程,所以你怎么区分你这个分区呢?另外还有一个,不是有路径吗?路径从进程来的,那进程的路径又是从哪来的,也就是进程最开始从哪来的?

其实我们Linux系统,如果有多个分区的话,那么在Linux系统一定要有一个分区包含根目录,这是肯定的,这个根目录这个分区,在OS加载的时候,就要在我们内核中构建出来,我们系统就要最开始找到他,如果有多个分区的话,一个分区一点要包含一个跟目录,其中一个一定要有,方便OS直接加载,而同时系统当中,在启动的时候,我们根目录所在的目录里面一定要有若干个普通目录,比如有十个分区,那至少有十个普通目录在根目录下,比如:/a、/b、/c....。然后Linux系统如果真正把分区分好了,默认这个分区是无法被直接使用的!!!而分区必须经过“挂载”到目录上,分区才可以通过路径的方式进行访问。

模拟制作一个分区: 

指令:df -h。查看磁盘详细情况的。 /dev/vda2被挂载到根目录上,所以现在使用的是这个分区。

通常,df -h命令的输出包含以下几列:

 
  • Filesystem:表示文件系统的名称,通常是磁盘分区或挂载点对应的设备名称或文件系统类型,如/dev/sda1tmpfs等。
  • Size:表示文件系统的总大小,以人类可读的单位显示,如G(GB)、M(MB)、T(TB)等。
  • Used:表示已使用的磁盘空间大小。
  • Avail:表示可用的磁盘空间大小。
  • Use%:表示已使用空间占总空间的百分比。
  • Mounted on:表示文件系统的挂载点,即文件系统在系统目录结构中的挂载位置,如//home/var等。

指令:dd dd指令是 Unix 和类 Unix 系统中的一个非常强大且灵活的工具,主要用于复制和转换数据,是以二进制形式在磁盘形成一个文件。dd命令要少用,后面这个形成的这个临时文件不要太大,要不然磁盘很容易打满。

基本语法

dd指令的基本语法为:dd [选项] [输入文件] [输出文件]。其中,输入文件和输出文件可以是普通文件、设备文件(如磁盘分区、磁带设备等)或其他特殊文件。如果不指定输入文件,默认从标准输入读取数据;如果不指定输出文件,默认将数据输出到标准输出。

常用选项

  • if = 文件名:指定输入文件,例如if=/dev/sda1表示从sda1磁盘分区读取数据。
  • of = 文件名:指定输出文件,如of=/home/user/backup.img表示将数据输出到backup.img文件中。
  • bs = 字节数:设置块大小,即每次读写的数据块大小,单位为字节。例如bs=4096表示每次读写 4096 字节的数据。
  • count = 块数:指定要复制的块数。例如count=100表示只复制 100 个数据块。
  • seek = 块数:在输出文件中跳过指定数量的块后再开始写入数据。常用于向已存在的文件中特定位置写入数据。
  • conv = 选项:指定数据转换的方式,如conv=ucase将输入数据中的小写字母转换为大写字母;conv=sync将每个输入块填充到bs指定的大小,不足部分用空字符填充。

常见应用场景

  • 磁盘克隆:可用于创建磁盘的镜像文件或克隆磁盘,如dd if=/dev/sda of=/home/user/disk.img,将整个sda磁盘的内容复制到disk.img文件中,可用于备份磁盘数据或创建可用于恢复的镜像。
  • 创建启动盘:将 ISO 镜像文件写入到 USB 闪存驱动器等可移动存储设备,使其成为可引导的启动盘,如dd if=ubuntu.iso of=/dev/sdb bs=4M,将ubuntu.iso镜像文件写入到sdb设备(通常为 USB 闪存驱动器)。
  • 提取特定数据:从文件或设备中提取特定位置和长度的数据,如dd if=file.bin of=extracted.bin bs=1024 skip=10 count=5,从file.bin文件中跳过前 10 个块,提取 5 个块的数据到extracted.bin文件中。
  • 填充文件或设备:可以用特定的数据填充文件或设备,如创建一个指定大小的空文件用于测试,dd if=/dev/zero of=testfile.dat bs=1M count=100,创建一个 100MB 的空文件testfile.dat,其中/dev/zero是一个特殊文件,会不断提供空字符(\0)。

注意事项

  • dd指令操作非常强大,使用不当可能会导致数据丢失或系统损坏,尤其是在对磁盘设备等重要设备进行操作时,务必确认指令的参数正确无误。
  • 在对大文件或设备进行操作时,dd指令可能需要较长时间才能完成,可结合-status=progress选项查看复制进度。

实验步骤:

1.#制作⼀个⼤的磁盘块,就当做⼀个分区

2.# 格式化写⼊⽂件系统 

指令:mkfs。mkfs是 Unix 和类 Unix 系统中的一个命令,用于在磁盘分区或存储设备上创建文件系统。它是一个通用的文件系统创建工具,根据不同的文件系统类型,可以使用不同的选项来创建相应的文件系统。

基本语法

mkfs [选项] [-t <文件系统类型>] <设备>

选项解释

  • -t <文件系统类型>:指定要创建的文件系统类型,例如 ext2ext3ext4xfsbtrfs 等。如果不使用 -t 选项,默认使用 ext2 作为文件系统类型。
  • -V:显示 mkfs 命令的详细信息,包括将要执行的操作步骤和所使用的参数。
  • -c:在创建文件系统之前,对设备进行坏块检查。
  • -l <文件名>:从指定的文件中读取坏块列表。

3.# 建⽴空⽬录

创建虚拟盘后,还要把他挂载到系统的某个目录下,一般在mnt目录下,该目录是Linux给我们提供的一个临时挂载文件系统的目录。

 4.# 将分区挂载到指定的⽬录

指令:mount。mount是 Unix 和类 Unix 系统中一个常用的命令,用于将文件系统挂载到指定的挂载点上,使文件系统中的文件和目录能够被访问。以下是关于 mount 命令的详细信息:

基本语法:

mount [-t 类型] [-o 选项] 设备 挂载点

选项解释

  • -t 类型:指定要挂载的文件系统类型,例如 ext4xfsnfscifs 等。如果不指定文件系统类型,mount 会尝试自动检测。
  • -o 选项:指定挂载的选项,常见的选项包括:
    • ro:以只读模式挂载文件系统。
    • rw:以读写模式挂载文件系统(默认)。
    • remount:重新挂载已挂载的文件系统,可用于修改挂载选项,如将文件系统从只读模式重新挂载为读写模式。
    • noatime:挂载时不更新文件的访问时间,可提高性能。
    • user:允许普通用户挂载该文件系统。

为什么挂载后名字变了:

1.分析挂载命令

  1. 从图中的命令 sudo mount -t ext4./.disk.iso /mnt/mydev3/可以看出,这是在 Ubuntu 系统中尝试将当前目录下的 .disk.iso 文件(假设它是一个 ext4 类型的文件系统镜像)挂载到 /mnt/mydev3/ 目录

2.查看 df -h 输出

  1. 在执行 df -h 命令后,出现了 /dev/loope 挂载到 /mnt/mydev3 的情况。
  2. 在 Linux 中,当挂载 IS0 文件或其他虚拟文件系统时,通常会使用 1oop 设备。 1oop 设备是一种伪设备,它允许将文件视为块设备来进行挂载操作。
  3. 当你挂载一个 IS0 文件时,系统会自动分配一个1oop设备(如/dev/loope、/dev/1oop1 等)来代表这个文件,以便将其作为一个文件系统进行挂载 
 5.#使用分区

6.# 卸载分区

根目录无法被卸载,因为永远处在根目录中!!!

指令:umount。umount 是 Unix 和类 Unix 系统中用于卸载已挂载文件系统的命令。

基本语法:

umount [选项] <设备或挂载点>

选项解释:

  • -h 或 --help:显示帮助信息。
  • -V 或 --version:显示 umount 命令的版本信息。
  • -n:执行卸载操作,但不更新 /etc/mtab 文件。这在一些情况下可以避免文件系统正在使用时无法卸载的问题,但可能会导致 /etc/mtab 与实际挂载情况不一致,使用时需谨慎。
  • -r:如果无法卸载文件系统(例如文件系统正在使用),尝试以只读模式重新挂载。
  • -d:若文件系统不再使用,删除其对应的 stale 挂载点,主要用于自动挂载的文件系统。
  • -a:卸载所有在 /etc/fstab 中列出的文件系统。可以与 -t 选项结合使用,指定要卸载的文件系统类型。
  • -t fstypes:指定要卸载的文件系统类型,例如 umount -t ext4 会卸载所有 ext4 类型的文件系统。可以指定多个文件系统类型,用逗号分隔,如 -t ext4,xfs
  • --fake:模拟卸载操作,但不执行实际的卸载动作,可用于测试或调试。
  • --no-canonicalize:不进行路径规范化,使用原始输入的路径。

📌总结:

这个实验的目录,首先一个分区建好没有用,要有目录,把这个分区挂载在目录上才能使用。所以任何一分区,天然有了基本路径了!!!从根目录上开始的路径,比如/mnt/mydev3。

路径一部分是挂载点提供一部分是你自己使用mkdir提供的,所以路径不就产生了吗,而你的进程就会把路径记录下来。

📌所以路径从哪来?

从你把文件分区做好,自己把分区挂载到路径上,然后cd进入这个分区里,然后自己新建很多目录,包括Linux系统可以给我们默认新建一些路径,所以这时cd进去一个目录,这一串的字符串路径名字都有了。

📌所以回答最开始问题,你怎么确认你在哪个分区里面?
访问任何一个文件,都要有路径,路径的前缀不就可以指名在哪一个分区下面。我的前缀是 /目录 就找到是/dev/vda2分区下。

比如刚才的前缀/mnt/mydev3,那分区不就是刚才我们创建的分区吗。

所以我在哪个分区下不就瞬间清楚了!!!

所以他能找到我的文件,又能更深刻知道在哪个分区里面,还能根据在哪个分区下面,找到哪一个个块组下,最终就能找到inode、block这些,就全部知道了。 

📌那最开始不是要路径解析吗,他不是要根据从根目录下解析吗?

并不妨碍路径解析,因为你的所有的路径都必须从根目录下开始,如果有十个分区,分别叫作:adv1-adv10。一般adv1会挂载到根目录下,所以为什么能跨组到其他分区,因为都是从第一个分区跳转到其他分区的,因为第一个分区新建一个目录可以把其他分区挂载上,进入这个目录时,就进入到那个分区里面,所以在哪个分区下不妨碍路径解析!!!

回到最开始:task_struct中有个fs_struct这个结构体对象

进入里面看看:

里面有path,pwd。

所以为什么一个进程会记录下pwd?path不就是包括了dentry,dentry不就是一个树状结构体当中的某一个节点吗,pwd每次找的时候从根目录开始进行树形遍历,找到的路径不就是pwd吗,找到对应的dentry,绝对路径不就有了吗,每个进程的cwd就有了,这就是为什么我们在用户层,我们看一个进程的cwd或者pwd,我们就能看到路径,原因就在这!!!

path里面还有个mnt:

这是什么呢?这代表着你要查的路径挂载在哪个挂载点上。

 这就是文件系统的深入理解!!!!从硬件------->逻辑抽象!!!


http://www.kler.cn/a/472892.html

相关文章:

  • Leecode刷题C语言之字符串中最大的3位相同数字
  • 攻防世界 wtf.sh-150
  • CANN 学习——基于香橙派 KunpengPro(1)
  • 30天开发操作系统 第 12 天 -- 定时器
  • 基于MATLAB的汽车热管理模型构建
  • w~自动驾驶~合集16
  • 自动化执行 SQL 脚本解决方案
  • 十六、Vue 组件
  • 《深入浅出HTTPS​​​​​​​​​​​​​​​​​》读书笔记(26):数字签名
  • 【数据结构-堆】【二分】力扣3296. 移山所需的最少秒数
  • 牛客网刷题 ——C语言初阶(5操作符)——BC90 矩阵计算
  • 解决word桌面图标空白
  • UTTracker背景矫正模块详解:解决无人机追踪中的摄像头运动问题
  • Ruby语言的正则表达式
  • WebSocket 设计思路
  • 怎样用云手机进行海外社媒矩阵引流?
  • 【Linux】lnav - 适用于Linux和Unix的出色终端日志文件查看器
  • windows从0开始配置llamafactory微调chatglm3-6b
  • 使用vue-pdf预览pdf和解决pdf电子签章显示问题
  • 【中标喜讯分享】泰迪智能科技实力中标长春医学高等专科学校健康大数据管理与服务专业实训软件采购项目
  • 计算机网络——期末复习(7)期末试卷样例3
  • CSS语言的软件工程
  • STM32-DMA数据转运
  • react-quill 富文本组件编写和应用
  • 【合作原创】使用Termux搭建可以使用的生产力环境(九)
  • el-table设置单元格行高间距