虚拟内存和物理内存有什么不同?

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

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

谁负责虚拟内存,虚拟内存的大小是多少?

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

160008 次浏览

虚拟内存是一种抽象,它使程序员产生一种错觉,认为他们的系统上有无限的可用内存。

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

虚拟内存通常比物理内存大——如果虚拟内存和物理内存大小相同,那么就没有必要进行虚拟内存映射。

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

我可能在这里假设错误,但我敢打赌,您觉得难以理解的事情一定与虚拟内存的具体实现有关,最有可能的是分页。没有 单行道做分页-有许多实现,你的教科书描述的那个可能不同于出现在像 Linux/Windows 这样的真实操作系统中的那个-可能有细微的差别。

我可以滔滔不绝地讲一千个关于分页的段落... ... 但我认为最好留给针对这个主题的另一个问题。

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

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

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

看这里: 物理 VS 虚拟内存

虚拟存储器存储在硬盘驱动器上,当 RAM 被填充时使用。物理内存只限于安装在计算机中的 RAM 芯片的大小。由于虚拟存储器受到硬盘大小的限制,所以虚拟存储器具有更大的存储空间。

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

  1. 软件 一直都是运行直到用户中止它,也就是说,它不应该自动中止,因为操作系统已经用完了内存。
  2. 上述活动,同时保持一个可观的表现为软件运行。

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

可能的解决方案1 : 让各个软件显式地指定它们将在设备中使用的内存地址。假设 PS 的声明它将始终使用从 01023的内存地址(假设内存是一个字节的线性数组,所以第一个字节在位置 0,第三个字节在位置 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 总量甚至不足以容纳所需内存的一小部分,因此不可避免地会侵占一个软件的内存界限,而侵占另一个软件的内存界限。那么,当 PS 的被分配内存位置 11023,而 VLC被分配 10001676时会发生什么呢?如果 PS 的在位置 1008存储一些数据,然后 VLC用它自己的数据覆盖这些数据,然后 PS 的访问它,认为它与之前存储在那里的数据是相同的,那该怎么办?你可以想象,坏事会发生。

所以很明显,正如你所看到的,这个想法是相当幼稚的。

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

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

enter image description here

问题1: 现在您无法启动任何其他进程,因为所有 RAM 都已用完。因此,编写程序时必须考虑到可用的最大内存(实际上可用的内存将更少,因为其他软件也将并行运行!).换句话说,你不能运行一个高内存消耗应用程序在您摇摇欲坠的 1 GB个人电脑。

好的,现在您决定不再需要保持 日食铬合金打开,您关闭它们以释放一些内存。这些进程占用的 RAM 空间被 OS 回收,现在看起来是这样的:

enter image description here

假设关闭这两个空间可以释放 700 MB空间-(400 + 300) MB。现在您需要启动 3001,它将占用 450 MB的空间。总的来说,你确实有比 450 MB更多的可用空间,但是... ... 它不是连续的,它被分成单独的块,没有一个大到足以容纳 450 MB。因此,您突然想到了一个绝妙的主意,让我们将下面的所有进程都移动到尽可能多的上面,这将在底部的一个块中留下 700 MB的空白。这叫做 3002。很好,除了... 所有的进程都在运行。移动它们将意味着移动它们所有内容的地址(记住,OS 维护软件吐出的内存到实际内存地址的映射。想象一下,软件用数据 123吐出一个 45的地址,操作系统将其存储在位置 4000中,并在地图中创建一个条目,将 45映射到 4000。如果软件现在在内存中移动,以前在 4000位置的东西将不再在 4000位置,而是在一个新的位置,操作系统必须相应地更新地图,将 45映射到新的地址,这样当软件查询内存位置 45时,就可以得到预期的数据(123)。就软件而言,它只知道地址 45包含数据 123!)!设想一个引用局部变量 3003的进程。当它再次被访问时,它的地址已经改变了,它将再也找不到它了。对于所有的函数,对象,变量都是一样的,基本上所有的东西都有一个地址,移动一个进程就意味着改变所有这些东西的地址。这让我们想到:

问题2: 无法移动进程。该进程中的所有变量、函数和对象的值都硬编码为 编译器在编译过程中吐出,该过程依赖于 它们在其生命周期内处于同一地点,改变它们是昂贵的。因此, 进程退出时会留下大的“ holesExternal Fragmentation.

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

enter image description here

Opera 平滑地放在底部,现在你的 RAM 看起来像这样:

enter image description here

很好。一切正常。但是,已经没有多少空间了,现在您需要再次启动 铬合金,一个众所周知的占用内存的程序!它需要大量的记忆开始,而你几乎没有任何剩余... 除了。.你现在注意到一些过程,最初占据了很大的空间,现在不需要很大的空间。也许你已经停止你的视频在 VLC,因此它仍然占用一些空间,但没有它所需要的高分辨率视频运行。记事本照片也是如此。您的 RAM 现在看起来像这样:

enter image description here

再来一次!又回到起点了!除了以前由于进程终止而产生的空洞,现在由于进程比以前需要更少的空间而产生的空洞!而且您还有同样的问题,holes组合产生的空间大于所需的空间,但是它们分散在各处,单独使用不多。因此,您必须再次移动这些进程,这是一个昂贵的操作,而且是非常频繁的操作,因为进程在其生命周期中经常会减小规模。

问题3: 进程在其生命周期中可能会减小规模,留下未使用的空间,如果需要使用,将需要使用这些空间 移动许多过程的昂贵操作。这被称为 Internal Fragmentation.

好的,现在,你的操作系统做所需要的事情,移动进程并启动 铬合金,一段时间后,你的 RAM 看起来像这样:

enter image description here

酷。现在假设您再次在 VLC中观看 阿凡达。它的内存需求将激增!但是... ... 已经没有空间留给它生长了,因为 记事本依偎在它的底部。因此,所有进程都必须移动到下面,直到 VLC找到足够的空间!

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

好吧。现在假设使用 照片从外部硬盘加载一些照片。访问硬盘需要从缓存和 RAM 领域转移到磁盘领域,磁盘的访问速度要慢几个数量级。痛苦的,不可挽回的,超乎寻常的慢。它是一个 I/O 操作,这意味着它不受 CPU 限制(恰恰相反) ,这意味着它现在不需要占用 RAM。但是,它仍然顽固地占用 RAM。如果您想同时启动 火狐,那是不可能的,因为没有太多可用的内存,而如果 照片在其 I/O 绑定活动期间从内存中删除,那么它将释放大量内存,然后是(昂贵的)压缩,接着是适应 火狐

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

所以,正如我们所看到的,即使使用虚拟内存的方法,我们也有很多问题。


有两种方法可以解决这些问题-segmentation5和 segmentation6。让我们讨论一下 segmentation5。在这种方法中,进程的虚拟地址空间以块的形式映射到物理内存——称为 segmentation8。一个典型的 segmentation9大小是 4 kB。这个映射是由一个叫做 paging0的东西维护的,给定一个虚拟地址,现在我们要做的就是找到这个地址属于哪个 segmentation9,然后从 paging0中找到这个 segmentation9在实际物理内存中的对应位置(称为 paging4) ,并且假设 segmentation9中虚拟地址的偏移量对于 segmentation9和 paging4是相同的,通过将这个偏移量加到 paging0返回的地址中来找到实际地址。例如:

enter image description here

左边是进程的虚拟地址空间。假设虚拟地址空间需要40个内存单元。如果物理地址空间(右边)也有40个内存单元,那么就有可能将所有位置从左边映射到右边的位置,我们会非常高兴。但不幸的是,物理内存不仅可用的内存单元较少(这里是24个) ,而且还必须在多个进程之间共享!好吧,看看我们怎么处理。

当进程启动时,假设发出了对位置 35的内存访问请求。这里的页面大小是 8(每个 80包含 8位置,因此 40位置的整个虚拟地址空间包含 5页面)。所以这个位置属于第一页。4(35/8).在此 80中,此位置具有 3(80)的偏移量。所以这个位置可以由元组 81 = 82指定。这仅仅是一个开始,因此进程的任何部分都还没有存储在实际的物理内存中。因此,维护左侧页面到右侧实际页面(称为 83)的映射的 82目前是空的。因此,操作系统放弃了 CPU,让设备驱动程序访问磁盘并获取页面号。这个进程的 4(基本上是磁盘上程序的内存块,其地址范围从 86到 87)。当它到达时,操作系统会将页面分配到 RAM 中的某个地方,比如说第一帧本身,用于此过程的 82会注意到页面 4映射到 RAM 中的帧 page0。现在数据终于在物理内存中了。OS 再次查询页面表中的元组 82,这一次,页面表显示页面 4已经映射到 RAM 中的帧 page0。因此,操作系统只需进入 RAM 中的 page0帧,访问该帧中偏移量 3的数据(花点时间来理解这一点。从磁盘提取的整个 80被移动到 86。因此,不管页面中单个内存位置的偏移量是多少,在帧中也是一样的,因为在 80/86中,内存单元仍然相对地位于相同的位置!)并返回数据!由于数据在第一次查询时不在内存中,而是必须从磁盘中提取才能加载到内存中,因此它构成了一个 89。

好吧。现在假设对位置 28进行了内存访问。归根结底就是 (3,4)Page table5现在只有一个条目,将页面 4映射到帧 0。所以这又是一个 Page table6,进程放弃 CPU,设备驱动程序从磁盘取出页面,进程重新获得对 CPU 的控制,并且它的 Page table7被更新。现在假设页面 3被映射到 RAM 中的帧 1。因此,(3,4)变成 (1,4),并且返回 RAM 中该位置的数据。很好。这样,假设下一个内存访问是位置 (3,4)0,转换为 (3,4)1。页面 1还没有在内存中,同样的过程被重复,并且 Page table8被分配到 RAM 中的帧 (3,4)4。现在 RAM 进程映射看起来就像上面的图片。此时,只有24个内存单元可用的 RAM 已经被填满。假设这个进程的下一个内存访问请求来自地址 (3,4)5。它映射到 (3,4)6,而 Page table7表示页面 3在 RAM 中,它映射到帧 1。耶!因此,从 RAM 位置 Page table0获取数据并返回。这构成了一个 40,因为所需的数据可以直接从 RAM 获得,因此非常快。类似地,接下来的几个访问请求,比如对于位置 Page table1、 Page table2、 Page table3、 Page table4,都是 41,也就是说,进程请求的数据直接在 RAM 中找到,而不需要寻找其他地方。

现在假设来了一个位置 3的内存访问请求。对于页面 134,它转换为 (0,3),而对于这个进程,page table2目前有3个条目,它表示这个页面不在内存中。与前面的情况一样,它是从磁盘中提取的,但是,与前面的情况不同,RAM 被填满了!那现在怎么办?这里躺在美丽的虚拟内存,一帧从 RAM 是驱逐!(各种因素决定哪一个框架将被驱逐。它可能是基于 page table3的,其中的帧最近访问了一个进程是要被驱逐。它可能是 page table4的基础,其中最早分配的帧被驱逐了,等等) ,所以一些帧被驱逐了。说帧1(只是随机选择它)。然而,那个 page table5被映射到一些 page table6!(目前,它由页表映射到我们的一个也是唯一一个进程的页 3)。所以这个过程必须被告知这个悲惨的消息,一个不幸属于你们的 page table5,将被从 RAM 中驱逐,为另一个 page table8腾出空间。进程必须确保它用这些信息更新它的 page table2,也就是说,删除页面框架对的条目,以便下次请求该 page table6时,它正确地告诉进程这个 page table6不再在内存中,必须从磁盘获取。很好。所以帧 1被驱逐,页 (0,3)7被带入并放置在 RAM 中,页 3的条目被删除,并被映射到同一帧 1的页 (0,3)7所取代。现在我们的映射看起来像这样(注意右边第二个 page table5的颜色变化) :

enter image description here

看到刚才发生什么了吗?进程必须增长,它需要比可用 RAM 更多的空间,但是不像我们之前的场景,RAM 中的每个进程都必须迁移以适应不断增长的进程,这里只需要更换一个 page!这是由于一个进程的内存不再需要是连续的,它可以以块的形式驻留在不同的位置,操作系统维护它们所在位置的信息,当需要的时候,它们会被适当地查询。注意: 您可能会想,如果大多数情况下它是 miss,并且数据必须不断地从磁盘加载到内存中,该怎么办?是的,理论上,这是可能的,但是大多数编译器的设计方式都遵循 locality of reference,也就是说,如果使用来自某个内存位置的数据,下一个需要的数据将位于非常接近的地方,也许来自同一个 page,也就是刚刚加载到内存中的 page。因此,下一次错过将发生在相当长的一段时间之后,大多数即将到来的内存需求将由刚刚引入的页面或最近使用的内存中已经存在的页面来满足。完全相同的原则允许我们驱逐最近使用最少的 page也,与逻辑,什么没有被使用了一段时间,是不可能被使用了一段时间。然而,情况并非总是如此,在特殊情况下,性能可能会受到影响。稍后再详谈。

问题4的解决方案: 现在流程可以很容易地增长,如果面临空间问题,所需要的只是做一个简单的 page替换,而不需要移动任何其他流程。


问题1的解决方案: 进程可以访问无限的内存。当需要比可用内存更多的内存时,磁盘被用作备份,所需的新数据从磁盘加载到内存中,最近使用最少的数据 frame(或 page)被移动到磁盘。这种情况可以无限地继续下去,由于磁盘空间很便宜,而且实际上是无限的,因此它给人一种内存无限的错觉。Virtual Memory这个名字的另一个原因是,它让你产生一种记忆的错觉,而这种错觉并不是真正可用的!

酷。前面我们遇到了一个问题,即即使一个进程的大小减小了,但是空白空间很难被其他进程回收(因为它需要昂贵的压缩)。现在很容易,当一个进程变得更小,它的许多 pages不再使用,所以当其他进程需要更多的内存,一个简单的基于 LRU的驱逐自动驱逐那些较少使用的 pages从 RAM,并取代它们与新的页面从其他进程(当然更新所有这些进程的 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!操作系统只是将其所有的 pages从 RAM 中驱逐出去(可能将其存储在某个缓存中) ,同时新的进程占用 RAM。当 I/O 操作完成后,操作系统只是简单地将那些 pages恢复到 RAM 中(当然,通过替换其他进程中的 pages,可能是替换原始进程的进程中的 pages,也可能是现在需要进行 I/O 操作的进程中的 pages,因此可以放弃内存!)

问题5的解决方案: 当一个进程执行 I/O 操作时,它可以很容易地放弃 RAM 的使用,这可以被其他进程利用。这将导致对 RAM 的正确利用。

当然,现在没有进程直接访问 RAM。每个进程都访问一个虚拟内存位置,该位置映射到一个物理 RAM 地址,并由该进程的 page-table维护。映射是基于操作系统的,操作系统让进程知道哪个框架是空的,这样就可以在那里安装进程的新页面。由于这种内存分配是由操作系统自己监督的,它可以很容易地确保没有进程侵犯另一个进程的内容,只分配来自 RAM 的空帧,或侵犯 RAM 中另一个进程的内容,通信到进程来更新它 page-table

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

因此,paging(以及其他技术)与虚拟内存相结合,就是当今在 OS-es 上运行的软件的动力!这样软件开发人员就不用担心用户设备上有多少内存可用,数据存储在哪里,如何防止其他进程破坏他们软件的数据等等。然而,它当然不是完全证明。这里有一些缺陷:

  1. Paging 最终使用磁盘作为备份,给用户无限内存的错觉。从辅助存储器检索数据以适应内存(称为 page swap,而在 RAM 中找不到所需页的事件称为 page fault)是昂贵的,因为它是一个 IO 操作。这会减慢这个过程。一些这样的页面交换连续发生,这个过程变得非常缓慢。有没有见过你的软件运行得很好,突然间它变得很慢,几乎挂起,或者让你没有选择重新启动它?可能有太多的页面交换正在发生,使它变慢(称为 thrashing)。

回到观察所,

为什么我们需要虚拟内存来执行一个进程?正如答案详细解释的那样,给软件一种设备/操作系统拥有无限内存的错觉,这样任何软件,无论大小,都可以运行,而不用担心内存分配,或者其他进程破坏其数据,即使在并行运行时也是如此。它是一个概念,通过各种技术在实践中实现,其中之一就是 呼叫。也可能是 分割

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

谁负责虚拟内存,虚拟内存的大小是多少?由操作系统和软件共同负责。假设代码中有一个包含局部变量 int i的函数(该函数最终编译并生成进程的可执行文件)。当代码执行时,i获取函数堆栈中的一个内存地址。该函数本身作为对象存储在其他地方。这些地址是编译器生成的(将代码编译成可执行文件的编译器)-虚拟地址。在执行时,i至少在该函数执行期间必须驻留在实际物理地址中的某个地方(除非它是一个静态变量!)因此,操作系统将编译器生成的 i的虚拟地址映射到一个实际的物理地址,这样,无论何时,只要该函数中的某些代码需要 i的值,该进程就可以向操作系统查询该虚拟地址,而操作系统反过来又可以查询存储的值的物理地址并返回该值。

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

物理内存 : 物理内存是指计算机中的 RAM 或主存。物理内存是易失性内存。因此,它需要持续的能量流来保存数据。

虚拟内存是一种逻辑内存。换句话说,它是一种由操作系统执行的内存管理技术。虚拟内存允许程序员为程序使用比可用物理内存更多的内存。如果物理内存为4GB,虚拟内存为16GB,程序员可以使用16GB 的虚拟内存来执行程序。使用虚拟内存,他可以执行比物理内存需要更多内存的复杂程序。

物理存储器和虚拟存储器的主要区别在于,物理存储器是指连接在主板上的系统的实际 RAM,而虚拟存储器是一种存储器管理技术,允许用户执行比实际物理存储器大的程序。