一、 磁盘
1、磁盘的物理结构
磁盘的是计算机中唯一一个机械设备,同时它还是一个外设,其图片如下:
其主要的核心物理结构有三个:
- 磁盘片:磁盘片是硬盘中承载数据存储的介制,磁盘是由多个盘片叠加在一起,互相之间由垫圈隔开的。
- 盘面:一片磁盘片是由两面的,每一面被称为盘面,磁盘片的两面都可以存储数据的。每一个盘面都有对应的磁头,也就是说一个磁盘片有两个磁头的。
- 磁头:磁头是向磁盘读取数据的媒介,其通过磁性原理读取磁性介质上数据。所以磁头不与盘面接触,磁头悬浮在盘面上面或下面。其中还有重要的一点是:磁头是共进退的!
2、磁盘的物理存储结构
在磁盘的盘面上,磁盘被一个个的同心圆以及射线进行分割,从而出现了:磁道, 扇面,扇区
- 扇区: 被一个个的同心圆以及射线进行分割出的一个个扇形区域。
- 扇面: 两条相邻的射线之间夹的所有扇区构成扇面。
- 磁道:盘面上半径相同的扇区构成一个磁道。
- 柱面:由于现实世界中磁盘的立体结构,所以把空间中所有半径相同的磁道定义为一个柱面
其中扇区是存储的基本单元, 每个扇区其大小为:512 byte
或4kb
,一般来说都是512 byte
(下面我们讨论时也是以512 byte
为准)
由于扇区是最小的存储单元,所以在硬件的角度:一个文件(内容 + 属性)无非是占用一个或多个扇区进行数据的存储。
那么在硬件上磁盘是怎么定位一个扇区的呢? 答案是CHS
定位法! cylinder
柱面 head
磁头 sector
扇区
- 磁盘中的磁头是有编号的,我们首先根据扇区所在的盘面先确定使用几号磁头。
- 每个扇区都有自己所在的磁道,根据扇区所在的磁道就可以确定磁头的偏移位置。
- 每一个扇区在所在的扇面上都已经被编好了号码,磁头最后根据扇面所在的号码确定扇区。
我们既然能够通过CHS
定位一个扇区,那么也能定位多个扇区,从而将文件能够从硬件的角度进行读取和写入!
二、磁盘的逻辑抽象
有了上面的知识我们知道能够通过CHS
去定位一个文件的基本单元,但是操作系统是不是采用这种方式去定位磁盘中的数据呢?答案是不是!
这主要有以下两点原因:
- 操作系统是软件,磁盘是硬件,硬件通过
CHS
定位扇区,操作系统如果采用和硬件一样的定位方式就会和硬件之间产生很大的耦合关系,如果我们的硬件变了(例如:机械磁盘变为固态硬盘),那么操作系统也要进行变化,这并不是一个好的情况。 - 扇区的大小是
512 byte
,以扇区为单位进行IO时的数据量太小了,在进行大量IO时这会极大的影响到运行的速度。
操作系统实际进行IO时,是以
4kb
为单位的(这个大小是可以调整的)4kb = 512 * 8 byte
。
因此将8个扇区定义为一个块,操作系统按照一个块的单位进行IO。
磁盘片的物理结构是一个圆环型结构,假设我们能够将每一个盘面按照磁道进行拉伸展开(就像使用胶布一样),那不就变成了一个线性结构了吗?
展开以后对于每一个磁道里面都有许多扇区,这些扇区组合起来便可以被抽象为一个数组!
但是这个数组太大了,而且每一个单位的数据量也有点太小了,我们还要对其进行抽象,我们将8个扇区组成一个块,这样数组的长度就缩短了8倍,经过这一层抽象后,由原来的一个扇区数组变为块数组。
其中逻辑块的数组下标被定义为逻辑块地址 (LBA地址) ,当我们想要访问数据时通过LBA地址可以找到对应的起始扇区的地址,然后向后偏移7个扇区地址便可以取出一个逻辑块的内容,这个操作的原理类似于指针的解引用。
于是操作系统通过LBA地址进行访问存储的数据,这就是操作系统对磁盘等存储硬件的逻辑抽象!
LBA地址与CHS地址的转换
但是我们还有一个重要的问题就是外部的磁盘只认识CHS
地址,操作系统只认识LBA
地址,所以这两个地址之间还要存在相互的转换关系!
当然这个转换关系也并不是特别难,也是借助于逻辑抽象进行一些简单的基础运算,在这里就不进行讨论了,有兴趣的朋友话可以自己研究学习一下。
三、文件系统
经过上面的抽象我们操作系统便拿到了一个逻辑块的大数组,但是这个数组太大了,我们对这个大数组直接管理还是太过于困难了,于是我们操作系统就可以对这个大数组进行分区管理(类似于windows的分盘),当我们管理好了一个分区就可以将一个分区的管理方法复制到其他的分区中,从而实现了全局的数据管理。
但是每个分区的数据还是太大了,操作系统还要对每一个区进行分组,通过分组再次降低管理的难度,其中每个分区其内部的结构如下图:
- Boot Block: 里面存放的是与操作系统启动相关的内容,诸如:分区表,操作系统镜像的地址等,一般这个区域被放在磁盘的
0
号磁头,0
号磁道,1
号扇区中,如果这个区域的内容受到破坏,那么操作系统将无法启动! - 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。
- GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
- 块位图(Block Bitmap):Block Bitmap中记录着Data Blocks中哪个数据块已经被占用,哪个数据块没有被占用。
- inode位图(inode Bitmap):每个bit位表示对应的inode是否空闲可用。
- i节点表(inode Table):一般来说,一个文件内部的所有属性被保存在一个
inode
节点中(一个inode节点的大小一般是128字节),一个分区会存在大量的文件,所以一个分区中会有大量的inode节点,每一个inode节点都有自己的编号,这个编号也属于文件属性。为了更好的管理inode节点,就要有一个专门的区域存放所有的inode节点,这个区域就是inode Table
,其内部可以看成一个数组结构。 - 数据区(Date blocks):里面是大量的数据块,每一个数据块都可以用来存放文件内容。
细节要点:
- 每一个块组都有
Block Bitmap
inode Bitmap
inode Table
Date blocks
,其他的部分每个块组里面可能没有。 Super Block
在每一个块组里都可能存在,也可能不存在但至少要有一个块组存在超级块!而且每一个存在超级块的块组里面的超级块是一样的,并且所有存在超级块的块组其里面的超级块是联动更新的。
超级块存在多份的意义是:万一其中一个超级块损坏,还有其他超级快可以使用,并且可以利用其他完好的超级块去修复已损坏的超级块。不至于一个超级块损坏导致整个分区的文件系统直接损坏。inode
节点中有一个数组,这个数组里面存放了对应文件使用的数据块的编号。inode
编号不能跨分区使用,每一个inode
编号在一个分区内唯一有效!- 根据
inode
可以确定一个分区内的分组
重新认识目录
在Linux
的命令行中,我们可以使用ls -il
命令可以看到文件的inode
编号:
其实在Linux
中系统对于文件只认识inode
编号,并不认识文件名,那你可能会很好奇:我们平时一直使用的都是文件名,没有使用过inode
编号为什么我们还能够操作文件呢?
这其实和目录有关!我们的打开任意一个文件都是在一个目录里面打开的,而且目录本身也是文件,目录也有inode编号!其里面也有内容,也需要数据块,其里面的内容是:该目录下文件名与该文件的inode
映射关系。
因此当我们使用文件名时,目录会自动帮我们找到对应的inode
编号,完成相应的要求。
例如我们在Linux
下使用cat log.txt
命令,其大致的执行过程是:
- 在目录下找到
log.txt
的inode
编号。 - 利用
inode
编号在inode Table
中找到inode
。 - 根据
inode
找到log.txt
使用的数据块的编号。 - 根据数据块的编号找到数据块,将内容提取出来并刷新到显示器上面。
理解文件系统中的增删查改
查:见上面的cat log,txt
的例子。
删 :
- 根据当前要删除的文件名到目录中找到对应的
inode
编号。 - 根据
inode
编号到inode Table
中找到inode
节点。 - 根据
inode
节点中的内容找到该文件对应的Block Bitmap
,然后将相应的bit
进行置0
表示内容的删除。 - 根据
inode
编号将inode bitmap
对应的bit
进行置0
表示属性的删除。 - 将当前目录中
inode
编号与文件名的映射关系进行删除。
(这就是为什么平时我们删除文件时比较快,而写入文件比价慢的原因)
增:(创建一个内容为空的文件)
- 操作系统在
inode bitmap
中从低向高依次扫描,将找到的第一个bit
为0
的位置置成1
。 - 然后在
inode Table
中的对应位置写入新的属性。 - 然后向当前目录中增加新的
inode
编号与文件名的映射关系。
改:
- 根据当前的文件名到目录中找到对应的
inode
编号。 - 根据
inode
编号到inode Table
中找到inode
节点。 - 计算需要的数据块的个数,在
Block bitmap
中找到未使用的数据块,并将相应的bit
由0
置成1
。 - 将分配给文件的数据块的编号填入
inode
中。 - 将数据写入到数据块中。
补充细节:
- 如果文件被误删了,该怎么办?数据应该怎么被恢复?(这里我们只讨论大致的原理)
答案是:最好什么都不做,因为Block bitmap
被置为0
以后,相应的数据块已经不受保护了,此时再创建新文件就有可能覆盖原来的文件。
数据恢复的原理是:Linux
系统为我们提供了一个日志,这个日志里面的数据会根据时间定期刷新,所有被删除的文件的inode
编号在这里都有记录,通过被删除文件的inode
编号,先把inode bitmap
相应的位置的bit
由0
重新置成1
,然后根据inode
编号到inode Table
中找到对应的数据块编号,然后到Block bitmap
中将相应位置的bit
由0
置1
。 - 上面我们说的分区,分组,填写系统属性是谁在做,什么时候做的呢?
答案是:是操作系统在做!是在格式化的时候做的!在我们操作系统分区完成以后,为了能让分区能够正常使用,需要对分区进行格式化,格式化的本质就是:操作系统向分区内写入文件系统管理属性的信息! - inode里面只是用数组来与数据块进行单纯的一 一映射吗?
答案是:并不是的,如果一个inode
里面存放数据块的编号的数组大小是15,如果只是单纯的一 一映射 ,那么一个文件只能存储15 * 4KB = 60 KB
的内容。
这显然是不合理的!
所以inode
里面存放数据块的编号的数组被规定它的前几个下标是直接索引,中间几个是二级索引,后面几个的三级索引, …
直接索引:直接指向数据块。
二级索引:指向一个数据块,这个数据块里面的内容是直接索引。
三级索引:指向一个数据块,这个数据块里面的内容是二级索引。
二级索引对应的数据量单位:4KB / 4 * 4KB = 4 MB
。
三级索引对应的数据单位:(4KB / 4 )^2 * 4KB = 4G
。
- 有没有一种可能一个分组,数据块没用完,inode没了,或者inode没用完,数据块用完了?
答案是:有可能的,如果我们一直创建空文件,就可能导致inode
使用完毕,而数据块没有使用完,如果我们的所有内容都放在一个文件中,就可能导致inode
没有使用完,而数据块使用完了。
四、理解软硬连接
软链接
当我们有一个文件在一个很深的目录时,我们是不方便去使用这个文件的,那有没有一种方式能够让我们能够轻松的找到并使用这个文件呢,有的,那便是软链接。软连接非常类似于windows
中的快捷方式。
我们可以在当前的link目录里面建立一个软连接,来让我们能够更加方便的去使用my.txt文件。
ln -s 文件名 软链接名
我们使用软连接访问文件与直接访问文件并没有任何差别!
我们在来看一看软连接与原文件的inode
编号
我们可以发现它们并不相同,这说明它们并不是同一个文件!
软连接的本质: 软连接是一个独立的文件,也有自己的inode编号与inode节点,软连接的内容就是自己所指向的文件的路径
硬链接
硬链接的建立方式:
ln 文件名 硬连接名
当我们进行创捷一个文件时,在文件权限后面会有一个数字,这个数字就是硬连接数。
我们先给log.txt文件创建一个硬链接
硬链接数增加了1,我们能够理解,但是为什么我们创建硬链接以后两个文件的inode
编号是一样的呢?
这就和原理有关了:当我们创建硬链接时,操作系统在当前目录里面建立新的映射关系,操作系统把原文件的inode
编号与硬链接建立映射关系,此时一个inode
编号就有了两个文件名,同时在inode
节点中会有一个引用计数的变量,当我们建立一个硬链接时,这个引用计数的变量就会自增一下,表示硬链接数目加一。
当我们删除一个文件时,目录会正常帮我们删除文件名与inode
的映射关系,但是操作系统不一定会为我们删除原文件,操作系统会将该文件对应的inode
节点中的引用计算变量也会自减一下,如果减完之后等于0就删除文件,否则只是修改了引用计数变量。
例如: 下面我们删除log.txt,然后查看log-link还存不存在?
刚才我们研究的是文件的硬链接,下面我们创建一个空目录,看一看它的硬链接。
可以看到目录的硬连接数是2,这是为什么呢?
其实一个“空目录”,并不是真正的空目录,每一个目录下面都会有两个硬链接!分别是.
..
硬链接.
链接的是当前目录,所以才会让一个目录默认有两个硬链接!
硬链接..
链接的是上级目录。
正是有了这两个硬链接,我们才能够在Linux
使用.
表示当前目录,..
表示上级目录。
那么我们下面尝试给一个目录建立硬链接
操作系统拒绝了我们的请求,操作系统不允许给一个目录建立硬链接,因为给目录建立硬链接可能导致环路路径问题
但是操作系统自己可以给目录建立硬链接,例如.
..
的硬链接。