12.3. 硬盘驱动器与固态硬盘¶
12.3.1. 辅助存储器上的文件¶
程序员通常将存储在辅助存储器上的 随机访问 文件视为连续的字节序列,这些字节可以组合形成数据记录。这称为 逻辑文件。实际存储在驱动器上的 物理文件 通常不是连续的字节序列。它很可能分散在磁盘各处。 文件管理器 是操作系统的一部分,负责接受对逻辑文件的数据请求,并将这些请求映射到磁盘上的物理位置。同样,当写入文件开头的特定逻辑字节位置时,这个位置必须由文件管理器转换为磁盘上对应的物理位置。要理解这些操作的大致时间成本,你需要了解磁盘驱动器的物理结构和基本工作原理。
磁盘驱动器和固态硬盘通常被称为 直接访问 存储设备。这意味着访问文件中任何记录的时间大致相同。这与 顺序访问 存储设备(如磁带驱动器)形成对比,后者需要磁带读取器从磁带开头处理数据直到到达所需位置。正如你将看到的,硬盘驱动器实际上只是近似直接访问:在任何给定时间,某些记录比其他记录更容易访问。
12.3.2. 硬盘驱动器¶
12.3.2.1. 硬盘驱动器架构¶
硬盘驱动器由一个或多个圆形 盘片 组成,它们一个接一个地堆叠并连接到中心 主轴。盘片以恒定速率持续旋转。每个盘片的每个可用表面都分配了一个 读/写头 或 I/O 头,数据通过它进行读取或写入,类似于唱机臂从唱片"读取"声音的装置。与唱针不同,磁盘读/写头实际上并不接触硬盘的表面。它保持在表面上方很短的距离,正常操作期间的任何接触都会损坏磁盘。这个距离非常小,比灰尘颗粒的高度还要小得多。可以把它比作一架在 5000 公里高度上飞越美国的飞机,而飞行高度只有一米!
一个典型的硬盘驱动器有多个盘片和多个读/写头,如图 12.3.1 (a) 所示。每个头连接到一个 臂,臂再连接到 摇臂。摇臂将所有头一起向内或向外移动。当头处于盘片上的某个位置时,每个盘片上都有数据可以直接被每个头访问。单个盘片上任何一个头位置可以访问的数据统称为一个 磁道,即盘片上所有与主轴距离固定的数据,如图 12.3.1 (b) 所示。所有与主轴距离固定的磁道集合称为一个 柱面。因此,柱面是当臂处于特定位置时可以读取的所有数据。
Figure 12.3.1: 磁盘驱动器示意图。 (a) 由一叠盘片组成的典型磁盘驱动器。 (b) 磁盘驱动器盘片上的一个磁道。¶
每个磁道被细分为 扇区。每个扇区之间有 扇区间隙,其中不存储数据。这些间隙使读取头能够识别扇区的结束。注意每个扇区包含相同数量的数据。因为外圈磁道的周长更大,它们每英寸包含的比特数比内圈磁道少。因此,大约一半的潜在存储空间被浪费了,因为只有最内圈的磁道才以最高可能的数据密度存储。这种排列如图 12.3.2 (a) 所示。如今的磁盘驱动器实际上将磁道分组为 区,使得最内圈区的磁道在向外移动时调整数据密度以保持相同的径向数据密度,然后下一个区的磁道重置数据密度以更好地利用其存储能力,以此类推。这种排列如图 12.3.2 (b) 所示。
Figure 12.3.2: 磁盘盘片的组织结构。点表示信息密度。 (a) 磁道的名义排列,显示从磁盘中心向外移动时数据密度降低。 (b) "分区"排列,扇区大小和密度在离中心更远的磁道中周期性重置。¶
与硬盘的物理布局不同,CD-ROM 由单个螺旋磁道组成。磁道上的信息比特间距相等,因此磁道外圈和内圈部分的信息密度相同。为了保持螺旋路径上的信息流速率恒定,当 I/O 头向磁盘中心移动时,驱动器必须加快磁盘旋转速率。这导致了更复杂和更慢的机制。
从硬盘读取特定字节或字节序列时,需要三个独立的步骤。首先,I/O 头移动到包含数据的磁道上方。这种移动称为 寻道。其次,包含数据的扇区旋转到头下方。使用时磁盘始终旋转。在撰写本文时,典型的磁盘转速为每分钟 7200 转(rpm)。等待所需扇区到达 I/O 头下方的时间称为 旋转延迟 或 旋转潜伏期。第三步是实际的数据传输(即读取或写入)。一旦第一个字节位于 I/O 头下方,读取信息所需的时间相对较少,只需等待所有数据经过头下方的时间即可。事实上,磁盘驱动器的设计不是一次读取一个字节的数据,而是在每次请求时读取整个扇区的数据。因此,扇区是一次可以读取或写入的最小数据量。
通常,希望将文件的所有扇区尽可能集中在较少的磁道上。这个愿望基于两个假设:
寻道时间很慢(它通常是 I/O 操作中最昂贵的部分),以及
如果读取了文件的一个扇区,很快可能需要读取下一个扇区。
假设 (2) 被称为 局部性原理,这个概念在计算机应用中频繁出现。
连续的扇区通常被分组形成一个 簇。簇是文件分配的最小单位,因此所有文件都是簇大小的整数倍。簇大小由操作系统决定。文件管理器跟踪哪些簇构成每个文件。
在微软 Windows 系统中,有一个名为 文件分配表 的磁盘分区,用于存储有关哪个扇区属于哪个文件的信息。相比之下,Unix 不使用簇。文件分配的最小单位以及可以读取/写入的最小单位是扇区,在Unix术语中称为 块 。Unix在某些磁盘块中维护有关文件组织的信息,称为 结点 。
同一文件的一组物理连续簇称为一个 区段。理想情况下,组成文件的所有簇在磁盘上是连续的(即文件由一个区段组成),以最小化访问文件不同部分所需的寻道时间。如果磁盘在创建文件时几乎满了,可能没有足够大的可用区段来容纳新文件。此外,如果文件增长,可能没有物理相邻的空闲空间。因此,一个文件可能由磁盘上分散的多个区段组成。磁盘越满,文件更改越频繁,这种文件碎片化(以及由此产生的寻道时间)就越严重。文件碎片化会导致性能明显下降,因为需要额外的寻道来访问数据。
当文件的逻辑记录大小与扇区大小不匹配时,会出现另一类问题。如果扇区大小不是记录大小的整数倍(反之亦然),记录将无法均匀地放入扇区。例如,一个扇区可能长 2048 字节,逻辑记录为 100 字节。这留下了存储 20 条记录的空间,还剩 48 字节。要么浪费多余的空间,要么允许记录跨越扇区边界。如果记录跨越扇区边界,可能需要两次磁盘访问来读取它。如果空间保持为空,这种浪费的空间称为 内部碎片。
第二种内部碎片的例子出现在簇边界处。大小不是簇大小整数倍的文件必须在最后一个簇的末尾浪费一些空间。最坏的情况是文件大小模簇大小等于一(例如,一个 4097 字节的文件和一个 4096 字节的簇)。因此,簇大小是顺序处理的大文件(需要较大的簇大小以最小化寻道)和小文件(需要较小的簇以最小化浪费的存储)之间的权衡。
每种磁盘驱动器组织都需要使用一些磁盘空间来组织扇区、簇等。磁道内扇区的布局如图 12.3.3 所示。必须存储在磁盘本身上的典型信息包括文件分配表、 扇区头 (包含地址标记和每个扇区的状态信息(是否可用))以及扇区之间的间隙。扇区头还包含错误检测代码,以帮助验证数据未被损坏。这就是为什么大多数磁盘驱动器的"标称"大小大于驱动器上可以存储的实际用户数据量。这个差值就是组织磁盘上信息所需的空间量。由于碎片化,还会损失更多空间。
12.3.2.2. 硬盘驱动器访问成本¶
当需要寻道时,它通常是访问磁盘信息的主要成本。这当然假设寻道是必要的。当按顺序读取文件时(如果组成文件的扇区在磁盘上是连续的),几乎不需要寻道。然而,当访问随机磁盘扇区时,寻道时间成为数据访问的主要成本。虽然实际的寻道时间差异很大,取决于 I/O 头当前所在的磁道与头要移动到的磁道之间的距离,我们只考虑两个数字。一个是逐道成本,即从一个磁道移动到相邻磁道所需的最短时间。这适用于分析磁盘上良好放置的文件的访问时间。第二个数字是随机访问的平均寻道时间。不幸的是,通常很难获得给定设备的这些数字。但典型的时间约为逐道时间 1-2 毫秒,平均寻道时间约 5-10 毫秒。多年来这没有太大变化,尽管存储密度(以及你看到的总容量)多年来有所提高。
多年来,磁盘驱动器的典型转速为 3600 rpm,即每 16.7 毫秒旋转一圈。到 2025 年,大多数磁盘驱动器的转速为 7200 rpm,即每圈 8.3 毫秒。一些低价驱动器运行在 5400 RPM,高性能驱动器运行在 10,800 RPM。随机读取扇区时,可以预期磁盘需要旋转半圈才能将所需扇区带到 I/O 头下方,对于 7200 rpm 的磁盘驱动器是 4.2 毫秒。
一旦位于 I/O 头下方,扇区数据可以以扇区旋转经过头下方的速度传输。如果要读取整个磁道,则需要一圈旋转(7200 rpm 时为 8.3 毫秒)来将整个磁道移到头下方。但这是大量的数据。更典型的是读取一个扇区。
举个例子,一个最近的磁盘标称大小为 2TB,大约有 1.8TB 的可用空间,分布在 3 个盘片和 6 个头上。这些盘片共有 62,016,065 个磁道,即每个面大约 10,000,000 个磁道。每个磁道有 63 个扇区,因此给定扇区(在寻道完成后和数据旋转到头下方后)的实际读取时间相当低。
这些信息应该让你感受到磁盘上的数据布局将如何影响性能。如果每条记录需要 5-10 毫秒的随机寻道和 4 毫秒的旋转延迟,这比读取一个包含例如 64 条记录的磁道(同样的寻道时间和旋转延迟)要慢得多。
12.3.3. 固态硬盘¶
固态硬盘的工作方式与硬盘非常不同。它们不需要任何物理移动就能到达数据,因此它们更接近真正的随机访问,即获取信息一个字节的时间无论在驱动器上的什么位置都大致相同。
获取 HDD 的详细信息(如逐道时间)可能很困难。获取有关 SSD 性能的有意义信息似乎更难。 SSD 的典型供应商规格与每秒读取的总理论吞吐量或每秒 I/O 操作数有关。这对数据中心经理可能有一些价值。对于一个在读取数据和做出决策之间循环的应用程序程序员来说,这些报告的值对于预测性能都没有帮助。更糟糕的是,SSD 的性能随时间退化相当快。
对个人程序员来说最有用的性能指标可能是获取下一个信息的延迟时间。 HDD 通常约为 10 毫秒,而 SSD 约为 0.1 毫秒。因此,在这种情况下 SSD 大约快两个数量级。从系统的角度来看,SSD 擅长同时处理多个数据请求。相比之下,即使在典型情况(如将文件从一个位置复制到另一个位置)下,HDD 也有困难。这导致 I/O 头在两个文件之间移动时不断竞争。这些对 SSD 都不是问题。
SSD 的典型最小读取大小与 HDD 相同:约 4K 字节。因此,无论使用哪种存储类型,在块级别缓冲数据都是有意义的。也就是说,既然一次读取会给你 4K 字节的数据,你应该编写应用程序,使得未来的读取请求很可能使用你已经可用的信息。
SSD 性能最重要的指标之一是"队列深度"。这是在需要从第一个 I/O 访问获取决策之前可以进行的 I/O 访问量。换句话说,如果我们必须读取一段数据,然后根据读取的内容决定下一步去哪里,这被称为 QD1。这通常是 SSD 最低效的数据访问范式。 SSD 非常擅长处理并行 I/O 请求,在极限情况下,性能规格中可能被称为 QD32。
