虚拟内存是计算机内存管理的一种技术。它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间)而实际上,它通常被分割为多个物理内存的碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写更加容易,对真正的物理内存(例如RAM)的使用也更有效率。
注意:虚拟内存不只是“用磁盘空间来扩展物理内存”的意思——这只是内存级别以使其包含硬盘驱动器而已。把内存扩展到磁盘只是使用虚拟内存的一个结果,它的作用也可以通过覆盖或者把处于不活动状态的程序以及他们的数据全部交换到磁盘上等方式来实现。
对虚拟内存的定义:基于对地址空间的重定义的,即把地址空间定义为“连续的虚拟内存地址”,以借此“欺骗”程序,使它们以为自己正在使用一大块的“连续”地址。
Linux将物理RAM(Random Access Memory)
划分为称为页面的内存块。交换是将一页内存复制到硬盘上的预配置空间(称为交换空间)以释放改内存页面上的过程。物理内存和交换空间的组合就是可用的虚拟内存量。
进程是与其他进程共享CPU和内存资源的。为了有效的管理内存并减少出错,现代操作系统提供了一种对主存的抽象概念,即:虚拟内存(Virtual Memory
)。虚拟内存为每个进程提供一个一致的,私有的地址空间,每个进程拥有一片连续完整的内存空间。
正如维基百科所说,虚拟内存不只是“使用硬盘空间来扩展内存”的技术。虚拟内存的重要意义是它定义了一个连续的虚拟地址空间,使得程序编写难度降低。并且,把内存扩展到硬盘空间只是使用虚拟内存的必然结果,虚拟内存空间会存在硬盘中,并且会被全部放入内存中缓冲(按需),有的操作系统还会在内存不够的情况下,将一进程的内存全部放入硬盘空间中,并在切换到进程时再从硬盘读取(这也是Windows会经常假死的原因...)。
虚拟内存主要提供了如下三个重要的能力:
- 把主存看作一个存储在硬盘上的虚拟地址空间的高速缓存,并且只在主存中缓存活动区域(按需缓存)。
- 为每个进程提供了一个一致的地址空间,降低程序员对内存的管理的复杂性。
- 保护了每个进程的地址空间不被破坏。
2. 虚拟内存如何运作
2.1 CPU寻址
内存通常被组织为一个由M个连续的字节大小的单元组成的数组。每个字节都有一个唯一的物理地址(Physical Address PA)
,作为到数组的索引。
CPU访问内存最简单直接的方法就是使用物理地址,这种寻址方式称为物理寻址。
现代计算机使用的是一种被称为虚拟寻址(Virtual Addressing)
的寻址方式。使用虚拟寻址,CPU需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。
虚拟寻址需要硬件与操作系统之间相互合作。CPU中含有一个被称为内存管理单元(Memory Management Unit,MMU)
的硬件,它的功能是将虚拟地址转换称为物理地址,MMU需要借助存放在内存中的页表
来动态翻译虚拟地址,该页表由操作系统管理。
2.2 页表
分页表是一种数据结构,它用于计算机操作系统中虚拟内存系统,其存储了虚拟地址到物理地址之间的映射。虚拟地址在访问进程中是唯一的,而物理地址在硬件(比如内存)中是唯一的。
在操作系统中使用虚拟内存,每个进程会认为使用一块大的连续的内存,事实上,每个进程的内存散布在物理内存的不同区域。或者可能被调出到备份存储中(一般是硬盘)。当一个进程请求自己的内存,操作系统负责把程序生成的虚拟地址,映射到实际存储的物理内存上。操作系统在分页表中存储虚拟地址到物理地址的映射。每个映射被称为分页表项(page table entry ,PTE)。
在一个简单的地址空间方案中,由虚拟地址寻址的页与物理内存中的帧之间的关系。物理内存可以包含属于许多进程的页。如果不经常使用,或者物理内存已满,可以将页面分页到磁盘。在上图中,并非所有页面都在物理内存中。
2.3 内存管理单元
内存管理单元
(memory management unit,MMU)
,是一种负责CPU的内存访问请求的计算机硬件。通常,操作系统会为每个程序分配虚拟内存空间。
2.3.1 MMU的功能
虚拟地址到物理地址的转换(即虚拟内存的管理)、内存保护、CPU高速缓存的控制。
2.3.2 MMU工作机制
现代的内存管理单元是以页的方式,分割虚拟地址空间(处理器使用的地址范围)的;页的大小是2的n次方,通常为几KB(字节)。地址尾部的n位(页大小的2的次方数)作为页内的偏移量保持不变。其余的地址位(address)为(虚拟)页号。
2.3.3 MMU页表条目
内存管理单元通常借助一种叫做转译旁观缓冲器(Translation Lookaside Buffer,TLB)和相联高速缓存来将虚拟页号转换为物理页号。当后备缓冲器中没有转换记录时,则使用一种较慢的机制,其中包括专用硬件的数据结构或软件辅助手段。这个数据结构称为分页表,页表中的数据叫做分页表项(page table entry PTE)。物理页号结合页偏移量便提供了完整的物理地址。
页表 或 转换后备缓冲器数据项应该包括的信息有:
- 脏位(页面重写标志位,dirty bit),表示该页是否被写过;
- 访问位(accessed bit),表示该页最后使用于何时,以便最近最少使用页面置换算法的实现;
- 哪种进程可以读写该页的信息,利用用户模式进程还是特权模式进程;
- 该页是否应被高速缓冲的信息。
有时候,TLB和PTE会禁止对虚拟页访问,这可能是因为没有RAM与虚拟页相关联。如果是这种情况,MMU将向CPU发出页错误的信号,操作系统将进行处理,也许会寻找RAM的空白帧,同时建立一个新的PTE将之映射到所请求的虚拟地址。如果没有空闲的RAM,可能必须关闭一个已经存在的页面,使用一些替换算法,将之保存到磁盘中(这被称为页面调度)。
2.3.4 MMU优点
- 内存保护:页错误的错误可能存在软件错误,进程的MMU可以使用内存保护特性避免非法访问其他进程内存。
- 减少内存碎片化:进程分配并释放内存块后,可能导致内存碎片化。导致最大连续可用的内存块远小于要分配的内存总量。在虚拟内存实现后,虚拟内存可以将多个不连续的物理内存块映射为一个连续区块。
2.4 转换过程
CPU的内存管理单元(MMU)存储最近用过的分页表的映射缓存。被称为转移后备缓冲器(TLB),TLB是一个索引缓存。
当需要将虚拟地址转换为物理地址时,首先搜索TLB,如果找到匹配(TLB)命中,则返回物理地址并继续存储器访问。然而,如果没有匹配(称为TLB未命中),则MMU或操作系统TLB未命中处理器通常会查找页表中的地址映射以查看是否存在映射(页面遍历),如果存在,则将其写回TLB(这必须完成,因为硬件通过虚拟存储器系统中的TLB访问存储器),并且重启错误指令(这也可以并行发生)。此后续转换找到TLB命中,并且内存访问将继续。
虚拟地址到物理地址的转换过程,如果虚拟内存不存在与TLB,转换会被重置并通过分页表和硬件寻找。
2.5 转换失败
如果该地址没有可用的转换,这意味该虚拟地址的存储器访问是无效的。这通常是程序错误导致的,操作系统需要处理这个问题。现代操作系统会发送一个段错误信息给出错程序。
当物理内存中不存在这个页,也会引起分页表查找失败。如果请求的页面没被调出物理内存,给其他页腾出空间,会引起这个错误。这种情况下,页被分配到存储在介质上的辅助存储,例如硬盘(这种辅助存储,或叫备用存储,如果是一个硬盘分区或者交换文件,经常称之为交换分区,如果是文件,叫做分区文件或页文件)。这时候,分页需要从硬盘放回到物理内存中,这类操作通常会导致系统抖动的情况,通过局部性原理来预测将来可能会访问的块,避免出现系统抖动。
当物理内存没满的时候,这是一个简单操作。页被写回物理内存,页表和转换备用缓冲会更新,指令重启。然而,当物理内存已满,一个或多个页面要被调,为请求的页面腾出空间时候。页表需要更新。标识出那些在物理内存被调出的页,然后标识那些从硬盘调用物理内存的页。TLB也需要更新,包括去掉调出的页,重启指令。
段错误:存储器区块错误
(Segmentation fault)
也称访问权限冲突(access violation)
,是一种程序错误。它出现在当程序企图访问CPU无法定址的存储器区块时。当发生错误时,硬件会通知操作系统产生了存储器访问权限冲突的状况。通常该错误是由于调用一个地址,而该地址为空(NULL)导致的。例如链表中调用一个未分配地址的空链表单元的元素,数组访问越界也可能产生这个错误。
2.6 页缺失
页缺失(page fault)指的是当软件试图访问已映射在虚拟地址空间中,但是当前未被加载在物理内存中的一个分页时,由CPU的内存管理单元所发出的中断。
通常情况下,用于处理此中断的程序是操作系统的一部分。如果操作系统判断此次访问有效,那么操作系统会尝试将相关的分页从硬盘上的虚拟内存文件调入内存。而如果访问是不被允许的,那么操作系统通常会结束相关的进程。
虽然叫做“页缺失”错误,但实际上这并不一定是一种错误。而且这一机制是利用虚拟内存来增加程序可用内存空间。
2.6.1 软性页缺失
软性页缺失指页缺失发生时,相关的页已经被加载进内存,但是没有向MMU注册的情况。操作系统只需要在MMU中注册相关页对应的物理地址即可。
发生这种情况的可能性:
- 一块物理内存被两个或者多个程序共享,操作系统已经为其中一个转载并注册了相关的页,但是没有为另一个程序注册。
- 该页已被从CPU的工作集中移除,但是尚未被交换到磁盘上。比如Open VMS使用次级页缓存的系统,就有可能会在工作集过大的情况下,将某页从工作集中删除,但是不写入硬盘也不擦除(比如这一页被读出硬盘后没被修改过),只是放入空闲页表。除非有其他程序需要,导致这一页被分配出去,不然这一页的内容也不会被修改。
当原程序再次需要该页内的数据时,如果这一页确实没有被分配出去,那么系统只需要重新为该页在MMU内注册映射即可。
2.6.2 硬性页缺失
与软性页缺失相反,硬性页缺失是指相关的页在页缺失发生时未被加载进内存的情况。
操作系统需要:
- 寻找一个空闲的页。或者把另外一个使用的页写到磁盘上(如果其在最后一次写入后发生了变化的话),并注销MMU内的记录;
- 将数据读入被选定的页;
- 向MMU注册该页;
硬性页缺失导致的性能损失是很大的。
另外,有些操作系统会将程序的一部分延迟到需要使用的时候再加载入内存执行,以此提升性能。这一特性也是通过捕获硬性页缺失达到的。
当硬性页缺失过于频繁发生时,称发生系统颠簸。
2.6.3 无效页缺失
当程序访问的虚拟地址是不存在与虚拟地址空间内的时候,则发生无效页缺失。一般来说这是个软件问题,但也不排除硬件可能,比如因为内存故障而损坏了一个正确的指针。
具体动作与所使用的操作系统有关,比如Windows会使用异常机制向程序报告,而类Unix系统则使用信号机制。
3. 分页
分页(Paging),是一种操作系统里存储器管理的一种技术,可以使计算机的主存使用存储在辅助存储器的数据。操作系统会将辅助存储器(通常是磁盘)中的数据分割成固定大小的区块,称为“页”
(pages)
。当不需要时,当不需要时,将分页由主存(通常是内存)移到辅助存储器;当需要时,再将数据取回,加载主存中。相对于分段,分页允许存储器存储于不连续的区块以维持文件系统的整齐。分页是磁盘和内存之间传输数据块的最下单位。
3.1 命中和缺页
虚拟存储器系统中,DRAM缓存命中,称读取了一个地址。命中过程不需要程序再从磁盘中缓慢读取数据。
相对的,DRAM不命中称为缺页(page fault)。由于页帧
(MMU中页表的组成单位 分页表项 PTE)
由有效位和物理页号(或磁盘地址)构成,当有效位标记为0时,即表明该地址未在缓存中,调用该地址会引起缺页异常。从磁盘中拷贝需要的内容,覆盖内存中的一个牺牲页,从而能够命中。过多缺页会导致反复的磁盘读取和写入,非常耗费时间。
3.2 系统颠簸
尽管在整个运行过程中,程序引用不同的页面总数(也就是虚拟内存大小)可能超出了物理存储器(DRAM)总大小,但是程序常常在较小的活动页面上活动,这个集合叫做工作集或者常驻集。在工作集被缓存后,对它的反复调用会使程序命中提高,从而提高性能。
大部分的程序都可以在存储器获取数据和读取中达到稳定的状态,当程序达到稳定状态时,存储器的使用量通常都不会太大。虚拟内存虽然可以有效率控制存储器的使用,但是大量的页缺失还是造成了系统迟缓的主要因素。当工作集的大小超过物理存储器大小,程序将会发生一种不幸的情况,这种情况称为“颠簸”,页面将不停的写入、释放、读取,由于大量的丢失(而非命中)而损失极大性能。用户可以增加随机存取存储器的大小或是减少同时在系统里运行程序的数量来降低系统颠簸的记录。
3.3 系统调度
- 当需要用到数据时再向系统请求,使得系统将数据由辅助设备传入到存储器上,这叫做“需求分页”。使得系统不需要将全部的程序都放在存储器上,减少了所需要的存储器的数量。
- 当系统查看分页表时认为某些数据可能需要用到,而先将数据传到存储器上的行为,就叫做“先行分页”,当存储器够大的话通常采取这种方式。
- Unix系统会定期使用sync程序来清理所有经过变更的帧,他会将所有被变更的帧存到辅助存储器(硬盘)上。windows系统有时也会进行类似的操作,使得新程序打开时更加快速。
推荐阅读: