【问题标题】:Why Linux has 4 layers of "page tables" and how it works exactly为什么 Linux 有 4 层“页表”以及它是如何工作的
【发布时间】:2020-03-26 03:27:59
【问题描述】:

我正在尝试了解page layouts according to these notes刚刚开始学习操作系统,所以这有点让我头疼。具体来说是这样的:

在 linux 中有 4 个“级别”的页表。一个表由一个 pXX_t 类型的条目数组组成,包装了一个 pXXval_t:

  1. 页面全局目录 (PGD) - pgd_t/pgdval_t
  2. 页面上层目录 (PUD) - pud_t/pudval_t
  3. 页面中间目录 (PMD) - pmd_t/pmdval_t
  4. 页表条目目录 (PTE) - pte_t/pteval_t

这些类型只是依赖于基本架构的类型的 typedef 包装器,将所有 x86-64 类型聚集在一起......

然后他们画这个:

    6         5         4         3         2         1
4321098765432109876543210987654321098765432109876543210987654321
0000000000000000000000000000000000000000101100010111000000010000
[   RESERVED   ][  PGD  ][  PUD  ][  PMD  ][  PTE  ][  OFFSET  ]
                         |        |        |        |-PAGE_SHIFT
                         |        |        |-----------PMD_SHIFT
                         |        |--------------------PUD_SHIFT
                         |---------------------------PGDIR_SHIFT

它仍然在我的脑海中,尤其是所有的缩写等等。

能否用稍微温和的术语解释一下我们这里到底有什么?我想在 JS 操作系统中实现虚拟内存分页,以模拟一种现实的操作系统。我想知道正在使用哪些结构以及它们在内存本身中的实际外观(或者如果通过汇编完成,则更易于使用来解释/教授)。

所以现在我正试图弄清楚(在 JavaScript 中)如何使用 1 数组 来管理多个进程的所有内存。没有作弊和使用其他 JavaScript 数组或对象或其他任何东西,只有 1 个数组,然后是整数。我一直试图弄清楚如何在不使用数组的情况下在内存中创建一个数组(一片内存)哈哈。然后为了让它更复杂,我需要在这个 1 数组中创建页面,这些页面映射到每个进程允许的内存部分。我试图详细弄清楚如何做到这一点,这些笔记是我最接近实际看到的,但它们有点太详细了。想知道是否可以帮助简化解释,并帮助描绘如何在单个数组中创建页面/进程映射。解释这个 Linux 系统将有助于形象化。

【问题讨论】:

  • This 似乎很有帮助,但也有点详细。

标签: linux memory process operating-system virtual-memory


【解决方案1】:

This is virtual memory.

具体来说,当在 ISA 上运行时,Linux 使用 x86-64 硬件页表布局将一个进程的虚拟地址空间映射到具有页粒度的物理内存。 Why in 64bit the virtual address are 4 bits short (48bit long) compared with the physical address (52 bit long)? 有图。

这是一个基数树,它将地址分成 9 位块,具有 12 个页面偏移位。 (12 + 4*9 = 48 位虚拟地址,需要正确符号扩展为 64 位)。 32 位 x86 页表使用 2 级,每级 10 位(12 + 2*10 = 32 位虚拟地址)。

在 TLB 未命中时,硬件会遍历此表以到达 PTE(页表条目)或“无效”条目,在这种情况下会引发 #PF 异常。


从您的其他问题来看,在您了解堆栈的工作原理等基础知识之后,您应该将其放在您的待办事项列表中很多

只是模拟一个进程,而不是整个系统,所以你只需要考虑它的私有地址空间。在真实的OS下,进程的地址空间是虚拟的,直接对应一个JS大数组。因此,每个进程都有一个 JS 数组,用于保存其内存(并单独注册,或作为该数组的一部分)。

这基本上是将虚拟内存留给 JS 实现,因为您是在 JS 上而不是在真实硬件上运行。

除非您正在为某些实际硬件 ISA 编写完整的模拟器,否则这应该很好。

CPU 使用专用硬件 (TLB) 来缓存翻译。在 JS 中,您需要通过映射函数间接 每个 内存访问,这会使您的源代码非常痛苦。您可以通过使用地图/字典数据结构而不是实际页面 tables 来使用 JS 版本的 TLB。

如果您可以对 try{}catch 执行任何操作以对应页面错误,请使用 IDK。

也许还考虑使用 TypedArray 来获取同一底层缓冲区的不同“视图”? IDK 如何帮助模拟其中有漏洞的非连续虚拟内存。我认为在 JS 中你会希望你的“进程”保持它们的虚拟内存使用非常密集/连续,否则你可能会浪费大量内存(并且没有检测到对“未映射”页面的访问)。

【讨论】:

    猜你喜欢
    • 2020-08-06
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    相关资源
    最近更新 更多