您现在的位置是:首页 > 数码 > 

操作系统OSTEP 虚拟化

2025-07-27 19:55:02
操作系统OSTEP 虚拟化 虚拟化 抽象:进程 进程的非正式定义非常简单:进程就是运行中的程序 操作系统如何提供几乎有无数个CPU可用的假象? 操作系统通过虚拟化CPU来提供这种假象 通过让一个进程只允许一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟CPU的假象。这就是时分共享CPU技术,允

操作系统OSTEP 虚拟化

虚拟化

抽象:进程

进程的非正式定义非常简单:进程就是运行中的程序

操作系统如何提供几乎有无数个CPU可用的假象?

操作系统通过虚拟化CPU来提供这种假象

通过让一个进程只允许一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟CPU的假象。这就是时分共享CPU技术,允许用户如愿允许多个并发过程

要实现CPU的虚拟化,操作系统就需要一些低级机制以及一些高级智能。我们将低级机制称为机制。机制是一些低级方法或协议,实现了所需的功能

时分共享是操作系统共享资源所使用的最基本的技术之一。通过允许资源由一个实体使用一小段时间,然后由另一个实体使用一小段时间,如此下去,所谓的资源(例如,CPU或网络连接)可以被许多人共享。时分共享自然对象技术是空分共享,资源在空间上被划分给希望使用它的人

在这些机制之上。操作系统中有一些智能以策略的形式存在。策略是在操作系统内做出某种决定的算法

4.1 抽象:进程

进程的及其状态有一个明显组成部分,就是它的内存。指令存在内存中,正在运行的程序读取和写入的数据也在内存中。因此进程可以访问的内存(称为地址空间)是该进程的一部分。

有一些非常特殊的寄存器构成了该机器状态的一部分。例如:程序计数器(PC)(有时称为指令指针,IP)告诉我们程序即将执行哪个指令;类似地,栈指针和相关的帧指针用于管理函数参数栈、局部变量和返回地址

在许多操作系统中,一个通用的设计范式是将高级策略与其低级机制分开。你可以将机制看成为系统的“如何(how)“问题提供答案。策略为”哪个(which)”问题提供答案,将两者分开可以轻松地改变策略。而不必重新考虑机制,因此这是一种模块化的形式,一种通用的软件设计原则

程序也经常访问持久存储设备。此类I/O信息可能包含当前打开的文件列表

4.2 进程API
  • 创建(create):操作系统必须包含一些创建新进程的方法。
  • 销毁(destroy):由于存在创建进程的接口,因此系统还提供了一个强制销毁进程的接口。当然,很多进程会在运行完成后自行退出。但是,如果他们不退出,用户可能希望终止它们,因此停止失控进程的接口非常有用
  • 等待(wait):有时等待进程停止运行是有用的,因此经常提供某种等待接口
  • 其他控制(miscellaneous control):除了杀死或等待进程外,有时还可能有其他控制。例如:大多数操作系统提供某种方法来暂停进程(停止运行一段时间),然后恢复(继续运行)
  • 状态(status):通常也有一些接口可以获得有关进程的状态信息,例如运行了多长时间,或者处于什么状态
4. 进程创建:更多细节

操作系统如何启动并运行一个程序?进程创建实际如何进行?

  • 操作系统运行程序必须做的第一件事是将代码和所有静态数据(例如初始化变量)加载到内存中,加载到进程的地址空间中。程序最初以某种可执行格式驻留在磁盘上。因此,将程序和静态数据加载到内存中的过程,需要操作系统从磁盘读取这些字节,并将它们放在内存中的某处

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kJAwFD0-161586117444)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1612626807018.jpg)]

在早期的操作系统中,加载过程尽早完成,即在运行程序之前全部完成。线代系统惰性执行该过程,即仅在程序执行期间需要加载的代码或数据片段,才会加载。

  • 将代码和静态数据加载到内存后,操作系统在运行此进程之前还需要执行其他一些操作。必须为程序的运行时栈分配一些内存。C程序使用栈存放局部变量、函数参数和返回地址,操作系统分配这些内存,并提供给进程。操作系统也可能会用参数初始化栈。

  • 操作系统也可能为程序的堆分配一些内存。在C程序中,堆用于显式请求的动态分配数据。起初堆会很小,随着程序运行,同malloc()库API请求更多内存,操作系统可能会参与分配更多内存给进程以满足这些调用

  • 操作系统还将执行一些其他初始任务,特别是与输入/输出(I/O)相关的任务

  • 最后一项任务:启动程序在入口处,即main()。通过跳转到main()例程,OS将CPU的控制权转移到新创建的进程中,从而程序开始执行

4.4 进程状态

进程可以处于以下种状态之一

  • 运行(running):在运行状态下,进程正在处理器上运行。这意味着它正在执行指令。
  • 就绪(ready):在就绪状态下,进程已准备好运行,但由于某种原因,操作系统选择不在此时运行
  • 阻塞(blocked):在阻塞状态下,一个进程执行了某种操作,直到发生其他时间时才会准备运行。一个常见的例子是,当进程向磁盘发起了I/O请求时,它会被阻塞,因此其他进程可以使用处理器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bCGWxyK-161586117447)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161262821505.jpg)]

4.5 数据结构

操作系统是一个程序,和其他程序一样,它有一些关键的数据结构来跟踪各种相关信息。例如,为了跟踪每个进程的状态,操作系统可能会为所有就绪的进程保留某种进程列表,以及跟踪当前正在运行的进程的一些附加信息。操作系统还必须以某种方式跟踪被阻塞的进程。当I/O事件完成时,操作系统应确保唤醒正确的进程,让它准备好再次运行。

有时候人们会将存储关于进程的信息的个体结构称为进程控制块,这是谈论包含每个进程信息的C结构的一种方式

机制:受限直接执行

如何高效、可控地虚拟化CPU?

6.1 基本技巧:受限直接执行

当OS希望启动程序运行时,它会在进程列表中为其创建一个进程条目,为其分配一些内存,将程序代码(从磁盘)加载到内存中,到入口点,跳转到那里,并开始运行用户的代码

6.2 问题1:受限制的操作

一个进程必须能够执行I/O和其他一些受限制的操作,但又不能让进程完全控制系统。操作系统与硬件如何写作实现这一点?

							提示:采用受保护的控制权转移
硬件通过提供不同的执行模式来协助操作系统。在用户模式下,应用程序不能完全访问硬件资源。在内核模式下,操作系统可以访问机器的全部资源。还提供了陷入内核和从陷阱返回到用户模式程序的特别说明,以及一些指令,让操作系统告诉硬件陷阱表在内存中的位置										

我们采用的方法时引入一种新的处理器模式,称为用户模式。在用户模式下运行的代码会受到限制。例如:在用户模式下运行时,进程不能发出I/O请求。这样做会导致处理器引发异常,操作系统可能会终止进程。

与用户模式不同的内核模式,操作系统(或内核)就以这种模式运行。在此模式下,运行的代码可以做它喜欢的事,包括特权操作,如发出I/O请求和执行所有类型的受限指令。


如果用户希望执行某种特权操作(如从磁盘读取)应该怎么做?

为了实现这一点,几乎所有的现代硬件都提供了用户程序执行系统调用的能力,它允许内核小心地向用户程序暴露某些关键功能。

要执行系统调用,程序必须执行特殊的陷阱指令。该指令同时跳入内核并将特权级别提升到内核模式。一旦进入内核,系统就可以执行任何需要的特权操作(如果允许),从而为调用进程执行所需的工作。完成后,操作系统调用一个特殊的从陷阱返回指令,如你期望的那样,该指令返回到发起调用的用户程序中,同时将特权级别降低,回到用户模式。

执行陷阱时,硬件需要小心,因为它必须确保存储足够的调用者寄存器,以便在操作系统发出从陷阱返回指令时能够正确返回。

为什么系统调用看起来像过程调用?
它是一个过程调用,但隐藏在过程调用内部的是著名的陷阱指令

陷阱如何知道在OS内运行哪些代码?

内核通过在启动时设置陷阱表来实现。当机器启动时,它在特权模式下执行,因此可以根据需要自由配置机器硬件。操作系统做的第一件事,就是告诉硬件在发生某些异常事件是要运行哪些代码。操作系统通常通过某种特殊的指令,通知硬件这些陷阱处理程序的位置。一旦硬件被通知,它就会记住这些处理程序的位置,直到下一次重新启动机器,并且硬件直到在发生系统调用和其他异常事件时要做什么(即跳转到哪段代码)

能够执行指令来告诉硬件陷阱表的位置时一个非常强大的功能,这也是一项特权操作。如果你试图在用户模式下执行这个指令,硬件不会允许。

时间线总结了该协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ckq2bfOZ-161586117447)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161269675516.jpg)]

6. 问题2:在进程之间切换

操作系统如何重新获得CPU的控制权,以便它可以在进程之间切换?

协作方式:等待系统调用

过去某些系统采用的一种方式称为协作方式。在这种风格下,操作系统相信系统的进程会合理运行。运行时间过长的进程被假定会定期放弃CPU,一遍操作系统可以决定运行其他任务。

一个友好的进程如何放弃CPU?事实证明,大多数进程通过进行系统调用。将CPU的控制权转移给操作系统。

提示:处理应用程序的不当行为
操作系统通常必须处理不当行为,这些程序通过设计(恶意)或不小心(错误),尝试做某些不应该做的事情。在现代系统中,操作系统试图处理这种不当行为的方式时简单地终止犯罪者。

如果应用程序执行了某些非法操作,也会将控制转移给操作系统。

因此,在协作调度系统中,OS通过等待系统调用,或某种非法操作发生,从而重新获得CPU的控制权。


非协作方式:操作系统进行控制

事实证明,没有硬件的额外帮助,如果进程拒绝进行系统调用(也不出错),因此不讲控制权交还给操作系统,那么操作系统无法做任何事情。当进程陷入无限循环时,唯一的办法就是使用古老的解决方案来解决计算机系统中的所有问题——重新启动计算机。

即使进程不协作,操作系统如何获得CPU的控制权?操作系统可以做什么来确保流氓进程不会占用机器?

答案很简单:时钟中断。时钟设备可以编程为每隔几毫秒旧产生一次中断。产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序会运行。此时,操作系统重新获得CPU的控制权,因此可以做它想做的事:停止当前进程,并启动另一个进程。

即使进程以非协作的方式运行,添加时钟中断也让操作系统能够在CPU上重新运行。因此,该硬件功能对于帮助操作系统维持机器的控制权至关重要。

注意:硬件在发生中断时有一定责任,尤其是在中断发生时,要为正在运行的程序保存足够的状态,以便随后从陷阱返回指令能够正确恢复正在运行的程序。这一组操作与硬件在显式系统调用陷入内核时的行为非常相似,其中各种寄存器因此被保存(进入内核栈),因此从陷阱返回指令可以容易地恢复


保存和恢复上下文

既然操作系统已经重新获得控制权,就必须决定:是继续运行当前正在运行的进程,还是切换到另一个进程。这个决定是由调度程序做出的,它是操作系统的一部分。

如果决定进行切换,OS就会执行一些底层代码,即所谓的上下文切换。上下文切换在概念上很简单:操作系统要做的就是为当前正在执行的进程保存一些寄存器的值(例如:到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)。这样一来,操作系统就可以确保最后执行从陷阱返回指令时,不是返回到之前运行的进程,而是继续执行另一个进程。

为了保存当前正在运行的进程的上下文,操作系统会执行一些底层汇编代码,来保存通用寄存器、程序计数器1、以及当前正在运行的进程的内核栈指针,然后恢复寄存器、程序计数器,并切换内核栈,供即将运行的进程使用。**通过切换栈,内核在进入切换代码调用时,是一个进程(被中断的进程)的上下文,在返回时,是另一进程(即将执行的进程)的上下文。**当操作系统最终执行从陷阱返回指令时,即将执行的进程变成了当前运行的进程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TtLwo7M4-161586117448)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1612696248414.jpg)]

在此协议中,有两种类型的寄存器保存/恢复。

  • 第一种是发生时钟中断的时候。在这种情况下,运行进程的用户寄存器由硬件隐式保存,使用该进程的内核栈。
  • 第二种事当操作系统决定从A切换到B。在这种情况下,内核寄存器被软件(即OS)明确地保存,但这次被存储在该进程结构的内存中。后一个操作让系统才从好像刚刚由A陷入内核,变成好像刚刚由B陷入内核

进程调度:介绍

我们如何开发一个考虑调度策略的基本框架?什么是关键假设?哪些指标非常重要?哪些基本方法已经在早期的系统中使用?

7.1 工作负载假设

探讨可能的策略范围之前,我们先做一些简化假设。这些假设与系统中运行的进程有关,有时候统称为工作负载。确定工作负载是构建调度策略的关键部分。工作负载了解的越多。你的策略就越优化。

我们对操作系统中运行的进程做出如下的假设:

  1. 每一个工作运行相同的时间
  2. 所有的工作同时到达
  3. 一旦开始,每个工作保持运行直到完成
  4. 所有的工作只是用CPU(即它们不执行I/O操作)
  5. 每个工作的运行时间是已知的
7.2 调度指标

除了做出工作负责假设之外,还需要一个东西能让我们比较不同的调度策略:调度指标。指标是我们用来衡量某些东西的东西,在进程调度中,有一些不同的指标是有意义的。

现在,我们只用一个指标:周转时间。任务的周转时间定义为任务完成时间减去任务到达系统的时间。更正式的周转时间定位T周转时间是:

T周转时间 = T完成时间-T到达时间

因为我们假设所有的任务在同一时间达到,那么T到达时间=0,因此T周转时间 = T完成时间

周转时间是一个性能指标,另一个有趣的指标是公平。性能和公平在调度系统中往往是矛盾的。例如:调度程序可以你优化性能,但代价是以阻止一些任务运行,这就降低了公平。

7. 先进先出(FIFO)

我们可以实现的最基本的算法,被称为先进先出(First In First Out或FIFO)调度,有时候也称为先到先服务(FCFS)

FIFO有一些积极的特性:它很简单,而且易于实现。而且,对于我们的假设,它的效果很好。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsz6DyJe-161586117449)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1612698557991.jpg)]

护航效应,一些耗时较少的潜在资源消费者被排在重量级的资源消费者之后。

7.4 最短任务优先(SJF)

事实上,考虑到所有工作同时到达的假设,我们可以证明SJF确实是一个最有调度算法。

但是,当我们放宽另一个假设,具体来说,我们可以针对假设2,现在假设工作随时到达,而不是同时到达。

抢占式调度程序
在过去的批处理计算中,开发了一些非抢占式调度程序。这一的系统会将每项工作做完,在考虑是否允许新工作。几乎所有现代化的调底程序都是抢占式的,非常愿意停止一个进程以运行另一个进程。这意味着调度程序采用了我们之前学习的机制。特别是调度程序可以进程上下文切换,临时停止一个运行进程,并恢复另一个进程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ewMjvhp-161586117449)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-202102071957410.png)]

7.5 最短完成时间优先(STCF)

向SJF添加抢占,称为最短完成时间优先(STCF)或抢占式最短作业优先(PSJF)调度程序。每当新工作进入系统时,它就会确定剩余工作和新工作中,谁的剩余时间最少,然后调度该工作。

因此,在例子中,STCF将抢占A运行B和C以完成。只有在它们完成后,才能调度A的剩余时间。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-et9eGBpD-161586117450)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-20210207200402614.png)]

7.6 新度量指标:响应时间

因此,如果我们知道任务长度,而且任务只使用CPU,而我们唯一的衡量是周转时间,STCF将是一个很好的策略。

然而,引入分时系统改变了这一切,因此,一个新的度量标准诞生了:响应时间。

响应时间定义为从任务到达系统到首次运行的时间。更正式的定义是:

T响应时间=T首次运行-T到达时间

STCF和相关方法在响应时间上并不是很好。还有一个问题:如何构建对响应时间敏感的调度程序?

7.7 轮转

轮转(Round-Robin,RR)调度基本思想:RR在一个时间片(time slice,有时被称为调度量子)内运行一个工作,然后切换到运行队列中的下一个任务,而不是运行一个任务到结束。它反复执行,直到所有任务完成。

因此,RR有时被称为时间切片。注意:时间片长度必须是时钟中断周期的倍数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21r88777-161586117450)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161271069754.jpg)]

然而,时间片太短是有问题的:突然上下文切换的成本将影响整体性能。因此,系统设计者需要权衡时间片的长度,使其足够长,以便摊销上下文切换成本,而又不会使系统不及时响应。

提示:摊销可以减少成本
当系统某些操作有固定成本时,通常会使用摊销技术。通过减少成本的频度(即执行较少次的操作),系统的总成本就会降低。

注意:上下文切换的成本不仅仅来自保存和恢复少量寄存器的操作系统操作。程序运行时,它们在CPU高速缓存、TLB、分支预测器和其他片上硬件建立了大量状态。切换到另一个工作会导致此状态被刷新,且与当前运行的作业相关的新状态被引入,这可能导致显著的性能成本。

任何公平的政策(如RR),即在小规模的时间内将CPU均匀分配到活动进程之间,在周转时间这类指标上表现不佳。事实上,这是固有的权衡:如果你愿意不公平,你可以运行较短的工作直到完成,但是要以响应时间为代价。如果你中使公平性,则响应时间会较短,但会以周转时间为代价。

所以开发了两种调度程序,第一种类型(SJF,STCF)优化周转时间,但对响应时间不利。第二种类型(RR)优化响应时间,但对周转时间不利。

重叠可以提供利用率
如有可能,重叠操作可以最大限度地提供系统的利用率。重叠在许多不同的领域很有用,包括执行磁盘I/O或将消息发送到远程机器时。在任何一种情况下,开始操作然后切换到其他工作都是一个好主意,这也提供了系统的整体利用率和效率。

7.8 结合I/O

首先,我们将放宽假设4:当然所有程序都执行I/O。调度程序显然要在工作发起I/O请求时做决定,因为当前正在运行的作业在I/O期间不会使用CPU,它被阻塞等待I/O完成。因此,这是调度程序应该在CPU上安排另一项工作。

调度程序还必须在I/O完成时做出决定。发送这种情况时,会产生中断,操作系统运行并将发出I/O的进程从阻塞状态移回就绪状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qeEPQDLY-161586117451)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\16127117199.jpg)]

这也做可以实现重叠,一个进程在等待另一个进程的I/O完成时使用CPU,系统因此得到更好的利用。

调度:多级反馈队列(MLFQ)

没有工作长度的先验知识,如何设计一个能同时减少响应时间和周转时间的调度程序?

8.1 MLFQ:基本规则

MLFQ中有许多独立的队列,每个队列有不同的优先级。任何时刻,一个工作只能存在于一个队列中。MLFQ总是优先执行较高优先级的工作(即在较高级队列中的工作)。

当然,每个队列中可能会有多个工作,因此具有同样的优先级。在这种情况下,我们就对这些工作采用轮转调度。

因此,MLFQ调度策略的关键在于如何设置优先级。MLFQ没有为每个工作指定不变的优先顺序而已,而是根据观察到的行为调整它的优先级。

MLFQ的两条基本规则:

  • 规则1:如果A的优先级>B的优先级,运行A(不运行B)
  • 规则2:如果A的优先级=B的优先级,轮转运行A和B
8.2 尝试1:如何改变优先级

要做到这一点,我们必须记得工作负载;既有运行时间很短、频繁放弃CPU的交互性工作,也有需要很多CPU时间、响应时间却不重要的长时间计算密集型工作。尝试优先级调整算法:

  • 规则:工作进入系统时,放再最高优先级(最上层队列)
  • 规则4a:工作用完整个时间片后,降低其优先级(移入下一个队列)
  • 规则4b:如果工作在其时间片以内主动释放CPU,则优先级不变

实例1:单个长工作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XPF9dl1M-161586117452)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-202102072569980.png)]

实例2:来了个短工作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HodTE8VP-161586117452)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161271479426.jpg)]

实例:如果有I/O呢

这条规则的意图很简单:假设交互型工作中有大量的I/O操作(比如等待用户的键盘或鼠标输入),它会在时间片用完之前放弃CPU。在这种情况下,我们不想处罚它,只是保持它的优先级不变。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQ7ATsKs-161586117452)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161271507646.jpg)]

8. 尝试2:提升优先级
  • 规则5;经过一段时间S,就将系统种所有工作重新加如最高优先级队列。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HViILe2X-16158611745)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-20210208000405120.png)]

当然,添加时间段S导致了明显的问题:S的值应该如何设置?

8.4 尝试:更好的计时方式

现在还有一个问题要解决:如何阻止调度程序被愚弄?

  • 规则4:一旦工作用完了其在某一层种的时间配额(无论中间主动放弃了多少次CPU),就降低其优先级(移入低一级队列)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBA8mhJs-161586117454)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-202102080009410.png)]

8.5 MLFQ调优及其他问题

调度:比例份额

比例份额调度程序,有时也称为公平份额调度程序。比例份额算法基于一个简单的想法:调度程序的最终目标,是确保每个工作获得一定比例的CPU时间,而不是优化周转时间和响应时间。

如何设计调度程序来按比例分配CPU?其关键的机制是什么?效率如何?

9.1 基本概念:数表示份额

调度背后是一个非常基本的概念:数代表了进程占有某个资源的份额。一个进程拥有的数占总数的百分比,就是它所占有资源的份额。

9.2 机制

调度还提供了一些机制,以不同且有效的方式来调度。一种方式是利用货币的概念。这种方式允许拥有一组的用户以他们喜欢的某种货币,将分给自己的不同工作。之后操作系统再自动将这种货币兑换为正确的全局。

另一个有用的机制是转让。通过转让,一个进程可以临时将自己的交给另一个进程。这种机制在客户端/服务端交互的场景中及其有用,在这种场景中,客户端进程向服务端发送消息,请求其按自己的需求执行工作,为了加速服务端的执行,客户端可以将自己的转让给服务端,从而尽可能加速服务端执行自己请求的速度。服务端执行结束后会将这部分归还给客户端。

最后,通胀有时也很有用。利用通胀,一个进程可以临时提升成降低自己拥有的熟练。

9.4 一个例子

可以看出,当工作执行时间很短时,平均公平度非常糟糕。只有当工作执行非常多的时间片时,调度才能道德期望的结果。

9.6 步长调度

系统中的每个工作都有自己的步长,这个值与票数值成反比。每次进程运行后,我们会让它的计数器[称为行程值]增加它的步长,记录它的总体进展。

调度算法只能一段时间后,在概率上实现比例,而步长调度算法可以在每个调度周期后做到完全正确。

调度有一个步长调度没有的有时——不需要全局状态。因此调度算法能够更合理地处理新加入的进程。

抽象:地址空间

1. 地址空间

操作系统需要提供一个易用的物理内存抽象。这个抽象叫做地址空间,是运行的程序看到的系统中的内存。

一个进程的地址空间包含运行的程序的所有内存状态。比如:程序的代码必须在内存中,因此它们在地址空间里。当程序在运行的时候,利用栈来保存当前的函数调用信息,分配空间给句不变量,传递参数和函数返回值。最后,堆用来管理动态分配的、用户管理的内存,就像从C语言中调用malloc()中调用new获得内存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kcGY0P8X-161586117454)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161278495992.jpg)]

当我们描述地址空间时,所描述的是操作系统提供给运行程序的抽象。程序不在物理地址0~16KB的内存中,而是加载在任意的物理地址。

操作系统如何在单一的物理内存上为多个运行的进程(所有进程共享内存)构建一个私有的、可能很大的地址空间的抽象?

隔离原则
操作系统力求让进程彼此隔离,从而防止相互造成伤害。通过内存隔离,操作系统进一步确保运行程序不会影响底层操作系统的操作。

1.4 目标

虚拟内存系统的一个主要目标是透明。操作系统实现虚拟内存的方式,应该让运行的程序看不见。

虚拟内存的另一个目标是效率。操作系统应该追求虚拟化尽可能高效,包括时间上(即不会使程序运行得更慢)和空间上(即不需要太多额外的内存来支持虚拟化)。

虚拟内存第三个目标使保护。操作系统应确保进程受到保护,不会受其他进程影响,操作系统本身也不会受进程影响。

如果你在一个程序中打印出一个地址,那就是一个虚拟的地址。虚拟地址只是提供地址如何在内存中分布的假象,只有操作系统(和硬件)才知道物理地址。

机制:地址转换

在实现CPU虚拟化时,我们遵循的一般准则被称为受限直接访问(LDE)。LDE背后的想法很简单:让程序运行的大部分指令直接访问硬件,只在一些关键点(如进程发起系统调用或发生时钟中断)由操作系统介入来确保“在正确时间,正确的地点,做正确的事”。

如何实现高效的内存虚拟化?如何提高应用程序所需的灵活性?如何保持控制应用程序可访问的内存位置,从而确保应用程序的内存访问受到合理的限制?如何高效地实现这一切?

基于硬件的地址转换,简称为地址转换。它可以看成是受限直接执行这种一般方法的补充。利用地址转换,硬件对每次内存访问进行处理(即指令获取、数据读取或写入),将指令中的虚拟地址转换为数据实际存储的物理地址。因此,在每次内存引用时,硬件都会进行地址转换,将应用程序的内存引用重定位到内存中实际的位置。

还必须管理内存,记录被占用和空闲的内存位置,并明智而谨慎地介入,保持对内存使用的控制。

介入是一种很常见又很有用的技术,计算机系统中使用介入常常能带来很好的效果。在虚拟内存中,硬件可以介入到每次内存访问中,将进程提供的虚拟地址转换为数据时间存储的物理地址。这种方式最基本的优点是透明,介入完成时通常不需要改动接口的客户端,因此客户端不需要任何改动。

15. 动态(基于硬件)重定位

基址加界限机制,有时又称为动态重定位,具体来说,每个CPU需要两个硬件寄存器2:基址寄存器和界限寄存器,有时称为限制寄存器。这组基址和界限寄存器,让我们能够将地址空间放在物理内存的任何位置,同时又能确保进程只能访问自己的地址空间。

转换方式:物理地址=虚拟地址基址

进程中使用的内存引用都是虚拟地址,硬件接下来将虚拟地址加上基址寄存器中的内容,得到物理地址,再发给内存系统。

由于这种重定位是再运行时发生的,而且我们甚至可以在进程开始运行后改变其地址空间,这种技术一般被称为动态重定位。

一个基址寄存器将虚拟地址转换为物理地址,一个界限寄存器确保这个地址在进程地址空间的范围内。

界限寄存器的用处在于,它确保了进程产生的所有地址都在进程的地址“界限”中。

这种基址寄存器配合界限寄存器的硬件结构式芯片中的(每个CPU一对)。有时我们将CPU的这个负责地址转换的部分统称为内存管理单元(Memory Management Unit,MMU)。

界限寄存器通常由两种使用方式:这两种方式在逻辑上是等价的

  • 一种是它记录地址空间的大小,硬件在将虚拟地址与基址寄存器内容求和钱,就检查这个界限。
  • 一种是界限寄存器中记录地址空间结束的物理地址,硬件在转换虚拟地址到物理地址之后才去检查这个界限

通过基址加虚拟地址(可以看作是地址空间的偏移量)的方式,很容易得到物理地址。虚拟地址“过大”或者为负数时,会导致异常。

15.4 硬件支持:总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQqTTh7-161586117456)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161278924401.jpg)]

15.5 操作系统的问题
  • 第一,在进程创建时,操作系统必须采取行动,为进程的地址空间到内存空间。

  • 第二, 在进程终止时(正常退出,就因行为不端被强制终止),操作系统也必须做一些工作,回收它的所有内存,给其他进程或者操作系统使用

  • 第三,在上下文切换时,操作系统也必须执行一些额外的操作。在切换进程时,操作系统必须保存和恢复基础和界限寄存器。

    注意:当进程停止时(即没有运行),操作系统可以改变其地址空间的物理位置。

  • 第四,操作系统必须提供异常处理程序,或要一些调用的函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a0Iw94dY-161586117456)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\16127897669.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKtZIgyv-161586117456)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-2021020821095881.png)]

分段

简单的通过基址寄存器和界限寄存器实现的虚拟内存很浪费。另外,如果剩余物理内存无法提供连续区域来放置完整的地址空间,进程便无法运行。

怎样支持大地址空间,同时栈和堆之间(可能)由大量空闲空间?

16.1 分段:泛化的基址/界限

分段的想法:在MMU中引入不止一个基址和界限寄存器对,而是给地址空间内的每个逻辑段一对。一个段只是地址空间里的一个连续定长的区域,在典型的地址空间里有个逻辑不同的段:代码、栈和堆。

分段的机制使得才做系统能够将不同的段放到不同的物理内存区域,从而避免了虚拟地址空间中的未使用部分占用物理内存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KB2BbaRF-161586117457)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161279020979.jpg)]

16.2 我们引用哪个段?

一种常见的方式,有时称为显式方式,就是用虚拟地址的开头几位来表示不同的段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-El9L7DJ7-161586117457)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\16127906551.jpg)]

在隐式方式中,硬件通过地址产生的方式来确定段。例如,如果地址由程序计数器产生,那么它在代码段。如果基于栈或基址指针,它一定在栈段。其他地址则在堆段。

16. 栈怎么办 16.4 支持共享

为了支持共享,需要一些额外的硬件支持,这就是保护位。基本为每个段增加了几个位,标识程序是否能够读写该段,或执行其中的代码。通过将代码段标识为只读,同一的代码可以被多个进程共享,而不用担心破坏隔离。

16.5 细粒度与粗粒度的分段

只分为几个段的系统(代码、栈、堆)。我们可以认为这种分段是粗粒度的,因为它将地址空间分成较大的、粗粒度的块。但是,一些早期系统更灵活,允许将地址空间划分为大量较小的段,这被称为细粒度分段。

16.6 操作系统支持

一般会遇到的问题是,物理内存很快充满了许多空闲空间的小洞,因而很难分配给新的段,或扩大已有的段,这种问题被称为外部碎片。

一种更简单的做法是利用空闲列表管理算法,试图保留大的内存块用于分配。但遗憾的是,无论算法多么精妙,都无法完全消除外部碎片,因此,好的算法只是试图减小它。

空闲空间管理

要满足变长的分配请求,应该如何管理空闲空间?什么策略可以让碎片最小化?不同方法的时间和空间开销如何?

17.1 假设

用户级内存分配库管理的空间由于历史原因被称为堆,在堆上个管理空闲空间的数据结构通常称为空闲列表。该结构包含了管理内存区域中所有空闲块的引用。该数据结构不一定真的是列表,而只是某种可以追踪空闲空间的数据结构。

如果分配程序给出的内存块超出请求的大小,在这种块中超出请求的空间(因此而未使用)就被认为是内部碎片(因为浪费发生在已分配单元的内部),这是另一种形式的空间浪费。

假设:1.基本的接口就像malloc()和free()提供的那样

​ 2.主要关心的是外部碎片

​ .内存一旦被分配给客户,就不可以被重定位到其他位置

​ 4.分配程序所管理的是连续的一块字节区域

17.2 底层机制

分割与合并

如果请求的空间大小小于某块空闲块,分配程序通常会进行分割。

分配程序会在释放一块内存时合并可用空间:在归还一块空闲内存时,仔细查看要归还的内存块的地址以及邻近的空闲空间块。如果新归还的空间与一个原有空闲块相邻,就将它们合并为一个较打的空闲块。

通过合并,分配程序可以更好地确保大块的空闲空间能提供给应用程序。


追踪已分配空间的大小

要完成这个任务,大多数分配程序都会在头块中保存一点额外的指针来加速空间释放,包含一个幻数来提供完整性检查,以及其他信息。

获得头块的指针后,库可以很容易地确定幻数是否符合预期值,作为正常性检查,并简单计算要释放的空间大小(即头块的大小加区域长度)。

注意:实际释放的时头块大小加上分配给用户的空间大小。


嵌入空闲列表

17. 基本策略

最优匹配

选择最接近用户请求大小的块,从而尽量避免空间浪费。但是要付出较高的性能代价。


最差匹配

尝试最大的空闲块,分割并满足用户需求后,将剩余的块(很大)加入空闲列表。但是会导致过量的碎片,同时还由很高的开销。


首次匹配

到第一个足够大的块,将请求的空间返回给用户。具有速度优势

地址排序:通过保持空闲块按内存地址有序,合并操作会很容易,从而减少了内存碎片。


下次匹配

下次匹配算法多维护一个指针,指向上一次查结束的位置。对空闲空间的查操作扩散到整个列表中去,避免对列表开头频繁的分割。避免了遍历查。


17.4 其他方式

分离空闲列表

如果某个应用程序经常申请一种(或几种)大小的内存空间,那就用独立的列表,只管理这也大小的对象。其他大小的请求都给更通用的内存分配程序。


伙伴系统

空闲空间首先从概念上被看出大小为2的大空间。当有一个内存分配请求时,空闲空间被递归地一分为二,直到刚好可以满足请求的大小(再一分为二就无法满足)这时,请求的块被返回给用户。


分页:介绍

分页不是将一个进程的地址空间分割成几个不同长度的逻辑段(即代码、堆、段),而是分割成固定大小的单元,每个单元称为一页。相应地,我们把物理内存看成是定长槽块的阵列,叫作页帧。每个这样的页帧包含一个虚拟内存页。

如何通过页来实现虚拟内存,从而避免分段的问题?基本技术是什么?如何让这些技术运行良好,并尽可能减少空间和时间开销?

18.1 一个例子

分页的灵活性:通过完善的分页方法,操作系统能够高效地提供地址空间的抽象,不管进程如何使用地址空间。

分页提供的空闲空间管理的简单性:为了记录地址空间的每个虚拟页放再物理内存中的位置,操作系统通常为每个进程保存一个数据结构,称为页表。页表的主要作用是为地址空间的每个虚拟页面保存地址转换,从而让我们知道每个页在物理内存中的位置。

这个页表是一个每个进程的数据结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P2qtgYkS-161586117457)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1612878502.jpg)]

为了转换该过程生成的虚拟地址,我们必须首先将它分成两个组件:虚拟页面号(VP)和页内偏移量(offset)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zKl8e6Be-161586117458)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161287865162.jpg)]

物理帧号(PF)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EZ4ywp94-161586117458)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161287881652.jpg)]

18.2 页表存在哪里

由于页表如此之大,我们没有在MMU中利用任何特殊的片上硬件,来存储当前正在运行的进程的页表,而是将每个进程的页表存储在内存中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8IHxjuEA-161586117459)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1612879124187.jpg)]

18. 列表中究竟有什么

页表就是一种数据结构,用于将虚拟地址(或者实际上,是虚拟页号)映射到物理地址(物理帧号)。因此,任何数据结构都可以采用。最简单的形式称为线性页表,就是一个数组。

操作系统通过虚拟页号(VP)检索该数组,并在该索引处查页表项(PTE),以便到期望的物理帧号(PF)。

PTE的内容:

  • 有效位通常用于指示特定地址转换是否有效。
  • 保护位(P)表明页是否可以读取、写入或执行。(R/W)
  • 存在位表示该页是在物理存储器还是磁盘上(即它已被换出)
  • 脏位(D)表明页面被带入内存后是否被修改过。
  • 参考位(A)有时用于追踪页是否被访问,也用于确定哪些页很受欢迎,因此应该保留在内存中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfOAmGr1-161586117459)(C:\Users\Silhouette76\AppData\Roaming\Typora\typora-user-images\image-20210209220925788.png)]

18.4 分页:也很慢

对于每个内存引用(无论是取指令还是显式加载或存储),分页都需要我们执行一个额外的内存引用,以便首先从页表中获取地址转换。额外的内存引用开销很大,在这种情况下,可能会使进程减慢两倍或更多。

18.5 内存追踪

现代操作系统的内存管理子系统中最重要的数据结构之一就是页表。
通常,页表存储虚拟——物理地址转换,从而让系统知道地址空间的每个页实际驻留在物理内存中的哪个位置。
由于每个地址空间都需要这种转换,因此一般来说,系统中每个进程都有一个页表。

当它运行时,每个获取值将产生两个内存引用:一个访问页表以查指令所在的物理页帧,另一个访问指令本身将其提取到CPU进行处理。

分页:快速地址转换(TLB)

如何才能加速虚拟地址转换,尽量避免额外的内存访问?需要什么样的硬件支持?操作系统该如何支持?

19.1 TLB的基本算法

大体流程如下:

首先从虚拟地址中提取页号(VP),然后检查TLB是否有该VP的转换映射。如果有了TLB命中,这意味着TLB有该页的转换映射,接下来从相关的TLB项中取出页帧号(PF),与原来虚拟地址中的偏移量组合形成期望的物理地址,并访问内存。

缓存是计算机系统中最基本的性能改进技术之一,一次又一次地用于让”常见的情况更快“。硬件缓存背后的思想是利用指令和数据引用的局部性。

通常有两种局部性:时间局部性和空间局部性

时间局部性是指,最近访问过的指令或数据项可能很快会再次访问

空间局部性是指,当程序访问内存地址x时,可能很快会访问邻近x的内存

19. 请谁来处理TLB未命中

软件管理的方法,主要优势是灵活性:操作系统可以用任意数据结构来实现页表,不需要改变硬件。另一个优势是简单些,硬件不需要堆未命中做太多工作,它抛出异常,操作系统的未命中处理程序会负责剩下的工作。

19.4 TLB的内容

典型TLB有2项、64项或128项,并且是全相联的。一条TLB项内容可能像下面这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5HwQsb7-161586117459)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\161559540761.jpg)]

在页表中,如果一个页表项(PTE)被标记为无效,就意味着该页并没有被进程申请使用,正常运行的程序不应该访问该地址。当程序试图访问这样的页时,就会陷入操作系统,操作系统会杀掉该进程。

TLB的有效位不同,只是指出TLB项是不是有效的地址映射。

TLB有效位在系统上下文切换时起到了很重要的作用。通过将所有的TLB项设置为无效,系统可以确保将要运行的进程不会错误地使用前一个进程的虚拟到物理地址转换映射。

19.5 上下文切换时对TLB的处理

如果发生进程间上下文切换,上一个进程在TLB中的地址映射对于即将运行的进程是无意义的。硬件或操作系统应该做些什么来解决这个问题呢?

一种方法是在上下文切换时,简单地情况TLB,这样在新进程运行前TLB就变成了空的。但是这样方法会使开销变大,为了减少这种开销,有的系统在TLB中添加了一个地址空间标识符(ASID)。可以把ASID看作是进程标识符(PID),但通常比PID位数少。

19.6 TLB替换策略

在向TLB添加新项时,应该替换哪个旧项?目标当然是减小TLB未命中率,从而改进性能。

一种常见的策略是替换最近最少使用(LRU)的项。LRU尝试利用内存引用流中的局部性,假定最近没有用过的项,可能是好的换出候选项。另一种典型策略就是随机策略,即随机选择一项换出去。

19.7 实际系统的TLB表项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24cZj0BE-161586117460)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\1615609944.jpg)]

  • 全局位(G),用来指示这个页是不是所有进程全局共享的。因此,如果全局位置为1,就会忽略ASID
  • 一致性位(C),决定硬件如何缓存该页
  • 脏位(D),表示该页是否被写入新数据
  • 有效位(V),告诉硬件该项的地址映射是否有效
  • 页掩码字段,用来支持不同的页大小

随机存取存储器RAM,RAM不总是RAM。有时候随机访问地址空间,尤其是TLB没有缓存的页,可能导致沿着的性能损失

分页:较小的表

简单的基于数组的页表(通常称为线性页表)太大,在典型系统上占用太多内存。如何让页表更小?关键的思路是什么?由于这些新的数据结构,会出现什么效率影响?

20.2 混合方法:分页和分段

杂合方法不是为进程的整个地址空间提供单个页表,而是为每个逻辑分段提供一个。使用基址寄存器不是指向段本神,而是保存该段的页表的物理地址,界限寄存器用于指示页表的结尾(即它有多少有效页)。

20. 多级页表

基本思想:首先,将页表分成页大小的单元。然后,如果整页的页表项(PTE)无效,就完全不分配该页的页表。为了追踪页表的页是否有效(以及如果有效,它在内存中的位置),使用了名为页目录的新结构。页目录因此可以告诉你页表的页在哪里,或者页表的整个页不包含有效页。

在一个简单的两级页表中,页目录为每页页表包含了一项。它由多个页目录项(PDE)组成。PDE至少拥有有效位和页帧号,类似于PTE。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dwkjUhQo-161586117460)(C:\Users\Silhouette76\Documents\Tencent Files\154862884\FileRecv\MobileFile\16156219640.jpg)]

有了多级结构,我们增加了一个间接层,使用了页目录,它指向页表的各个部分。这种间接方式,让我们能够将页表页放在物理内存的任何地方。

多级页表是有成本的。在TLB未命中时,需要从内存加载两次,才能从页表中获取正确的地址转换信息(一次用于页目录,另一次用于PTE本身),而线性页表只需要一次加载。

超越物理内存:机制

操作系统如何利用大而慢的设备,透明地提供巨大虚拟地址空间的假象?

21.1 交换空间

在硬盘上开辟一部分空间用于物理页的移入和移出,为了达到这个目的,操作系统需要记住给定页的硬盘地址。交换空间的大小是非常重要的,它决定了系统在某一时刻能够使用的最大内存页数。

21.2 存在位

硬件(或操作系统,在软件管理TLB时)判断受否在内存中的方法,是通过页表项中的一条新信息,即存在位。

如果存在位设置为1,则表示该页存在于物理内存中,并且所有内容都如上述进行。如果存在位设置为零,则页不在内存中,而在硬盘上。访问不在物理内存中的页,这种行为通常被称为页错误。

21. 页错误

如果一个页不存在,它已被交换到硬盘,在处理页错误的时候,操作系统需要将该页交换到内存中。操作系统可以用PTE中的某些位来存储硬盘地址,这些位通常用来存储像页的PF这样的数据。当操作系统接收到页错误时,它会在PTE中查地址,并请求发送到硬盘。

当硬盘I/O完成时,操作系统会更新页表,将此页标记为存在,更新页表项(PTE)的PF字段以记录新获取页的内存位置,并重新指令。

注意:当I/O在运行时,进程将处于阻塞状态。因此,当页错误正常处理时,操作系统可以自由地运行其他可执行的进行。因为I/O操作是昂贵的,一个进程进行I/O时会执行另一个进程,这种交叠是多道程序充分利用硬件的一种方式。

21.5 页错误处理流程

有中重要情景:

  • 第一种情况:该页存在且有效。在这种情况下,TLB未名字处理程序可以简单地从PTE中获取PF,然后重试指令
  • 第二种情况:页错误处理程序需要运行。虽然这是进程可以访问的合法页,但它并不在物理内存中。
  • 第三种情况:访问的是一个无效页,可能由于程序中的错误。在这种情况下,PTE中的其他页都不重要了。硬件捕获这个非法访问,操作系统陷阱处理程序运行,可能会杀死非法进程。
21.6 交换何时发生

为了保证有少量的空闲内存,大多数操作系统会设置高水位线(HW)和低水位线(LW),来帮助决定何时从内存中清除页。

原理是:当操作系统发现有少于LW个页可用时,后台负责释放内存的线程会开始运行,直到有HW个可用的物理页。这个后台线程有时称为交换守护进程或页守护进程。

通过同时执行多个交换过程,我们可以进程一些性能优化。例如:许多系统会把多个要写入的页聚集或分组,同时写入道交换区间,从而提高硬盘的效率

超越物理内存:策略

操作系统如何决定从内存中踢出哪一页(或哪几页)?这个由系统的替换策略做出,替换策略通常会遵循一些通用的原则。但也会包括一些调整,以避免特殊情况下的行为。

22.1 缓存管理

由于内存只包含系统中所有页的子集,因此可以将其视为系统中虚拟内存页的缓存。

我们的目标是让缓存为命中最少,即使得从磁盘获取页的次数最少。或者,可以将目标看成让缓存名字最多,即在内存中到待访问页的次数最多。

计算程序的平均内存访问时间(AMAT),计算机架构师衡量硬件缓存的指标。

AMTA=(PHit*TM)(PMiss*TD),其中TM表示访问内存的成本,TD表示访问磁盘的成本,PHit表示在缓存中到数据的概率(命中),PMiss表示在缓存中不到数据的概率(未命中)。PHit和PMiss从0.0变化到1.0,并且PMissPHit=1.0。

22.2 最优替换策略

即替换内存中在最远将来才会被访问到的页,可以达到缓存未命中率最低。

22. 简单策略:FIFO

页在进入系统时,简单地放入一个队列。当发生替换时,队列尾部的页(“先入”页)被踢出。FIFO有一个很大的优势:实现相当简单。

FIFO根本无法确定页的重要性:即使页0已被多次访问,FIFO仍然会将其踢出,因为它是第一个进入内存的。

22.4 另一简单策略:随机

在内存满的时候它随机选择一个页进程替换。随机策略取决于当时的运气。

22.5 利用历史数据:LRU

页替换策略可以使用的一个历史信息是频率。页更常用的属性是访问的近期性,越近被访问过的页,也许再次访问的可能性也就越大。

这一系列的策略是基于人们所说的局部性原则,这个原理简单地说就是程序倾向于频繁地访问某些代码和数据结构。因此,我们应该尝试用历史数据来确定哪些页面更重要,并在需要踢出页时将这些页保存在内存中。

一系列简单的基于历史的算法诞生了。“最不经常使用”(LFU)和“最少经常使用”(LRU)

22.8 近似LRU

操作系统如何利用使用位来实现近似LRU?

有一个简单的方法称作时钟算法,系统中的所有页都放在一个循环列表中。时钟指针开始时指向某个特定的页(哪个页不重要)。当必须进行页替换时,操作系统检查当前指向页P的使用位是1还是0,时钟指针递增到下一页(P1)。该算法一直持续到一个使用位为0的页,使用位为0意味着这个页最近没有被使用过(在最坏的情况下,所有的页都已经被使用了,那么久将所有页的使用位都设置为0)。

22.9 考虑脏页

硬件应该包括一个修改为(又名脏位)。每次写入页时都会设置此位,因此可以将其合并到页面替换算法中。例如,时钟算法可以被改变,以扫描未使用又干净的页先踢出。无法到这种页时,再查脏的未使用页面。

22.10 其他虚拟内存策略

页面替换不是虚拟内存子系统采用的唯一策略。

对于大多数页而言,操作系统指示使用按需分页,这意味着操作系统再页被访问时页载入内存中,“按需”即可。当然,操作系统可能会猜测一个页面即将被使用,从而提前载入。这种行为被称为预取。

许多系统会再内存中收集一些待完成写入,并以一种(更高效)的写入方式将他们写入硬盘。这种行为通常称为聚集写入,或者就是分组写入,这样做有效是因为硬盘驱动器的性质,执行单词大的写操作,比许多小的写操作更有效。

22.11 抖动

当内存被超额请求时,操作系统应该做什么,这组正在运行的进程的内存需求是否超出了可用物理内存?

在这种情况下,系统将不断地进行换页,这种情况有时被称为抖动

VAX/VMS虚拟页内存系统


  1. 程序计数器(PC)(有时称为指令指针,IP)告诉我们程序即将执行哪个指令 ↩︎

  2. 寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址。 ↩︎

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/shuma/857092.html

相关标签:无
上传时间: 2024-02-10 07:04:52
留言与评论(共有 9 条评论)
本站网友 西安合租房
4分钟前 发表
64项或128项,并且是全相联的
本站网友 天山华庭
22分钟前 发表
但是,一些早期系统更灵活,允许将地址空间划分为大量较小的段,这被称为细粒度分段
本站网友 算排卵期
16分钟前 发表
第一种是发生时钟中断的时候
本站网友 马油怎么用
11分钟前 发表
由于这种重定位是再运行时发生的,而且我们甚至可以在进程开始运行后改变其地址空间,这种技术一般被称为动态重定位
本站网友 降低
1分钟前 发表
我们的目标是让缓存为命中最少,即使得从磁盘获取页的次数最少
本站网友 吉林敖东洮南药业股份有限公司
23分钟前 发表
几乎所有现代化的调底程序都是抢占式的,非常愿意停止一个进程以运行另一个进程
本站网友 王琢
7分钟前 发表
但是,当我们放宽另一个假设,具体来说,我们可以针对假设2,现在假设工作随时到达,而不是同时到达
本站网友 下城租房
15分钟前 发表
建议将图片保存下来直接上传(img-XPF9dl1M-161586117452)(C