46

我一直在学习这些主题并阅读了许多文章和书籍,但它们都缺乏一些补充信息,让我更加困惑。所以在这里,我想解释一下我在提问时所知道的。希望这个主题对像我这样的许多人有用。我还想了解我的知识的有效性,并在必要时进行更正。

虚拟内存

一些文章说“虚拟内存是硬盘的一些空间,它模拟物理内存,因此我们可以拥有比实际更多的内存。”。其他一些文章说“虚拟内存是物理内存 (RAM) 的组合,它是硬盘的一部分,其作用类似于物理内存和页表。” 但是它们是不同的东西,我不明白为什么会有这样的不同解释。

让我们继续第二个解释,因为它也是 Wikipedia 描述虚拟内存的方式。在这一点上,虚拟地址是有意义的,因为我们在虚拟内存中使用地址而不是直接使用物理内存。

顺便说一句,我的 Mac 说我有 8GB 物理内存和 8GB 虚拟内存。在这种情况下,VM 是否包括物理内存或者它是 HD 中用作内存的空间量?我的程序是否有 16GB 内存可用?

在此处输入图像描述

问题一:

Intel i5 具有 36 位地址总线,这意味着您可以寻址 64GB 内存。假设我在我的电脑上安装了 4GB RAM。但是,我的程序可能不知道安装的内存大小,因为它将用于具有不同内存大小的许多不同系统。这就是虚拟内存变得方便的地方。它抽象出安装的内存的实际大小。

但是,当我的程序想要访问内存地址 0xFFFFFFFFF 时会发生什么?我只安装了 4GB,也许还有一些 HD 内存空间。

对于这个问题,我有两个理论:

1.由于页表是由 OS 维护的,OS 对该地址进行解码并找出是哪个页,并在页表中检查该页以查看它们是否具有与其关联的物理地址(有效和无效标志),如果是然后转到物理内存中页面入口点的物理地址+虚拟地址中定义的偏移量并带来该值。否则会发生页面错误,操作系统在辅助存储中查找该页面,获取它并将其放入内存并更新页表。

2.它抛出 OutOfMemory 类型的异常,表示我没有给定地址可以寻址的任何内存。

第一种理论的缺点是当程序想要使用 64GB 内存时会发生什么?然后我们需要有 60GB 的 HD 内存空间,因为我们只有 4GB。但是,在下面的屏幕截图中,MAC 告诉我只有 8GB 虚拟内存。

问题2:

进程如何放入虚拟内存中?我的意思是每个进程都有 0x0 - 0xFFFFFFFFF 可用的虚拟内存空间,还是只有一个虚拟内存地址空间可以放置所有进程?

如果每个进程都假设它们拥有所有可用的内存,那么这些内存如下所示:

在此处输入图像描述

如果只有一个虚拟内存概念,那么它看起来像这样:

在此处输入图像描述

页表

所以页表是一种位于物理地址和虚拟地址之间的数据结构。它是一个关联数组(或类似字典),对于每个页面(键),都有一个关联的物理地址(值)。

操作系统使用 MMU(内存管理单元)来执行从虚拟地址到物理地址的转换。

在此处输入图像描述

问题 3:

是否有一个巨大的页表,其中包含每个进程的所有页面,或者每个进程都有自己的页表?

寻呼

分页是一种内存管理方法。虚拟内存和物理内存由内存管理单元划分为页面(它们是固定且大小相同的块)。当您在内存和辅助存储之间交换页面时,此技术很有用,以便您可以在它们之间交换页面。例如,您的程序请求位于地址中的数据。但是,您的程序使用的地址是虚拟地址,MMU 使用页表对其进行转换。在此期间,MMU 检查页表中是否存在请求的页表,如果没有,则 OS 从辅助存储中获取并更新页表。

问题4:

假设一个进程从一个地址请求数据,该地址被转换为一个已经有一些数据的物理地址。怎么知道数据不属于请求者进程而应该替换为二级存储中的数据?

例如,有脏位用于是否将该页面写回硬盘,但我认为这不是决定所有者进程的因素。

4

2 回答 2

19

在我回答你的问题之前(我希望我这样做),这里有一些介绍性的评论:

评论

这里的问题是,“虚拟内存”有两种含义。正如向消费者解释的那样,“虚拟内存”作为低级程序员使用的技术术语(几乎)与“虚拟内存”无关。

从技术上讲,“虚拟内存”是一种内存管理系统,每个进程都有自己的虚拟地址空间,该地址空间中的内存地址由操作系统内核在硬件支持下映射到物理内存地址(使用 TLB 等术语,多级页表、页错误和游走等)。这是您感兴趣的 VM 的含义(如下所述)。

在非技术意义上,“虚拟内存”是用于代替 RAM 的磁盘空间(使用交换、后备存储等术语)。这是 VM 的含义,您并不特别感兴趣,但似乎您已经看到了一些主要处理该术语含义或混淆两者的材料。

问题 1

当我的程序想要访问内存地址 0xFFFFFFFFF 时会发生什么?我只有4GB

在这种情况下,您的“理论 1”更接近。

VM 将您的程序“看到”并使用的地址(虚拟地址)与物理地址分离。您的 4GiB 内存可能位于从 0x0 到 0xFFFFFFFF(8 个 F)的物理地址,但地址 0xFFFFFFFFF(9 个 F)位于虚拟地址的用户空间(规范布局)中。假设 0xFFFFFFFFF 在分配给进程的块中,CPU 和内核(协同工作)会将页面地址 0xFFFFFF000(假设是 4k 页面,我们只删除低 12 位)转换为真实的物理页面,这可能具有(几乎)任何物理基地址。假设该页面的物理地址是 0xeac000(当内核给你虚拟页面 0xFFFFFF000 时建立的关系),那么虚拟地址 0xFFFFFFFFF 的字节位于物理地址 0x00eacfff。

当您取消引用 0xFFFFFFFFF(假设 4k 页)时,内核“要求”CPU 访问该虚拟地址,CPU 会切断低 12 位,并在 dTLB 中查找该页(翻译后备缓冲区是虚拟到物理页面映射缓存;至少有一个用于数据,一个用于指令)。如果命中,CPU 会构造真实的物理地址并获取值。如果发生 TLB 未命中,CPU 会引发页面错误,这会导致内核查询(或“遍历”)页表以确定正确的物理页面,并将该值“返回”给 CPU,CPU 将其缓存在dTLB(它很可能几乎立即被重用)。然后内核再次向 CPU 请求该地址,这一次,它将成功而不会触发遍历。

我承认这个描述很糟糕(反映了我自己的知识水平)。特别是,在 TLB 中识别特定进程的确切方式对我来说并不是 100% 清楚,至少在某种程度上是特定于硬件的。过去每个上下文切换都需要一个完整的 TLB 刷新,但最近的 Intel CPU 有一个 6 位“PID”字段,这意味着刷新虽然有时仍然需要,但在上下文切换中并不总是需要。由于我未能描述多级 TLB、PTE(页表条目)并解决其对数据和指令缓存的重要性(尽管我确实知道现代硬件可以查看是否有可能地址是与 TLB 查找同时在某个缓存级别中)。

问题2

进程如何放入虚拟内存中?我的意思是每个进程都有 0x0 - 0xFFFFFFFFF 可用的虚拟内存空间,还是只有一个虚拟内存地址空间可以放置所有进程?

每个进程都有自己完全不同的虚拟内存空间。这(几乎)是 VM 的全部内容。

在过去,TLB 在任何意义上都没有“过程感知”。每次上下文切换都意味着必须完全刷新 TLB。如今,TLB 条目有一个简短的“进程上下文”(PCID?)字段并支持选择性刷新,因此您可以将其视为 PID(或者,更确切地说,PCID:PID 的某种散列)正在附加到虚拟页面地址,因此 TLB 更能感知进程,并且只有在与另一个进程发生 PCID 冲突时才需要刷新这些条目(两个进程映射到同一个 PCID)。

问题 3

是否有一个巨大的页表,其中包含每个进程的所有页面,或者每个进程都有自己的页表?

当然,这是特定于操作系统的,但我的理解是Linux有一组多级页表,其中条目 (PTE) 用 PID 标记,而不是有单独的每个进程的页表。我认为其基本原因是许多虚拟到物理的映射是n :1 而不是 1:1,因为它们都是 1:1 将在很大程度上破坏 VM 的主要目的:考虑共享只读页面包含库的说明libc,或在分叉后在父子之间共享的写时复制数据页。在每个进程的页表中为每个进程复制这些条目的效率低于在创建/退出进程时将特定于进程的条目添加到/从一组公共页表中删除。

磁盘的来源

一旦你有了一个 VM 系统,添加在页面错误发生时从磁盘检索页面的能力,并为 PTE 实现“老化”,以便将最近最少使用的页面放在磁盘上,这几乎是微不足道的。尽管这是内存受限系统的一个重要特性,但与理解 VM 系统的实际工作原理几乎完全无关。

于 2014-03-12T01:17:19.943 回答
17

有些人使用术语“虚拟内存”就好像它是页面文件的同义词,因为页面文件代表您分配的内存中不是“真实”内存(即 RAM)的部分。但大多数人认为“虚拟内存”是操作系统赋予程序的整个抽象层,它结合了 RAM 和页面文件。

我不确定 Mac OS 偏爱这些定义中的哪一个,尽管您的计算机似乎不太可能没有分配任何分页内存,所以我猜测它可能会在您的 8GB 实际 RAM 中添加 8GB 分页内存,总共 16GB 的可用(虚拟)内存。

请记住,因为操作系统管理内存分配和释放请求,所以它可以自由地做它想做的任何事情。我的理解是,大多数操作系统对每个进程都有不同的内存分配表,因此它们可以为多个程序提供相同的虚拟内存地址,但这些内存地址会映射到内存中的不同实际块。所以一个 64 位操作系统可以为多个 32 位程序分配最大数量的 32 位地址——它们并不都限于相同的 32 位内存地址。

但是,有一些限制:操作系统可以将限制设置为允许页面文件增长到的大小。因此,除非您故意告诉您的操作系统这样做,否则它可能不会有 64 GB 的总虚拟内存。即使这样做了,它也无法为每个程序分配全部 64 GB,因此OutOfMemory在操作系统为您的程序分配虚拟地址之前,您很可能会遇到错误0xFFFFFFFFF。(事实上​​,得知它实际上0xFFFFFFFFF是一个保留的错误代码位置,类似于0x0最终被分配了一个你的程序认为的内存地址0xFFFFFFFFF,即使操作系统没有使用那么多内存。

是否有一个巨大的页表,其中包含每个进程的所有页面,或者每个进程都有自己的页表?

可能两者都有……然后是一些。

  1. 每个进程都有自己的私有内存表,操作系统会主动阻止您的程序访问尚未分配给该表的内存地址。
  2. 还有像共享内存这样的东西,因此需要使用相同信息的两个进程可以创建一个共享内存区域,并且在该内存空间中的地址可以被两者访问。
  3. 操作系统本身显然需要有一些方法来跟踪有多少总内存可用,哪些地址空间是空闲/使用的,哪些虚拟内存块已分配到 RAM 或页面文件中的哪些位置。

因此,假设一个进程已经在 address 分配了内存0x00000002,当它从该内存地址加载值时,操作系统可能会识别出这实际上映射到了实际内存地址0x00000F23,这就是它的值实际上将被读取到 CPU 寄存器中。或者,它可能意识到它已将包含该地址的页面移动到磁盘上的某个位置,在这种情况下,操作系统将找到内存的空白部分并首先将页面的数据从磁盘加载到该内存中。(同样,这个内存地址与程序请求的原始内存地址没有任何关联。)

如果没有任何空内存可以从中提取页面,则操作系统首先必须将一些数据从内存中移出并放入页面文件中。它试图智能地确定在不久的将来最不可能使用的内存。但有时你最终会在内存被交换到磁盘后不久就不断地被请求,只是为了替换程序即将请求的下一块内存。这种“颠簸”是导致内存不足的计算机运行速度非常非常慢的原因,因为磁盘访问比内存访问慢几个数量级。

于 2014-03-11T23:17:04.070 回答