计算机系统(三):内存管理(中篇)

目录

引言

4.4 虚拟内存

4.4.1 分页

4.4.2 页表

4.4.3 加速分页过程

4.4.3.1 转换检测缓冲区

4.4.3.2 软件TLB管理


引言

我们在上篇中学习了交换技术,尽管基址寄存器和界限寄存器可以用于创建地址空间的抽象,还有另个问题需要解决:管理软件的膨胀(bloatware)。虽然存储器容量增长快速,但是软件大小的增长更快。需要运行的程序往往大到内存无法容纳。交换技术并不是一个具有吸引力的解决方案。

4.4 虚拟内存

虚拟内存(virtual memory)的基本思想是:

每个程序拥有自己的地址空间,这个空间被分割成多个块,每一块称作页面(page)。 每一页有连续的地址范围。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻执行必要的映射。当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的指令。

从某个角度来讲,虚拟内存是对基址寄存器和界限寄存器的一种综合。之前的做法是为正文和数据分离出专门的基址寄存器(但不包括界限寄存器)。但重要的区别是:

而虚拟内存使得整个地址空间可以用相对较小的单元映射到物理内存,而不是为正文段和数据段分别进行重定位。

虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。

4.4.1 分页

大部分虚拟内存系统中都使用一种称为分页(paging) 的技术。在任何一台计算机上,程序引用了一组内存地址。当程序执行下面这个指令时:

MOV REG, 1000

它把地址为1000的内存单元的内容复制到REG中。地址可以通过索引、基址寄存器、段寄存器或其他方式产生。

由程序产生的这些地址称为虚拟地址(virtual address), 它们构成了一个虚拟地址空间(virtual address space)。在没有虚拟内存的计算机上,系统直接将虚拟地址送到内存总线上,读写操作使用具有同样地址的物理内存字:而在使用虚拟内存的情况下,虚拟地址不是被直接送到内存总线上,而是被送到内存管理单元(Memory Management Unit,MMU), MMU把虚拟地址映射为物理内存地址。

上述过程如下图所示:

那么这种映射是如何工作的的呢,我们也来看一下具体的例子:

在这个例子中,有一台可以产生16位地址的计算机,地址范围从0到64K-1,且这些地址是虚拟地址。然而计算机的物理内存只有32KB,因此,虽然可以编写64KB的程序,但它们却不能被完全调入内存运行。在磁盘上必须有一个最多64KB的程序核心映像的完整副本,以保证程序片段在需要时能被调入内存。

页面(page)

虚拟地址空间按照固定大小划分成被称为页面的若干单元。

页框(page frame)

物理内存中对应的单元称为页框。

页面和页框的大小通常是一样的,实际系统中的页面大小从512字节到1GB。对应于64KB的虚拟地址空间和32KB的物理内存,可得到16个虚拟页面和8个页框。

RAM和磁盘之间的交换总是以整个页面为单元进行的。很多处理器根据操作系统认为适合的方式,支持对不同大小页面的混合使用和匹配。后面会介绍为什么有时候用一个较大的页面好于用一堆较小的页面。

让我们先回到上面这张图:

标记0K~4K的范围表示该页的虚拟地址或物理地址是0-4095,4K~8K表示4096-8191,等等。每一页包含了4096个地址,起始于4096的整数倍位置,结束于4096倍数缺1的位置。

当程序试图访问地址0时,执行下面这条指令:

MOV REG, 0

MMU看到虚拟地址0落在页面0(0~4095), 根据其映射结果,这一页面对应的是页框2(8192~12 287),因此MMU把地址变换为8192,并送到总线上。内存对MMU一无所知,它只看到一个读或写地址8192的请求并执行它。MMU从而有效地把所有从0~4095的虚拟地址映射到了8192~12 287的物理地址。

再举一个例子,执行指令:

MOV REG, 20500

被有效地转换为:

MOV REG, 12 308

虚拟地址20500落在页面5起始地址20字节处,并且被映射到物理地址12,288+20= 12,308

通过上面的学习,我们确实发现,通过恰当地设置MMU,可以把16个虚拟页面映射到8个页框中的任何一个。但这并没有解决虚拟地址空间比物理内存大的问题。

只有8个物理页框的情况下最多只能映射8个虚拟页面,怎么区分被映射和未被映射的虚拟页面呢?

在实际的硬件中,会有一个“在/不在”位(present/absent bit)记录页面在内存中的实际存在情况。

当程序访问了一个未映射的页面,例如执行指令

MOV REG, 32780

MMU注意到该页面没有被映射(在图中是叉号),于是使CPU陷入到操作系统,这个陷阱称为缺页中断或缺页错误(page fault)

发生缺页中断之后,如何修复呢?

操作系统会找到一个很少使用的页框且把它的内容写入磁盘(如果它不在磁盘上)。随后把需要访问的页面读到刚才回收的页框中,修改映射关系,然后重新启动引起陷阱的指令。

举个具体的例子,如果操作系统决定放弃页框1,它想把虚拟页面8装入物理地址4096,这需要对MMU映射做两处修改。首先,它要将虚拟页面1的表项标记为未映射,使以后任何对虚拟地址4096~8191的访问都导致陷阱。随后把虚拟页面8的表项的叉号改为1,因此在引起陷阱的指令重新启动时,它将把虚拟地址32780映射为物理地址4108(4096+12)。

现在我们来看一下MMU的内部结构,了解它是怎么工作的,以及为什么我们选用的页面大小都是2的整数次幂。如下图所示:

16位虚拟地址前4位代表页号,后12位代表偏移量。4位的页号可以表示16个页面,12位的偏移可以为一页内的全部4096个字节编址。

图中虚拟地址8196(二进制是0010,00000000100)用MMU映射机制进行映射,页号(0010)作为页表(page table)的索引,得到页框号是2。“在/不在”位是1,则将在页表中查到的页框号复制到输出寄存器的高3位中(是0,则将引起一个操作系统陷阱),再加上虚拟地址中的低12位偏移量(00000000100)。如此就构成了15位的物理地址(110,00000000100)。输出寄存器的内容随即被作为物理地址送到内存总线。

4.4.2 页表

上面我们已经学习了如何通过将虚拟地址划分成虚拟页号和偏移量,虚拟页号通过页表找到页框号,再加上偏移量,构成了物理地址。

下面将讨论单个页表项的细节。下图给出了页表项的一个例子:

页框号

最重要的域是页框号毕竟页映射的目的是找到这个值。

“在/不在”位

其次重要的是“在/不在”位,通过0和1判断该表项有效或者引起缺页中断。

保护位(protection)

指出一个页允许什么类型的访问。比如:0表示读/写,1表示只读。一个更先进的方法是使用三位,分别对应读、写、执行该页面。

为了记录页面的使用状况,引入了修改位(modified)访问位(referenced)

修改位(modified)

在写入一页时由硬件自动设置修改位。该位在操作系统重新分配页框时是非常有用的。如果一个页面已经被修改过(“脏”的),则必须把它写回磁盘。如果一个页面没有被修改过(“干净”的),则丢弃它,因为它在磁盘上的副本仍然是有效的。这一位有时也被称为脏位(dirty bit),因为它反映了该页面的状态。

访问位(referenced)

不论是读还是写,系统都会在该页面被访问时设置访问位。它的值被用来帮助操作系统在发生缺页中断时选择要被淘汰的页面。不再使用的页面要比正在使用的页面更适合淘汰。这一位用在多页面置换算法中。

高速缓存禁止位

最后一位用于禁止该页面被高速缓存。对那些映射到设备寄存器而不是常规内存的页面而言,这个特性是非常重要的。假如操作系统正在紧张地循环等待某个I/O设备对它刚发出的命令作出响应,保证硬件是不断地从设备中读取数据而不是访问一个旧的被高速缓存的副本是非常重要的。通过这一位可以禁止高速缓存。具有独立的I/O空间而不使用内存映射I/O的机器不需要这一位。

注意!

若某个页面不在内存中,用于保存该页面的磁盘地址不是页表的组成部分。原因很简单,页表只保存把虚拟地址转换为物理地址时硬件所需要的信息。操作系统在处理缺页中断时需要把该页面的磁盘地址等信息保存在操作系统内部的软件表格中。硬件不需要它。

值得再次强调的是:虚拟内存本质上是用来创造一个新的抽象概念——地址空间,这个概念是对物理内存的抽象,类似于进程是对物理处理器(CPU)的抽象。虚拟内存的实现,是将虚拟地址空间分解成页,并将每一页映射到物理内存的某个页框或者(暂时)解除映射。

因此,上面的基本内容是操作系统创建的抽象,以及如何管理这个抽象。

4.4.3 加速分页过程

我们已经学习了虚拟内存和分页的基础。现在可以更具体地讨论实现了。在任何分页系统中,都需要考虑两个主要问题:

虚拟地址到物理地址的映射必须非常快

这是是由于每次访向内存都需要进行虚拟地址到物理地址的映射,所有的指令最终都必须来自内存,并且很多指令也会访问内存中的操作数。因此,每条指令进行一两次或更多页表访问是必要的。

如果虚拟地址空间很大,页表也会很大

现代计算机使用至少32位,一般64位的虚拟地址。假设页面大小为4KB,32位的地址空间将有100万页,而64位地址空间简直多到超乎你的想象。如果虚拟地址空间中有100万页,那么页表必然有100万条表项。另外请记住,每个进程都需要自己的页表(因为它有自己的虚拟地址空间)。

最简单的设计(至少从概念上)是使用由“快速硬件寄存器”阵列组成的单一页表,每一个表项对应一个虚拟页面,虚拟页号作为索引,就是上面所讲的内容。

当启动一个进程时,操作系统把保存在内存中的进程页表的副本载入到寄存器中。在进程运行过程中,不必再为页表而访问内存。这个方法的优势是简单并且在映射过程中不需要访问内存。而缺点是在页表很大时,代价高昂,因为寄存器的容量小且成本高。而且每一次上下文切换都必须装载整个页表,这样会降低性能。

另一种极端方法是,整个页表都在内存中。那时所需的硬件仅仅是一个指向页表起始位置的寄存器。这样的设计使得在上下文切换时,进行“虚拟地址到物理地址”的映射只需重新装入一个寄存器。当然,这种做法的缺陷是在执行每条指令时,都需要一次或多次内存访问来完成页表项的读入,速度非常慢。

4.4.3.1 转换检测缓冲区

现在讨论加速分页机制和处理大的虚拟地址空间的实现方案。

先介绍加速分页问题。我们使用页表是有效率问题的,因为在不分页的情况下,一条指令只访问一次内存,即从内存中取指令。有了分页机制后,会因为要访问页表而引起更多次的内存访问。由于执行速度通常被CPU从内存中取指令和数据的速度所限制,所以两次访问内存才能实现一次内存访问会使性能下降一半。在这种情况下,没人会采用分页机制。

该如何提高效率呢?我们发现 大多数程序总是对少量的页面进行多次的访问。因此,只有很少的页表项会被反复读取,而其他的页表项很少被访问。

一个解决方案是为计算机设置一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不必再访问页表。这种设备称为转换检测缓冲区(Translation Lookaside Buffer,TLB),有时又称为相联存储器(associate memory)快表,如下图所示:

 它通常在MMU中,包含少量的表项,在此例中为8个,在实际中很少会超过256个。每个表项记录了一个页面的相关信息,包括虚拟页号、页面的修改位、保护码(读/写/执行权限)和该页所对应的物理页框。除了虚拟页号(不是必须放在页表中)。这些域与页表中的域是一一对应的。另外还有一位用来记录这个表项是否有效(即是否在使用)。

TLB是如何工作的:

将一个虚拟地址放入MMU中进行转换时,硬件首先通过将该虚拟页号与TLB中所有表项同时(即并行)进行匹配,判断虚拟页面是否在其中。如果发现了一个有效的匹配并且要进行的访问操作并不违反保护位,则将页框号直接从TLB中取出而不必再访问页表。如果虚拟页号确实是在TLB中,但指令试图在一个只读页面上进行写操作,则会产生一个保护错误,就像对页表进行非法访问一样。如果MMU检测到没有有效的匹配项,就会进行正常的页表查询。接着从TLB中淘汰一个表项,然后用新找到的页表项代替它。这样,如果这一页面很快被再次访问,第二次访问TLB时自然将会命中而不是未命中。当一个表项被清除出TLB时,将修改位复制到内存中的页表项,而除了访问位,其他的值不变。当页表项中从页表中装入TLB中时,所有的值都来自内存。

4.4.3.2 软件TLB管理

我们假设每一台具有虚拟内存的机器都具有由硬件识别的页表,以及一个TLB。在这种设计中,对TLB的管理和TLB的失效处理都完全由MMU硬件来实现。只有在内存中没有找到某个页面时,才会陷入到操作系统中。但是,现代几乎所有的页面管理都是在软件中实现的。

TLB表项被操作系统显式地装载。当发生TLB访问失效时,不再是由MMU到页表中查找并取出需要的页表项,而是生成一个TLB失效并将问题交给操作系统解决。系统必须先找到该页面,然后从TLB中删除一个项,接着装载一个新的项,最后再执行先前出错的指令。

上面说的软件失效处理必须在有限的几条指令中完成,因为TLB失效比缺页中断发生得更加频繁。让人感到惊奇的是,如果TLB大到可以减少失效率时,TLB的软件管理就会变得足够有效。这种软件方法的最主要的好处是获得了一个非常简单的MMU,这就在CPU芯片上为高速缓存以及其他改善性能的设计腾出了相当大的空间。

有多种不同的策略来改善软件TLB管理机制。其中一种策略是在减少TLB失效的同时,又要在发生TLB失效时减少处理开销。为了减少TLB失效,有时候操作系统能用“直觉”指出哪些页面下一步可能会被用到并预先为它们在TLB中装载表项。

用硬件处理TLB失效和软件处理TLB失效的相同点是,常见方法都是找到页表并执行索引操作以定位将要访问的页面。

用软件做这样的搜索的问题是,页表可能不在TLB中,这就会导致处理过程中的额外的TLB失效。可以通过在内存中的固定位置维护一个大的TLB表项的软件高速缓存(该高速缓存的页面一直保存在TLB中)来减少TLB失效。通过首先检查软件高速缓存,操作系统能够实质性地减少TLB失效。

当使用软件TLB管理时,一个基本要求是要理解两种不同的TLB失效的区别在哪里。

软失效

当一个页面访问在内存中而不在TLB中时,将产生软失效。那么此时所要做的就是更新一下TLB,不需要产生磁盘I/O。典型的处理需要10~20个机器指令并花费几纳秒完成操作。

硬失效

当页面本身不在内存中(当然也不在TLB中)时,将产生硬失效。此刻需要一次磁盘存取以装入该页面,这个过程大概需要几毫秒。硬失效的处理时间往往是软失效的百万倍。

上面所涉及到的在页表结构中查找相应的映射被称为页表遍历

实际中遇到的情况可能会更加复杂,未命中的情况可能既不是软失效也不是硬失效。一些未命中相比其他未命中会更“软”(或更“硬”)。 举例来说,假设页表遍历没有在进程的页表中找到需要的页,从而引发了一个缺页错误,那么这时有三种可能。

第一种,所需的页面可能就在内存中,但却未记录在该进程的页表里。比如该页面可能已由其他进程从硬盘中调入内存,这种情况下只需要把所需的页面正确映射到页表中,而不用再从硬盘调入。这是一种典型的软失效,称为次要缺页错误

第二种,如果需要从硬盘重新调入页面,这就是严重缺页错误

第三种,程序可能访问了一个非法地址,根本不需要向TLB中新增映射。此时,操作系统一般会通过报告段错误来终止该程序。只有第三种缺页属于程序错误,其他缺页情况都会被硬件或操作系统以降低性能为代价而自动修复。

本篇博客学习了虚拟内存的相关知识,从分页到页表再到加速分页过程,我们最后简单提到了页面置换,那么页面置换是如何实现,发生置换时,我们如何选择被置换的页面,又有哪些依据,下一篇博客我们将具体学习页面置换算法。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值