持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
磁盘驱动
想到操作系统的持久化,很多人首先想到的是文件系统,然而从硬件角度,持久化的基本是硬盘。因此,在构建文件系统之前,首先要里了解存储文件的磁盘是怎么驱动的。
硬盘的接口很简单,对于系统来说硬盘的存储空间是由一个个扇区(512 bytes)组成,每个扇区都能够读和写。这一个个扇区组成的列表就是硬盘的地址空间。 对于硬盘空间有一个知名的假设,对于连续空间的读写是速度最快的读写操作,这种假设的根据我们在后面会提到。
我们首先了解一下机械硬盘的硬件组成,硬盘中最核心的部位是盘片,盘片是一个圆形的硬表面,通过诱导磁变化将数据持久地存储在上面。一个磁盘可以有一个或多个盘片;每个盘片有两面,每一面都被称为面。这些盘片通常由一些硬材料(如铝)制成,然后涂上一层薄薄的磁性层,使驱动器即使在断电时也能持久存储比特信息。
盘片都被绑在一起围绕着主轴,主轴与一个电机相连,电机以恒定的速度旋转盘片。转速通常以转速每分钟(RPM) 来测量,典型的现代RPM值在7,200RPM到15,000RPM范围内。数据以扇区的同心圆在每个表面进行编码;我们称这样一个同心圆为轨道。一个单一的表面包含成千上万的轨道,紧密地排列在一起,数百条轨道的宽度相当于一根头发。读写过程是由磁头来完成,驱动器的每个表面都有一个这样的磁头。磁盘头附着在单个磁盘臂上,该臂在表面移动,将磁盘头定位在所需的轨道上。
现代盘片下有数百万个轨道,了解了硬盘的物理模型后,我们看一个多轨盘片下的IO请求处理流程,如果磁头想要读写某个扇区的数据,会经历变轨-旋转-传输三个过程。通常来讲其中变轨开销很大,因为磁头要抬头-旋转-再低头。但是现代硬盘,变轨和旋转两个过程的开销可以做到几乎相同。
硬盘中各个结构还有一些其它特性,首先各个轨道并不是对称的,因为磁头的转动需要时间,一些倾斜可以做到变轨也能读取连续扇区。然后是外部轨道往往比内部轨道有更多的扇区,每个分区在每个轨道上有相同数量的扇区,外部区域比内部区域有更多扇区。最后,任何现代磁盘驱动器的一个重要部分是它的缓存,通常是8或16MB,例如,当从磁盘读取一个扇区时,驱动器可能决定读取该磁道上的所有扇区,并将它们缓存到内存中,这样做可以使驱动器快速响应对同一轨道的任何后续请求。还有一些其它非性能行为,例如,当数据还在内存里面,数据读写完成的报告就已经返回给系统了,这些在这里不表。
如何理解磁盘性能呢?我们提出IO性能的概念,其计算公式如下。
我们以两种情况下的IO请求来说明现代磁盘IO性能的到底是怎样的。两种IO分别是随机工作负载,即将少量(例如,4KB)的数据读取到磁盘上的随机位置,和顺序工作负载,即连续地从磁盘读取大量扇区,而不需要跳转。我们用两款不同定位的硬盘来分别进行实验。硬盘参数和实验结果如下。
可以看出在随机和顺序工作负载之间的性能存在巨大差异,Cheetah的差异接近200倍左右,Barracuda的差异超过300倍。由此可知,尽量大块的传输数据对读写速度提升帮助巨大。
IO请求在实际运行之前需要进行调度,具体的,给定一组I/O请求,磁盘调度程序检查这些请求并决定下一步调度哪个请求。由于我们可以很简单地猜测一个磁盘请求将花费多长时间,因此,磁盘调度器在其操作中将尝试遵循SJF(最短作业优先)的原则。
第一种尝试是SSTF(Shortest Seek Time First),SSTF根据跟踪对I/O请求队列进行排序,选择最近的跟踪上的请求先完成。例如下图的模型。看起来不错,但是SSTF有两个问题,第一个是操作系统无法看到硬盘内部信息,相反,它看到的是一个块数组,所以操作系统可以简单地实现最近邻块优先(NBF),而不是SSTF, NBF调度接下来使用最近邻块地址的请求。第二个问题是starving,如果对于当前头部的位置有一个稳定的IO请求,那么其它所有的IO请求都会被完全无视。
针对这些问题有过一些SSTF的变体,如F-SCAN,S-SCAN等,但是这些都没有完全解决SSTF的问题,相反的,它们也忽视了旋转的开销。
第二种尝试是SPTF,它会“视情况而定”来选择先处理哪个扇区的读写,这取决于寻找的相对时间和旋转的相对时间。在现代磁盘上,查找和旋转基本上是同等开销的,这取决于具体的请求,SPTF重点解决了旋转的开销问题,但是似乎没有解决starving问题,这里我们要提出IO请求的调度这个概念。
磁盘调度在哪里执行?在旧的系统中,操作系统完成所有的调度,但是由于不清楚磁盘情况,操作系统实现SPTF这样的方法比较困难,而在现代系统中,磁盘可以容纳多个未完成的请求,并且具有复杂的内部调度器(可以精确地实现SPTF)。OS调度器通常选择它认为最好的几个请求(比如16个),并将它们全部发送到磁盘。然后,磁盘利用其内部对磁头位置的了解和详细的轨道布局信息,以尽可能好的顺序(SPTF)为上述请求提供服务。由此,解决了starving问题。
关于磁盘调度器还有一些方面可以讲,磁盘调度器执行的另一个重要相关任务是I/O合并。例如,假设有一系列读取block 33、block 8、block 34的请求,调度器应该将block 33和block 34的请求合并成一个单独的两个block请求,从而降低开销。而系统在向磁盘发出I/O之前应该等待多长时间?对预期磁盘调度的研究表明,有时最好是等一下其它请求再发给磁盘(具体的原因我也不太懂)。