目录
一. 磁盘的结构和读写数据的方式
1.1 磁盘级文件和内存级文件
1.2 磁盘的物理结构
1.3 访问磁盘数据的方式
二. 磁盘文件系统
2.1 磁盘的分区管理方法
2.2 文件名和inode的关系
三. 结合文件系统对文件创建和删除的相关问题的理解
3.1 文件创建时操作系统进行的工作
3.2 文件删除时操作系统进行的工作
3.3 查看文件时操作系统进行的工作
3.4 已删除文件的恢复问题
四. 总结
如果设备断电,内存中的数据会丢失,但是磁盘上存储的数据依旧会被保存下来。也就是说,磁盘是一种永久性的存储介质,永久性的存储介质还有:SSD、光盘、磁带等。
磁盘,是计算机系统中唯一的机械设备。
现代笔记本电脑和很多台式机,都采用SSD来替代机械硬盘。SSD,就是我们常说的固态硬盘,它以固态电子芯片作为存储介质,相对于磁盘,SSD的读写速度更快,成本也较高。大型互联网公司的服务器,一般还是使用磁盘来存储数据。
一个磁盘的主要结构包括:磁盘盘片、磁头、音圈马达、主轴等。磁盘盘片用于存储数据,磁头用于从磁盘中读取数据和向磁盘中写数据。
计算机只认识二进制数据,也就是0/1,因此,只要能在磁盘盘片上读写0/1信号即可。磁盘盘片上涂有磁性物质,通过磁头放电,改变磁盘上磁性物质的正负极,从而实现数据的读写。通过磁盘上的正负极信号,来记录二进制数据,从而实现对数据的永久性存储。
磁盘盘片,根据区域划分原则,可分为如下几块区域:
一个扇区,为一个数据存储的单元。一般一个扇区可以存储512bytes的数据,也有些磁盘一个扇区存储4KB的数据,现在的技术也支持了不同扇区的容量有所不同。
如果我们想要访问某个特定的数据,要依次进行如下的工作,找到数据存储的位置:
上述寻址方法,被称为CHS寻址法, 有了CHS,我们就能够访问到任意扇区的数据。
我们可以通过磁带结构,来抽象类比磁盘。如图1.3所示,磁带被环形缠绕在一个柱面上,每层磁带叠加起来,就好比磁盘的盘片结构,磁带上也存在正负极来记录二进制信号。磁带可以展开为线性结构,这个线性结构就类似于数组,而我们可以想象将磁盘当做磁带展开,这样盘形结构就变为了数组似的线性结构。每个数组下标位置,就相当于一个磁盘上的扇区,根据下标就可以转换获取对应的盘面、磁道和扇区。
假设存储空间为1T的磁盘,那么我们就可以假想整个磁盘的存储空间就是一个数组,而向磁盘的某个扇区写数据,就等同于向这个数组中某个特定下标位置写数据。
由于一个磁盘的存储空间相对较大,为了便于管理,一般会对磁盘进行分区管理,这就好比我们将电脑的存储空间分为C盘、D盘、E盘。磁盘的存储空间,会被划分为若干个Block Group和一个Boot Block,Boot Block记录整个文件系统相关的属性信息,如果Boot Block中的数据丢失,那么对整个文件系统来说将是灾难性的,因此,在每个block group中,会有一块Super Block,用于备份文件系统的属性信息。当Boot Block中的信息丢失时,OS会去Block Group中寻找备份信息。
每个Block Group又可以根据存储数据的不同,分为这几个区域:Super Block、Group Descriptor Table、Block Bitmap、inode Bitmap、inode Table、Data Blocks。在这些分区里,存储了文件的数据和属性,以及块组和文件系统相关的属性信息。
每块Block Group(块组)内部分区存储的内容为:
在磁盘的每个块组上记录上面的这些信息,让每个文件的属性和内容可追溯、可管理。在磁盘中,文件的内容和文件的属性是分开存储的,文件 = 文件内容 + 文件属性。
一个Data Block存储4KB的数据,磁盘和内存进行IO操作的基本单位为4KB,但是,一个扇区一般存储512Bytes的数据,不让一个扇区存储的数据量和磁盘和内存进行IO操作的基本单位保持一致主要出于以下两个方面考虑:(1)如果IO的基本单位太小,那么就会大幅增加IO的次数,降低计算机系统的整体效率。(2)如果让磁盘扇区大小和IO基本单位大小一致,那么如果磁盘扇区大小发生变化,那么就需要更改OS的源代码,不让它们一致,是为了实现硬件层面和软件层面的解耦,软件工程要求高内聚低耦合。
存储在特定块组中的每个文件,都会有一个独立的、与之对应的inode编号,通过这个inode编号,可以在inode Table中,查找到特定的下标位置,以获取文件的属性信息。
Linux系统内置的ll、stat等获取文件属性信息的指令,其底层都是通过拿到文件的inode编号,进而在inode Table中获取文件的属性信息来实现的。
那么,文件的内容又是怎样获取的呢,怎样知道某个文件的内容存储在哪个Data Block中呢?
每个Data Block,都有一个特定的编号,每个文件的inode中,会记录有文件对应的Data Block编号, 只需要根据inode中记录的Data Block编号,就可以找到与之对应的Data Block,从而找到文件的内容。如图2.2所示,inode中有一个blocks数组,记录存储这个文件内容的Data Block编号。
获取某个文件的内容的流程为:拿到文件的inode编号 -> 找到与之对应的inode -> 找到inode内部的Blocks数组,获取存储这个文件的内容Data Blocks -> 访问Data Blocks获取文件内容。
那么,如果文件很大,需要庞大数量的Data Blocks才能放下的时候,又该怎么办呢,总不能将这些Data Blocks的编号全都放到inode中的Blocks数组中吧? -- Data Blocks中不仅可以存储文件的具体内容,也可以存储其它的Data Block编号,这个可以根据Data Blocks中记录的其他Data Block编号,找到存储了具体文件内容的Data Blocks。
inode中,并不会存储文件的文件名!
提问,如何通过文件名获取文件对应的inode编号呢?目录(文件夹),也属于文件,有与之对应的inode编号和Data Blocks,目录的Data Blocks中存储该目录下文件名和inode编号之间的映射关系,这就解释了为什么同一目录下不允许有同名的文件,因为如果有同名文件,就无法建立文件名与inode编号之间准确的映射关系。
在某一特定磁盘块组Data Group中,inode和Data Block的数量都是固定的。这就解释了为什么有时候明明块组中还有内存空间,但却无法成功创建新文件的现象。这有可能是因为inode Table被用完了,但Data Blocks还没用完,或者Data Blocks被用完了,但是inode Table还没有被用完。
遍历块组的inode Bitmap,查找一个还没有被使用的inode编号,分配给新建的文件 -> 将对应的inode Bitmap的二进制为置为1 -> 向inode中写入文件的属性信息 -> 为文件分配Data Blocks,记入inode中,并将Block Bitmap中与之对应的二进制位置为1 -> 将文件名及其与inode编号之间的映射关系写入到文件所在目录对应的Data Blocks中。
根据文件名,从文件所在路径的Data Blocks中获取该文件的inode编号 -> 根据inode编号,在inode Tables中,找到与之对应的inode,从inode中获取该文件占用的Data Blocks编号 -> 将inode Bitmap和Blocks Bitmap中对应的二进制位置0 -> 在文件所在的目录的Data Blocks中,解除文件名和inode编号的映射关系。
注意:删除文件不需要清除Data Blocks中的文件内容,只要改变位图标记、解除文件名和inode编号之间的映射关系即可。因此,删除一个大文件所耗费的时间远低于拷贝一个大文件的时间。
根据文件名拿到与之对应的inode编号 -> 通过inode编号找到该文件的inode -> 从inode中获取存储该文件内容的Data Blocks -> 访问指定的Data Blocks,查看文件内容。
当用户删除一个文件时,OS会有记录相应的删除日志,日志中会包含文件的inode编号。由于删除文件并不会删除掉inode Tables和Data Blocks中的内容,因此,可以通过已删除文件的inode编号,获取存储文件内容的Data Blocks,这样就可以实现对删除文件的恢复。
但是,恢复删除文件有一个必要的前提:原来文件所使用的inode Tables和Data Blocks均没有被覆盖。如果被覆盖,那么文件就无法恢复,写文件、新建文件等各种操作,都有可能造成被删除文件的inode Tables和Data Blocks被覆盖,因此,如果误删了重要文件,在文件恢复之前,什么都不应该做!
文件根据是否被进程打开,分为内存级文件和磁盘级文件。
磁盘的主要结构有:盘片、磁头、主轴、音圈马达等。数据存储在磁盘盘片上,磁盘盘片被划分为一个个的磁道和扇区,一个扇区为一个磁盘存储单元。磁盘盘片上涂有磁性物质,磁性物质的正负极,代表二进制数据的0和1,磁头负责在磁盘盘片上读取数据和向磁盘写数据。
通过CHS法,找到数据在磁盘中的盘片、磁道和扇区,就可以获取对应的数据。
磁盘中是被分为若干个块组进行分区管理的,每个块组存储有文件的属性信息和文件的内容,文件的属性信息和内容是分开存储的,每个文件占用一个inode来存储属性信息,占用若干个Data Blocks存储文件内容。
目录也是文件,与它自己的inode和Data Blocks,目录的Data Blocks中存有该目录下文件的文件名和与之对应的inode编号。