目录
磁盘逻辑结构与分区分组概念
boot block
操作系统的对磁盘的管理方式——分而治之
组里面的内容
data blocks
inode table
寻址的细节
inode bitmap
block bitmap
group descriptor table
Super block
磁盘的初始化
文件操作细节
新建一个文件
删除一个文件
查找一个文件
修改一个文件
理解目录文件
如何找到目录文件的inode
本片的内容我们都以一个800G大小的磁盘为例, 下面是这个磁盘的逻辑结构
这800G磁盘因为太大, 所以操作系统就可以把他们分成一块一块的, 就如同下图:
上面的一块一块的空间, 就相当于我们使用的电脑上面的C盘D盘。 这些分区对于操作系统来说, 如何分?
假如想要分5个区, 那么就可以定义一个数组: struct partion part[5]。 这个数组就代表着分成了五个区。
ps:我们的分区的概念其实和上面讲解的CHS寻址方法并不冲突——CHS寻址看的是盘面个数, 每个盘面的磁道个数, 每个磁道的扇区个数。
另外, 每个分区也有更加详细的划分,如下图:
理论上boot block是在磁盘第0面的第0磁道上的第1号扇区上面。 其他分区的第一个扇区也可能有。——而为什么会这样呢? 是因为如果有一天, 我们的boot block挂掉了, 那么我们的操作系统, 计算机整个地就起不来(图中已经写到, boot block中包含着操作系统启动的相关信息, 以及开机相关的字段), 本质上就是文件系统坏掉了, 那么它就要拷贝几份在多个分区中。
上面的每个分区会分成一个一个地小分区, 然后每一个分区都是一个block group。 那么, 以前大分区是250GB, 那么分成小分区后, 每个小分区可能就是10GB。那么管理好一个小分区10GB, 就能管好所有的小分区, 管理好所有的小分区, 就代表着能管理好一个大分区250GB, 那么就能管理好所有的大分区。 那么就变成了对整个磁盘地管理。
如此, 我们就将对整个磁盘800GB的管理转化成了对10GB的管理。
这种思想就叫做“分而治之”。
现在, 我们看一下上面的block group里面具体有什么
保存数据的区域, 以块的形式呈现。最常见的就是4kb——文件系统的块大小——这也就是说, 如果我们想要在磁盘中写1字节的数据, 我们就要在某一个分区里面找一个组, 然后在里面申请4kb大小的块空间。
这个规则如果不想明白可能会觉得非常浪费空间。 但是真的会非常浪费空间吗?——答案是不会浪费很多空间, 因为每一个文件都只会浪费一个块, 为什么? 这里可以想一下, 如果一个文件几百mb, 那么文件里面的数据将会占满许多的块空间, 然后在最后一个块空间可能会浪费一点空间。 如果是一个1字节的文件, 那么也只会浪费一个块空间。
单个文件的所有的属性, 128字节, 一般而言, 一个文件一个inode, inode table里面有很多个inode, inode有唯一的编号。
一般而言, 每一个块只有自己的数据。
我们知道, 我们的文件,可能有很多的块, 但是只有一个inode文件的属性包含在inode里(inode table就是一个inode数组, 里面是一个个inode结构体, 保存的内容就是inode编号以及文件的各个属性和文件的内容)。 文件的内容保存在块里。 而文件的内容也保存在inode里, 所以inode和块一定有某种连接关系。
这里我们需要知道的是, 在linux中, 文件的属性中, 不包含文件的名称!!!那么我们如何找到一个文件的属性呢? 在linux系统里面表示文件用的是inode编号!!!
那么inode里面有什么内容呢? inode的结构体定义如下:
#define NUM 15
struct inode
{
//inode number;
//文件类型
//权限
//引用计数
//拥有者
//所属组
//ACM时间
//int blocks[NUM]
}
下面是blocks[NUM];
在上面这些块里面, 我们如何知道那些快是被使用的, 哪些块是没有被使用的呢?
所以我们就有了一个block bitmap。 有多少个块, 就有多少个比特位。 有15000个块, 就有15000 / 8个字节来开辟block bitmap。 而15000 / 8等于1875字节。 不到4kb, 也就是一个块也不到。
比特位的位置和块号映射, 比特位的内容, 表示该块有没有被使用!!!比如该比特位为1, 代表该块被使用, 该比特位为0, 代表该块没有被使用。 ——删除一个文件的时候, 用不用把块(文件内容清空呢?)——答案是不用, 等到删文件的时候, 我们只需要找到文件对应的inode, 然后找到对应的block, 把里面的编号在位图的映射全部清空就可以了。
我们需要知道的是, 无论是inode, 还是data block, 到最后一定可以被操作系统解释成为一个个的扇区编号, 因为inode table有对应的编号, data block也有对应的编号。 那么根据特定的算法, 就能得到最后的扇区编号。 ——这个过程是操作系统必须做的。 ——寻找的算法:
对于这个图里面的信息, 除了inode table 属于文件的, data blocks属于文件块的, 这两个空间只需要预留好即可。 前面的super block、group descriptor table、block bitmap、inode bitmap四个中的super block、group descriptor table是需要提前初始化信息的, block bitmap、inode bitmap是要清空的。
每一个分区在被使用前, 都必须提前将部分文件系统的属性信息提前设置进对应的分区中, 方便我们后续使用这个分区或者分组。 ——前面的四个信息, 是对后面两个部分的属性的描述。而这种提前将文件系统的信息写到文件中的过程叫做格式化。
其实我们电脑内部只有一块物理磁盘(硬盘), 而之所以会有C盘、D盘、F盘, 其实这些盘就是硬盘的分区。 而一旦格式化, 就会将两个bitmap全部清零, 并且向super block、group descriptor table初始化文件信息。
目录也是文件, 也有自己的inode, 目录也要有自己的属性, 但是, 目录有内容吗? 我们知道, 文件 = 文件内容 + 属性——》这里, 目录有没有内容的本质, 其实就是目录有没有数据块。
答案是目录也有数据块, 但是这个数据块应该存什么呢? ——存的是该目录下文件的文件名和对应文件的inode的映射关系!!!
目录是文件, 同样有inode, 我们可以通过拿到目录的inode找到里面的数据块, 进而找到目录里面的各个文件。 那么我们如何找到目录的inode呢? ——我们知道, 我们的目录一定是上一级目录里面的文件。 那么我们就一定能够通过上一级目录的inode, 找到其中的数据块, 进而找到这一级的目录的inode。 而我们一级一级的追溯, 我们最终会追溯到根目录, 而好在我们有根目录的inode, 也就是说, 我们要先递归的找到根目录, 然后再一步步返回。
但是, 一步步访问太慢了, 所以linux操作系统会一般把我们最常访问的目录文件保存起来, 以及他路径上的所有的inode以及路径信息缓存起来。 这个缓存, 就叫做dentry缓存。
----------------------------------------------------------以上, 就是本节的全部内容, 下面是本节笔记: