共计 6953 个字符,预计需要花费 18 分钟才能阅读完成。
概念
零碎架构
咱们先来看一个典型计算机系统的架构。其中,CPU 通过某种内存总线(memory bus)或互连电缆连贯到零碎内存。图像或者其余高性能 I / O 设施通过惯例的 I / O 总线(I/O bus)连贯到零碎,在许多古代零碎中会是 PCI 或它的衍生模式。更上面是外围总线(peripheral bus),比方 SCSI、SATA 或者 USB。它们将最慢的设施连贯到零碎,包含磁盘、鼠标及其他相似设施。
为什么要用这样的分层架构?起因在于物理布局及造价老本。越快的总线越短,因而高性能的内存总线没有足够的空间连贯太多设施。另外,在工程上高性能总线的造价十分高。所以,零碎的设计采纳了这种分层的形式,这样能够让要求高性能的设施离 CPU 更近一些,低性能的设施离 CPU 远一些。将磁盘和其余低速设施连到外围总线的益处很多,最重要的就是能够在外围总线上连贯大量的设施。
规范设施
当初来看一个规范设施(不是实在存在的),通过它来帮忙咱们更好地了解设施交互的机制。从图中能够看到一个蕴含两个重要组件的设施。 第一局部是向零碎其余局部展示的硬件接口(interface),让系统软件来管制它的操作。因而,所有设施都有本人的特定接口以及典型的交互协定。
第二局部是它的内部结构(internal structure)。 这部分蕴含设施展现给零碎的形象接口相干的特定实现,非常简单的设施通常用一个或几个芯片来实现它们的性能。更简单的设施会蕴含简略的 CPU、一些通用内存、设施相干的特定芯片,来实现它们的工作。
标准协议
在上图中, 一个简化的设施接口蕴含 3 个寄存器:一个状态(status)寄存器,能够读取并查看设施的以后状态;一个命令(command)寄存器,用于告诉设施执行某个具体任务;一个数据(data)寄存器,将数据传给设施或从设施接收数据。通过读写这些寄存器,操作系统能够管制设施的行为。
操作系统与该设施的典型交互协定如下:
While (STATUS == BUSY)
; // wait until device is not busy
Write data to DATA register
Write command to COMMAND register
(Doing so starts the device and executes the command)
While (STATUS == BUSY)
; // wait until device is done with your request
该协定蕴含 4 步。
- 操作系统通过重复读取状态寄存器,期待设施进入能够接管命令的就绪状态。咱们称之为轮询(polling)设施。
- 操作系统下发数据到数据寄存器。例如,你能够设想如果这是一个磁盘,须要屡次写入操作,将一个磁盘块(比方 4KB)传递给设施。
- 操作系统将命令写入命令寄存器;这样设施就晓得数据曾经筹备好了,它应该开始执行命令。
- 操作系统再次通过一直轮询设施,期待并判断设施是否执行实现命令。
这个协定的益处是足够简略并且无效,然而难免会有一些低效和不不便。咱们留神到轮询会导致在期待设施执行实现时节约大量 CPU 工夫,如果此时操作系统能够切换执行下一个就绪过程,就能够大大提高 CPU 的利用率。
利用中断缩小 CPU 开销
多年前,工程师们创造了咱们目前曾经很常见的中断(interrupt)来缩小 CPU 开销。有了中断后,CPU 不再须要一直轮询设施,而是向设施收回一个申请,而后就能够让对应过程睡眠,切换执行其余工作。当设施实现了本身操作,会抛出一个硬件中断,引发 CPU 跳转执行操作系统事后定义好的中断服务例程(Interrupt Service Routine,ISR),或更为简略的中断处理程序(interrupthandler)。中断处理程序是一小段操作系统代码,它会完结之前的申请(比方从设施读取到了数据或者错误码)并且唤醒期待 I / O 的过程继续执行。
不过,应用中断并非总是最佳计划。如果有一个十分高性能的设施,它解决申请很快:通常在 CPU 第一次轮询时就能够返回后果。此时如果应用中断,反而会使零碎变慢:切换到其余过程,解决中断,再切换回之前的过程会有肯定的代价。因而, 如果设施十分快,那么最好的方法反而是轮询。如果设施比较慢,那么采纳中断更好。如果设施的速度未知,或者时快时慢,能够思考应用混合(hybrid)策略,先尝试轮询一小段时间,如果设施没有实现操作,此时再应用中断 。
另一个最好不要应用中断的场景是网络。网络端收到大量数据包,如果每一个包都产生一次中断,那么有可能导致操作系统发生存锁(livelock),即一直解决中断而无奈解决用户层的申请。
还有一个基于中断的优化就是合并(coalescing)。设施在抛出中断之前往往会期待一小段时间,在此期间,其余申请可能很快实现,因而屡次中断能够合并为一次中断抛出,从而升高解决中断的代价。 当然,期待太长会减少申请的提早,这是零碎中常见的折中。
利用 DMA 进行更高效的数据传送
标准协议还有一点须要咱们留神。如果应用这种形式,CPU 的工夫会节约在向设施传输数据或从设施传出数据的过程中。如何能力拆散这项工作,从而进步 CPU 的利用率?
解决方案就是应用 DMA(Direct Memory Access)。DMA 引擎是零碎中的一个非凡设施,它能够协调实现内存和设施间的数据传递,不须要 CPU 染指。
DMA 工作过程如下:为了可能将数据传送给设施,操作系统会通过程序通知 DMA 引擎数据在内存的地位,要拷贝的大小以及要拷贝到哪个设施。在此之后,操作系统就能够解决其余申请了。当 DMA 的工作实现后,DMA 控制器会抛出一个中断来通知操作系统曾经实现数据传输。
与设施交互的办法
在理解了如何晋升 I / O 的效率后,咱们来讨论一下操作系统到底如何与设施通信。
随着技术的一直倒退,目前次要有两种形式来实现与设施的交互。 第一种方法绝对古老一些,就是用明确的 I / O 指令。这些指令规定了操作系统将数据发送到特定设施寄存器的办法,从而容许结构上文提到的协定。
例如在 x86 上,in 和 out 指令能够用来与设施进行交互。当须要发送数据给设施时,调用者指定一个存入数据的特定寄存器及一个代表设施的特定端口。执行这个指令就能够实现冀望的行为。
第二种办法是内存映射 I /O(memory-mapped I/O)。通过这种形式,硬件将设施寄存器作为内存地址提供。当须要拜访设施寄存器时,操作系统装载或者存入到该内存地址;而后硬件会将装载 / 存入转移到设施上,而不是物理内存。
两种办法都没有绝对显著的劣势。内存映射 I / O 的益处是不须要引入新指令来实现设施交互,但两种办法明天都在应用。
纳入操作系统:设施驱动程序
还有最初一个问题:每个设施都有十分具体的接口,那么如何将它们纳入操作系统,并且让操作系统尽可能通用呢?
这个问题能够通过古老的形象技术来解决。 在最底层,操作系统的一部分软件分明地晓得设施如何工作,咱们将这部分软件称为设施驱动程序(device driver),所有设施交互的细节都封装在其中。
咱们来看看 Linux 文件系统栈,了解形象技术如何利用于操作系统的设计和实现。下图粗略地展现了 Linux 软件的组织形式。能够看出, 文件系统(当然也包含在其之上的应用程序)齐全不分明它应用的是什么类型的磁盘。它只须要简略地向通用块设施层发送读写申请即可,块设施层会将这些申请路由给对应的设施驱动,而后设施驱动来实现真正的底层操作 。
磁盘驱动器
介绍了通用的 I / O 设施概念后,咱们将更具体地介绍一种设施:磁盘驱动器(hard disk drive)。磁盘驱动器始终是计算机系统中长久化数据存储的次要模式,文件系统技术的倒退大部分都是基于它们的行为。因而,在构建治理它的文件系统软件之前,有必要先理解磁盘操作的细节。
接口
所有古代驱动器的根本接口都很简略。 驱动器由大量扇区(512 字节块)组成,每个扇区都能够读取或写入。在蕴含 n 个扇区的磁盘上,扇区从 0 到 n−1 编号。因而,咱们能够将磁盘视为一组扇区,0 到 n−1 是驱动器的地址空间(address space)。
多扇区操作是可能的,实际上许多文件系统一次读取或写入 4KB(或更多)。但在更新磁盘时,驱动器制造商惟一保障的是单个扇区的写入是原子的。因而,如果产生不合时宜的掉电,则只能实现较大写入的一部分(有时称为不残缺写入)。
通常能够假如拜访驱动器地址空间内的间断块(即程序读取或写入)是最快的拜访模式,并且通常比任何更随机的拜访模式快得多。
根本形成
一个磁盘可能有一个或多个盘片(platter),每个盘片有两面,称为外表。这些盘片通常由一些硬质资料(如铝)制成,而后涂上薄薄的磁性层,即便驱动器断电,驱动器也能长久存储数据位。
所有盘片都围绕主轴(spindle)连贯在一起,主轴以一个恒定的速度旋转盘片。旋转速率通常以每分钟转数(Rotations Per Minute,RPM)来测量,典型的古代数值在 7200~15000 RPM 范畴内。
数据在扇区的同心圆中的每个外表上被编码,咱们称这样的同心圆为一个磁道(track)。一个外表蕴含数以千计的磁道,严密地排在一起。
磁盘的读写过程由磁头(disk head)实现,驱动器的每个外表有一个这样的磁头。磁头连贯到单个磁盘臂(disk arm)上,磁盘臂在表面上挪动,将磁头定位在冀望的磁道上。
工作原理
让咱们每次都构建一个简略的模型,来理解磁盘是如何工作的。首先假如咱们有一个繁多磁道的简略磁盘。该磁道只有 12 个扇区,每个扇区的大小为 512 字节,用 0 到 11 的数字示意。
单磁道提早:旋转提早
假如咱们当初收到读取块 0 的申请,磁盘应如何解决该申请?
具体来说,它必须期待冀望的扇区旋转到磁头下。这种期待在古代驱动器中常常产生,并且是 I / O 服务工夫的重要组成部分,它有一个非凡的名称:旋转提早(rotational delay,有时称为 rotation delay)。在这个例子中,如果残缺的旋转提早是 R,那么磁盘必然产生大概为 R / 2 的旋转提早,以期待 0 来到读 / 写磁头上面。
多磁道:寻道工夫
磁盘只有一条磁道,这是不太事实的,古代磁盘有数以百万计的磁道。因而,咱们来看看更事实一点的磁盘外表,这个外表有 3 条磁道。磁头以后位于最内圈的磁道上。下一个磁道蕴含下一组扇区,最里面的磁道蕴含最后面的扇区。
假如当初须要读取扇区 11。为了服务这个读取申请,驱动器必须首先将磁盘臂挪动到正确的磁道,这个过程也即所谓的寻道(seek)。寻道和旋转都是最低廉的磁盘操作之一。
寻道有许多阶段:首先是磁盘臂挪动时的减速阶段,随着磁盘臂全速挪动而惯性滑动,而后随着磁盘臂加速而加速,最初在正确的磁道上停下来。
在寻道过程中,盘片也会跟着一起旋转,在这个例子中,大概旋转了 3 个扇区。因而,寻道实现时,磁头下方是扇区 9。接着咱们只需再期待短暂的转动提早,当扇区 11 通过磁头时,数据才开始真正读写,称为传输(transfer)。因而,咱们失去了残缺的 I / O 工夫图:首先寻道,而后期待转动提早,最初传输。
一些其余细节
许多驱动器采纳某种模式的磁道偏斜(track skew),以确保即便在逾越磁道边界时,也能够很不便地程序读取。从一个磁道切换到另一个磁道时,如果没有这种偏斜,所需的下一个块曾经旋转到磁头后的地位,因而驱动器将不得不期待整个旋转提早,能力拜访下一个块。
外圈磁道通常比内圈磁道具备更多扇区,这是几何构造的后果。这通常被称为多区域(multi-zoned)磁盘驱动器,其中磁盘被组织成多个区域,区域是外表上间断的一组磁道。每个区域每个磁道具备雷同的扇区数量,并且外圈区域具备比内圈区域更多的扇区。
任何古代磁盘驱动器都有一个重要组成部分,即它的缓存(cache),因为历史起因有时称为磁道缓冲区(track buffer)。该缓存只是大量的内存(通常大概 8MB 或 16MB),驱动器能够应用这些内存来保留从磁盘读取或写入磁盘的数据。例如,当从磁盘读取扇区时,驱动器可能决定读取该磁道上的所有扇区并将其缓存在其存储器中。这样做能够让驱动器疾速响应所有后续对同一磁道的申请。
在写入时,驱动器面临一个抉择:它应该在将数据放入其内存之后还是理论写入磁盘之后,回报写入实现?前者被称为后写(write back)缓存,后者则称为直写(write through)。后写缓存有时会使驱动器看起来“更快”,但可能有危险。如果文件系统或应用程序要求将数据按特定程序写入磁盘以保障正确性,后写缓存可能会导致问题。
I/ O 工夫和性能
对于磁盘驱动器的无关 I / O 的工夫和性能问题,咱们只须要记住以下几点:
- I/ O 总工夫 = 寻道工夫 + 旋转工夫 + 传输工夫,通常前两者的提早大抵相当,同时远大于传输工夫。
- 由上一条能够得出,磁盘的随机和程序工作负载之间的驱动性能差距很大,对于一般磁盘来说个别在几百倍左右。
- 高端的“性能”驱动器与低端的“容量”驱动器之间的性能差别很大。
磁盘调度
因为 I / O 的高老本,操作系统在决定发送给磁盘的 I / O 程序方面从来施展重要作用。给定一组 I / O 申请,磁盘调度程序查看申请并决定下一个要调度的申请。
与任务调度不同,对于磁盘调度,咱们能够很好地猜想磁盘申请须要多长时间。通过预计申请的查找和可能的旋转提早,磁盘调度程序能够晓得每个申请将破费多长时间,因而抉择先服务破费起码工夫的申请。因而,磁盘调度程序将尝试在其操作中遵循 SJF(最短工作优先)的准则。
SSTF:最短寻道工夫优先
一种晚期的磁盘调度办法被称为最短寻道工夫优先(Shortest-Seek-Time-First,SSTF)。SSTF 按磁道对 I / O 申请队列排序,抉择在最近磁道上的申请先实现。 例如,假如磁头以后地位在内圈磁道上,并且咱们申请扇区 21 和 2,那么咱们会首先收回对 21 的申请,期待它实现,而后收回对 2 的申请。
在这个例子中,SSTF 运作良好,首先寻找两头磁道,而后寻找外圈磁道。但 SSTF 不是万能的,起因如下。第一个问题,操作系统无奈晓得驱动器的几何构造,而是只会看到一系列的块。不过这个问题很容易解决。操作系统能够简略地实现最近块优先(Nearest-Block-First,NBF),而后用最近的块地址来调度申请。
第二个问题更为基本:饥饿(starvation)。在咱们下面的例子中,如果有对磁头以后所在位置的内圈磁道有稳固的申请,纯正的 SSTF 办法则将齐全疏忽对其余磁道的申请。
电梯
电梯算法也是一种比拟古老的算法。 该算法最后称为 SCAN,简略地以逾越磁道的程序来服务磁盘申请。咱们将一次逾越磁盘称为“扫一遍”,如果申请的块所属的磁道在这次“扫一遍”中曾经服务过了,它就不会立刻解决,而是排队期待下次“扫一遍”。
SCAN 有许多变种,C-SCAN 是一种常见的变体,即循环 SCAN(Circular SCAN)的缩写。该算法不是在一个方向扫过磁盘,而是从外圈扫到内圈,而后从内圈扫到外圈,如此周而复始。
然而,SCAN 及其变种并不是最好的调度技术。特地是,SCAN 还有 SSTF 实际上并没有严格遵守 SJF 的准则,因为它们漠视了旋转工夫。
SPTF:最短定位工夫优先
为了更好地了解这种算法,咱们来看一个例子。
在这个例子中,磁头以后定位在内圈磁道上的扇区 30 上方。此时有两个别离针对扇区 8 和 16 的 I / O 申请因而,调度程序如何决定接下来应该服务哪个申请?
答案是“视状况而定”。这里须要思考的状况是旋转与寻道相比的绝对工夫。如果在咱们的例子中,寻道工夫远远高于旋转提早,那么 SSTF 就好了。然而,如果寻道比旋转快得多,那么在咱们的例子中,寻道远一点的、在外圈磁道的服务申请 8,比寻道近一点的、在两头磁道的服务申请 16 更好。
在古代驱动器中,查找和旋转大抵相当,因而 SPTF 是有用的,它进步了性能。然而,它在操作系统中实现起来更加艰难,操作系统通常不太分明磁道边界在哪,也不晓得磁头以后的地位(旋转到了哪里)。因而,SPTF 通常在驱动器外部执行 。
其余调度问题
在较早的零碎中,操作系统实现了所有的磁盘调度。然而在古代零碎中,磁盘能够承受多个拆散的申请,而且它们自身具备简单的外部调度程序。因而, 操作系统调度程序通常会抉择它认为最好的几个申请,并将它们全副发送到磁盘。磁盘而后利用其磁头地位和具体的磁道布局信息等外部常识,以最佳可能(SPTF)程序服务于这些申请 。
磁盘调度程序执行的另一个重要相干工作是 I / O 合并(I/O merging)。例如,构想一系列申请读取块 33,而后是 8,而后是 34。在这种状况下,调度程序应该将块 33 和 34 的申请合并(merge)为单个两块申请。 调度程序执行的所有申请都基于合并后的申请。合并在操作系统级别尤其重要,因为它缩小了发送到磁盘的申请数量,从而升高了开销。
古代调度程序关注的最初一个问题是:在向磁盘收回 I / O 之前,零碎应该期待多久?有人认为,即便只有一个磁盘 I /O,也应立即向驱动器发出请求,这种办法被称为工作顾全(work-conserving)。然而,钻研表明, 有时最好期待一段时间,即所谓的非工作顾全(non-work-conserving)办法。通过期待,新的或“更好”的申请可能会达到磁盘,从而进步整体效率 。当然,决定何时期待以及期待多久,可能会很辣手,须要大量实际和察看。