0x00 机械硬盘
机械磁盘由磁头(head)、磁道(track)、柱面(cylinder)、扇区(sector)和盘片(platter)组成。其中,磁头悬浮在盘片上,并且每张盘片上下各有一个磁头;每张盘片的磁道数是相同的,每张盘片相同位置的磁道组成柱面;而每一个磁道由数量相同的扇区组成,我们知道离主轴越远的扇区面积越大,而扇区大小一般为512B,必然导致存储密度越低,这样做明显浪费空间,为了解决问题,我们将磁盘密度改为等密度结构,这就意味着外围磁道的扇区数量要大于内圈的数量。
- 物理图
- 逻辑图
- 数据读取流程
假设目标扇区和当前磁头位置不在同一个磁道上,首先,磁头从一个磁道移动到另一个磁道,同时,盘片也在旋转,此时,虽然磁头到达了该磁道,但是还没有到达目标扇区,因此还需要等待,最后读取红色区域。
0x01 文件
基于上述介绍的机械磁盘,我们如何访问磁盘扇区?通过访问(柱面,磁头,扇区)确定一个扇区,但是用户是无法直接访问扇区的,操作系统将扇区抽象成文件! 因此,文件是我们操作操作系统最小的单位。
2.1 文件系统设计
文件系统采用分层设计,最底层的是设备,接着是IO控制(设备驱动),基本文件系统(向设备驱动程序发出命令),文件组织模块(负责文件的逻辑块和物理块,以及管理空闲空间),逻辑文件系统(管理元数据)
2.2 文件系统的实现
文件系统的实现包括磁盘结构和内存结构,即我们将文件以什么结构存储于磁盘,以什么结构存储于内存。
- 磁盘结构
这里以Linux中的Ext2的磁盘结构为例,需要注意的是我们需要对硬盘进行格式化生成如下图所示的磁盘结构。其中,主引导扇区(0柱面、0磁头、1扇区,大小为512B)由MBR(Main Boot Record,主引导记录,大小为446B)和各个分区组成。MBR又包含位于磁盘最前边的一段引导代码即Boot Loader、磁盘分区表和魔数。磁盘分区表记录的是分区信息,魔数指定哪个分区为活动分区,也就是说把控制权交给这个活动分区的操作系统。需要注意的是图中我只化了2个分区,而在实际情况下我们最多可以化4个分区,这里说明一下为什么最多只能化4个分区,我们知道一个扇区的大小是512B,其中主MBR就占用了446B,结束标志占用2B,所以分区表的大小就只有64B,而每个分区信息用16B表示,因此硬盘只能划分为4个主分区。
由于每个分区都可以安装操作系统,因此每个分区中都有引导块,引导块是包含引导操作系统所需要的信息。由于分区容量很大,因此,我们会将分区进一步划分为各个块组,块组包含超级块、块组描述、磁盘块位图、inode位图、inode表以及数据块,其中,超级块(superblock)描述的是整个分区的详细信息,如分区的块数、块的大小、空闲块的数量和指针、空闲FCB的数量和指针,需要注意的是同一个分区中的块组中的超级块信息是一样的,只有块组0 的超级块会被加载到内存,而且由于每个块组都保留超级块信息,保证了磁盘高可用;块组描述记录inode表的起始位置、数据块的起始位置、空闲inode表和空闲数据块;磁盘块位图管理的是空闲磁盘块;inode位图管理空闲的inode;inode表存放inode信息。
- 内存结构
我们每次打开一个文件,即open 文件,需要发起操作系统调用,系统调用会查看一个表:系统范围打开的文件表,每个进程会维护打开的文件表,索引号指向的是系统范围打开的文件表,这个索引号在Linux上称为文件描述符,在windows上称为文件句柄。
0x02 文件存储
3.1 连续存储
- 顺序访问速度快,随机访问速度也快,例如:访问Hello.java,磁头定位到编号为1的磁盘块,接着顺序读取编号为2、3磁盘块。但是,容易出现碎片。
- 注意:图中的每一块表示逻辑块
3.2 链式存储
- 增、删内容很快;
- 顺序访问速度还好,但是随机访问速度很慢。
3.3 折中方案 - 索引存储
由于连续存储和链式存储都存在一些缺点,因此我们的先辈们想出了一个折中方案即索引存储。首先有一个磁盘块(索引节点)不存实际数据,它存放的是元数据和这些数据应该存放在哪些磁盘块的信息。例如下图中的索引节点20,它存放的元数据包含权限、所有者和时间戳,数据信息包含磁盘1、3、12、13、19。
- 优点:顺序访问和随机访问速度很快
- 缺点:需要一个额外的空间,即索引节点存储了大量的信息。
通过一个索引节点是不是可以解决所有问题呢?由于一个节点的大小是有限的,当出现一个特别大的文件,一个索引节点不放不了这么多磁盘块信息,那应该怎么办?我们将索引节点划分为若干个区域,元数据区域保持不变,磁盘索引区域划分为直接块和间接块,直接块表示通过索引直接找到数据块,间接块表示索引指向的是另一个索引节点。
0x03 文件读取过程
以查找/tmp/Test.log为例,首先获取根目录/的inode号,一般情况下根目录的inode号是固定的,假设为1,通过inode号找到根目录属性和它的磁盘块,然后从硬盘中读取第405磁盘块,该磁盘块内容中包含tmp的inode号,以此类推,最终找到Test.log的磁盘块。