本期我们从硬件底层来详细讲解文件系统:
目录
说起磁盘,我想各位小伙伴并不陌生。虽然现在我们电脑存储设备都用上了硬盘,但是架不住磁盘价格便宜,现在很多公司还在拿磁盘作为它们的存储设备。
所以下面我们来谈谈磁盘:
对于磁盘的基础知识大家可以看这里:磁盘基础知识-磁盘的构成_磁盘组成-CSDN博客
我们如果想确定磁盘上的一个物理空间的地址(磁盘是以扇区为存储的基本单位),首先需要找到该空间对应的磁头(head),通过磁头再找到盘面所对应的柱面(Cylinder),最后定位到磁道上所对应的扇区(sertor)
上面这种物理寻址的方法被称为CHS,用柱面号(即磁道号)、磁头号(即盘面号)和扇区号来表示一个特定扇区:

我们可以想象一下将磁盘中的磁道剪一个小口,将其像磁带一样拉出,成为一条线性的地址空间(我们以一个扇区为512字节大小来举例):

这样子我们就可以将块状线性的地址空间看成一个数组了,每一个扇区都有自己对应的元素下标,当操作系统想访问磁盘空间时,只需要给出扇区对应的下标即可
这样的地址就可以被操作系统抽象为一个个的块空间(Linux操作系统与外设的交互的基本单位为4KB):

我们可以看到操作系统中一个块空间对应磁盘中八个扇区,操作系统中就将磁盘中八个扇区作为一个单位来进行数据的存储
所以操作系统想要访问数据时,只需要知道存储数据的块空间对应的首个扇区地址+4KB数据的偏移量即可
最终块地址就是对应首地址的扇区的元素下标,这样子我们就可以用一个元素下标N来定位任何一个块了
这种定位方式被叫做逻辑块地址(LBA)
但是我们的磁盘只认识CHS类型的地址,对于操作系统给出的LBA类型的地址我们将其转换成CHS地址,再交给磁盘。
虽然LBA和CHS的两种定位方式不同,但其实两者间还是有一个转换关系的:
用C表示当前柱面号,H表示当前磁头号,S表示当前扇区号,CS表示起始柱面号,HS表示起始磁头号,SS表示起始扇区号,PS表示每磁道有多少个扇区,PH表示每柱面有多少个磁道,计算公式如下:
LBA = ( C – CS ) * PH * PS + ( H – HS ) * PS + ( S – SS )
变量命名依然同上,计算公式如下:
C = LBA DIV ( PH * PS ) + CS
H = ( LBA DIV PS ) MOD PH + HS
S = LBA MOD PS + SS
如果不运行MOD运算符,只用DIV运算符,则:
C = LBA DIV ( PH * PS ) + CS
H = LBA DIV PS – ( C – CS ) * PH + HS
S = LBA – ( C – CS ) * PH * PS – H – HS)* PS + SS
● OS是软件,磁盘是硬件,硬件使用CHS定位一个地址,但是如果OS直接用了这个地址,万一硬件变了呢?
那OS也要发生变化,整个OS要从底层进行更新是十分复杂的,所以OS要和硬件做好解耦工作就必须有一套自己的定位方式!
● 即便是扇区,一般只有512字节,IO的基本数据量也是很小的所以,OS需要有一套新的地址,来进行块级别的访问。
下面我们来步步深入到Linux下的文件管理方式:
对于上面的块空间,OS要想对其整体进行管理是十分困难的,所以会将整个块形成的空间分区再分组划分为一个个区域来进行管理:

其中最小的管理单位为块单位形成的块组
(下面是Linux ext2文件系统的块组,为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设 定block大小为1024、2048或4096字节。而下图中启动块(Boot Block)的大小是确定的):

Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成
其中有Block Super/Group Descriptor Table/Block Bitmap/inode Bitmap/inode Table/Data blocks这属性模块:
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck和inode的总量, 未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。Super Block可能在每个Block Group都存在,其是统一更新的(这是为了防止Super Block被破坏而做的备份机制)
GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):Block Bitmap中记录着Data Blocks中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用
inode节点表(inode Table):每个inode Table都有很多个inode节点,每个inode节点有自己的编号并且对应唯一的文件,用来存放文件属性(一般是128字节),如文件大小,所有者,最近修改时间等
数据区(Data blocks):存放文件内容
光看概念怎么行,下面我们来实操看看:
我们可以在ls指令后面加上-i选项来查看文件对应的inode的编号:

Linux系统只认inode的编号,但文件的inode属性中,并不存在文件名,文件名,是给用户用的
● 目录是文件,这一点毋庸置疑。那文件一定有inode,目录也不例外,我们从上面的ls指令的操作也能看到。
● 那目录中保存的内容是什么?任何一个文件,一定在一个目录内部,所以目录的内容是什么?
目录的数据区(Data blocks)里面保存的是该目录下文件名和文件inode编号对应的映射关系。而且,在目录内文件名和inode互为key值
● 当我们访问一个文件的时候,我们是在特定目录下访问的,我们拿指令cat log.txt来举例:
我们先要在当前目录下,找到log.txt的inode编号
一个目录也是一个文件,也一定隶属于一个分区,结合inode,在该分区中找到分组,在该分组中inode table中,找到文件的inode,通过inode和对应的data block的映射关系,找到该文件的数据块,并加载到OS,并完成显示到显示器!
● 当我们要删除一个文件时:先根据文件名找到对应的inode编号;再根据inode编号找到inode属性中的映射关系,设置block bitmap对应的比特位,置0即可;最后inode设置inode bitmap对应的比特位设置为0
所以删除文件,只需要修改block bitmap和inode bitmap位图即可!
● 当我们要增添一个文件时:先在inode bitmap中查找没有被使用的比特位,将其置为1后,再去inode Table中创建对应的inode节点;接着由系统计算出要写入数据内容的大小,去修改Block Bitmap,最后开辟Data blocks的空间将数据拷贝进去
所以我们现在可以理解为什么删除文件总是比拷贝文件快了
● 如果文件被误删了,我们该怎么办?
文件被删修改的只是block bitmap和inode bitmap位图,inode节点和Data blocks的空间数据并没有被修改(除非做其他文件操作对原始数据进行了覆盖),所以我们可以通过文件恢复工具来恢复被删除的文件
● inode的编号只在一个分区内唯一有效,不能跨分区
● 分区完成之后,后面要让分区能够被正常使用,我们需要对分区做格式化,格式化的过程,其实是OS向分区写文件系统的管理属性信息(重置block bitmap和inode bitmap位图),所以格式化并不能删除磁盘原有的数据,格式化后的分区依然可以恢复。若想彻底删除磁盘中的数据,需要使用物理手段对磁盘进行磁化。
● 如果inode只是单单的用数组建立和data block的映射关系,是不是意味着一个文件内容最多放入的数据受数组元素个数的约束?
并不是,我们可以对大数据文件建立索引(像mysql数据库一样),建立二级或者三级索引来扩充其容量
● 有没有可能,一个分组,data block没用完,inode没了,或者inode没用完,data block用完了?
这是有可能的,因为我们并不知道用户会对磁盘怎么进行使用,这是不可预测的,目前还没有办法可以很好的解决这个问题
下期会详细讲解软硬链接,不见不散~