目录
一、理解磁盘
当一个文件被打开时,他会加载到内存进行相应操作,而未被打开的文件则一直存放在磁盘(或者是固态硬盘,但是本篇文章是依据磁盘来讲的)上
1. 磁盘物理结构
可以看到,磁盘有许多盘片,一个盘片有上下两个盘面,每一个盘面都需要有一个磁头去读写数据
每个盘面可以分割成许多的同心圆,这个同心圆称为磁道,盘片都是上下堆叠的,因此一个磁道在上,下或者上和下都存在其他盘面的磁道,这些相对位置相同的磁道组成柱面
一个磁道又可以分割为许多扇区(一个弧面),扇区是磁盘读写的基本单位
磁盘在工作时,盘片高速旋转,磁头左右摆动,这样就可以找到盘面上的任意位置了
2. 磁盘如何找到一个指定的扇区
磁盘的基本单位是扇区,如果给扇区从0开始按顺序编号,那么就可以抽象成一个数组,扇区号就是下标,访问某个扇区给定下标后,磁盘根据下标(扇区号)去寻找存储区域
首先找到对应的磁头(Header),在找到对应的磁道(柱面,Cylinder),在找到对应的扇区(Sector),这三步也称作CHS 定址法。
如果知道盘面数量,磁道数量,扇区数量,就可以通过扇区号找到指定扇区
举例:
假设一个磁盘有8个盘面,一个盘面有10个磁道,一个磁道被分为8个扇区,即一个盘面80个扇区。要找到扇区号为346的扇区
磁头号 H = 346 / 80 = 4
磁道号 C = (346 % 80)/ 8 = 26 / 8 = 3
磁道内扇区号 S = (346 % 8)% 8 = 2
即找到4号磁头3号磁道2号扇区就为扇区号为346的扇区
二、操作系统管理磁盘
我们下文都以Linux操作系统下的ext2文件系统进行说明
1. 管理核心理念
一般来说,操作系统和磁盘交互的时候,最基本的单位是4KB,也称作数据块,假设一个磁盘的一个扇区大小为512字节,那么一个数据块就是8个扇区,
操作系统读取数据时,首先确定数据所在的块地址(逻辑块地址,LBA),根据块号计算出扇区号,交由磁盘,磁盘根据CHS定址法找到指定扇区,接着连续读取8个扇区交给操作系统,操作系统在根据数据的扇区号计算出块内地址,进行操作
对于操作系统而言,管理磁盘时可以将磁盘看成以数据快为基本元素的数组,但是一个磁盘可能很大,管理起来可能很困难,因此可以将磁盘划分成更小的分区,将管理一个分区的方法用于管理其他分区,就可以对磁盘进行整体管理。Windows操作系统中的c盘,d盘就是对磁盘(或者是固态硬盘)的分区。
分区后对一个分区可以继续进行分组,管理每一个小分组就可以管理好每个分区,
这种管理方式采用了分治的思想
2. 文件分组框架
Linux文件系统特点:文件内容和文件属性(也是数据)分开存储
Linux中文件的属性是一个大小固定的集合体,大小为128字节,一个文件对应一个struct inode
struct inode
{
int size;
mode_t mode;
int creater;
int time;
//……
int inode_number;
int datablocks[N];//记录该文件的文件内容所占的数据块块号,ext2下N一般为15
//……
} //inode 内部不包含文件名,我们通过inode number表示文件
分区分组示意图:
Data blocks:数据区,存放文件内容,其内部都是数据大小为4kb的数据块(与磁盘交互的单位),该数据块里只存储文件的内容,每个块都有自己的块号
Block bitmap: 块位图,记录着data Block中那个数据快已经被占用,那个数据快没有被占用。该位图中比特位的位置表示块号,比特位的内容表示该块是否被占用
Inode Table : inode表,存放文件属性结构体的数据块集合(struct inode),如文件大小,所有者,最近修改时间等
Inode Bitmap :(inode位图)记录inode table中那个已经被占用
GDT,Group Descriptor Table:快组描述符,描述块组属性信息
Super Block,超级块:存放文件系统本身的结构信息,记录的整个分区的信息,记录的主要信息有:block和inode的总量和未使用的block和inode的数量,一个block和inode的大小,最近一次挂载时间,最近一次检验磁盘的时间等其他文件系统的相关信息。
Super Block的信息被破坏,可以说整个文件系统结构就被破坏了,超级块不是每个分组里都有,而是在某几个分组中存在,这样设计的主要意义是为了让文件系统更稳定。超级块在一个分区只保存一份的话,一旦受到破坏,那么该分区将被破坏,但是也不是每个分组中都保存一份,太浪费空间,综合考虑,只在部分分组中保存,其中一个意外损坏可以用其他的来拷贝恢复,提高稳定性
3. 文件管理流程
在使用文件系统时,在每个分区内部分组,然后写入文件系统的管理数据(上面的各种数据块),该过程就是格式化,即在磁盘中写入文件系统
我们在找到一个文件时,首先要得到该文件的inode number,该编号是以分区为单位分配的,不是以分组为单位分配,一个分区内的所有inode number都不能重复,是唯一的,inode number不能夸分区访问
Super Block 和 Group Descriptor Table中会存储分组的inode number范围,
得到一个inode number后去判断范围找到所在分组,减去该分组的起始inode 范围后得到在该组内的序号,在用该序号去查inode bitmap检查是否合法
Data Block同inode number相同,也是以分区为单位分配,一般情况下优先使用分组内的数据块,不跨组,若一个文件的大小大于该分组剩余的空间,则可以进行跨组,但是非常不建议这样,会影响效率和内存的连续性
inode中的datablocks表中,前12个直接寻址(存放的块号,类似于一级指针直接寻找),剩余3个中的前两个间接寻址(存放的块号,该块号对应的块内存放的不是数据,而是更多的块号,第二次找到的块号内存放的是数据,类似于2级指针),最后一个块号是2次的间接寻址(类似于3级指针)
这样可以找到文件的属性和数据,就可以进行文件的操作,具体操作流程如下:
新建文件
- 从 Superblock 中获取最近一次分配的编号,确定下一个待分配的编号。
- 根据此 inode number 找到其所属的分组。
- 将该分组中的 inode Bitmap 中对应此 inode number 的 bit 位置为 1。
- 在该分组的 inode table 中找到对应此 inode number 的条目,填入相应数据进行初始化。
- (如需写入数据) 遍历该分组的 block bitmap 找到空闲的数据块,写入数据,并将其编号填入该 inode 的 datablocks 中,同时将 block bitmap 中对应 bit 位置为 1。
删除文件
- 根据 inode number 找到其所属的分组。
- 将该分组中的 inode Bitmap 中对应此 inode number 的 bit 位置为 0。
- 将该分组中的 inode table 中对应此 inode number 的 inode 内的 datablocks 数组中记录的所有数据块编号取出。
- 将这些数据块编号在该分组的 block bitmap 中对应的 bit 位全部置为 0。
查找文件
- 根据给定的 inode number 找到其所属的分组。
- 在该分组的 inode Bitmap 中查看对应此 inode number 的 bit 位,检查是否合法(为1)。
- 在该分组的 inode table 中找到对应此 inode number 的 inode 条目,读取文件属性。
- 查看该 inode 条目中对应的 datablocks 数组,根据其中记录的数据块编号找到对应的数据块,读取文件内容。
修改文件
根据目标文件的 inode number 找到其所属的分组。
在该分组的 inode Bitmap 中查看对应此 inode number 的 bit 位,检查是否合法(为1)。
修改文件属性:
在该分组的 inode table 中找到对应此 inode number 的 inode 条目。
直接修改该 inode 条目中需要更新的部分数据(文件属性)。
修改文件数据:
将新数据直接覆盖写入到由该 inode 的 datablocks 数组指向的原有数据块中(即相同处理方式)。
4. 理解文件名与inode
目录本质上也是一个文件,他也有自己的属性和内容
目录的属性和内容
他的属性与普通文件的属性一样,也有自己的权限,所属组,文件名,修改时间等
他的内容存放的是文件名与inode编号的映射,在一个目录下创建的文件,其文件名由该目录保存
结论
一个目录下不能建立同名文件
目录的r权限,本质是允许我们查看文件内容,即文件名和inode 编号的映射关系,去掉之后无法查看有哪些文件 目录的w权限,本质是允许我们修改文件内容,我们新建文件时最有一定需要在当前所 处目录内容中写入文件名和inode编号的映射关系,没有w权限则不能创建
对文件进行操作时我们使用文件名,操作系统使用inode number,文件名和inode编号由目录维护
我们要根据路径找到一个文件,则必须找到该文件所在目录,该目录也是文件,则必须找到该目录所在目录,以此类推,最终找到系统开机时就确定位置的根目录,在往回找知道找到文件,该过程是逆向的路径解析,由操作系统完成,并且会对常用的路径进行缓存(记录文件名和inodenumber),这样可以快速访问到常用的文件。
实际上,缓存常用路径时,操作系统内核会提供一个struct dentry{……};该数据结构会记录文件的路径解析信息,一个文件一个dentry,可以高效的进行通过路径访问文件,找到其对应的inode number
三、分区挂载
在访问文件时,用inode去访问文件的前提是知道所在分区,在linux中存在默认分区和其他分区。我们也可以自己创建分区。
创建分区流程:
创建或识别分区:
在存储设备(如硬盘)上创建分区(或识别现有的默认分区、其他分区)。
写入文件系统(格式化):
在目标分区上写入文件系统(进行格式化),创建 Superblock、块组、inode Bitmap、inode Table、Block Bitmap 等结构。
挂载到目录:
将包含文件系统的分区挂载到操作系统目录树中的某个指定目录下(该目录称为挂载点)。
访问文件:
进入该挂载点目录或其子目录。
所有在该目录及其子目录下进行的文件操作(新建、删除、查找、修改),都将在该挂载点对应的分区(文件系统)中进行。
所以说目录也可以确定分区。文件操作时使用的路径(目录)隐式地确定了操作发生在哪个分区(文件系统)。路径解析过程中遇到的第一个挂载点,其指向的分区即为后续路径操作的目标分区。