Linux - 进一步理解 文件系统 - inode - 机械硬盘

详谈机械磁盘

在上一篇博客当中,已经对 用户级缓冲区 和 系统缓冲区 的区别,和 初步认识 C 库函数 封装的 文件接口这些做了阐述。具体可以参考下述博客:

Linux - 用户级缓冲区和系统缓冲区 - 初步理解Linux当中文件系统-CSDN博客

本博客将在上篇博客的基础之上,对 Linux 当中的 文件系统进行进一步理解。


 当前 很多 用户级笔记本电脑,使用的都是 SSD 的 固态硬盘,而在可能 10 年之前,很多计算机使用还是 磁盘这种硬件设备来 存储 数据。我们所熟知的 机械硬盘 和 磁盘是同一个概念,机械硬盘是一种使用磁盘作为数据存储介质的计算机硬件设备。

像上述就是一个 机械硬盘,当在访问数据之时,主轴上的马达会带动 盘片,顺时针或者是逆时针高速旋转,磁头臂 带动 顶部的 磁头 在盘片的半径上 来回移动。

当 机械硬盘停止 工作,断电时,磁头臂就会回到 磁头停靠点。而盘片是双面的,磁头访问数据也是 双面进行访问的。

同样,上述是一个盘片,如果想要增大 容量可以叠加 盘片 和 磁头,磁头臂。 

需要注意的是:

  • 磁头臂 和 盘片的盘面是不接触的,尽管我们看上去很近,但实际上是不接触的。 
  • 而且,在机械硬盘启动之时,一般是不能随便移动的,因为这毕竟的是机械结构来运行的,何况,在上述看来,机械硬盘是一个很精密的 设备。很小的震动都可能会在很短的时间之内 让 磁头 和 磁头臂 与 盘面进行接触,从而刮花 盘面。而盘面上存储的是数据,就可能会损坏数据的存储的  二进制信息。
  • 所以,笔记本电脑,手机等等移动设备就不适合 机械磁盘,但是像服务器数据存储,这些设备十几年多不动的,就非常适合 便宜的 机械硬盘。
  • 而且,在拆开,查看机械硬盘之时,需要非常的小心,要带上手套等等的防护措施,因为 磁头 和 盘面之间的间隙很小,如果在盘面上落上一点大一点的灰尘,都可能导致盘面被刮花。
  • 磁盘当中对数据进行写入和读取,其实是磁头在对应位置进行充放电,来进行写入的读取。机械硬盘在外部接受到表示二进制数据的电信号,把这个电信号 利用磁头 在对应位置进行充放电,那么在盘面上的对应位置就表示一个 二进制的数据。 
  • 机械硬盘当中访问数据和写入数据,其实本质上和 吸铁石的原理差不多;我们可以用另一块吸铁石 来检测 另一块吸铁石当中的 某一端 是 N级 还是 S级,同样,就可以使用 N级 和 S级来表示 二进制数据。 
  • 在机械硬盘当中,可以使用 充放电来 改变 某一个 位置 的两端 N级 和 S级,从而达到修改数据的 效果。
  • 如果想销毁磁盘当中的数据,我们知道 高温是能够退磁的,但是要想把磁盘当中的所有数据销毁干净,成本还是有点高的。
  • 如果我们单纯的在软件层面,对数据进行删除,例如 rm -f xxx 这种操作的话,其实是有风险的。因为,修改操作,就是退磁或者加磁的操作,难免就会有磁性的残留,这些就是残留的二进制数据,那么,如果残留量不少,是可以进行二次修复的。这种数据称之为 -- 影子数据

 在盘面上,有很多个同心圆,这些同心圆是有宽度的,如上述黄色标注示例。就和操场跑道类似,只不过在 盘面上是圆形的线,这些线 是有宽度的,而且是同心的。

 把这些有宽度的 同心圆线,切分成一小段一小段的 区域,我们把这些区域称之为扇区,如上述在同心圆线当中,画出“一格一格”的小区域,就是扇区,如果放大来看就是这样的:

像上述的一圈一圈的黑线,就是一个一个的同心圆(磁道),而上述的红色扇形区域就是 -- 扇区

磁盘上被访问的 基本单元是一个一个的 扇区。--- 一般是 512 Byte。我们可以把磁盘看做是 由 无数个 扇区所构成。 

所以,我们要想把数据存入到磁盘当中,第一个要解决的问题就是要定位一个 扇区

要定位任意一个扇区,先要判断这个扇区在哪一面,而判断在哪一面其实就是判断 在哪一个磁头。然后在判断在哪一个 磁道,在判断在磁道的哪一个扇区当中。

所以,磁头在上下摆动本质上 就是在寻找 对应 磁道 和 扇区的过程


所以 ,其实在 机械硬盘 看来,通电之后所做的 机械运动越多,效率也就越低;反之,做的 机械运动越少,效率也就越高;因为 从固态硬盘的 光电信号的修改和访问速度看来,所以有的 机械运动就太慢了。

所以,对于软件的制造者,也就是在磁盘上存储数据的算法,一定是要尽可能的把 相关数据放在一起

 这样才能是一次访问 我们 想访问的多个数据,不同一次一次的在 磁盘当中去寻找。


磁盘在物理上他是圆形的,比如上述所说的 磁道,扇区等等都是类圆形的。但是,我们知道,磁带是可以其中的 存储数据的 磁条 拉扯成一个 线性 的 条状结构。

 磁带是 有两个轴承,在往一个方向进行 传送 磁条 的时候,其实,是在最上面有一个 线性的,我们可以看做是一个 类似于 "传送带" 的方式,在上述访问磁带当中的数据。

而这个 “传送带”  其实就是一个线性的 结构,我们把 磁条 单独从  磁带 当中抽出,把他线性的摆放。

其实,磁盘当中的一个一个磁道 也可以看做是一个 线性的结构。这个是我们在逻辑上理解的结构通,虽然,磁盘的盘面还是一圈一圈的 同心圆,但是,我们在访问和查找修改数据的时候,说可以 把它理解为一个 线性排列的地址结构。

可以把 机械硬盘当中的 每一个 盘面理解为一个 线性地址 存储的空间,然后,每一个盘面就是一个 线性地址 存储的空间。一个 机械磁盘存储的 地址空间 就可以这样作为 数据 增删查改的 逻辑结构。

在上述的 一个一个 盘面空间当中,又可以分为 无数个 磁道所代表的空间:

 所以,磁头在定位某一块物理地址空间的时候,其实在逻辑的一直看来,就是如下所示:

我们把每一个 盘面都上上述一样 把 逻辑结构抽象出来,那么 ,就可以得到一个 以 扇区为基本单位的 扇区数组

 这个扇区数组的每一个元素就是一个一个的扇区,所以,数组当中的一个一个下标,就代表着:机械硬盘当中,抽象出来的 一个一个扇区的地址空间。


 那么,像上述一样把 物理空间 划分为 逻辑结构之后,如何通过逻辑地址找到物理地址呢?

其实,在一个盘面的不同磁道当中,磁道距离圆心越近,那么所对应的 扇区面积就越少,也就是存储的 空间就越小。

而,每一个磁道上的扇区个数都上相等的,因为都是按照 从圆心开始的 两条线来分隔出的 一个一个 扇区,如下所示:
 

使用上述的方式就计算出了,28888 这个在整个机械硬盘当中的扇区编号,是在哪一个 机械硬盘当中的那一个位置了。 

  

这也是上述计算出的 物理地址。

 像上述,我们把 逻辑扇区地址称之为 --- LBA 地址

机械硬盘当中的地址称之为-- CHS地址

 所以,操作系统直接可以通过 LBA地址,即逻辑地址来直接 映射到 物理地址,从而访问到 机械硬盘当中的 物理地址处的空间存储的数据。


固体硬盘是随着 闪存技术的发展,所带来的 硬件层面的革新。

固态硬盘 的 存储效率 相对于 磁盘要高,

  1. 无机械结构:固态硬盘没有旋转盘片和移动的磁头等机械结构,数据存取更加稳定和快速。相比之下,磁盘需要等待盘片旋转到正确位置并移动磁头才能进行数据读写,这会导致访问延迟较高。

  2. 闪存技术。

  3. 随机访问速度:固态硬盘可以直接访问任意地址的数据,而磁盘需要等待盘片旋转到正确位置才能进行读写操作。

  4. 磁盘的机械部件会产生噪音,并且需要更多的电力来驱动。

在上述看来,固态硬盘 很明显要高于 磁盘。

但是相对的,固态硬盘价格也更贵。所以,一般如果在公司当中,比如是服务器,要存储大量的数据,所用的存储设备肯定是要非常大的 ,固态硬盘在价格上就不合适了。而且,磁盘 要做到 大容量 其实在硬件实现上 要比 SSD 要简单。

所以,在服务器数据的存储方面,现在大多数使用的还是 磁盘的方式来存储的。

同样,固态硬盘 也是有使用周期的,只不过我们用户使用的笔记本电脑使用周期可能就是几年,所以 SSD 也肯定是够用的。


除了上述的 几种存储的之外,还有一种 混盘。在混盘当中既有 磁盘,也有的 SSD。当用户紧急写入数据之时,就用 SSD, 这样IO的速度比较快。当电脑的当中的大部分硬件闲置之时,也就是当前电脑没有压力的时候,就慢慢的把 SSD 当中数据写到 磁盘的当中。

当然,这种在 两个盘当中交换数据肯定是有的 损耗的,而且,混盘 处于 固态硬盘 和 磁盘之间,效率比不上 固态硬盘;存储空间上 和 价格上又比不上 磁盘;而且 中间交换数据还有消耗,所以,混盘的方案 使用较少。

但是在大公司当中,为了考虑到 存储效率,可能会用上 几百台 ,上千台电脑 使用 固态硬盘的方式来对经常访问的数据进行 存储,但是,要存储 大量的数据,还是要是用 磁盘的方式,因为 固态硬盘的成本太高。

比如:用户常访问的数据就放到 固态硬盘当中,不常用的就不用放到这些的 高功耗,高效率的存储设备当中,我们一般把这种存储方式称为 -- 存储分级 或者  冷热分离

在本篇博客 Linux 当中的文件系统的描述当中,主要使用 磁盘的方式来进行 举例说明。

文件系统

我们知道,一个文件当中,有 文件的属性 和 文件的内容;所以,在磁盘当中存储的 文件数据,就包括了 要存储文件的内容 和 文件的 属性。

在磁盘当中,对于文件内容的存储,是按照 数据块 的方式来进行存储的。

在磁盘当中存储的 文件属性当中,存储的有 inode 这个属性。

而在 Linux 当中,文件磁盘当中存储,是将 属性内容 分开存储的!!!

也就是说,在硬件层面上,文件的存储的是分开存储的,分为 属性内容


磁盘的寄存器

其实,在计算机硬件组成当中,不仅仅 CPU 有自己的寄存器,其他设备(外设)也有自己的寄存器。磁盘也有自己的寄存器

比如:磁盘控制寄存器数据寄存器地址寄存器状态寄存器······

 CPU 虽然不能直接访问到 磁盘当中的数据,但是CPU有能力向外设发送一些信号

 有了上述的几个 寄存器,CPU 要想控制这个磁盘,此时是要 R(读)还是 (W)写,写那些数据,把这个数据写到 磁盘当中的那一个位置(LAB地址 来控制),在磁盘写完之前,磁盘可能是 未就绪状态吗,写完之后就是就绪状态,那么磁盘就像向这个 状态寄存器当中写入当前自己的状态,好让CPU 知道磁盘当前处于什么状态。

像上述的 数据寄存器,其实不是单纯的把数据寄存,然后写入到 磁盘,因为 寄存器当中的空间太小的,所以,一般 数据寄存器存储的是 要写入磁盘当中的数据,在内存当中的存储位置

而且,在现在的计算机当中,其实CPU已经不在管 IO 的过程了,它现在主要是在处理进程的代码的运行。

DMA芯片是主要负责 IO 工作的


那么,在我们的计算机当中,外存存储设备的存储空间一般是很大的,比如 500G,800G,1T 等等,都是非常大的,那么操作系统 和 CPU 要想管理这么大个空间是不好管理的。

所以,就有了分区的操作。其实,就和在分盘操作是一样的,我们在windows 当中,可能有 D 盘,C 盘,F 盘等等很多的盘,其实就是把外存存储空间 分为了很多个区。

而,在操作系统层面上,想做到分区的操作其实很简单。只需要定义一个结构体,每一个结构体对象管理一个 区的 起始地址 和 终止地址,然后用一个 该结构体类型的数据来管理这些个区就可以了:
 

struct partion
{
    int start;
    int end;
};

#define N 5 

// 数组
struct partion part[N];

我们上述并没有对 扇区数组当中存储的 地址进行修改,只是利用 结构体 和 该结构体的数组,把这个 扇区数组   划分为 空间大小不一的 不同分区。

然而在不同 分区当中访问之时,本质上还是访问的 LAB地址,然后映射到 CHS物理地址当中来访问。

所以,在进行管理整个 磁盘空间的 任务就划分为了 管理各个分区,只要把 各个分区都管理好了,就可以把 整个磁盘空间管理好。


inode 管理分区

在上去分区,我们可能分出来的是 200G,500G,那么对于这 200G,500G 同样还是大呀,还是不好管,但是相对于 单独的 1T来说,已经好了很多。

为了 管理这些个 分区,我们使用 inode。

200G 还是很大,所以,就会继续划分:

划分成 无数个 block group块。

像这种,把 大问题转化为小问题的思想,其实就是 分治

而,上述的一个一个 的 block group块 又是向如下一样划分的:
 

Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的。
 

  • bootblock:一般在磁盘的 0号位置处,也就是首元素位置放置。也有可能是在 其他分区的 0号位置处 放置(放置 磁盘的 0号位置处 存储的数据出现问题,导致操作系统软件找不到)。
  • Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子。
  • 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。
  • GDT,Group Descriptor Table:块组描述符,描述块组属性信息。
  • 块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
  • inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
  • i节点表(inode Table):存放文件属性 如 文件大小,所有者,最近修改时间等,一般大小是 128 字节。一般而言,一个文件,一个inode。所以,在一个分区当中会有很多个 inode;inode 有唯一的编号,也就相当于是这个文件的编号了。
  • Data blocks (数据区) :存放文件数据内容,以块的方式呈现,常见的是以 4kb 为大小。是文件系统的 块大小。比如:操作系统在访问 文件,或者是向文件当中写入数据,在上述情况下,就是按照 4kb 一次的方式来访问的。
     

Data blocks (数据区) 当中有 无数个个以  4kb 为大小 分割的 块。

所以,对于操作系统来存储数据的话,就是以 块 为单位来存储,那么按照 一个块是 4kb的话,就是以 一次 4kb 的方式 ,一次一次 像文件当中写数据的

这种一次加载 4 kb,可能某一次就会加载当前使用不到的数据,其实这种就是一种简单的 预加载

 一个块当中有一个 inode,在 inode 当中有一个 数组 blocks[] ,在这个数组当中就存储了 Data blocks 当中各个块 对应 块号,从而找到这些块来进行 增删查改。

在 数组 blocks[] 数组当中,前大部分的 存储的就是 当前文件的内容 所对应的块号,我们称之为 -- 直接索引

而后 小部分存储的是 用于索引出 真正存储 文件内容的块号的多级 索引,这个我们称之为 -- 多级索引

为什么是多级索引呢?因为 比如在第一个 blocks[] 的最后某一个元素,存储了 某一个 用于索引的 块号,当访问下一个块的时候,其实访问的不是这个文件真正的内容,其中其实还有索引,还可以指向 下一个块号,如果下一个块存储的就是文件内容,那么 第一个 blocks[] 的最后某一个元素 存储的就是 二级索引。

 利用这种多级索引的方式,就可以创建出很大的 文件了。


那么,在上述这么多个 块当中,我们怎么知道哪一个块有没有被使用呢?比如现在有 2.5W 这么多个 块,我怎么知道 那些块有没有被使用呢?

所以,就有用到位图!!

 如果有  2.5W 个 块,那么 块位图(Block Bitmap)当中就有 2.5W 个 比特位,用一个比特位上的 0 和 1 来表示某一个编号的上的块是否被 使用。用比特位的位置,和 块号映射。

所以,删除一个文件,不需要把这个文件对应的 块空间给删除掉。

 这就是为什么,一个非常大的文件,拷贝非常慢,但是删除非常快。

所以,像上述所说的 inode Bitmap 和 inode Table 你就可以理解了,因为  Data blocks (数据区)当中存储的文件肯定有很多个,所以 inode 也有很多个,这些都用 inode Table 来保存。 inode Bitmap 块位图(Block Bitmap) 一样都是位图,但是 inode位图(inode Bitmap) 保存的事 全部的 inode 的使用情况。

所以删除文件,只需要把inode位图(inode Bitmap)和 块位图(Block Bitmap)  当中 对应的比特位 的值做修改即可。

通过 inode bitmap 映射找到 所有的 inode 和 各个 inode 当中映射的所有的 存储 文件内容 对应的块号,然后就是对 inode位图(inode Bitmap)和 块位图(Block Bitmap)  当中 对应的比特位 的值做修改。

所以,理论上 Linux 当中是可以做到 像在 windows 当中的回收站,在回收站当中 恢复文件,在Linux当中 就是把 inode位图(inode Bitmap)和 块位图(Block Bitmap)  当中 对应的比特位 的值做 恢复。


注意:

在文件的 inode 属性当中,不包含文件的 名称!!

可以使用上述命令,在Linux 当中查看 一个文件的编号。

在Linux 当中识别某一个文件,使用的是 inode 编号。这个inode 编号就是存储在 inode 结构体当中的。 


inode 在各个当中是独立存在的。这里所说的区,其实就是  我们上述所说的 开始把 一个磁盘分区(分盘)操作。

一个分区当中,所能够使用的 inode 总数是确定的。


GDT,Group Descriptor Table  块组描述符。在一个分区当中有多个分组,那么一个分组当中 :有多少个块;inode Table 当中一共有多少个 inode;在这些 inode 和 块当中,已经被使用的 inode 和 块占多少个;还没一被使用的 inode 和 块 占多少。整个分组的容量是多少?在整个分组当中,多少空间已经被使用了,多少空间还没有被使用。以及,在需要分配 inode 之时,应该从什么位置开始分配,也就是 当前inode 的下一个编号等等

也就是与这个组相关的所有属性,都放在 GDT,Group Descriptor Table  当中,GDT,Group Descriptor Table  用来 描述整个分组的使用情况,也就是当前的这个 Blocks group 分组的使用情况。


超级块(Super Block):
在 Super Block 当中,保存的是整个分区的 文件系统的基本信息。

 在一个分组当中,有上述说过的很多 不同的空间区域,用于存储不同的文件信息,因为 不同的操作系统有自己的一套管理文件的 文件系统,那么就有不同的方式来管理着这些个文件,其实就是使用了不同的 属性来更好的描述这个文件的信息。

那么这个属性是要占空间的,每一个属性应该存储在什么位置,前后的 属性是那些,这个属性应该存储的在 某一个段空间的区间当中。

这些关于文件系统的基本信息,是这个分区当中 分出的 每一个 分组都应该遵守的。

那么在 Super Block 当中存储的是 : 整个分区的使用情况,包括 一共有多少个组;每个组的大小;每一个组的 inode 数量;每一个组的 block 数量;每个组的起始inode ;文件系统的类型和名称等等 关于整个分区的 文件系统的基本信息。

Super Block没有 必要在 每一个分组当中都存储一份,因为它存储的是整个 分区的使用情况。

 但是,也并不是只存储一份,在下图当中你也可以看到,Super Block是被放到 分组当中。因为,Super Block   这个不仅仅只存储一份,可能在不同的 broup 当中会存储多份

其实为为了方式,当前使用的 Super Block 当中的信息出现异常不能访问了,那么在后续的 group 当中有备份的替代的Super Block 可以使用。

因为 在Super Block 当中存储的信息非常的重要,是 整个分区的 文件系统基本信息,如果这个分区当中的唯一一个 Super Block 挂掉了,那么影响的 不仅仅是 存储这个Super Block 的分组,而是这个分区当中的所有 分组都会挂掉,那么所带来的后果就是,整个分区都会挂掉。

所以,对于Super Block 的存储,要在 不同的 group 当中,零零散散的 存储几个,用于备份信息。


总结

所以,在上述的 关于一个分组当中各种信息的 存储,其实只有 inode Talble Data blocks 是分别属于文件的  属性和内容

其他的像是 超级块,还有两张 位图,实际上,是 管理这个分组的 信息,这些信息是在这个分组被创建之前就要 被 初始化的,比如:两张Bitmap ,创建分组之初就要 把这两张表 清空。

所以,每一个分区在被使用之前,多必须将文件系统的基本属性信息,体检设置进对应的分区当中,方便我们后续使用这个分区。这个提前 写入 文件系统的基本信息的操作,称之为 --- 格式化


什么叫内容数据为 “脏”?

当缓冲区当中的数据为脏的时候,我们要把这个数据刷新到 磁盘当中,那么什么叫 内存数据为 “脏” 呢?

首先,当我们要把数据往 磁盘当中写入的时候,其实是 先把 磁盘当中的数据写到 内存当中,然后,此时内存当中的数据就和 磁盘当中是一样的 ,然后进程才上内存当中这段区域当中写入数据。

如果,在上述写入数据之后,如果内存当中的数据已经和 磁盘当中原本的数据不一样了,就说明此时的数据为 “脏”。

此时,才把缓冲区当中的数据,刷新到磁盘当中。

 所以,在磁盘操作上,无论是 读 还是 写,都需要把数据 先写到 内存当中。

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chihiro1122

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值