磁盘与文件系统
2026/5/16 20:56:27 网站建设 项目流程

前面学的是从进程角度看待文件
操作系统不会区分你写的是字符还是二进制,调用的参数全是void*
实际上是语言,用户区分。
文件操作,使用语言操作,除非特殊情况,用系统调用
上面谈论的是文件被打开之后会发生什么

概括:用户层->分配fd
语言层->语言级缓冲区
操作系统->页面缓冲
磁盘->保存你这次的记录

文件:owner
group
other
时间戳(是否重新编译,看obj和.c文件的时间戳比较)
文件大小,物理位置
打开模式,r,w,a
下面讲文件未被打开

磁盘级文件系统

  • 理解硬件
  • 磁盘的物理结构
  • 如何把数据写入到扇区
    • 定位扇区:LBA
    • 实际情况:CHS寻址法
    • LBA vs CHS
    • CHS转成LBA:
    • LBA转成CHS:
  • 引入文件系统
    • 磁盘管理
    • 如何管理(inode Table)
      • inode属性和内容如何关联(储存数据:Data Blocks)
    • 判断Data Blocks数据块是否被使用(Block Bitmap位图)
    • 判断 Inode Table的块是否被使用(Inode Bitmap位图)
    • GDT(Group Descriptor Table)
    • 超级块(Super Block)
    • 文件的增删查改
  • 文件名
      • 问题1
      • 问题2
    • 重谈inode编号和块号的联系
      • inode和datablock映射(弱化)(数组储存数据时可以学习这种思想)
      • 问题3
    • 路径缓存
        • 问题1
        • 问题2
        • 问题3
        • 问题4
  • 总结

磁盘级文件的保存,由磁盘级文件系统决定

理解硬件

机械磁盘是计算机中唯⼀的⼀个机械设备
• 磁盘— 外设
• 慢(冯诺依曼)
• 容量大,价格便宜

这是一个机械硬盘,内部的结构是

在大型企业中,我们经常看到这个服务器机箱,这些格子里面储存到就是机械硬盘

这个就是机柜,里面可以防止多个服务器机箱

后面有多根线,是网络跳线,用于传输网络数据

我们想想看,计算机当中的01到底是什么,其实是把磁铁中的南极定义0,北极定义成1,当你存入数据的时候,磁盘中会有弱电,由电磁感应效应,产生磁场,翻转南北级,就实现了改变01,所以就能储存信息

所以彻底删除数据:削磁,或者把磁铁全部置为0或1(削磁高温即可)

磁盘的物理结构


磁盘的理论图是这样的,看起来你光滑的,但实际上是一圈圈的,这一圈圈的叫做磁道,磁道也不是连续的,是有扇区和间隙,看图b,有多个磁头,所有磁头共进退,所有盘片共旋转

这里这个红色的部分,就是对应的扇区,扇区:是磁盘存储数据的基本单位,512字节,块设备(现在主流的有4k的)
基本单位:读或者写磁盘的时候,必须是512为单位的,所以想从内存级缓冲区刷新到磁盘中,必须要够512字节才可以

柱面:看到那个类似圆柱的形状了嘛,那个就是

细节:离圆心越远,扇区的r起来越小,是因为满足都是512字节

如何把数据写入到扇区

如何找到对应位置的扇区
1.
2.
a.先确定在哪一面->磁头编号
b.在当前面中,在哪一个磁道中?
c.确认在当前磁道中,在哪一个扇区

文件=内容+属性=数据(两者都是数据,无非就是在扇区中储存)

定位扇区:LBA


想象一下,把上面那个光滑的盘面任意切一刀,再拉直,这样每一个扇区,就有了一个线性地址(其实就是数组下标),这种地址叫做LBA

实际情况:CHS寻址法

磁盘在进行访问的时候,先确定我们要访问哪一个柱面
然后在通过磁盘旋转找到对应扇区
最后找到扇区之后,还有多个面,确定是哪个面——>磁头编号
磁盘是由柱面构成的
帮助理解:
磁头摆动的意义:本质是访问哪一个磁道或者柱面
盘片旋转的意义:为了让磁头定位到扇区
读取一个扇区:选定哪一个磁头即可

上面有三个变量,能不能把他定义成三维数组呢?
可以的,柱面的变量定义为第一维度,磁头定义为第二维度,扇区定义为第三维度;
所以磁盘的本质,就是一个三维数组,在磁盘中定位扇区,又可以有新的理解了
1.柱面(第一维,cylinder,相当于磁道)
2.磁头(第二维,选择哪一面,head)
3.扇区(第三维,sector)
这就是CHS寻址法,本质就是数组下标
OS只需要使用LBA就可以了

LBA vs CHS

定义:每磁道扇区数(每个磁道所有的扇区数量的总和)
柱面号C (他是在哪个柱面,不是柱面总数,半径)
扇区号S (这个扇区的数组下标,CHS中是不存在下标0的)
“//”:表示除取整
磁头数:有多少个盘面
磁头号H (在哪一层)
柱面号,磁头号,LBA都是从0开始的
磁头数 *每磁道扇区数=单个柱面的扇区总数(一个盘面有多少个扇区)
总扇区/单个柱面的扇区总数=磁道数

CHS转成LBA:

磁头数每磁道扇区数=单个柱面的扇区总数
LBA =柱面号C * 单个柱面的扇区总数(他所在的这个面,(不算他所在的磁道),有多少个扇区)+磁头号H
每磁道扇区数(在他上面的面,有多少个磁道)+扇区号S -1

LBA转成CHS:

本质:一维数组下标转化为三维
柱面号C =LBA //(磁头数* 每磁道扇区数)
磁头号H =(LBA %(磁头数* 每磁道扇区数))(走过来多少个扇区)//每磁道扇区数
扇区号S =(LBA %每磁道扇区数)+1(不存在下标0)

先看LBA转CHS的第一条公式:
LBA除以一个柱面的扇区数,商:得到他是在第几磁道,余数:还剩下多少个扇区
我只要商,就是得到在第几磁道,也叫做柱面号

再看第二条公式
LBA取模一个柱面的扇区数,得到的是还剩下多少个扇区,而这些扇区是应该分配在这个柱面上的,每一个面应该分配的就是每磁道扇区数,所以这两个整除取商不就是应该在第几个面嘛,第几个面就是磁头号呀;

第三条公式:
LBA除以每磁道扇区数,就是看他可以填满多少个磁道,取余就是剩下的还不能填满这个磁道,我要看他具体能填到哪里,加一是因为CHS不存在下标0,所以要加1;

好,现在做一道题目了解了解

好,我现在要插入到粉色的第二个,他的LBA应该是15(蓝色是0到15),+2(粉色两格)
得到LBA=17;
先求柱面C:
磁头数=2(盘面数量是2)* 2(一个盘面有两边)=4;
每磁道扇区数=4;
C=17//(4*4)=1;第二个格子确实是在柱面1;正确;
再求磁头号:
(17% ( 4 * 4))=1;
1//4=0;说明他在第一面,注意,有很多人看图就会说,这不应该是在第三面嘛?注意,最下面的是柱面,蓝色的0-3是分配到第一面的第一磁道的,蓝色的4-7是分配到第二面的第一磁道的,同理,粉色的0-3是分配到第2磁道的第一面的。正确
第三:17%4=1;1+1=2;正确;
这个工作是磁盘自己做的,,磁盘就是一个元素为扇区的一维数组,数组的下标就是每一个扇区的LBA地址。OS使用磁盘,就可以用一个数字访问磁盘扇区了。
所以如何写入数据呢?

引入文件系统

硬盘的每个分区是被划分为一个个的”块”。一个”块”的大小是由格式化的时候确定的,并且不可以更改,最常见的是4KB,即连续八个扇区组成一个”块”。”块”是文件存取的最小单位。

为什么操作系统不以扇区为单位呢?
1.提高效率
2.软硬件解藕(不想磁盘和操作系统有关联)

磁盘管理

假设你有一个800G的磁盘,他要对这内存800G的进行管理,是先进行分区,再进行分组管理,

结论1:文件=内容+属性;->linux系统中,文件的内容和属性分开储存的
结论2:一个组里面,包含了文件数据和管理文件数据的信息(都是数据)
写入管理信息的过程,叫做写入文件系统,也叫做格式化,是在分区后的,如果是按照上面那种分组,是在分组前的,只有先格式化才能分得清组1,组2,组3;
注:如何确定你在哪一个分区呢?格式化,本质是写入管理信息,写入完之后无法直接写入信息的,必须把你指定的分区或者文件系统挂载到指定目录下才可以写入信息,你的文件,要访问是要有路径的,可以通过路径判断是否在哪个分区。采用最长前缀匹配。

如果是按照底层物理实现(如以8扇区为1Block单位),则格式化与8扇区一块是同步的,格式化这一动作本质上就是在执行物理空间划分和分组,两者同步发生的。

如何管理(inode Table)


文件管理信息放在图片中的前4个
Data Blocks就是物理底层,以8扇区为1块,这个Data Blocks储存着所有数据,每一个Data Blocks都有属于他的编号
文件的属性储存在一个结构体struct inode(这个是写在inode Table里面的)
{
//文件的所有属性,就是指令ll
//固定128字节,基本所有的属性都是int类型的
int inode;
}
文件=inode+Data Blocks
文件名不属于文件inode的属性,理论上,一个文件,一个inode,整个分组可能会存在很多文件,每个文件都会存在int inode(编号)
inode table也是由4kb的块组成的
一个文件128字节,4096/128,所以在一个inode表中,可以储存32个文件的属性,这32个文件的inode的值是不一样的。

inode属性和内容如何关联(储存数据:Data Blocks)

在inode属性中,存在一个变量,会记录下来用到Data Blocks中用到哪几个数据块
__le32 i_block[EXT2 _N_BLOCKS];/* Pointers to blocks */
在一个分组里面,找一个文件,根据文件的inode number,找到对应的inode结构体,再通过结构体里面的上面的成员,找到对应的Data Blocks

判断Data Blocks数据块是否被使用(Block Bitmap位图)

Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用

4096* 8=32768,也就是一个块可以管理32768个Data Block是否被使用

判断 Inode Table的块是否被使用(Inode Bitmap位图)

每个bit表示一个inode是否空闲可用。
在linux中如何删除文件?把Inode Bitmap置0把Block Bitmap置0
在linux中如何创建文件?申请inode属性,把Inode Bitmap第一个为0的属性置为1,初始化Inode Tablel,分配Block Bitmap

GDT(Group Descriptor Table)

前面4个储存的是组内部的,一个组的信息是储存在GDT里面的,也叫块组描述符表,描述块组属性信息。每个块组描述符存储一个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有一份拷贝。

超级块(Super Block)

刚才讨论的都是以组为单位的,但我们知道,一个区是有多个组的,那这个区的信息储存在哪里呢?没错,就是储存在Super Block里面的,他存放文件系统本身的结构信息,描述整个分区的文件系统信息。记录的信息主要有:bolck 和inode 的总量,未使用的block和inode 的数量,一个block和inode 的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了

理论上一个分区只需要一个super Block就好了,为什么看起来每个组的都有super block呢?
不是每一个组都有super Block的,而是有2,3个组有super block点,这是为了防止物理坏道、突然断电或误操作而损坏,相当于备份的做作用了

文件的增删查改

在磁盘的某一个组里面如何创建一个文件?
touch 创建进程,调用系统函数来创建文件,内存中有临时的inode属性(这个是在内存级缓冲区),然后操作系统在组里面查找inode Bitmap,找第一个为0的(位置),然后在对应下标的inode table放入属性(这里是磁盘中的内存),后面如果写入数据,在block Bitamap查找第一个0的,对应下标的Data Block的数据块放入元素

在磁盘的某一个组里面如何删除一个文件?
需要先知道inode编号,在你这个分组里面,在inode bitmap查看对应的位置是否是1(文件是否存在),然后去inode Table通过下标找到对应的inode结构体,包含了所有的属性,把inode对应查找Data block下标的数组里面的编号拿出来,再把Block Bitmap对应的位置置0。

在磁盘的某一个组里面如何修改一个文件?
把磁盘中文件对应的属性加载到内存级缓冲区中,在内存级缓冲区中修改,然后再加载到磁盘中,无法直接在磁盘中直接修改哦,修改属性同理,通过inode然后找到对应的inode table中的inode结构体,再找到对应的数据块,然后文件会加载到内存级缓冲区中,你再通过fd找到进行修改

在磁盘的某一个组里面如何查看一个文件?
文件=内容+属性,必须要先知道inode,在inode bitmap查看对应的位置是否是1(文件是否存在),然后去inode Table通过下标找到对应的inode结构体,包含了所有的属性,再从中找到你想要的属性

文件名

我们在linux中操作,一直用的是文件名,但是文件名却不在inode结构体内部保存,那操作系统是如何找到对应的inode编码呢?
这里我们就要提到目录了,如何理解目录呢?
在linux下一切皆文件,目录当然也是文件,文件=内容+属性,属性里面和普通文件没区别,但是内容储存什么呢?他的内容是当前目录所包含的文件名和inode编号的映射表,这两者都是数据,可以保存在同一个数据块当中,当你输入了文件名的时候,就在对应的数据块里面找到对应的inode编号

问题1

在磁盘和文件系统的角度,储存目录和储存普通文件有区别吗?
没有区别,只是数据块里储存的内容不同(理解linux下一切皆文件)

问题2

目录的r w x权限是如何实现的
目录内容里保存的是当前目录下的文件名和inode number的映射关系,当没有r权限,你又想往这个文件里面写入的时候,先通过文件名找到对应的inode编码,然后在通过inode找到inode结构体,发现没有r权限,返回。所以同一个目录下,文件名不能重复。
说到这里,可能就会和页表记混了
前者的对象是磁盘上的文件,后者如下图,对象是进程里面的变量的权限。

重谈inode编号和块号的联系

首先,inode编号和块号:不是只有组内有效,而是整个分区有效;但是不能跨分区。
其次,在一个分区内部,一个文件系统内部,有多少个inode,有多少个数据块,都是固定的,都是提前设计好的。

举例:通常情况下,每 16KB 的磁盘空间,会分配 1 个 inode。
假定一个分区,有16G;
16GB=16×1024×1024×16KB
系统会计算出需要分配大约 1,048,576 (约 100 万) 个 inode。
这意味着,在这个 16GB 的分区里,你最多只能创建 100 万个文件,哪怕这些文件全是空文件(0字节),只要 inode 用完了,你就再也无法创建新文件了。
当然,不止文件创建数量有上限,文件的大小也是有上限的,如果本组的 Data Blocks 满了,文件的数据块可能会被分配到其他组的 Data Blocks 区域。所以:单个文件的上限 = Min(文件系统理论上限, 分区实际剩余空间)。

每个分区的inode都是从1开始计算的,每个分区的分组的inode是连续的
例如:在第一分区中,组1的inode的编号可能是1-10000,组2的inode编号可能是10001-20001,想确定在哪个组,可以把inode//10000,就得到他在哪个分组,
当然,inode分配是趋向连续的,他的分配规则是和fd相同的。

inode和datablock映射(弱化)(数组储存数据时可以学习这种思想)

• inode 内部存在
le32 i_block [EXT2 _N_BLOCKS];/* Pointers to blocks */,
EXT2 _N_BLOCKS =15,就是用来进行inode和block映射的
• 这样文件=内容+属性,就都能找到了,但是看数组只有15大小的空间,可能有人就认为怎么储存得下大文件呢?

这个inode结点下储存对应数据块的数组前12个是储存着直接下标,可以直接通过地址访问到对应的数据块,而13,14,15是分别储存着一级指针,二级指针,三级指针,通过多次解引用,可以扩大实际储存的数据块数量,从而实现了数组只要15个大小就可以储存全部都数据块。

所以块内不止储存文件自己的数据,也可以储存自己文件用到的更多的块号

问题3

我如何找到当前目录的inode?
他只有当前目录下文件名和对应的inode,可没有当前目录的inode;
举例:

假设你要往test里面写入代码,首先你要先知道code的inode,这样才能访问到test的inode;递归,你要想知道code的inode,你要先知道lesson30的inode,到最后,你要知道根目录/的inode,这算是递归出口。
所以linux的根目录是直接的,固定的,直接能让我们访问到的
当我们要访问任何文件的时候,linux内核都要为我们左从/(根目录)开始的路径解析。当然,每次都这样子从头到尾找太慢了,linux对用户访问过的路径是会做路径缓存的。linux要对你访问过的路径进行管理,要先描述,再组织。
如果缓存中没有储存对应的strcut dentry结构体, 目录被打开都会创建自己的struct结构体

路径缓存

Linux中,在内核中维护树状路径结构的内核结构体叫做:struct dentry
当然,这是多叉树,用的兄弟结点法

struct dentry
{
//这里会有一个指针,指向自己的inode结构体
}
如果你这里要用到code和test,就会创建对应的多叉树,把这些结点插入进去;这个多插树会在内存里面常驻

当然,前面学过linux目录结构,前者是后者的子集,

问题1

这颗多叉树会动态变化嘛?会动态变化,在内存中有进有出,因为会储存一个最近用得最少的结点。

问题2

只有目录才会有dentry嘛?不是,普通文件也有dentry,只是是目录的叶子结点,每一个被访问的结点都要有dentry,包括普通文件
例如我要访问/usr/src;
我一开始什么都没有,我就会访问/usr,再找到src,此刻这个多叉树里面就有usr了,下次我想找/usr/local,我就可以遍历这个多叉树,发现树里面有usr,可以直接从usr下面开始找了,就不用从根目录开始找了

问题3

搜索的时候,第一次可能卡顿,第二次就快了,因为路上的路径已经被缓存了。

所以,访问文件一定要有路径

问题4

相对路径呢?
确认当前路径是否在多叉树里面,如果在就以当前路径开始找,如果当前路径不在,从进程pcb识别当前路径,从根目录开始找当前路径。

总结

int fd=open(“log.txt”,xxxx);
我们打开这个文件,我们需要对这个文件进行路径解析与缓存,创建对应的inode结构体和数据块。在内核创建对应的struct file,然后在这个struct file结构体里面,有个成员,是strcut* dentry,指向自己所对应的路径缓存,指向自己目录树中的缓存结点,而在dentry中找到inode,就能找到磁盘中对应的数据和属性。

访问任何文件,都要做路径解析和路径缓存的,直到解析到这个文件。
可是路径是谁提供的?
• 你访问文件,都是指令/工具访问,本质是进程访问,进程有CWD!进程提供路径。
• 你open文件,提供了路径
可是最开始的路径从哪里来?
• 所以Linux为什么要有根目录,根目录下为什么要有那么多缺省目录?
• 你为什么要有家目录,你自己可以新建目录?
• 上面所有行为:本质就是在磁盘文件系统中,新建目录文件。而你新建的任何文件,都在你或者系统指定的目录下新建,这不就是天然就有路径了嘛!
• 系统+用户共同构建Linux路径结构.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询