虚拟内存和物理内存有什么区别?

问题描述 投票:82回答:4

我经常对操作系统中的虚拟化概念感到困惑。考虑将RAM作为物理内存,为什么我们需要虚拟内存来执行进程?

当来自外部硬盘驱动器的进程(程序)被带到主存储器(物理存储器)以执行时,该虚拟存储器的位置。

谁负责虚拟内存以及虚拟内存的大小?

假设RAM的大小是4GB(即2 ^ 32-1地址空间),虚拟内存的大小是多少?

memory-management operating-system virtualization ram virtual-memory
4个回答
76
投票

除其他外,虚拟内存是一种抽象,为程序员提供了在系统上拥有无限内存的错觉。

虚拟内存映射与实际物理地址相对应。操作系统创建并处理这些映射 - 利用页表以及其他数据结构来维护映射。虚拟内存映射总是在页表或某些类似的数据结构中找到(在虚拟内存的其他实现的情况下,我们可能不应该将其称为“页表”)。页表也在物理内存中 - 通常在内核保留空间中,用户程序无法写入。

虚拟内存通常比物理内存大 - 如果虚拟内存和物理内存大小相同,虚拟内存映射的理由就不多了。

只有程序所需的部分通常驻留在内存中 - 这是一个名为“分页”的主题。虚拟内存和分页紧密相关,但不是相同的主题。还有其他虚拟内存实现,例如分段。

我可能会在这里假设错误,但我敢打赌你发现难以解决的问题与虚拟内存的特定实现有关,很可能是分页。没有一种方法可以进行分页 - 有许多实现,你的教科书描述的那个可能与Linux / Windows等实际操作系统中出现的实现不一样 - 可能存在细微差别。

我可以写一千段有关分页的内容......但我认为最好留给另一个专门针对该主题的问题。


63
投票

软件在操作系统上运行的前提非常简单 - 它们需要内存。设备OS以RAM的形式提供它。所需的内存量可能会有所不同 - 某些软件需要大量内存,有些需要微不足道的内存。大多数(如果不是全部)用户同时在OS上运行多个应用程序,并且鉴于内存很昂贵(并且设备大小有限),可用内存量总是有限的。因此,鉴于所有软件都需要一定数量的RAM,并且所有软件都可以同时运行,操作系统必须处理两件事:

  1. 软件总是运行直到用户中止它,即它不应该自动中止,因为操作系统内存不足。
  2. 上述活动,同时为运行的软件保持了可观的性能。

现在主要问题归结为如何管理内存。究竟是什么决定了存储器中属于给定软件的数据所在的位置?

可能的解决方案1:让各个软件明确指定它们将在设备中使用的存储器地址。假设Photoshop声明它将始终使用从01023的内存地址(将内存设想为字节的线性数组,因此第一个字节位于0位置,1024th字节位于1023位置) - 即占用1 GB内存。同样,VLC声明它将占用内存范围12441876等。

好处:

  1. 每个应用程序都预先分配了一个内存插槽,因此在安装和执行时,它只是将其数据存储在该内存区域中,一切正常。

缺点:

  1. 这不规模。从理论上讲,应用程序在执行非常重要的操作时可能需要大量内存。因此,为了确保它永远不会耗尽内存,分配给它的内存区域必须始终大于或等于该内存量。如果最大理论内存使用量为2 GB(因此需要从RAM中分配2 GB内存)的软件安装在只有1 GB内存的机器上会怎么样?该软件是否应该在启动时中止,并说可用的RAM小于2 GB?或者它应该继续,并且当所需的内存超过2 GB时,只是中止并挽救没有足够的内存可用的消息?
  2. 无法防止内存损坏。有数百万的软件,即使每个软件都只分配1 kB内存,所需的总内存将超过16 GB,这比大多数设备提供的更多。那么,如何为不同的软件分配不会侵占彼此区域的内存插槽呢?首先,没有集中的软件市场可以规范当一个新软件被发布时,它必须从这个尚未占用的区域分配这么多的内存,其次,即使有,也不可能这样做,因为没有。软件实际上是无限的(因此需要无限的内存来容纳所有这些),并且任何设备上可用的总RAM不足以容纳所需的一小部分,因此不可避免地会侵占一个软件的内存边界在另一个。那么当Photoshop被分配内存位置11023并且VLC被分配10001676时会发生什么?如果Photoshop在1008位置存储了一些数据,那么VLC会用自己的数据覆盖它,后来Photoshop访问它,认为它是先前存储的相同数据?可以想象,糟糕的事情会发生。

很明显,正如你所看到的,这个想法很天真。

可能的解决方案2:让我们尝试另一种方案 - 操作系统将执行大部分内存管理。软件无论何时需要任何内存,都只需要操作系统,操作系统也会相应地适应。假设OS确保每当新进程请求内存时,它将从可能的最低字节地址分配内存(如前所述,RAM可以想象为线性字节数组,因此对于4 GB RAM,地址范围为从02^32-1的一个字节)如果进程正在启动,否则如果它是一个请求内存的正在运行的进程,它将从该进程仍然驻留的最后一个内存位置进行分配。由于软件将在不考虑存储数据的实际存储器地址的情况下发送地址,因此OS必须维护软件发出的地址到实际物理地址的每个软件的映射(注意:这是我们称之为Virtual Memory概念的两个原因之一。软件并不关心存储数据的实际内存地址,它们只是动态地吐出地址,操作系统找到适合它的地方并找到它以后如果需要)。

假设设备刚刚打开,操作系统刚刚启动,现在没有其他进程在运行(忽略操作系统,这也是一个进程!),并且您决定启动VLC。因此,VLC从最低字节地址分配RAM的一部分。好。现在,当视频正在运行时,您需要启动浏览器才能查看某个网页。然后你需要启动记事本来涂写一些文字。然后Eclipse做一些编码..很快你对4 GB的记忆就用完了,RAM看起来像这样:

enter image description here

问题1:现在你无法启动任何其他进程,因为所有的RAM都用完了。因此,必须编写程序,保持最大可用内存(实际上甚至更少,因为其他软件也将并行运行!)。换句话说,你不能在摇摇欲坠的1 GB PC中运行高内存耗费的应用程序。

好的,现在您决定不再需要保持Eclipse和Chrome打开,关闭它们以释放一些内存。这些进程在RAM中占用的空间由OS回收,现在看起来像这样:

enter image description here

假设这两个释放了700 MB空间 - (400 + 300)MB。现在你需要启动Opera,它将占用450 MB空间。嗯,你确实有超过450 MB空间可用,但是......它不是连续的,它被分成单独的块,其中没有一个足够大以适合450 MB。所以你想出了一个绝妙的想法,让我们将下面的所有过程尽可能地移动到上面,这将使700 MB空白区域位于底部的一个块中。这叫做compaction。太好了,除了...所有进程都在运行。移动它们将意味着移动所有内容的地址(记住,操作系统维护软件的内存吐出到实际内存地址的映射。想象一下,软件用数据45吐出了123的地址,操作系统已经存储了它在位置2012并在地图中创建了一个条目,将45映射到2012。如果软件现在在内存中移动,那么曾经位于2012的地方将不再是2012,而是在新的位置,操作系统必须更新地图相应地将45映射到新地址,以便软件在查询内存位置123时可以获得预期数据(45)。就软件而言,它只知道地址45包含数据123 !)!想象一下引用局部变量i的过程。当它再次被访问时,它的地址已经改变,它将无法再找到它。对于所有函数,对象,变量都是一样的,基本上所有东西都有一个地址,移动一个进程意味着改变所有这些的地址。这导致我们:

问题2:您无法移动流程。该进程中的所有变量,函数和对象的值都具有硬编码值,在编译期间由编译器吐出,该进程依赖于它们在其生命周期内处于相同位置,并且更改它们是昂贵的。结果,当他们退出时,流程留下了大的“holes”。这叫做External Fragmentation

精细。假设某种方式,通过一些奇迹般的方式,你确实设法将进程向上移动。现在底部有700 MB的自由空间:

enter image description here

Opera平稳地适应底部。现在你的RAM看起来像这样:

enter image description here

好。一切都很好看。但是,剩下的空间不大,现在你需要重新启动Chrome,这是一个已知的记忆猪!它需要大量的内存才能启动,你几乎没有剩下任何东西......除了......你现在注意到一些最初占据大空间的进程现在不需要太多空间。可能你已经在VLC中停止了你的视频,因此它仍然占用了一些空间,但没有运行高分辨率视频时所需的那么多。同样适用于记事本和照片。你的RAM现在看起来像这样:

enter image description here

Holes,再一次!回到原点!除了以前,由于过程终止而发生了漏洞,现在这是因为过程需要的空间比以前少!并且你再次遇到同样的问题,holes合并产生的空间超过了所需的空间,但是它们分散在一起,没有太多的孤立使用。因此,您必须再次移动这些流程,这是一项昂贵的操作,而且非常频繁,因为流程在其生命周期内的规模经常会缩小。

问题3:进程在其生命周期内可能会减小尺寸,留下未使用的空间,如果需要使用,则需要移动许多进程的昂贵操作。这叫做Internal Fragmentation

很好,所以现在,你的操作系统完成所需的操作,移动进程并启动Chrome,一段时间后,你的RAM看起来像这样:

enter image description here

凉。现在假设你再次继续观看VLC中的阿凡达。它的内存需求将会上升!但是......没有留下任何空间让它成长,因为记事本在它的底部依偎着。因此,所有流程都必须向下移动,直到VLC找到足够的空间!

问题4:如果流程需要增长,那将是一项非常昂贵的操作

精细。现在假设,照片用于从外部硬盘加载一些照片。访问硬盘会使您从缓存和RAM的范围转移到磁盘的范围,磁盘的数量级会慢一些。痛苦,不可逆转,超越性。它是一个I / O操作,这意味着它不受CPU限制(它恰恰相反),这意味着它现在不需要占用RAM。但是,它仍然固执地占用RAM。如果你想在此期间启动Firefox,你不能,因为没有太多的可用内存,而如果在I / O绑定活动期间照片被占用内存,它将释放大量内存,然后是(昂贵的)压缩,然后是Firefox适合。

问题5:I / O绑定作业继续占用RAM,导致RAM利用率不足,同时可能被CPU绑定作业使用。

因此,正如我们所看到的,即使采用虚拟内存的方法,我们也遇到了很多问题。


有两种方法可以解决这些问题 - pagingsegmentation。让我们讨论一下paging。在这种方法中,进程的虚拟地址空间以块的形式映射到物理内存 - 称为pages。典型的page尺寸是4 kB。映射由称为page table的东西维护,给定一个虚拟地址,现在我们要做的就是找出地址所属的page,然后从page table找到实际物理内存中page的相应位置(称为frame),并且鉴于page中的虚拟地址的偏移对于page以及frame是相同的,通过将该偏移量添加到page table返回的地址来找出实际地址。例如:

enter image description here

左侧是进程的虚拟地址空间。假设虚拟地址空间需要40个单元的内存。如果物理地址空间(右侧)也有40个单位的内存,则可以将所有位置从左侧映射到右侧的位置,我们会非常高兴。但是,幸运的是,物理内存不仅具有较少的(24个)存储单元,它还必须在多个进程之间共享!好吧,让我们看看我们如何处理它。

当进程开始时,请说出位置35的内存访问请求。这里的页面大小是8(每个page包含8位置,因此40位置的整个虚拟地址空间包含5页面)。所以这个位置属于第页。 435/8)。在这个page,这个位置有335%8)的偏移。所以这个位置可以由元组(pageIndex, offset) = (4,3)指定。这只是一个开始,所以过程的任何部分都没有存储在实际的物理内存中。所以page table,它保持左边页面到右边实际页面的映射(它们被称为frames)目前是空的。因此OS放弃CPU,让设备驱动程序访问磁盘并获取页面号。 4用于此过程(基本上是磁盘上程序的内存块,其地址范围从3239)。当它到达时,OS将页面分配到RAM中的某个位置,比如第一帧本身,并且此过程的page table注意到页面4映射到RAM中的帧0。现在数据最终存在于物理内存中。操作系统再次查询页表中的元组(4,3),这次,页表表示页面4已经映射到RAM中的帧0。操作系统只需转到RAM中的0th帧,访问该帧中偏移量3的数据(花一点时间来理解这一点。整个page,从磁盘取出,移动到frame。所以无论个人的偏移量如何一个页面中的内存位置,它在框架中也是一样的,因为在page / frame中,内存单元仍然相对位于同一个地方!),并返回数据!因为在第一次查询本身时没有在内存中找到数据,而是必须从磁盘中获取数据以加载到内存中,因此它构成了未命中。

精细。现在假设,对位置28进行了内存访问。它归结为(3,4)Page table现在只有一个条目,映射页面4到框架0。所以这又是一个错过,进程放弃了CPU,设备驱动程序从磁盘获取页面,进程再次重新获得CPU的控制,并更新其page table。现在说,页面3映射到RAM中的帧1。所以(3,4)变成(1,4),返回RAM中该位置的数据。好。通过这种方式,假设下一个内存访问是针对位置8,转换为(1,0)。页面1尚未在内存中,重复相同的过程,并且page在RAM中的帧2处分配。现在,RAM进程映射如上图所示。此时,只有24个可用内存的RAM被填满。假设此进程的下一个内存访问请求来自地址30。它映射到(3,6)page table说页面3在RAM中,它映射到帧1。好极了!所以数据从RAM位置(1,6)获取,并返回。这构成了一个命中,因为所需的数据可以直接从RAM获得,因此非常快。类似地,接下来的几个访问请求,例如位置11322627都是命中,即进程请求的数据直接在RAM中找到而无需查看其他地方。

现在假设有一个位置3的内存访问请求。它转换为(0,3)page table这个过程,目前有3个条目,页面134说这个页面不在内存中。像以前的情况一样,它是从磁盘中获取的,但是,与以前的情况不同,RAM已经填满了!那现在该怎么办?这就是虚拟内存的美妙之处,来自RAM的框架被驱逐! (各种因素决定了哪个帧被驱逐。它可能是基于LRU的,其中最近最少访问过程的帧将被驱逐。它可能是first-come-first-evicted基础,其中最长时间分配的帧被驱逐等等)所以一些框架被驱逐。说第1帧(只是随机选择它)。然而,frame映射到一些page! (目前,它由页表映射到我们唯一一个进程的页面3)。所以这个过程必须被告知这个悲惨的消息,一个不幸属于你的frame将被赶出RAM,以便为另一个pages腾出空间。该过程必须确保它使用此信息更新其page table,即删除该页框架二重奏的条目,以便下次请求该page时,它正确告诉进程此page不是内存更长,必须从磁盘中获取。好。因此框架1被驱逐,页面0被引入并放置在RAM中,页面3的条目被删除,并被页面0映射到相同的框架1。所以现在我们的映射看起来像这样(注意右侧第二个frame的颜色变化):

enter image description here

看到刚刚发生了什么?这个过程必须增长,它需要比可用RAM更多的空间,但不像我们之前的情况,RAM中的每个进程都必须移动以适应不断增长的过程,这里只发生了一次page更换!这可以通过以下事实实现:进程的内存不再需要连续,它可以驻留在块中的不同位置,OS维护关于它们的位置的信息,并且在需要时,它们被适当地查询。注意:您可能会想,如果大多数时候它是miss,并且数据必须不断地从磁盘加载到内存中会怎么样?是的,从理论上讲,它是可能的,但是大多数编译器都是按照locality of reference的方式设计的,即如果使用来自某个内存位置的数据,则所需的下一个数据将位于非常接近的某个地方,可能来自相同的pagepage这只是加载到内存中。结果,下一次未命中将在相当长的一段时间后发生,大多数即将到来的内存要求将由刚刚引入的页面或最近使用的内存页面满足。完全相同的原则允许我们驱逐最近最少使用的page,其逻辑是一段时间内没有使用的逻辑也不可能在一段时间内使用。但是,并非总是如此,在特殊情况下,是的,性能可能会受到影响。稍后会详细介绍。

问题4的解决方案:流程现在可以轻松增长,如果面临空间问题,它只需要进行简单的page替换,而无需移动任何其他流程。


问题1的解决方案:进程可以访问无限的内存。当需要更多可用内存时,磁盘用作备份,所需的新数据从磁盘加载到内存中,最近最少使用的数据frame(或page)被移动到磁盘。这可以无限制地进行,并且由于磁盘空间便宜并且几乎无限制,因此它给出了无限内存的错觉。名字Virtual Memory的另一个原因,它给你的内存幻觉,这是不可用的!

凉。早些时候我们面临的问题是,即使工艺尺寸减小,其他工艺也难以回收空的空间(因为它需要昂贵的压实)。现在很容易,当一个过程变得越来越小时,它的许多pages就不再使用了,所以当其他进程需要更多内存时,一个简单的基于LRU的驱逐会自动从RAM中驱逐那些较少使用的pages,并将其替换为来自其他过程的新页面(当然更新所有这些过程的page tables以及现在需要更少空间的原始过程),所有这些都没有任何昂贵的压缩操作!

问题3的解决方案:每当进程减小时,RAM中的frames将被用尽,因此基于LRU的简单驱逐可以逐出这些页面并用新进程所需的pages替换它们,从而避免使用Internal Fragmentation而不需要compaction

对于问题2,花一点时间来理解这一点,场景本身就完全删除了!没有必要移动流程以适应新流程,因为现在整个流程永远不需要立即适应,只有它的某些页面需要适合临时,这通过从RAM中驱逐frames来实现。一切都以pages为单位发生,因此现在没有hole的概念,因此没有任何移动的问题!由于这个新的要求,可能需要移动10个pages,有数千个pages未被触及。而早些时候,所有流程(它们的每一个流程)都必须被移动!

问题2的解决方案:为了适应新流程,必须根据需要逐出其他流程中最近使用过的部分的数据,这种情况发生在称为pages的固定大小单位中。因此,holeExternal Fragmentation不可能使用此系统。

现在当进程需要进行一些I / O操作时,它可以轻松放弃CPU!操作系统只是从RAM中驱逐所有pages(可能将其存储在某个缓存中),同时新进程占用RAM。当I / O操作完成时,操作系统只是将那些pages恢复到RAM(当然,从其他一些进程中替换pages,可能来自替换原始进程的那些,或者可能来自一些他们自己需要做的事情) I / O现在,因此可以放弃记忆!)

问题5的解决方案:当进程正在进行I / O操作时,它可以轻松地放弃RAM使用,这可以被其他进程使用。这导致RAM的正确使用。

当然,现在没有进程直接访问RAM。每个进程都访问一个虚拟内存位置,该位置映射到物理RAM地址,并由该进程的page-table维护。映射是由OS支持的,OS允许进程知道哪个帧是空的,以便可以在那里安装进程的新页面。由于这个内存分配是由操作系统本身监督的,它可以很容易地确保没有进程通过从RAM分配空帧,或者在侵入RAM中另一个进程的内容时,对进程进行通信,从而侵占另一个进程的内容。更新它page-table

原始问题的解决方案:进程不可能访问另一个进程的内容,因为整个分配由操作系统本身管理,并且每个进程都在其自己的沙盒虚拟地址空间中运行。

所以paging(以及其他技术)与虚拟内存相结合,是今天在OS-es上运行的软件的强大功能!这使软件开发人员不必担心用户设备上有多少可用内存,存储数据的位置,如何防止其他进程破坏其软件数据等等。但是,它当然不是完全可靠的。有瑕疵:

  1. Paging最终通过使用磁盘作为辅助备份为用户提供无限内存的幻觉。从二级存储中检索数据以适应内存(称为page swap,并且在RAM中未找到所需页面的事件称为page fault)是昂贵的,因为它是IO操作。这会减慢过程。几个这样的页面交换连续发生,并且该过程变得非常缓慢。曾经看过你的软件运行良好和花花公子,突然它变得如此缓慢,几乎挂起,或让你没有选择重新启动它?可能发生了太多的页面交换,使其变慢(称为thrashing)。

所以回到OP,

为什么我们需要虚拟内存来执行进程? - 正如答案详细解释的那样,为软件提供具有无限内存的设备/操作系统的假象,以便可以运行任何大小软件,而无需担心内存分配或其他损坏其数据的进程,即使并行运行。这是一个概念,通过各种技术在实践中实现,其中之一,如此处所述,是分页。它也可能是分段。

当外部硬盘驱动器的进程(程序)被带到主存储器(物理内存)执行时,这个虚拟内存在哪里? - 虚拟内存本身并不存在,它是一个抽象,始终存在,当引导软件/进程/程序时,为它创建一个新的页表,它包含来自spat out的地址的映射。处理到RAM中的实际物理地址。由于进程吐出的地址不是真正的地址,从某种意义上说,它们实际上就是你能说的,the virtual memory

谁负责虚拟内存以及虚拟内存的大小? - 由操作系统和软件协同处理。想象一下您的代码中的一个函数(最终编译并生成可生成该进程的可执行文件),该函数包含一个局部变量 - int i。代码执行时,i获取函数堆栈中的内存地址。该函数本身作为对象存储在其他地方。这些地址是编译器生成的(将代码编译成可执行文件的编译器) - 虚拟地址。执行时,i必须至少在该函数的持续时间内驻留在实际物理地址中的某个位置(除非它是静态变量!),因此OS将编译器生成的i虚拟地址映射到实际物理地址,以便每当,在内部该函数,某些代码需要i的值,该进程可以查询该虚拟地址的操作系统,而OS又可以查询物理地址以获取存储的值,并将其返回。

假设RAM的大小是4GB(即2 ^ 32-1地址空间),虚拟内存的大小是多少? - RAM的大小与虚拟内存的大小无关,它取决于操作系统。例如,在32位Windows上,它是16 TB,在64位Windows上,它是256 TB。当然,它也受磁盘大小的限制,因为这是备份内存的地方。


16
投票

我无耻地复制顶部手册页的摘录

VIRT - 虚拟映像(kb)任务使用的虚拟内存总量。它包括所有代码,数据和共享库以及已换出的页面和已映射但未使用的页面。

SWAP - 交换大小(kb)非常驻但存在于任务中的内存。这是已被换出但可能包含额外非驻留内存的内存。通过从虚拟内存中减去物理内存来计算此列


6
投票

见这里:Physical Vs Virtual Memory

虚拟内存存储在硬盘驱动器上,并在RAM填满时使用。物理内存仅限于安装在计算机中的RAM芯片的大小。虚拟内存受硬盘驱动器大小的限制,因此虚拟内存可以提供更多存储空间。

© www.soinside.com 2019 - 2024. All rights reserved.