[架构之路-123]-《软考-系统架构设计师》-操作系统-2-操作系统原理 - 存储层次结构与存储管理(寄存器CacheMMU内存外存页表)

Posted 文火冰糖的硅基工坊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[架构之路-123]-《软考-系统架构设计师》-操作系统-2-操作系统原理 - 存储层次结构与存储管理(寄存器CacheMMU内存外存页表)相关的知识,希望对你有一定的参考价值。

前言:

操作系统的本质就是创建一个并发的应用程序执行的环境,使得各种应用程序可以动态、共享相同的计算机物理硬件资源,计算机的三大物理资源包括:

  • CPU

  • 内存

  • 外设

应用程序(管理应用程序):以进程和现成的方式组织,所有的应用程序被抽象成了一个个的进程与现成;然后,有了进程间同步、互斥与通信、进程的优先级调度等概念。

所有的外设(管理外设资源):以统一的文件来组织,所有的应用程序通过文件的方式访问所有的外设,操作系统通过把文件映射成设备的驱动程序访问外设硬件。并以中断的手段提供异步抢占的方式临时占用计算机的资源。

所有的内存(管理内存资源)一虚拟地址的方式来组织,每个应用程序拥有0G~3G的用户地址空间和3G~4G的内核专有空间,于是了内存管理、MMU和地址映射等等概念。

内核调度程序(管理CPU资源):如何管理上述资源呢?于是就有了操作系统的调度程序!

本文就是探讨操作系统上述核心理念和原理!!!

第11章 操作系统

第4节 存储管理:寄存器+cache+内存+外存

4.1计算机存储层次模型

4.1.1 层次模型的内容

存储不仅仅包括内存,还包括寄存器、cache、外存。

4.1.2 通用的思想和思路

在上述层析模型中,寄存器离CPU的距离最近,外存离CPU最远。

离CPU越近,速度越高,容量越低,价格越高。

理CPU越远,容量越高,速度越低,价格越便宜。

在上述通用模型中, 本质是“一对多”映射,要解决如下问题:

(1)按照什么规则实现“一对多”映射 =》 算法

(2)按照什么方式实现“一对多”映射 =》 映射表

(3)命中直接获取 =》硬件

(4)不命中替换 =》 硬件

(5)N+1层数据替换N层数据时,需要使用一定的算法 =》算法。

通用处理过程

  • CPU自顶向下,按照某种地址映射规则,逐层访问数据下一层的数据。

  • 地址映射规则,称为地址映射表,表的内容,可以由软件按照某种规则填写,也可以由硬件按照某种规则填写。

  • 当数据地址空间在N层,称为命中,直接从N层获取数据

  • 当数据不在N层时,称为不命中,从N+1层获取数据

  • 然后用N+1层中的数据替换N层中的数据

  • 由于N层中的容量小于N+1层中的容量,且部分内容已经被已有数据占用,因此,必须有某种替换算法,使用新的数据替换掉旧的数据。

4.1.3 内存抽象

为了更好的管理内存,操作系统将内存抽象成地址空间。

4.1.3 多核并行架构

4.2 位于CPU内部的寄存器

实际上,无论是指令还是数据,最终都会被加载到CPU内部的数据和指令寄存器中,并作为ALU运算单元的输入,这样CPU的指令才得以执行。

CPU内部的指令寄存器和数据寄存器非常少,指令寄存器就只有几个,取决于指令流水线的个数,数据寄存器也就是几十个。而程序员编写的高层代码,无论是指令的地址空间,还是数据的地址空间,高达几个G, 这么大的地址空间的程序和数据,如何复用有限的几个CPU内部寄存的呢???

这个艰巨的任务是由CPU内部的硬件控器+汇编语言+编译器+程序员共同完成的,程序员通过汇编语言指示计算机如何在内部寄存器和内存之间倒换数据,如load和save指令,或直接操作CPU内部的寄存器(只有汇编语言才具备操作CPU内部寄存器的能力)。然后经过汇编语言的编译、链接,把文本的汇编语言转换成CPU能够识别的二进制代码程序,CPU内部的硬件控制器负责取指令、解析指令、执行指令,在执行指令的过程中,通过Next指针、PC指针、跳转指令等源源不断地中外部存储器中获取指令,并执行指令,在每个执行的指令中完成大容量的内存空间与有限的CPU寄存器之间的映射,而这个映射是在汇编语言中完成的、实现的,因此,这个映射的职责,要么就是汇编语言的程序员,要么就是把C/C++等高层语言编译成汇编语言的编译器,而不是操作系统!!!!不需要操作系统本身参与。

4.3 位于SOC芯片内部的Cache (纯硬件、解决速率不匹配问题)

4.3.1 概述

汇编语言和程序员解决了大容量的内存空间与有限数量的CPU寄存器之间的映射,但他们解决不了一个问题:就是内存的访问速度远远低于CPU内部寄存器的访问速度,这导致CPU执行指令的效率极大地受到了内存访问速率的影响。如何提升CPU访问内存的速度呢?Cache技术因运而生

Cache被安排在了CPU内部寄存器和内存之间,其CPU访问Cache速度接近CPU访问内部寄存器的速度,Cache的容量在CPU内部寄存器和内存之间,有几十K。这就带来一个新的问题:难道要让汇编语言程序员通过编码代码实现内存到Cache的数据搬移?然后再实现Cache到CPU内部寄存器的搬移

如果这样,对汇编语言的改动有点大,对程序员的要求有点高,编程的复杂度也会极大的提升。如何在增加Cache的情况下,而不影响汇编语言的编程呢?或者说如何设计一个简易的机制,使得Cache对CPU汇编指令是透明的呢?

实际上,内存与Cache之间的地址映射和数据搬移,以及Cache到内部寄存器之间的映射与数据搬移,完成是通过硬件电路实现的,对于汇编语言完成是透明的,与汇编语言无关,更不需要操作系统的干预

CPU的Cache机制,是如何实现该神奇的效果的呢??

4.3.2 为什么需要cache

在思考为什么需要cache之前,我们首先先来思考另一个问题:我们的程序是如何运行起来的?

我们应该知道程序是运行在 RAM之中,RAM 就是我们常说的DDR(例如: DDR3、DDR4等)。我们称之为main memory(主存)。当我们需要运行一个进程的时候,首先会从磁盘设备(例如,eMMC、UFS、SSD等)中将可执行程序load到主存中,然后开始执行。在CPU内部存在一堆的通用寄存器(register)。如果CPU需要将一个变量(假设地址是A)加1,一般分为以下3个步骤:

CPU 从主存中读取地址A的数据到内部通用寄存器 x0(ARM64架构的通用寄存器之一)。

  • 通用寄存器 x0 加1。

  • CPU 将通用寄存器 x0 的值写入主存。

  • 我们将这个过程可以表示如下:

其实现实中,CPU通用寄存器的速度和主存之间存在着太大的差异。

两者之间的速度大致如下关系:

CPU register的速度一般小于1ns,主存的速度一般是65ns左右。速度差异近百倍。因此,上面举例的3个步骤中,步骤1和步骤3实际上速度很慢。当CPU试图从主存中load/store 操作时,由于主存的速度限制,CPU不得不等待这漫长的65ns时间。如果我们可以提升主存的速度,那么系统将会获得很大的性能提升。如今的DDR存储设备,动不动就是几个GB,容量很大。如果我们采用更快材料制作更快速度的主存,并且拥有几乎差不多的容量。其成本将会大幅度上升。我们试图提升主存的速度和容量,又期望其成本很低,这就有点难为人了。因此,我们有一种折中的方法,那就是制作一块速度极快但是容量极小的存储设备。那么其成本也不会太高。这块存储设备我们称之为cache memory。在硬件上,我们将cache放置在CPU和主存之间,作为主存数据的缓存。 当CPU试图从主存中load/store数据的时候, CPU会首先从cache中查找对应地址的数据是否缓存在cache 中。如果其数据缓存在cache中,直接从cache中拿到数据并返回给CPU。当存在cache的时候,以上程序如何运行的例子的流程将会变成如下:

CPU和主存之间直接数据传输的方式转变成CPU和cache之间直接数据传输。cache负责和主存之间数据传输。

4.3.3 多级cache存储结构

cahe的速度在一定程度上同样影响着系统的性能。一般情况cache的速度可以达到1ns,几乎可以和CPU寄存器速度媲美。但是,这就满足人们对性能的追求了吗?并没有。当cache中没有缓存我们想要的数据的时候,依然需要漫长的等待从主存中load数据。为了进一步提升性能,引入多级cache。前面提到的cache,称之为L1 cache(第一级cache)。我们在L1 cache 后面连接L2 cache,在L2 cache 和主存之间连接L3 cache。等级越高,速度越慢,容量越大。但是速度相比较主存而言,依然很快。不同等级cache速度之间关系如下:

经过3级cache的缓冲,各级cache和主存之间的速度最萌差也逐级减小。在一个真实的系统上,各级cache之间硬件上是如何关联的呢?我们看下Cortex-A53架构上各级cache之间的硬件抽象框图如下:

在Cortex-A53架构上,L1 cache分为单独的instruction cache(ICache)和data cache(DCache)。L1 cache是CPU私有的,每个CPU都有一个L1 cache。一个cluster 内的所有CPU共享一个L2 cache,L2 cache不区分指令和数据,都可以缓存。所有cluster之间共享L3 cache。L3 cache通过总线和主存相连。

4.3.4 多级cache之间的配合工作

首先,引入两个名词概念,命中和缺失。

CPU要访问的数据在cache中有缓存,称为“命中” (hit),反之则称为“缺失” (miss)。

多级cache之间是如何配合工作的呢?我们假设现在考虑的系统只有两级cache。

当CPU试图从某地址load数据时,首先从L1 cache中查询是否命中,如果命中则把数据返回给CPU。如果L1 cache缺失,则继续从L2 cache中查找。当L2 cache命中时,数据会返回给L1 cache以及CPU。

如果L2 cache也缺失,很不幸,我们需要从主存中load数据,将数据返回给L2 cache、L1 cache及CPU。

这种多级cache的工作方式称之为inclusive cache。某一地址的数据可能存在多级缓存中。与inclusive cache对应的是exclusive cache,这种cache保证某一地址的数据缓存只会存在于多级cache其中一级。也就是说,任意地址的数据不可能同时在L1和L2 cache中缓存。

4.3.5 直接映射缓存(Direct mapped cache)

我们继续引入一些cache相关的名词。cache的大小称之为cache size,代表cache可以缓存最大数据的大小。我们将cache平均分成相等的很多块,每一个块大小称之为cache line,其大小是cache line size。例如一个64 Bytes大小的cache。如果我们将64 Bytes平均分成64块,那么cache line就是1字节,总共64行cache line。如果我们将64 Bytes平均分成8块,那么cache line就是8字节,总共8行cache line。现在的硬件设计中,一般cache line的大小是4-128 Byts。为什么没有1 byte呢?原因我们后面讨论。

这里有一点需要注意,cache line是cache和主存之间数据传输的最小单位。什么意思呢?当CPU试图load一个字节数据的时候,如果cache缺失,那么cache控制器会从主存中一次性的load cache line大小的数据到cache中。例如,cache line大小是8字节。CPU即使读取一个byte,在cache缺失后,cache会从主存中load 8字节填充整个cache line。又是因为什么呢?后面说完就懂了。

我们假设下面的讲解都是针对64 Bytes大小的cache,并且cache line大小是8字节。我们可以类似把这块cache想想成一个数组,数组总共8个元素,每个元素大小是8字节。就像下图这样。

现在我们考虑一个问题,CPU从0x0654地址读取一个字节,cache控制器是如何判断数据是否在cache中命中呢?cache大小相对于主存来说,可谓是小巫见大巫,因此cache与主存的关系是1对多的关系,一个cache地址存放多个不同的主存的地址所以cache肯定是只能缓存主存中极小一部分数据

我们如何根据地址在有限大小的cache中查找数据呢?

现在纯硬件(不需要软件设置映射表)采取的做法是对地址进行hash 散列(可以理解成地址取模操作)。

我们接下来看看是如何做到的?

我们一共有8行cache line,cache line大小是8 Bytes。所以

我们可以利用地址低3 bits(如上图地址蓝色部分)用来寻址8 bytes中某一字节,我们称这部分bit组合为offset

同理,8行cache line,为了覆盖所有行。我们需要3 bits(如上图地址黄色部分)查找某一行,这部分地址部分称之为index

现在我们知道,如果两个不同的地址,其地址的bit3-bit5如果完全一样的话,那么这两个地址经过硬件散列之后都会找到同一个cache line。所以,当我们找到cache line之后,只代表我们访问的地址对应的数据可能存在这个cache line中,但是也有可能是其他地址对应的数据。所以,我们又引入tag array区域,tag arraydata array一一对应。每一个cache line都对应唯一一个tag,tag中保存的是整个地址位宽去除index和offset使用的bit剩余部分(如上图地址绿色部分)。

tag、index和offset三者组合就可以唯一确定一个地址了。因此,当我们根据地址中index位找到cache line后,取出当前cache line对应的tag,然后和地址中的tag进行比较,如果相等(异或值为0),这说明cache命中。如果不相等(异或为1),说明当前cache line存储的是其他地址的数据,这就是cache缺失。

在上述图中,我们看到tag的值是0x19,和地址中的tag部分相等,因此在本次访问会命中。由于tag的引入,因此解答了我们之前的一个疑问“为什么硬件cache line不做成一个字节?”。这样会导致硬件成本的上升,因为原本8个字节对应一个tag,现在需要8个tag,占用了很多内存。

tag也是cache的一部分,暂用cache空间的大小,需要专有的cache空间,虽然我们谈到cache size的时候并不考虑tag占用的cache空间大小。

我们可以从图中看到tag旁边还有一个valid bit,这个bit用来表示cache line中数据是否有效(例如:1代表有效;0代表无效)。当系统刚启动时,cache中的数据都应该是无效的,因为还没有缓存任何数据。cache控制器可以根据valid bit确认当前cache line数据是否有效。所以,上述比较tag确认cache line是否命中之前还会检查valid bit是否有效。只有在有效的情况下,比较tag才有意义。如果无效,直接判定cache缺失

上面的例子中,cache size是64 Bytes并且cache line size是8 bytes。offset、index和tag分别使用3 bits、3 bits和42 bits(假设地址宽度是48 bits)。我们现在再看一个例子:512 Bytes cache size,64 Bytes cache line size。根据之前的地址划分方法,offset、index和tag分别使用6 bits、3 bits和39 bits。如下图所示。

需要强烈说明的是:Cache映射(命中与缺失两种情况),都是硬件完成的,Cache中的Tag映射表,也是由硬件在缺失情况下,自动填充的,并不需要软件的参与,因此cache的映射,完全对汇编程序是透明的,汇编程序并不需要关系在CPU寄存器和内存之间是否有cache的存在。汇编程序只需要在适当的地方打开或关闭cache即可。

直接映射缓存的优缺点:

  • 优点:

直接映射缓存在硬件设计上会更加简单,因此成本上也会较低。

根据直接映射缓存的工作方式,我们可以画出主存地址0x00-0x88地址对应的cache分布图。

我们可以看到,

每行的大小为8个字节,整个cache为64个字节。

地址0x00-0x3f地址处对应的数据可以覆盖整个cache,

地址0x40-0x7f地址的数据也同样是覆盖整个cache,每行的大小为8个字节。

  • 缺点:

我们现在思考一个问题,如果一个程序试图依次访问地址0x00、0x40、0x80,cache中的数据会发生什么呢?

首先我们应该明白0x00、0x40、0x80地址中index部分是一样的,都是0x0。因此,这3个地址对应的cache line是同一个。所以,当我们访问0x00地址时,cache会缺失,然后数据会从主存中加载到cache中第0行cache line。当我们访问0x40地址时,依然索引到cache中第0行cache line,由于此时cache line中存储的是地址0x00地址对应的数据,所以此时依然会cache缺失。然后从主存中加载0x40地址数据到第一行cache line中。同理,继续访问0x80地址,依然会cache缺失。这就相当于每次访问数据都要从主存中读取,所以cache的存在并没有对性能有什么提升。访问0x40地址时,就会把0x00地址缓存的数据替换。这种现象叫做cache颠簸(cache thrashing)。

针对这个问题,我们引入多路组相连缓存。

我们首先研究下最简单的两路组相连缓存的工作原理。

4.3.6 替换算法

cache在使用一段时间后,空间会满,新进来的数据需要替换一定的数据块。

常见的算法有:

  • Hash映射法:适用于直接映射缓存

  • 先进先出算法FIFO:用一个计数器记录先进来的数据,新进来的需要替换时替换掉最先进来的数据,即计数大的数据

  • 最不经常使用算法LFU:用计数器记录每个数据命中的次数,替换时替换命中次数最小的。

  • 近期最少使用算法LRU:用一个计数器记录一定时间周期中一个数据没有命中的次数,如果命中计数器减一,没有命中加一,替换掉计数器大的数据。

  • 随机算法:随机地选择一个单元来替换

4.3.7 更多信息

参考:https://zhuanlan.zhihu.com/p/102293437

4.4 CPU指令的虚拟地址到物理内存地址的映射问题:MMU =》 多线程

4.4.1 概述

MMU(Memory Management Unit),即内存管理单元,是现代CPU架构中不可或缺的一部分。

MMU与Cache一样,位于CPU与内存之间.

MMU负责指令中的虚拟地址物理内存的物理地址的转换,并提供硬件机制的内存访问权限检查。

虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。

在 MMU 开启之前,CPU 都是通过物理地址来访问内存。

在MMU 开启之后,CPU 都是通过虚拟地址来访问内存。

虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。

4.4.2 MMU的功能

  • 虚实地址翻译:在用户访问内存时,将用户访问的虚拟地址翻译为实际的物理地址,以便CPU对实际的物理地址进行访问。

  • 访问权限控制:可以对一些虚拟地址进行访问权限控制,以便于对用户程序的访问权限和范围进行管理,如代码段一般设置为只读,如果有用户程序对代码段进行写操作,系统会触发异常。

  • 引申的物理内存管理:对系统的物理内存资源进行管理,为用户程序提供物理内存的申请、释放等操作接口:malloc与free

MMU是纯硬件内存控制器,它的职责是完成CPU指令中的虚拟地址到内存总线的物理地址的映射的硬件逻辑 控制。

它的输入是:CPU指令中的虚拟地址。

它映射的依据是:TLB表。

它的输出是:内存的物理地址。

4.4.3 MMU的优势与好处

使用MMU带来的好处或者优势:

  • 提升物理内存利用率

  • 物理内存按需申请,如代码段的内存在执行时进行映射和转换,进程fork后,通过写时复制(Copy-On-Write)进行真正的物理内存分配

  • 解决内存管理碎片化的问题,即在系统运行一段时间后,频繁的内存申请和释放会导致内存碎片化,无法申请到一块足够大的地址连续的内存。

  • 对内存地址的访问进行控制

  • 如上述代码段只读权限控制,多线程的栈内存之间的空洞页隔离可以防止栈溢出后改写其他线程的栈内存,不同进程之间的地址隔离等等。

  • 将进程的地址空间隔离

  • 不同进程之间可以使用相同虚拟内存地址空间,而进程间的物理内存又可以做到隔离,这保证了进程的独立性同时,又简化了地址的访问方式,如在早期32位CPU上,为了支持4G以上的物理内存,一般物理地址有36-bit(如PowerPC-604系列),但是用户的虚地址仍然使用32-bit,做法就是将用户的不同进程的32-bit虚地址在MMU转换时,转换为36-bit的物理地址,这样每个进程仍然能访问0-3G虚地址范围,将多个进程的3G空间映射到36-bit的物理内存空间中去。

4.4.4 虚拟地址到物理地址翻译过程

在上图中,如果MMU关闭,CPU指令地址,就是物理地址,而不是虚拟地址,比如uboot,就是没有使能MMU的单体、单线程程序,因此不需要使能MMU.

只有MMU使能,才会进行虚拟地址到物理地址的翻译,以支持多进程系统。

4.4.5 进程页表

例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。

4.4.6 关于TLB:页表缓冲区(Translation Lookaside Buffer)

4.4.6.1 TLB工作原理

快表,直译为旁路快表缓冲,也可以理解为页表缓冲,地址变换高速缓存。

TLB位于SOC芯片内部的专有的高速缓存中。

而页表存放在SOC芯片外部的主存中,因此程序每次访存至少需要两次:一次访存获取数据的物理地址,第二次进行地址映射,访问物理地址才获得数据。

提高访存性能的关键在于依靠页表的访问局部性。当一个转换的虚拟页号被使用时,它可能在不久的将来再次被使用到。

TLB内核本身是一种高速缓存,内存管理硬件使用它来改善虚拟地址到物理地址的转换速度。当前所有的个人桌面,笔记本和服务器处理器都使用TLB来进行虚拟地址物理地址的映射。

使用TLB内核可以快速的找到虚拟地址指向物理地址,而不需要请求RAM内存获取虚拟地址到物理地址的映射关系。这与data cache和instruction caches有很大的相似之处

当cpu要访问一个虚拟地址/线性地址时,CPU会首先根据虚拟地址的高20位(20是x86特定的,不同架构有不同的值)在TLB中查找。如果是表中没有相应的表项,称为TLB miss,需要通过访问慢速RAM中的页表计算出相应的物理地址。同时,物理地址被存放在一个TLB表项中,以后对同一线性地址的访问,直接从TLB表项中获取物理地址即可,称为TLB hit。

TLB表项

高速缓冲TLB内核内部存放的基本单位是页表条目,对应着RAM中存放的页表条目。页表条目的大小固定不变的,所以TLB容量越大,所能存放的页表条目越多,TLB hit的几率也越大。但是TLB容量毕竟是有限的,因此RAM页表和TLB页表条目无法做到一一对应。因此CPU收到一个线性地址,那么必须快速做两个判断:

  • 1 所需的也表示否已经缓存在TLB内部(TLB miss或者TLB hit)

  • 2 所需的页表在TLB的哪个条目内

为了尽量减少CPU做出这些判断所需的时间,那么就必须在TLB页表条目和内存页表条目之间的对应方式做足功夫

备注:

RAM页表中的内容是有操作系统的软件维护的,包括新建、增加、删除等操作。

TLB高速缓存的内容是由硬件完成的,并非有操作系统维护。

4.4.6.2 页面置换算法

在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。

页面置换算法和缓存淘汰策略类似,可以将内存看成磁盘的缓存。在缓存系统中,缓存的大小有限,当有新的缓存到达时,需要淘汰一部分已经存在的缓存,这样才有空间存放新的缓存数据。

页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。

1. 最佳OPT, Optimal replacement algorithm

所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率

是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。

举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列:

7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1

开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。

缺页中断:硬件复杂替换。

2. 最近最久未使用LRU, Least Recently Used

虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。

为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。

因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。

4,7,0,7,1,0,1,2,1,2,6

3. 最近未使用:NRU, Not Recently Used

每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:

R=0,M=0

R=0,M=1

R=1,M=0

R=1,M=1

当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。

NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。

4. 先进先出FIFO, First In First Out

选择换出的页面是最先进入的页面。

该算法会将那些经常被访问的页面换出,导致缺页率升高。

5. 第二次机会算法

FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改:

当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。

6. 时钟

Clock 第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。

4.5 MMU + Cache协同工作

4.6 位于内存与外存之间的内存管理单元(代码文件=》内存的映射)

引言:

对于单体、多进程的程序,所有的程序都一次性加载到物理内存空间中,比如VxWorks操作系统的可执行程序。然而,在Windows和Linux操作系统中,在文件系统中存在无数个可执行文件,这些可执行文件可以动态创建、执行,同时执行的可执行程序的总的代码长度往往超过物理内存的大小,甚至单个可执行程序的代码空间的大小就可以大于物理内存的大小。操作系统如何做到这一点的呢?

比如,在实际系统中,内存的容量通常几个G级别的,而硬盘高达几百兆,而硬盘终端可执行程序,其长度可以高达几个G, 甚至超过物理内存的大小。在内存中执行的可执行程序的总和可以远远超过物理内存的大小。

基本思想:

直观上理解:在Windows和Linux操作系统的可执行程序,在执行时,并不是一次性把自己的代码全部加载到物理内存空间中的,而是执行一段代码,加载到内存一段代码,避免没有执行的代码无端地占用紧张的物理内存空间大小,确保能够有更多的进程可以并发执行。

如果不支持上述的能力,这就要求所有并发执行的程序,必须一次性全部加载到物理内存中,这就带来几个麻烦:

  • 某个时刻,某个应用程序的大部分代码都的不到执行,无端地占用了紧张的物理内存的空间。

  • 同时并发执行的程序的数量极大的受到了程序大小的影响。比如物理内存16M, 可执行程序的大小8M, 10M, 则该系统最多只能执行一个程序,无法并发执行着两个程序,因为物理内存容不小同时加载这两个程序的代码。

一种可能的解决办法是:每隔一程序在执行时,只加载执行部分的程序,比如1K, 其他程序亦然留在硬盘中,等需要执行的时候在加载到内存空间中。这样16M的内存,就可以同时执行16M/1K = 16000个程序。这种思想,称为页式存储管理和段式存储管理。 这就是本节要讨论的存储管理技术!!!

即运行可执行程序按需加载到内存中执行,而大部分暂时不执行的程序存放在硬盘中,等需要执行的时候再加载到内存中执行,这种技术充分利用了程序的空间局部性和时间局部性原理!!!

4.6.1 页式存储管理

页式管理是一种内存空间存储管理的技术,页式管理分为静态页式管理和动态页式管理。将各进程的虚拟空间和物理磁盘划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。

页式管理采用请求调页或[架构之路-111]-《软考-系统架构设计师》-软件架构设计-4-特定领域软件架构

[架构之路-109]-《软考-系统架构设计师》-软件架构设计-2-软件架构概述:架构风格

[架构之路-118]-《软考-系统架构设计师》-软架构设计-11-可靠性相关设计

[架构之路-110]-《软考-系统架构设计师》-软件架构设计-3-架构描述语言ADL与UML

[架构之路-113]-《软考-系统架构设计师》-软件架构设计-6-六大软件质量

[架构之路-116]-《软考-系统架构设计师》-软架构设计-9-构件与中间件技术