前言
IO体系结构是计算机系统和外部的接口,同时也是操作系统中设计最难的部分,因为存在许多不同的设备和它们的应用,难有统一一致的解决方案。 IO体系结构的设计目标是提供一种系统化方法来控制与外部的交互,并且给操作系统提供有效管理IO所需的信息。
IO功能的逻辑结构
操作系统具有层次的特性,分层的原理是操作系统的功能可以根据其复杂度、特征时间尺度和抽象层次被分开。按照该方法,可以将操作系统组织分成一系列层次。每一层都执行操作系统所需要的功能的一个相关子集,同时给高一层提供服务。理想情况下,这些层被设计为某一层的变化不会影响其他层的改动。因此我们可以把一个问题分解为一些更易于控制的子问题。
一般来说,层次越低,处理的时间尺度越短。操作系统的某些部分必须直接与计算机硬件交互,此时一个时间的时间尺度只有几十个亿分之一秒。而另一端,操作系统必须与用户交互,而用户以一种比较悠闲的速度发出命令,可能是每几秒一次。多层结构非常适合这种情况。
把该原理应用于IO机制可以得到如图所示的组织类型
组织的细节取决与设备的类型和应用程序。图中有三个重要逻辑结构,一个特定的操作系统可能并不完全符合这些结构,但他的基本原则是有效的,且大多数操作系统都通过该类似的途径进行I/O。
本地外围设备
以一种简单的方式来进行通信,如字节流或记录流,会涉及以下几层
逻辑I/O: 该模块把设备当做一个逻辑资源来处理,不关心实际控制设备的细节。逻辑I/O模块代表用户进程管理的一般I/O功能,允许用户进程根据设备标识符以及诸如打开、关闭、读、写之类的简单命令与该设备打交道。
设备I/O: 请求的操作和数据(缓冲的数据、记录等)被转换成适当的I/O指令序列、通道命令和控制器指令。可以使用缓冲技术来提高使用率。
调度和控制: I/O操作的排队、调度实际上发生在该层。这一层处理中断,收集并报告I/O状态。这一层是与I/O模块和设备硬件真正发生交互的软件层。
通信端口
就一个通信设备而言,I/O结构看上去和上面描述的几乎一样。注意差别是逻辑I/O模块被通信体系结构取代,通信体系结果自身也是由许多层组成,一个例子是TCP/IP。
文件系统
该结构常用于支持文件系统的辅存设备上管理I/O。这里用到了上面没有提到的三层
目录管理: 这一层符号文件名为转换为标识符,用标识符可以通过文件描述符表或索引表直接或间接地访问文件。这一层还处理影响文件目录的用户操作,如添加、删除、重新组织等。
文件系统: 这一层处理文件的逻辑结构以及用户指定的操作,如打开、关闭、读、写等,同时管理访问权限
物理组织: 就像考虑到分段和分页结构,虚拟内存地址必须转换为物理内存地址一样。考虑到辅存设备的物理磁道和扇区结构,对于文件和记录的逻辑访问也必须转换为物理外存地址。辅助存储空间和内存缓存区的分配通常也在这一层处理。
I/O缓冲
I/O的一个重要方面是使用缓冲区。缓冲区有I/O实用程序控制,而不是由应用程序进程控制。缓冲可以平滑计算机系统内部速度和I/O设备速度之间的差异。使用缓冲区还把实际的I/O传送从应用程序进程的地址空间分离出来,这就使得操作系统能够更加灵活地执行存储器管理功能。
假设某个用户进程需要从磁盘中读入多个数据块,每次读一块,每块的长度为512字节。这些数据将被读入用户进程地址空间的一个区域,如从虚拟地址1000到1511区域。最简单的方法是对磁盘单元执行一个IO命令,并等待数据传输完毕。可以是忙等待(一直测试设备状态),也可以是进程被中断挂起。
该方法有两个问题:
程序被挂起,等待相对比较慢的I/O完成
该方法干扰了操作系统的交换决策 在数据块传送期间,从1000到1511的虚拟地址单元必须保留在内存中,否则某些数据就可能丢失。如果使用了分页机制,那么至少需要将包含目标地址单元的页锁定在内存中。因此,尽管该进程的一部分页面可能被交换到磁盘,但不可能把该进程全部换出,即使操作系统想不这么做想这么做也不行。还需要注意的是有可能出现单进程死锁。如果一个进程发出一个I/O命令并挂起等待结果,然后在开始I/O操作前被换出,那么该进程被阻塞,其等待I/O事件的发生,此时,I/O操作被阻塞,它等待该进程被换入。为避免死锁,在发出I/O请求之前,参与I/O操作的用户存储空间必须立即锁定在内存中,即使这个I/O操作正在排队,并且在一段时间内不会被执行。
同样的考虑也适用于输出操作。如果一个数据块从用户进程区域直接传送到一个I/O模块,那么在传送过程中,该进程被锁定,并且不会被换出。
为避免这些开销和低效操作,优势为了方便起见,在输入请求发出前就开始执行输入传送,并且在输出请求发出一段时间之后才开始执行输出传送,执行技术称为缓冲。
在讨论各种缓冲方法时,有时候需要区分两类I/O设备:面向块的I/O设备和面向流的I/O设备。
面向块:的设备将信息保存在快中,快的大小通常是固定的,传输过程中一次传送一块。通常可以通过块号访问数据。磁盘和USB智能卡都是面向块的设备。
面向流:以字节流的方式输入输出数据,没有块结构。如终端、打印机、通讯端口、鼠标和其他指示设备以及其他大多数的非辅存设备。
I/O缓冲方案(输入)
单缓冲
是操作系统里最简单的类型,如图b,当用户发出I/O请求时,操作系统给该操作分配一个位于内存中系统部分的缓冲区。 对于面向快的设备,单缓冲方案可以描述如下:输入传送的数据被放到系统缓冲区中。当传送完成时,进程把该快移动到用户空间,并立即请求另一块,这称作预读,或者预先输入。这样做原因是期望这块数据最终会被使用。对于许多计算类型来说,这个假设在大多数情况下是合理的,因为数据通常是被顺序访问的。只有在处理序列的最后,才会读入一个不必要的块。
相对于无系统缓冲的情况,这种方法通常会提高系统速度。用户进程可以在在下一数据块读取的同时,处理已读入的数据块。由于输入发生在系统内存中而不是用户进程内存,因此操作系统可以将该进程换出。但是,这种技术增加了操作系统的逻辑复杂度。操作系统必须记录给用户进程分配系统缓冲区的情况。交换逻辑也受到影响:如果I/O操作所涉及的磁盘和用于交换的磁盘是同一磁盘,则磁盘写操作排队等待将进程换出到同一个设备上是没有意义的。若试图换出进程并释放内存,则要在I/O操作完成后才能开始,而这个时候把进程换出到磁盘已经不合适了。
类似的考虑也可以用于面向块的输出。当准备将数据发送到一台设备时,首先把这些数据从用户空间复制到系统缓冲区,其最终是从系统缓冲区中被写出的。发请求的进程现在可以自由地继续执行,或者在必要是换出。
关于使用单缓冲与不适用缓冲的粗略比较:T为输入一个数据块的时间、C为输入请求的计算时间、M为从系统缓冲区复制到用户内存所需要的时间。如果是无缓冲,则每块的执行时间为T+C;如果有一个缓冲区,执行时间为max[C,T]+M。大多数情况使用单缓冲时每块执行时间显著小于没有使用缓冲的情况。
对于面向流的I/O,单缓冲方案能以每次传送一行的方式或者每次传送一个字节的方式使用。每次传送一行的模式适合于滚动模式的终端(又称为哑终端)。对于这种类型的终端,用户每次输入一行,用回车表示到达行尾,并且输出到终端时也是类似的每次输出一行。行式打印机是这类设备的另一个例子。每次传送一个字节的模式适用于表格模式终端,每次击键对它来说都很重要,还有其他如传感器和控制器的外设,都属于这种类型。
对于每次传送一行的I/O,可以用缓冲区保持单一行数据。在输入期间用户进程被挂起,等待整行的到达。对于输出,用户进程可以报一行输出放置在缓冲区,然后继续执行。它不需要挂起,除非在第一次输出操作的缓冲区内容清空之前,又需要发生第二行输出。对于每次传送一个字节的I/O,操作系统和用户进程直接的交互参照生产者/消费者模型。
双缓冲
作为对单缓冲方案的改进,可以给操作分配两个系统缓冲区,如图c所示。在一个进程往一个缓冲区传送数据(或从这个缓冲区中取数据)的同时,操作系统正在清空(或填充)另一个缓冲区,该技术称作双缓冲或缓冲交换。
对于面向块的传送,可以粗略地估计执行时间为max[C,T]。如果C <= T,则有可能使面向块的设备全速运行;另一方面,如果C > T,双缓冲能确保该进程不需要等待I/O。在任何一种情况下,比单缓冲都有所提高,但这种提高是以增加了复杂性为代价的。
对面向流的输入,再次面临两种可选择的操作模式。对于每次传送一行的I/O,用户进程不需要为输入或输出挂起,除非该进程的运行超过双缓冲的速度。对于每次传送一个字节的操作,双缓冲具有两倍的单缓冲相比,并没有特别的优势。这两个情况都采用生产者/消费者模型。
循环缓冲
双缓冲方案可以平滑I/O设备和进程之间的数据流。如果关注的焦点是某个特定进程的性能,那么常常会希望相关I/O操作能够跟上这个进程。如果该进程需要爆发式的执行大量的I/O操作,仅有双缓冲就不够了,在这种情况下,通常使用对于两个缓冲区的方案来缓解不足。
当使用两个以上的缓冲区时,这组缓冲区自身被当做循环缓冲区,如图d,其中的每一个缓冲区是这个循环缓冲区的一个单元。
缓冲的作用
缓冲是用来平滑I/O需求峰值的一种技术,但是当进程的平均需求大于I/O设备的服务能力时,缓冲再多也不能让I/O设备与这个进程一直并驾齐驱。即使有多个缓冲区,所以得缓冲区终将会被填满,进程在处理完每一大块数据后不得不等待。但是在多道程序设计环节中,当存在多种I/O活动和多种进程活动时,缓冲是提高操作系统效率和单个进程性能的一种方法。
磁盘调度与磁盘高速缓存
对整个系统性能产生重要影响的是磁盘I/O,因而关于该领域的研究和设计工作远远超过了其他任何一种类型的I/O。为提高I/O的性能,使用最广泛的两个方法是磁盘调度和磁盘高速缓存。
磁盘调度
在任何时候,总是有一个关于同一个磁盘上的I/O请求队列,这正是磁盘调度的对象,磁盘调度的目的是按某种方式满足这些请求,并使得磁盘的机械寻道时间最小,从而提高性能。其中被挂起请求的物理布局与对局部性的考虑在调度中起着主要作用。
磁盘性能参数
当磁盘驱动器工作时,磁盘以一种恒定的速度旋转。为了读或写,磁头必须定位于指定的磁道和该磁道中指定的扇区的开始处。磁道旋转包括在活动头系统中移动磁头或者在固定头系统中电子旋转一个磁头。在活动头系统中,磁头定位到磁道所需要的时间称作寻道时间。一旦选择好磁道,磁盘控制器就开始等待,直到适当的扇区旋转到磁头处。磁头到达扇区开始位置的时间称作旋转延迟。寻道时间与旋转延迟的总和为存取时间,这是到达读或写位置所需要的时间。一旦磁头定位完成,磁头就通过下面选择的扇区执行读或写操作。这是操作的数据传输部分,传输所需时间为传输时间。
除了存取时间和传送时间之外,一次磁盘I/O操作通常还会有一些排队延迟。当进程发出一个I/O请求时,它必须首先在一个队列中等待该设备可用。在合适的时候,该设备被分配给这个进程。如果该设备与其他磁盘驱动器共享一个或一组I/O通道,还可能需要额外的等待时间,直到该通道可用,之后才开始访问磁盘。
在一些高端服务器系统中,使用了一种称为旋转定位感知的技术。具体工作流程如下:当发出一个寻道命令时,通道被释放以处理其他I/O操作;当寻道完成后,设备确定何时数据旋转到磁头下面;当该扇区接近磁头时,这个设备视图重新建立到逐渐的通信路径;如果控制单元或通道正忙于处理另一个I/O,则重新连接的尝试失败,设备必须旋转一周,然后才可以再次尝试重新连接,这一次称作RPS失败,是个除上面所述的额外延迟。
寻道时间: 是将磁头臂移动到指定磁道所需的时间。事实证明这个时间很难减少。寻道时间有两个重要部分组成:最初启动时间和访问臂到达一定速度是横跨那些必须跨越的磁道所需要的时间。横跨磁道的时间不是关于磁道数目的线性函数,还包括一个稳定时间(从磁头定位于目标磁道直到确认磁道标识之间的时间)。许多提高都来自于更小更轻的磁盘部件,以前的磁盘直径有14英寸,如今常见大学为3.5英寸,减少磁头臂所需移动的距离。
旋转延迟: 旋转延迟是指将磁盘的待访问地址区域旋转到读/写磁头可访问的位置所需要的时间。是磁盘而非软盘,器旋转速度从3600r/m(如数码相机等手持设备)到15000r/m(15000r/m为每4ms转一圈)。因此,在此速度下,平均旋转延迟为2ms。软盘的转速通常在300r/m到600r/m之间,因而其平均延迟在50ms到100ms之间。
传送时间: 网磁盘传送或从磁盘传送的时间取决于磁盘的旋转速度,公式为:
T = b/rNT=b/rN
其中T表示传送时间,b表示要传送的字节数,N表示一个磁道中的字节数,r表示旋转速度(r/s 转/秒) 因此总的平均存取时间可以表示为:
Ta = Ts+1/2r+b/rNTa=Ts+1/2r+b/rN
其中,Ts为平局寻道时间。
时序比较: 通过前面定义的参数,现在考虑两种不同的I/O操作来说明依赖平均值的危险性。考虑一个典型的磁盘。平均寻道时间为4ms,转速为7500r/m,每个磁道有500个扇区,每个扇区有512个字节。如果读取一个包含2500个扇区,大小为1.28M的文件。
首先假设文件尽可能紧凑地保存在磁盘上,即文件占据5个相邻磁道中的所有扇区(5磁道*500个扇区/磁道=2500个扇区),这就是通常所说的顺序组织。现在,读第一个磁道的时间为:平均寻道(4ms)+旋转延迟(4ms)+读500个扇区(8ms)=16ms
假设不需要寻道时间而读取其余的磁道,即I/O操作可以跟得上来自磁盘的数据流。则最多需要为随后的每个磁道处理旋转延迟。因此后面每个磁道可以在4+8=12ms内读入,则读入总文件耗时16+(4*12)=0.064s
如果是随机访问的情况下读相同的数据所需要的时间(对扇区的访问随机分布在磁盘上)。每个扇区所需时间:平均寻道(4ms)+旋转延迟(4ms)+读一个扇区(0.016ms)=8.016ms;总时间=2500*8.016=20.04s
显然,从磁盘读扇区的顺序对I/O的性能有很大的影响。当文件访问需要读写多个扇区时,可以对数据使用扇区的方式进行一定的控制。但即使在访问同一个文件的多道程序环境中,也会出现I/O请求竞争同一个磁盘的情况。
磁盘调度策略
在前面所述的例子中,产生性能差异的原因可以追溯到寻道时间。如果扇区访问请求包括随机选择磁道,磁道I/O系统的性能非常低。为提高性能需要减少花费在寻道上的时间。
考虑一种在多道程序环境中的典型情况。操作系统为每个I/O设备维护一条请求队列。因此对一个磁盘,队列中可能有来自多个进程的许多I/O请求(写和读)。如果随机地从队列中选择项目,那么磁道I/O完全是被随机访问的,这种情况下的性能最差。随机调度可用于其他技术进行对比,以评估这些技术。
假设磁盘有200个磁道,磁道请求队列中是一些随机请求。被请求的磁道按照磁盘调度程序接收顺序分别为55、58、39、18、90、160、150、38、184。下表给出不同算法的调度结果(都是从磁道100处开始)
调度过程表
FIFO 访问的磁道 | FIFO 横跨磁道数 | SSTF 访问的磁道 | SSTF 横跨磁道数 | SCAN 访问的磁道 | SCAN 横跨磁道数 | C-SCAN 访问的磁道 | C-SCAN 横跨磁道数 |
---|---|---|---|---|---|---|---|
55 | 45 | 90 | 10 | 150 | 50 | 150 | 50 |
58 | 3 | 58 | 32 | 160 | 10 | 160 | 10 |
39 | 19 | 55 | 3 | 184 | 24 | 184 | 24 |
18 | 21 | 39 | 16 | 90 | 94 | 18 | 166 |
90 | 72 | 38 | 1 | 58 | 32 | 38 | 20 |
160 | 70 | 18 | 20 | 55 | 3 | 39 | 1 |
150 | 10 | 150 | 132 | 39 | 16 | 55 | 16 |
38 | 112 | 160 | 10 | 38 | 1 | 58 | 3 |
184 | 146 | 184 | 24 | 18 | 20 | 90 | 32 |
平均寻道长度: | 55.3 | 平均寻道长度: | 27.5 | 平均寻道长度: | 27.8 | 平均寻道长度: | 35.8 |
磁盘调度算法
名称 | 解释 | 特点 |
---|---|---|
RSS | 随机调度 | 用于分析和模拟 |
FIFO | 先进先出 | 最公平的调度 |
PRI | 进程优先级 | 在磁盘队列管理之外控制 |
LIFO | 后进先出 | 局部性最好,资源的使用率最高 |
SSTF | 最短服务时间优先 | 使用率高,队列小 |
SCAN | 在磁盘上往复 | 服务分布比较好 |
C-SCAN | 一条道路,快速返回 | 服务变化低 |
N-step-SCAN | 一次N个记录的SCAN | 服务保证 |
FSCAN | N-step-SCAN,N=SCAN循环开始处的队列大小使用率 | 负载敏感 |
先进先出 FIFO
最简单的调度,按顺序处理队列中的项目。改策略具有公平性的优点,因为每个请求都会得到处理,并且是按照接收到的顺序进行处理的。由调度过程表可知,磁盘的访问顺序和请求被最初接收到的顺序是一致的。
使用FIFO时,如果只有一些进程需要访问,且大多数请求是访问簇聚的文件扇区,则有望达到较好的性能。但是如果有大量进程竞争一个磁盘,这种技术在性能上往往接近于随机调度。因此需要考虑一些更复杂的调度策略。
进程优先级 PRI
对于基于优先级 PRI的系统,有关调度的控制在磁盘管理软件控制之外。这种方法并不会优化磁盘的使用率,但可以满足操作系统的其他目标。通常比较短的批作业和交互作业的优先级高,而较长计算时间的长作业的优先级较低。这就使得大量的短作业能够迅速地通过系统,并且可以提供比较好的交互响应时间。但是长作业可能不打不等待过长的时间。此外,这种策略可能会导致部分用户采用对抗手段:把作业分成小块以应对系统的这种策略。对于数据库系统,这类策略往往导致性能较差。
后进先出 LIFO
改选取最近请求的策略有许多优点:在事务处理系统中,把设备资源提供给最近的用户,会导致磁头臂在一个顺序文件中移动时移动得很少,甚至不移动。利用这种局部性溃疡提高吞吐量,减少队列长调。只要一个作业积极地使用文件系统,它就可以尽可能地得到处理。但是如果由于工作量大而是磁盘保持忙状态,就有可能出现饿死的情况。当一个作业已经往队列中送入一个I/O请求,并且错过了可以提供服务的位置时,改作业可能永远得不到服务,除非它前面的队列变为空。
FIFO、PRI和LIFI调度都仅仅基于队列或请求者的属性。如果调度程序知道当前磁道位置,就可以采用基于被请求项的调度策略
最短服务时间优先 SSTF
选择磁头臂从当前位置开始移动最小的磁盘I/O请求。因此,SSTF策略总是选择导致最小寻道时间的请求。当然总数选择最小寻道时间并不能保证平均寻道时间最小。但是能提供比FIFO更好的性能。由于磁头臂可以沿着两个方向移动,因此可以使用一种随机选择算法解决距离相等的情况。
扫描 SCAN
除了FIFO,上面的策略都可能使某些请求直到整个队列为空时才可以完成。即有可能总有新请求到达,并且在队列存在的请求之前被选择。为避免出现这类饥饿的情况,比较简单的做法是SCAN算法,由于其运行和电梯类似,因此也被称为电梯算法。
从调度过程表表可知。假设SCAN最初按照磁道序号递增的方向,则第一个选择的磁道是150,因为该磁道是递增方向距磁道100最近的磁道。
SCAN和SSTF非常相似,实际上如果在例子开始时磁头臂沿磁道号减少的方向移动,那么SSTF和SCAN的调度方式是相同的。但这仅仅是一个静态的例子,队列在这期间不会增加新的请求。甚至当队列动态变化时,除非请求模式不符合常规,否则SCAN仍然类似于SSTF。
SCAN对最近横跨过的区域不公平,因此在开发局部性方面不如SSTF和LIFO。
SCAN偏爱请求接近最靠里或靠外的磁道的作业,且偏爱最近的作业。第一个可以通过C-SCAN避免,第二个可通过N-step-SCAN避免。
循环SCAN C-SCAN
扫表一个限定的方向上,因此当访问到最后一个磁道时,磁头臂返回到磁盘相反方向磁道的末端,并再次开始扫描。这就减少了新请求的最大延迟,对于SCAN。如果从最里面的磁道扫描到最外面的磁道的期望时间为t,则这个外设的扇区期望服务间隔为2t。而对于C-SCAN,间隔约为t+s,其中s为最大寻道时间。
N-step-SCAN N步扫描和FSCAN
SSTF、SCAN和C-SCAN,磁头臂可能在很长一段时间内不会移动,如果一个或多个进程对应磁道有较高的访问速度,它们可以通过重复请求这个磁道以垄断整个设备。高密对多面磁盘比低密度磁盘以及单面或双面磁盘更容易受到这种特性的影响。为避免这种“磁头臂的粘性”,磁盘请求队列被分成段,一次只有一段被完全处理,这种方法的两个例子是N-step-SCAN N步扫描和FSCAN
N-step-SCAN策略把请求分成长度为N的的子队列,每一次用SCAN处理一个子队列。在处理一个队列时,新请求必须添加到其他某个队列中,如果在扫描的最后剩下的琦琦书小于N,则它们全都将在笑一次扫描是处理。对于比较大的N值,N-step_SCAN的性能与SCAN接近;当N=1时,实践上就是FIFO。
FSCAN是一种使用两个子队列的策略。当扫描开始时,所有请求都在一个队列中,而另一个队列为空。在扫描过程中所有新到的请求都被放入另一个队列中。因此对新请求的服务延迟到处理完所有老请求之后。
磁盘高速缓存
磁盘高速缓存是一个缓冲区。通常保存在内存中,充当磁盘块在磁盘存储器和其余内存之间的高速缓存。由于局部性的原理,磁盘高速缓存的使用可以充分地减少内存和磁盘之间I/O传送的快数。
高速缓冲存储器通常指一个比内存小且比内存快的存储器,改存储器位于内存与处理器直接。这种高速缓存存储器通过利用局部性原理,可以减少平均存储器存取时间。
同样的原理可以用于磁盘存储器。一个磁盘高速缓存是内存中为磁盘扇区设置的一个缓冲区,它包含有磁盘中某些扇区的副本。当出现一个请求某一特定扇区的I/O请求时,首先进行检测,以确定改扇区是否在磁盘高速缓存中。如果在则改期可以通过这个高速缓存来满足;如果不存在,则把被请求的扇区从磁盘读到磁盘高速缓存中。由于访问的局部性现象的存在,当一块数据被取入高速缓存以满足一个I/O请求时,很有可能将来还会访问到这一块数据。
设计考虑
首先,当一个I/O请求从磁盘高速缓存中得到满足时,磁盘高速缓存中的数据必须传送到发送请求的进程。这可以通过在内存中把这一块数据从磁盘高速缓存传送到分配给改用户的存储空间中,或者简单地通过使用一个共享内存,传送指向磁盘高速缓存中响应项的指针。后一种方法节省了内存到内存的传送时间,并且允许其他进程使用读者-写者模型进行共享访问。
其次是置换策略。当一个新扇区被读入磁盘高速缓存时,必须置换出来一个已存在的块。最常用的算法是最近最少使用算法LRU:置换在高速缓存中未被访问的时间最长的块。逻辑上,高速缓存有一个关于块的栈组成,最近访问过的块在栈顶。当高速缓存中的一块被访问到时,它从栈中当前的位置移到栈顶。当一个快从辅存中取入时,包位于栈底的一个移出,并把新到来的块压入栈顶。
另一种看的算法是最不常用算法(LFU):置换集合中被访问次数最少的块。LFU可以通过给没个块关联一个计数器来实现。当一个块被读入时,它的计数器为1;当每次访问到这一块时,它的计数器加1.当需要置换时,选择计数器最小的块。直觉上LFU比LRU更适合,因为LFU使用了关于每个块的更多的相关信息。
一个简单的LFU算法有以下问题:可能存在一些块,从整体看很少发生对它们的访问,当时它们被访问时,由于局部性原理,会在一段很短的时间间隔里出现很多次重复访问,从而使访问计数器的值很高。当这个间隔过去后,访问计数器的值可能会让人误解,它并不表示很快又会访问到这一块。因此受局部性影响,LFU算法不是一个好的置换算法。
为克服上面的缺点,有一种基于频率的置换算法。一个简化的版本是:块在逻辑上被组织为一个栈。栈顶的一部分留作一个新区。当出现一次高速缓存命中时,被访问的块移到栈顶。如果该快已经在这个新区中,它的访问计数器不会增加,否则加1。如果有足够大的新区,在一个短时间内被重复访问的块的访问计数器不会改变,当有一次未命中时,访问计数器最小且不在新区的块被置换出。如果有不只一个这样的块,则选择近期最少使用的块。
改策略被LRU仅有略微的提高,并存在一下问题:
当出现一次高速缓存未命中时,一个块被取入新区,计数器为1。
只有该块留在新区中,计数器的值保持为1。
最终这个块的年龄超出了新区,但它的计数器仍为1。
如果这个块没有很快的被再次访问到,很有可能被置换,因为与那些不在新区的块相比,其访问计数器为最小的,最后移出新区后没有足够长的时间间隔让它们建立新的访问计数。
关于这个问题的进一步改进的方案是,将栈划为三个区:新区、中区、老区。类型上面,位于新区的块,访问计数器不会增加;老区的块才符合置换条件。假设有足够大的中间区,这就使得相对比较频繁地被访问到的块,在它们变成符合置换条件的块之前,有机会增加自己的访问计数器。
不论采用哪种特殊的置换策略,置换都可以按需求发送或预先发送。对LRU情况,只有当需要用到存储槽时才置换这个扇区。对于LFU情况,一次可以释放许多存储槽。使用LFU的原因与写回扇区的要求相关。如果一个扇区被读入高速缓存,=并且仅仅用于读,那么当它被置换时,并不需要写回磁盘。但是如果改扇区已经被修改了,则在他被置换出之前必须写回磁盘,这时成簇地写回并按顺序写以减少寻道时间是非常有意义的。
性能考虑
高速缓存的性能问题可以简化成是否可以到达某个给定的未命中率,这取决于访问磁盘的局部性行为、置换算法和其他设计因素。但是,未命中率主要是关于磁盘高速缓存大小的函数。下图概括了使用LRU的多个研究结果,一个是运行在VAX上的UNIX系统,一个是IBM大型机操作系统。下图给出了基于频率的置换算法的模拟研究结果。通过这两个图的比较可以登出这类性能评估的一个风险。这些图看上去说明了LRU的性能由于基于频率的置换算法,但是当使用相同高速缓存结构和相同访问模式时,再对它们比较则基于拼了的置换算法由于LRU。因此访问模式的顺序和相关的设计问题(如块大小)将对性能产生重要的影响。
使用LRU的磁盘高速缓存性能的结果
使用基于频率置换算法的磁盘高速缓存性能