虚拟内存(Virtual Memory),是
计算机系统内存管理的一种技术,它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),而实际上,它通常被分隔成多个物理内存碎片,还有部分暂时存储在外部
磁盘存储器上,当计算机缺少运行某些程序所需的物理内存时,操作系统会使用
HDD上的虚拟内存进行替代。
在虚拟内存概念提出前,
计算机内存管理依靠
随机存取存储器(RAM)和辅助存储,面临程序规模增长与内存成本高昂的挑战。虽然关于虚拟内存概念的首次提出存在一些争议,但普遍认为是由
德国物理学家弗里茨·鲁道夫·冈施(Fritz-Rudolf Güntsch)于1956年率先提出,
曼彻斯特大学于1959年开发的Atlas计算机实现了分页技术,成为早期采用虚拟内存技术的系统之一,而伯勒公司(Burroughs Corp.)随后于1961年推出了首款商用的虚拟内存
计算机系统。1982年,
英特尔在80286处理器中引入虚拟内存技术,并在1985年的80386处理器中提供分页支持,推动了个人电脑技术的快速发展。
虚拟内存器的调度方式包括分页式、段式和段页式。影响虚拟
内存的因素包括设置虚拟内存的大小、存储位置、内存管理单元等,涉及到地址变换、替换算法等重要技术,其具有扩大地址空间、对特定的内存地址提供写保护和公平分配内存等优点,但同时也存在浪费内存、增加指令的执行时间等局限性。此外,虚拟
内存广泛应用在多任务操作系统、大型应用程序等场景中。
发展历程
背景
在虚拟内存的概念被提出之前,
计算机的内存管理主要依赖于
随机存取存储器(RAM)和辅助存储器。在早期的
计算机系统中,磁芯存储器被用作
主存储器,而
磁鼓则作为辅助存储器使用。然而,在20世纪40年代和50年代,由于存储器的价格昂贵且供应不足,程序的大小和复杂性不断增加使得
内存管理成为了一个迫切需要解决的问题。
为了解决这个问题,早期程序员通过覆盖的过程来运行大于可用内存的程序,这种技术将程序中不常使用的部分设置为可覆盖状态,当需要运用内存时,这些部分可以覆盖内存中已有的内容。实施覆盖需要精细的编程工作,为了提升效率和简化操作,开发自动化虚拟内存技术已成为当时主要的内存发展方向。
早期
虽然关于虚拟内存概念的首次提出存在一些争议,但普遍认为是由
德国物理学家弗里茨·鲁道夫·冈施(Fritz-Rudolf Güntsch)于1956年率先提出,尽管他描述的只是一种高速
缓存存储器的形式,但他的研究为虚拟内存的发展奠定了基础。
曼彻斯特大学的Atlas
计算机是早期实现虚拟内存的系统之一,于1959年开发,并于1962年开始使用,其采用了分页技术,将虚拟地址
映射到物理内存。
1961年,伯勒公司(Burroughs Corp.)推出了首款商用的虚拟内存计算机系统,这款计算机采用了分段的方式来管理虚拟内存,而非传统的分页管理。
1960年代中期,IBM在托马斯·J·沃森研究中心(Thomas J. Watson Research Center)进行M44/44X
计算机研究项目。M44/44X项目基于IBM 7044(M44)实现了多个具有突破性的虚拟化概念,包括部分硬件共享(partial hardware sharing)、时间共享(
时间 sharing)、
内存分页(memory paging)等,并实现了虚拟内存管理的
虚拟机监视器。通过这些虚拟化技术,应用程序可以运行在这些虚拟的内存之中,实现了在同一台主机上模拟出多个7044系统(44X)。M44/44X项目首次使用了虚拟机(Virtual Machine,VM)和虚拟机监视器(Virtual Machine
监听,VMM)一词,被认为是世界上第一个支持虚拟机的系统。
1969年,随着IBM相关人员的研究,证明了虚拟内存覆盖系统相较于早期手动系统具有更高的效率,虚拟内存技术开始逐渐得到广泛的认可和应用。随着时间的推移,虚拟内存技术逐渐成为了
大型计算机和小型计算机的标配。
发展
1982年,
英特尔在80286处理器
保护模式下引入了虚拟内存技术,并在1985年为80386的推出提供了分页支持,带动了个人电脑技术的快速发展。
2000年,作为真正意义上的第一个功能完整的操作系统虚拟化技术,
FreeBSD jail得到迅速发展。利用这个技术,
FreeBSD的系统管理者可以创造出几个小型的软件系统,这些软件系统被称为监狱(jails)。
2004年,
微软发布Virtual Server 2005计划,象征着虚拟化技术正式进入主流市场。
2010年10月21日,
美国航空航天局发布了云操作系统
OpenStack,推动了
云计算在中国的全面爆发。2011年5月,IBM和
Red Hat,联合
惠普和
英特尔一起,成立了开放虚拟化联盟(Open Virtualization Alliance)。
工作原理
在启用虚拟
内存时,每个程序都有不同的虚拟内存空间,可以自由访问预先定好的内存地址,而实际保存在物理内存的地址则交给内存管理单元管理。虚拟内存中的内容不仅可以保存在物理内存中,而且可以保存在
HDD中,也就是所谓的分页文件(Swap)。当物理内存不足时,操作系统可以把一部分内容转移到硬盘上的虚拟内存文件,下次使用到该部分内容时再从虚拟内存文件转移回物理内存。
程序A发出了“写入4
字节到内存地址0x1020”的指令,CPU实际上可能在物理
内存地址0x2020处进行写入操作;而当程序B执行相同的“写入4字节到
内存地址0x1020”的指令时,CPU可能选择在物理内存地址0x4020处进行写入。这样,虽然程序A和程序B使用的是相同的虚拟内存地址,但实际的物理内存地址不同,因此它们的操作不会互相影响。
关键技术
地址变换
地址变换是逻辑地址到物理地址的变换。一般情况下,一个作业在装入内存时分配到的存储空间和它的地址空间是不一致的。因此,作业在CPU上运行时,其所要访问的指令和数据的物理地址与地址空间中的逻辑地址是不同的。如果在作业装入
内存或者在它运行时不对相关地址加以修改就会导致错误的结果。这种由于一个作业装入与其地址空间不一致的存储空间所引起的对有关地址部分的调整,就是地址变换,又称为地址重定位。这种地址变换的过程也称为地址
映射。地址变换的目的,是消除在高级寻址方式中程序地址与物理地址的语义差距,使程序能够在任意位置的物理内存中运行,而不必关心其物理位置。
替换算法
替换策略在任何一个调页系统中都很重要。在为调页系统设计替换策略时,可以选择很多种算法。页面替换算法通常从实际程序执行过程中收集引用串,评估算法对引用串产生的效果来衡量,同时也可以对算法采用形式化分析,但是除非对执行环境增加很多限制条件,否则这种分析很困难。衡量页面替换算法效率最常用的指标是缺页率。常用的替换算法包括以下四种:
随机算法
随机(Random)算法是指通过算法从服务器列表中随机选取一台服务器进行访问。结合
概率论的相关知识可知随着客户端调用服务器端的次数增多,其实际效果趋近于平均分配请求到服务器端的每一台服务器,即达到
轮询的效果。算法描述如下:
假设有N台服务器S=|S0,S1,S2.…,Sn|,算法可以描述为通过随机函数生成0~N之间的任意整数,将该数字作为
索引,从S中获取对应的服务器。假定现在有4台服务器初始化服务列表后,每台服务器对应一个地址,地址分别有相应的权重,见下表。
随机算法与服务器权重没有关系,每个服务器会被随机地访问到。由
概率论可以得知,当样本量足够大时,每台服务器被访问到的概率近似
相等,随机算法的效果就越趋近于
轮询调度算法,将请求随机分配到各节点。随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。
先进先出算法
先进先出(FIFO)算法根据块在
内存(
缓存)中的时间长短作为替换依据。FIFO算法认为是最早调入Cache中的块,其以后不再被使用的可能性比刚调入的可能性大,故总是选择在Cache中停留时间最长的块替换。在算法实现上,将进入Cache的块按照时间先后顺序排队,队首是最早进入Cache的块,队尾是最晚进入Cache的块,需要替换时,队首的块首先被替换掉。
近期最少使用算法
近期最少使用算法(LRU)的基本思想是挑选近期最久没有使用过的块作为被替换块,这种算法能较好地反映程序的局部性特点。LRU算法可用
堆栈来实现,所以又称为堆栈型算法。当所设堆栈已满,又有一块要求调入
缓存时,首先检查堆栈中是否已经有这一块。如果有,则将这一块从堆栈中取出压入堆栈的栈顶;如果没有,则将该块直接压入栈顶,于是在原栈底上的块被压出了堆栈,保证任何时候栈顶上的块总是刚被访问过的块,而栈底上的块总是最久没有被访问过的块。
最佳置换算法
OPT置换算法是由贝莱迪(Belady)于1966年提出的一种理论上的算法。其所选择的被淘汰页面,将是以后永不使用的,或者是在未来的最长时间内不再被访问的页面。采用最佳置换算法,通常可保证获得最低的缺页率。但由于人们还无法预知一个进程在
内存的若干个页面中,哪一个页面是未来最长时间内不再被访问的,但可以利用该算法去评价其他算法。
工作过程
虚拟存储器是由硬件和操作系统自动实现存储信息调度和管理的。它的工作过程包括6个步骤:
调度方式
调度方式有段式、分页式、段页式3种。
段式
程序通常具有模块性,一个复杂的大程序总可以分解成多个在逻辑上相对独立的模块,这些模块可以是主程序、
子程序或过程,也可以是数据块。模块的大小各不相同,有的甚至事先无法确定,每个模块都是一个单独的段,都以该段的起点为0相对编址。当某个段由辅存调入主存时,只要赋予该段一个基地址(即该段存放在主存中的起始地址),就可以由此基地址和单元在段内的相对位移形成单元在主存中的实际地址。将
主存储器按段分配的
存储管理方式称为段式管理。
假设系统在
主存储器中最多可同时有N道程序,可设N个段表基址
寄存器,对应于每道程序,由基号(程序号)指明使用哪个段表基址寄存器,段表基址寄存器中的段表基地址字段指向该道程序的段表在主存中的起始地址,段表长度字段指明该道程序所用段表的行数即程序的段数。由系统赋予某道程序(用户、进程)一个基号,并在调入/调出过程中对有关段表基址寄存器和段表的内容进行记录和修改。
分页式
段式存储中各段装入
主存储器的起点和各段长度是随意的,段表中的地址字段和段长字段很长,增加了辅助硬件开销,降低了查表速度,导致主存管理繁琐。例如,主存中已有A、B、C三个程序,其大小和位置如图所示,现有一长度为12KB的D道程序想要调入。段式管理时尽管D道程序长度小于主存所有可用区零头总和16KB,但是没有零头能装得下,所以无法装入,页式存储由此开始发展。
页式存储是把
主存储器空间和程序空间都机械等分成固定大小的页(页面大小随机器而异,一般在512B到几KB之间),按页顺序编号,任一主存单元的地址np就由实页号nv和页内位移nt两个字段组成。每个独立的程序也有自己的虚页号顺序,如此例中,若页面大小取4KB,则独立编址的D程序就有3页长,页号为0-2。如果虚拟
内存中的每一页均可装入
主存储器中任意的实页位置,如下图所示,D程序中各页就可分别装入主存的第2、6、7三个实页位置,只要系统设置相应的页(映像)表,保存好虚页装入实页时的页面对应关系,就可由给定的程序(虚)地址查页表,变换成相应的实(主)存地址访存。
页面调度策略
虚拟内存系统定义了三种分页调度策略:取页策略、置页策略和替换策略。
取页策略:决定什么时候页面调度程序把一个页从
磁盘调入
内存中,取页策略有两种做法,一种是尽量把一个进程需要的页面在使用之前就装入内存,另一种是按需页面调度策略,即仅当发生页面错时才将所需页装入。
置页策略:是指当一线程收到一个页面错时,内存管理系统必须确定把页面放入物理内存中什么地方。在段式内存体系结构中,置页策略较复杂,但在线性结构中较简单,它只要求找到一个未分配的页帧。
替换策略:是指当产生页面错误且物理内存已满时,使用什么算法来确定哪一个虚拟页要从内存中移去,以便为新的页留出空间,常用的替换算法有近期最少使用算法和先进先出算法。
段页式
段页式存储是把实(主)存机械等分成固定大小的页,程序按模块分段,每个段又分成与实(主)存页面大小相同的页。每道程序通过一个段表和相应的一组页表进行定位。段表中的每一行对应一个段,其中“装入位”表示该段是否已装入
主存储器,若未装入主存,则访问该段时将引起段失效故障,请求从辅存中调入页表;若已装入主存,则地址字段指出该段的页表在主存中的起始地址。
对于多道程序而言,每道程序(用户或进程)都需要有一个用户标志号u(转换成基号b)以指明该道程序的段表起点存放在哪个基址
寄存器中,从而使多用户虚地址就由用户标志u、段号s、页号p、页内位移d四个字段组成。设系统中主存最多可容纳N道程序,下图表示采用段页式管理的定位映像机构及由多用户虚地址变换成主存实地址的过程。
在虚拟存储器中每访问一次
主存储器都要进行一次程序地址向实(主)存地址的转换。段页式的主要问题是地址变换过程至少需要查表两次,即查段表和页表。因此,要想使虚拟存储器的速度接近于主存,必须在结构上采取措施加快地址转换中查表的速度。
影响因素
内存大小
为虚拟内存分配合适的
HDD空间的大小非常重要,如果虚拟内存设置不当,就可能导致出现内存不足问题,如果分配太小,会出现“存储器用完”错误。因此可以把虚拟内存文件的最小值和最大值设为相同值,就会迫使操作系统在开机时就分配整个页文件,防止程序在运行过程中增大页文件,从而改进性能。许多图像应用程序可因此避免在硬盘和
盒式录音磁带之间读写图像信息时停顿,通过减少操作系统调整页文件大小的需要,从而减少了对
磁盘的写操作。
存储位置
影响虚拟内存性能的另一个因素是页文件在磁盘上的存储位置。系统盘空余的容量并不小,但因为经常安装、下载软件,并反复删除造成
磁盘碎片太多,也会造成虚拟内存不足。虚拟内存需要一片连续的空间,尽管磁盘空余容量大,但没有连续的空间,也无法建立虚拟
内存区。如果系统有多个物理
HDD,可以把每个驱动器上的页文件减小,而把工作分配到各个驱动器,从而使频繁使用虚拟内存的系统大大加速。
文件碎块
磁道和扇区上产生大量属于同一个程序但又非连续存放的数据簇,即所谓的文件碎块。当这些碎块积累到一定程度,有可能导致文件丢失数据,更重要的是导致硬盘读写数据的速度严重下降,并且会使得系统的高速
缓存程序如SmartDrive的性能也大打折扣,影响到虚拟内存的工作效率。
内存管理单元
在
内存管理单元中,处理器的虚拟地址空间被划分为页面大小的分配单元。内存管理单元能够将虚拟内存分为内核空间和用户空间,内核空间保留给操作系统和相关组件(如设备驱动程序)使用;
用户空间可供应用程序使用,也可以用于用户启动的其他操作,例如处理输入到命令提示符窗口中的命令。用户级代码无法直接访问系统内存,必须调用系统函数来请求内存分配等服务。
系统负载
处理器的速度影响各个任务的执行速度,同时它能够将
HDD空间用作虚拟
内存,当系统的全部RAM都被用完时,就可以通过将内存块换进或换出硬盘而继续执行其他的运算。但一旦系统负载过大而需要使用虚拟内存时,会因为
机械硬盘的速度比RAM芯片慢而使系统的性能明显下降。
优缺点
优点
允许程序分段加载到内存:虚拟内存可以使得大程序在
主存储器配置比程序本身还小的机器上运行。对于内存大小中等的机器,由于程序运行时不必全部驻留内存,虚拟内存允许更多的程序驻留在主存中竞争CPU时间,当程序一段时间内只使用部分代码段或数据段,而不用其他部分的时候,那些没有用到的部分就不用放在
内存中。另外,使用虚拟内存可以加速程序启动,通常程序在处理命令行参数和决定采取何种操作之前,只需要把一小段代码载入内存,在程序的一次执行过程中,可能不需要程序的其他部分。随着程序不断运行,其代码段和数据段的其余部分在需要时才被调入
内存(请求调页机制)。
为算法技术提供支持:相比仔细地将
数据结构放到较小的内存区域,有许多算法更容易通过稀疏使用大地址空间进行编程实现。如果不用虚拟内存,这些技术实现起来代价太大。但是一旦有了虚拟内存,在没有太多物理
内存的情况下,它们依然会运行得更快。
扩大了地址空间:无论段式虚拟内存,还是页式虚拟内存,或是段页式虚拟内存,寻址空间都比实存大。
公平分配内存:采用了虚拟内存之后,每个进程都相当于有同样大小的虚拟
内存空间。
实现进程间通信:当进程需要通信时,可采用虚拟内存共享的方式实现。内存页可以标记为可在应用程序间共享,这意味着一个页面被明确授权为可以从多个进程进行访问。这实现了有效的进程间通信。
确保操作系统正常运行:由于每个内存页都有一组属性,这些属性限制了该页面所支持的操作类型,通过合理设置这些属性,可以提高操作系统稳定性。虚拟内存管理硬件负责确保每个应用程序只能访问分配给它的内存页。试图访问另一个进程的
内存或其分配的内存空间之外的任何其他地址,都会导致访问冲突异常。此外,虚拟内存能够特定的内存地址提供写保护,以防止代码或数据被恶意篡改。虚拟内存还可以将内存页标记为所需的最低权限级别,从而仅允许内核级代码访问该内存页,此限制可确保即使在应用程序运行异常的情况下,操作系统也可以正常运行。
局限性
降低性能:使用虚拟内存会降低性能。把程序一次性载入内存,要比根据需要一块块地将整个程序载入内存的效率高得多。因为每次操作都需要一定的开销:保存和恢复状态决定必须载入哪个页面。所以,有些系统只对大小超过某一最小值的程序才使用请求调页机制。
地址
映射问题:在访问
主存储器时把虚地址变为主存物理地址(这一过程称为内地址变换):在访问辅存时把虚地址变成辅存的物理地址(这一过程称为外地址变换),以便换页。此外还要解决主存分配、存储保护与程序再定位等问题。
占用
内存:虚拟内存的管理需要建立很多
数据结构,这些数据结构要占用额外的内存。此外,如果一页中只有一部分数据,会浪费内存。
耗费时间:虚拟地址到物理地址的转换,增加了指令的执行时间。此外,页面的换入换出需要
磁盘I/O,也需要耗费时间。
安全与隔离
随着虚拟化技术的发展,越来越多的单位将业务应用迁移至虚拟计算平台,相应地,针对虚拟计算环境的恶意代码也日益猖。2016年发布的《2015年中国互联网服务器安全报告》显示,
虚拟主机及云平台已成为黑客攻击的主要目标之一。虚拟环境恶意行为的一个最大特点是
内存化、隐蔽化以及多态,使得部分关键数字证据只存于易失性
内存中或暂存于页面交换文件中。虚拟环境恶意行为分析技术主要分为虚拟隔离环境分析、
虚拟机监控器外部分析以及虚拟机内部分析等。
虚拟隔离环境分析
虚拟隔离环境分析技术利用虚拟机隔离恶意软件运行环境,在虚拟机内部安装分析软件或使用APIHOOK等方式捕获恶意软件行为,可通过驱动方式加载内部分析系统,利用APIHOOK
技术分析恶意软件行为。
虚拟机监控器外部分析
虚拟机监控器外部分析技术利用虚拟机监控器(virtual machine
监听,VMM)实现虚拟机代理或自省,捕获虚拟机内部恶意软件的运行行为并加以分析。可利用
虚拟机自省技术实现虚拟机内部运行状态的监控与行为重构。
虚拟机内部分析
虚拟机内部分析技术可利用虚拟机自身的某种功能特性实现恶意行为的分析,可通过虚拟机的软件模拟特性完成了恶意软件动态行为检测并通过增量获取
内存数据,实现恶意行为的连续分析。
序列化重构
为动态刻画虚拟计算环境中的进程行为,须将虚拟内存中获取到的多个单一行为以证据链的方式进行序列化重构。如图所示,针对主流的虚拟计算环境(如
威睿),通过
VMware虚拟机安装目录下扩展名为.wmem的文件获取内存镜像,通过对该文件的逆向分析破解其
数据结构,遍历搜索内核对象,从而准确定位并提取相应的原始内存证据信息。
权限控制和内存资源隔离
从密码软件实现所需的运行环境来看,
计算机通过软硬件配合实现了权限控制和
内存资源隔离,具体包括:虚拟内存管理是操作系统基于软硬件机制实现的内存管理功能,通过将用户态进程使用的虚拟地址
映射到
内存的物理地址,使得用户态进程拥有连续的虚拟地址空间,同时也便于操作系统内核更好地控制程序对资源的申请和使用,控制进程可访问资源的范围并且限定进程异常后影响的范围,防止其他进程非法读取和写入进程资源保证用户态进程访问资源互不影响。
权限控制和
进程隔离是主流密码软件实现所依赖的重要基础,密码软件实现所在进程中的
密钥等敏感信息被安全地逻辑隔离,使得其他恶意进程无法随意访问。内核态和用户态的隔离是最基本的基于硬件特权机制的内存地址空间隔离,而且随着技术的发展,出现了利用CPU硬件的
虚拟机监控器的
内存地址空间隔离、利用CPU硬件机制的可信执行环境等新型隔离机制。
应用场景
多任务操作系统
在支持多任务的操作系统中,如果所有任务的内存需求加起来的总量超过了当前系统的物理内存总量,那系统要么停掉一些任务,要么把一些任务转移到外存(如
磁盘)中,以后当内存空闲时再把这些任务转换回来;或者系统有选择地把部分不常用的内存转换到外存,并且根据适当的规则将来再慢慢地转换回来。
Windows多任务操作系统
Windows多任务操作系统采用虚拟
内存的管理方式,对虚拟内存的管理完全靠操作系统控制,它让每个在系统中运行的作业都认为自己有足够的内存空间,而事实上虚拟
内存的空间是由内存和外存共同构成的,这种足够的内存空间的感觉是由程序页面在内存与外存之间交换实现的。程序运行初始,通常只装入必要的页面在内存,其余的都放在辅存中,在需要的时候,将外存的相应页面交换到内存中,保证程序的正常运行。
Linux多任务操作系统
Linux多任务操作系统采用的是请求页式虚拟
内存管理技术,它为应用程序提供比实际内存大得多的虚拟内存。当一个程序执行时,Linux只为它分配虚拟空间,只有发生缺页中断时,才为它分配物理空间,将虚页装入内存,即只将当前运行最需要的代码和数据装入内存。这样,当该程序运行结束时,会有一些页面从未装入
内存(比如错误处理代码,帮助代码),最大限度地利用了
主存储器空间,因而可使更多的程序同时运行。
大型应用程序
20世纪70年代,由于当时的存储容量,特别是内存容量成本非常高,容量也很小,对于大型应用程序或多程序的应用就受到了很大的限制。为了克服这样的限制,人们就采用了虚拟存储的技术,最典型的应用就是虚拟内存技术。以
Windows为例,Windows采用“请页式”虚拟
内存管理技术,运行于386以上的机器上,提供32位虚地址,每个进程都有多达4GB(232B)的虚地址空间。进程4GB的地址空间被分成两部分:高地址的2GB保留给操作系统使用,低地址的2GB是用户存储区,可被用户态和核心态线程访问,Windows提供一个引导选项,允许用户拥有3GB地址空间,留给系统1GB以改善大型应用程序运行的性能。
相关概念
物理内存
物理内存也称为实际内存,是指有硬件存储介质的内存。在实际内存管理中,必须为作业分配足够的存储空间,以装入有关作业的全部信息,作业的大小不能超出
主存储器的可用空间,否则,这个作业无法运行。事实上,当把有关作业的全部信息都装入主存储器后,作业执行时不是同时使用全部信息的,有些部分只运行一遍,有些部分在作业执行的整个过程中甚至都不会被使用(如错误处理部分)。