dex*_*ous 1 c linux memory-management
假设我有一个构建为 prog1.out 的 prog1.c。在 prog1.out 中有一个链接器信息,它将告诉精灵将被加载到哪里。这些地址将是一个虚拟地址。加载程序将查找这些信息并将其作为一个进程启动。像 DS,BSS 这样的每个部分都将加载到链接器中提到的虚拟地址上。例如,我有 prog2.out 也有相同的加载器地址、BSS、DS 等,那么它会冲突吗?我知道它不会发生冲突,但会出现性能问题。由于两个进程具有相同的虚拟地址但它们映射到不同的物理地址?我很困惑,它如何保护具有相同虚拟地址的两个进程。
问题是当一个进程使用一个内存地址时,它在谈论一个虚拟地址,它可能与相同的物理地址不同。这意味着两个进程可以引用同一个地址并且不会混合它们的数据,因为它们将位于两个不同的物理位置。
下面我将描述如何在典型计算机上将虚拟地址转换为物理地址(这在其他架构上略有不同,但思路相同)
因此,一方面您有一个虚拟内存地址,并且您想要获得一个物理内存地址(即:RAM 上的实际地址),工作流程主要是这样的:
虚拟地址 -> [分段单元] -> [分页单元] -> 物理地址
每个操作系统都可以定义分段单元和分页的工作方式。例如,Linux 使用Flat Segmentation Model,这意味着它被忽略,所以我们现在也这样做。
现在,我们的虚拟地址通过称为分页单元的东西,并以某种方式转换为物理地址......就是这样。
内存被分成一定大小的块,在intel这个大小可能是4KB或4MB。
每个进程在内存中定义一组表,以便计算机知道它应该如何转换内存地址。这些表以分层方式组织,实际上,您要访问的内存地址在这些表的索引中分解。
我知道,这听起来令人困惑,但请多说几句。你可以用这张图片跟随我的写作:

有一个称为CR3的内部 CPU 寄存器,它存储第一个表的基地址(我们将称该表为页目录,它的每个条目都称为页目录条目)。当一个进程被执行时,它的CR3被加载(除其他外)。
所以,现在你想访问,比方说,内存地址0x00C30404,
分页单元说“好的,让我们获取页面目录基”,查看CR3寄存器并知道页面目录的基址在哪里,我们称这个地址为PDB(页面目录基)。
现在您想知道应该使用哪个目录条目。正如我之前所说,地址被分解为一堆索引。最重要的10位(bits 22 trough 31),对应的是Page Directory的索引。在这种情况下,0x00C30404是 0000 0000 1100 0011 0000 0100 0000 0100,其最高有效位是: 00010 11即0x3。这意味着我们要查找第 3 页目录条目。
请记住,这些表是分层的:每个页目录条目都有下一个表的地址,称为页表。(对于每个页面目录条目,此表可能不同)。
所以现在,我们得到了另一个表。地址的下 10 位将告诉我们我们将访问该表的哪个索引(让我们称它们为页表条目)。
00 0011 0000 是接下来的 10 位,它们是数字:0x30。这意味着我们必须访问第 30页表条目..
最后,这个页表条目保存了所需PAGE FRAME的偏移量(记住内存被分成 4k 块)。最后,我们地址的最低 12 位,是这个PAGE FRAME的内存偏移量,注意PAGE FRAME是一个实际的物理内存地址。
这称为 3 级分页,在 64 位(或使用 PAE)上非常相似,但还有一层分页。
你可能认为仅仅为了获取一个变量而获得所有这些内存访问是一个真正的无赖。这是真的。计算机中有一些机制可以避免所有这些步骤,其中包括 TLB(表后备缓冲区),它存储所有已完成翻译的缓存,因此可以轻松获取内存。
此外,这些结构的每个条目都有一些关于权限的属性,例如“此页面是否可写?” “这个页面是可执行的吗?”。
因此,既然您了解了内存分页的工作原理,就很容易掌握 Linux 是如何处理内存的:
| 归档时间: |
|
| 查看次数: |
975 次 |
| 最近记录: |