深入理解操作系统(24)第十章:虚拟存储器(1)物理和虚拟寻址+地址空间+虚拟内存作为缓存的工具(地址翻译/线性地址空间/物理,虚拟页/页帧/DRAM缓存/页表/页命中/缺页/交换,页面调度/分配页面)
1. 前沿
1.1 虚拟存储器(VM)定义
为了更加有效地管理存储器并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟存储器(VM)。
1.2 虚拟存储器的三个重要的能力
虚拟存储器是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的、私有地址空间。
通过一个很清晰的机制,虚拟存储器提供了三个重要的能力:
1. 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,
在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,
通过这种方式,它高效地使用了主存
2. 它为每个进程提供了一致的地址空间,从而简化了存储器管理
3. 它保护了每个进程的地址空间不被其他进程破坏
1.3 虚拟存储器自动地工作,不需要干预
虚拟存储器是计算机系统最重要的概念之一。
它成功的一个主要原因就是因为它是沉默地、自动地工作的,不需要应用程序员的任何干涉。
1.4 理解虚拟存储器的重要性
既然虚拟存储器在幕后工作得如此之好,为什么程序员还需要理解它呢?
1.4.1 理解虚拟存储器的三个原因
有以下几个原因:
原因一:虚拟存储器是中心的
虚拟存储器遍及计算机系统的所有层面,在硬件异常、汇编器、链接器、加载器、共享对象、文件和进程的设计中扮演着重要角色。理解虚拟存储器将帮助你更好地理解系统通常是如何工作的。
原因二:虚拟存储器是强大的
虚拟存储器给予应用程序强大的能力,可以创建和破坏存储器块、将存储器块映射到磁盘文件的某个部分,以及与其他进程共享存储器。
比如,你知道你可以通过读写存储器位置读或者修改一个磁盘文件的内容吗?
或者是你可以加载一个文件的内容到存储器中,而不需要进行任何显式地拷贝吗?
理解虚拟存储器将帮助你利用它的强大功能在你的应用程序中添加动力。
原因三:虚拟存储器是危险的
每次应用程序引用一个变量、间接引用一个指针,或者调用一个诸如malloc这样的动态分配包程序时,它就会和虚拟存储器发生交互。如果虚拟存储器使用不当,应用将遇到复杂险恶的与存储器有关的错误。例如,一个带有错误指针的程序可以立即崩溃于以段错误”或者“保护错误它可能在崩溃之前还默默地运行了几个小时,或者是最令人惊慌地,运行完成,却产生不正确的结果。理解虚拟存储器以及诸如malloc之类的管理虚拟存储器的分配程序包,可以帮助你避免这些错误。
1.4.2 本章从两个角度来讨论虚拟存储器
这一章从两个角度来讨论虚拟存储器
1. 本章的前一部分描述虚拟存储器是如何工作的
2. 后一部分描述的是应用程序如何使用和管理虚拟存储器
无可避免的事实是虚拟存储器很复杂,本章很多地方都反映了这一点。
好消息就是如果你掌握这些细节,你就能够手工模拟一个小系统的虚拟存储器机制,而且虚拟存储器的概念将永远不再神秘。
第二部分是建立在这种理解之上的,向你展示了如何在程序中使用和管理虚拟存储器。
你将学会如何通过显式的存储器映射和对像malloc程序包这样的动态存储分配程序的调用,来管理虚拟存储器。你还将了解到C程序中的一大群常见的与存储器有关的错误,并学会如何避免它们的出现。
1.5 参考:虚拟地址空间
https://blog.csdn.net/lqy971966/article/details/119378416
2. 物理和虚拟寻址
2.1 物理主存就是一个大数组
计算机系统的主存被组织成一个由M个连续的字节大小的单元组成的数组。
2.2 物理地址 和 物理寻址
每字节都有一个惟一的物理地址(physical address(PA)。
第一个字节的地址为0,接下来的字节地址为1,再下一个为2,依此类推。
给定这种简单的结构,CPU访问存储器的最自然的方式就是使用物理地址。
我们把这种方式称为物理寻址(physical addressing)
图10.1展示了一个物理寻址的示例,该示例的上下文是一条加载指令,读取从物理地址4处开始的字。
图10.1
当CPU执行这条加载指令时,它会生成一个有效的物理地址,通过存储器总线,把它传递给主存。主存取出从物理地址4处开始的4字节的字,并将它返回给CPU,CPU会将它存放在一个寄存器里。
早期的PC使用物理寻址,而且诸如数字信号处理器、嵌入式微控制器以及Cray超级计算机这样的系统仍然继续使用这种寻址方式。然而,为通用计算设计的现代处理器使用的是虚拟寻址(virtual addressing),参见图10.2。
图10.2
2.3 地址翻译 MMU
根据虚拟寻址,CPU通讨生成一个虚拟地址(VA)来访问主存,这个虚拟地址在被送到存储器之前先转换成适当的物理地址。
将一个虚拟地址转换为物理地址的任务叫做地址翻译(address translation)
就像异常处理一样,地址翻译需要CPU硬件和操作系统之间的紧密合作。
CPU芯片上叫做MMU(memory management unit,存储器管理单元)的专用硬件.利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由襟作系统管理的.
3. 地址空间
3.1 地址空间
地址空间〔addressspace)是一个非负整数地址的有序集合:
{0,1,2,……}
3.2 线性地址空间
如果地址空间中的整数是连续的.那么我们说它是一个线性地址空间。
为了简化我们的讨论,我们总是假设使用的是线性地址空间。
3.3 虚拟地址空间
在一个带虚拟存储器的系统中,CPU从一个N=2的n次方个地址的地址空间中生成虚拟地址,
这个地址空间称为虚拟地址空间
{0,1,2,…… N-1}
一个地址空间的大小是由表示最大地址所需要的位数来描述的。例如,一个包含N=2的n次方个地址的虚拟地址空间就叫做一个n位她址空间。
现代系统典型地都支持32位或者64位虚拟地址空间。
一个系统还有一个物理地址空间(physical address space)它与系统中物理存储器的M个字节相对应:
{0,1,2,…… M-1}
M不要求是2的幂,但是为了简化讨论,我们假设M=2的m次方。
地址空间的概念是很重要的,因为它清楚地区分了数据对象〔字节)和它们的属性(地址),
一旦我们认识到了这种区别,那么我们就可以概括总结,允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间。这就是虚拟存储器的基本思想。主存中的每字节都有一个选自虚拟她址空间的虚拟地址,和一个选自物理地址空间的物理地址。
4. 虚拟内存作为缓存的工具
4.1 虚拟存储器
4.1.1 虚拟存储器就是硬盘上的数组
概念上而言,虚拟存储器(VM)被组织为一个由存放在磁盘上的n个连续的字节大小的单元组成的数组。
每字节都有一个惟一的虚拟地址,这个惟一的虚拟地址是作为到数组的索引的。
4.1.2 磁盘上数组的内容被缓存在主存中
磁盘上数组的内容被缓存在主存中。
和存储器层次结构中其他缓存一样,磁盘(较低层)上的数据被分割成块,这些块作为磁盘和主存(较高层)之间的传输单元,
4.1.3 虚拟页 物理页/页帧
VM系统通过将虚拟存储器分割为称为虚拟页(virtual page,VP)的大小固定的块,来处理这个问题。每个虚拟页的大小为P字节。
类似地,物理存储器被分割为物理页大小也为p字节,物理页也被称为页帧。
4.1.4 虚拟页的三个部分:未分配的,缓存的,未缓存的
在任意时刻,虚拟页面的集合都分为三个不相交的子集:
1. 未分配的:VM系统还未分配(或者创建)的页。
未分配的块没有任何数据和它们相关联,因此也就不占用任何磁盘空间.
2. 缓存的:当前缓存在物理存储器中的己分配页。
3. 未缓存的:没有缓存在物理存储器中的己分配页。
4.1.5 例子:一个虚拟存储器系统是如何使用主存作为缓存的
图10.3的示例展示了一个有8个虚拟页的小虚拟存储器。
虚拟页0~3还没有被分配,因此在磁盘上还不存在。
虚拟页1、4和6被缓存在物理存储器中。页2、5和7己经被分配了,但是当前并未缓存在主存中。
图10.3
4.2 DRAM缓存的组织结构
4.2.1 SRAM 和 DRAM
为了帮助我们清晰理解存储层次结构中不同的缓存概念,我们将使用术语SRAM缓存来表示位于CPU和主存之间的LI和L2高速缓存。
并且用术语DRAM缓存来表示虚拟存储器系统的缓存,它在主存中缓存虚拟页。
SRAM 是“static RAM(静态随机存储器)”的简称,之所以这样命名是因为当数据被存入其中后不会消失。
CPU中的二级缓存属于SRAM
DRAM 动态随机存储器不同,DRAM 必须在一定的时间内不停的刷新才能保持其中存储的数据。
电脑中的内存属于DRAM,
4.2.2 DRAM结构
在存储层次结构中,DRAM缓存的位置对它的组织结构有很大的影响。
回想一下:
DRAM比SRAM要慢大约10倍,而磁盘要比DRAM慢大约100000多倍。因此,DRAM缓存中的不命中(miss)比起SRAM缓存中的不命中要昂贵得多,因为DRAM缓存不命中要由磁盘来服务,而SRAM缓存不命中通常是由基于DRAM的主存来服务的。而且,从磁盘的一个扇区读取第一字节的时间开销比起读这个扇区中后面的字节要慢大约100倍。
归根到底,DRAM缓存的组织结构完全是由巨大的不命中开销驱动的。
4.2.3 虚拟页大小:4-8KB
因为大的不命中处罚和访问第一字节的开销,虚拟页趋向于很大,典型地是4-8KB。
由于大的不命中处罚,DRAM缓存是全相联的。也就是说,任何虚拟页都可以放置在任何的物理页中。
不命中时的替换策略也很重要,因为替换错了虚拟页的处罚也非常之高。因此,比起硬件对SRAM缓存,操作系统对DRAM缓存使用了更复杂精密的替换算法(这些替换算法超出了我们的讨论范围)。
最后,因为对磁盘的访问时间很长,DRAM缓存总是使用写回而不是直写。
4.3 页表
4.3.1 页表
1. 同任何缓存一样,虚拟存储器系统必须有某种方法来判定一个虚拟页是否存放在DRAM中的某个地方。
2. 如果是,系统还须确定这个虚拟页存放在哪个物理页中。
3. 如果不命中,系统必须判断这个虚拟页存放在磁盘的哪个位置,
然后在物理存储器中选择一个牺牲页,并将虚拟页从磁盘拷贝到DRAM中,替换这个牺牲页。
这些功能是由许多软硬件联合提供的,包括:操作系统软件、MMU(存储器管理单元)中的地址翻译硬件,和
一个存放在物理存储器中叫做页表(page table)的数据结构。
页表将虚拟页映射到物理页。
每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。操作系统负责维护页表的内容,以及在磁盘与DRAM之间来回传送页。
4.3.2 页表基本结构
图10.4展示了一个页表的基本组织结构。
页表就是一个PTE〔page table entry 页表条目)的数组。
虚拟地址空间中的每个页在页表中的一个固定偏移量处都有一个PTE。
为了我们的目的,我们将假设每个PTE是由一个有效位(valid bit)和一个n位地址字段组成的。
1. 有效位表明了该虚拟页当前是否被缓存在DRAM中。
2. 如果设置了有效位,那么地址字段就表示DRAM中相应的物理页的起始位置,这个物理页中缓存了该虚拟页。
3. 如果没有设置有效位,那么有一个空地址表示这个虚拟页还未被分配,否则,这个地址就指向磁盘上虚拟页的起始位置。
图10.4
上图展示了一个有8个虚拟页和4个物理页的系统的页表。
1. 四个虚拟页(VP1、VP2、VP4和VP7)当前被缓存在DRAM中。
2. 两个页(VPO和VP5)还未被分配,
3. 而剩下的页(VP3和VP6)己经被分配了。但是当前还未被缓存。
上图中有一个要点要注意,因为DRAM缓存是全相联的,任意物理页都可以包含任意虚拟页。
4.4 页命中
考虑一下当CPU读虚拟存储器的一个字时,它被VP2包含且被缓存在DRAM中会发生什么?
使用我们将在10.6节中详细描述的一种技术,地址翻译硬件将虚拟地址作为一个索引来定位PTE2,并从存储器中读取它。
既然设置了有效位,那么地址翻译硬件就知道VP2是缓存在存储器中的了,所以它使用PTE中的物理存储器地址(该地址指向PP0中缓存页的起始位置),构造出这个字的物理地址。
图10.5
4.5 缺页
4.5.1 缺页概念
在虚拟存槠器的习惯说法中
DRAM缓存不命中称为缺页(page fault)
图10.6展示了在缺页之前我们的示例页表的状态。
图10.6
一个缺页处理过程:
1. CPU引用了VP3中的一个字,这个字并未缓存在DRAM中。
2. 地址翻译硬件从存储器中读取PTE3,从有效位推断出未缓存,并且触发一个缺页异常。
3. 缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4。
4. 如果VP4已经被修改了,那么内核就会将它拷贝回磁盘。
5. 无论哪种情况,内核都会修改VP4的页表条目,反映出VP4不再缓存在主存中这一事实。
6. 接下来,内核从磁盘拷贝VP3到存储器中的PP3,更新PTE3,随后返回。
7. 当异常处理程序返回时。它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译件。
8. 但是现在,VP3己经缓存在主存中了,那么页命中也能由地址翻译硬件正常处了,就像我们在图10.5中看到的那样。
参考:
页错误 Page Fault /缺页异常 详解
https://blog.csdn.net/lqy971966/article/details/106910442
4.5.2 一些名词:交换/页面调度,换入,换出,按需页面调度
虚拟存储器是在20世纪年代早期发明的,远在CPU一存储器之间差距的加大引发产生SRAM缓存之前。
因此,虚拟存储器系统使用了和SRAM存不同的术语,即使它们的许多概念是相似的。
1. 在虚拟存储器的习惯说法中,块被称为页
2. 在磁盘和存储器之间传送页的活动叫做交换(swapping)或者页面调度(paging)
3. 页从磁盘换入(或者页面调入)DRAM·和从DRAM换出到(或者页面调出到)磁盘
4. 一直等待,直到最后时刻,也就是当有不命中发生时,才换入页面的这种簽略被称为按需页面调度(demandpaging)。
其他的方法也是可能的,例如尝试着预测不命中,在页面实际被引用之前就换入页面。然而,所有现代系统都使用的是按需页面调度的方式。
4.6 分配页面
图10.8展示了当操作系统分配一个新的虚拟存储器页时,对我们示例页表的影响。
例如,调用malloc的结果。
在这个示例中,通过在磁盘上创建空间,并更新PTE5,使它指向磁盘上这个新创建的页面,从而分配VP5。
图10.8
4.7 局部性再次搭救(工作集/常驻集合,颠簸)
当我们中的许多人都了解了虚拟存储器的概念之后,我们的第一印象通常是它的效率想必是非常低。
假设不命中处茳很大,我们会担心页面调度会破坏程序性能。
实际上,虚拟存储器工作得相当好,这主要归功于我们的老朋友局部性。
尽管在整个运行过程中程序引用的不同页面的总数可能超出物理存储器总的大小,
但是局部性原则保证了在任意时刻,这些页面将趋向于在一个较小的活动页面(acuvepage)集合上工作,
这个集合叫做工作集(working set)或者常驻集合(resident set)。
在初始开销,也就是将工作集页面调度到存储器中,之后,接下来对这个工作集的引用将导致命中,而不会产生额外的磁盘流量。
只要我们的程序有好的时间局部性,虚拟存储器系统就能工作得相当好。
但是,当然,不是所有的程序都能展现良好的时间局部性。如果工作集的大小超出了物理存储器的大小,
那么程序将产生一种不幸的状态,叫做顛簸(thrashing〕,这时页面将不断地换进换出。
虽然虚拟存储器通常是有效的,但是如果一个程序性能慢得像爬一样,那么聪明的程序员会考虑看是不是发生了颠簸