地址空间

在操作系统中,内存管理提供了将部分内存动态分配给进程,并在不再需要时释放内存以供重复使用。现代操作系统使用地址空间来隔离进程内存。

地址空间表示进程用于引用内存的一组虚拟地址。虚拟地址直接映射到实际地址。Fuchsia 使用 VMAR(虚拟内存地址区域)来表示地址空间。

VMAR、映射和 VMO

在 Fuchsia 中,每个进程都有一个根 VMAR,并且可以划分为 VMAR 和映射的层次结构。映射指向底层 VMO(虚拟内存对象)

显示根 VMAR、子级 VMAR、映射和 VMO

VMAR 和虚拟机映射

VMAR 是特定进程地址空间中连续的虚拟地址范围:

  • VMAR 可以具有不重叠子区域的子 VMAR 和/或虚拟机映射
  • 它们将保护位应用于内存的某个部分(读写、可执行文件等)
  • 它们具有子项的 WAVL 树,以实现高效搜索

虚拟机映射表示地址空间中的“映射的”范围,即由物理页面支持的虚拟地址:

  • 虚拟机映射没有子级
  • 它们从 VMO 映射一系列页面
  • 成功的网页搜索到此结束

以下是可用的 VMAR 和虚拟机映射系统调用:

另请参阅虚拟内存地址区域参考

虚拟机操作系统

VMO 是内存字节的容器。 它们存储可通过虚拟机映射映射到地址空间的物理页面。以下是 VMO 系统调用:

另请参阅虚拟内存对象参考文档

虚拟内存管理器 (VMM)

虚拟内存管理器 (VMM) 负责维护进程地址空间,包括:

  • 为已映射的虚拟地址范围提供指向后备物理页面的指针。
  • 确保地址范围已设置正确的访问保护位。

为此,它会管理 VMAR、虚拟机映射、VMO 和硬件页面表之间的关系。另请参阅 VMM 源代码

当进程启动时,其整个地址空间表示为一个 VMAR。随着地址空间的不同部分被映射,VMAR 层次结构树会进行填充。随着 VMAR 和虚拟机映射的创建和销毁,树中的节点会被创建和销毁。

节点表示 VMAR 或虚拟机映射:

  • VMAR 指向子级列表,这些子级是父级 VMAR 地址范围内的其他 VMAR 和虚拟机映射。
  • 虚拟机映射指向的 VMO 内映射在该地址范围内的范围。

详细了解如何通过根 VMAR 直观呈现内存用量

VMM 使用地址空间布局随机化来控制在地址空间内创建新 VMAR 的地址范围。aslr.entropy_bits 内核命令行选项可用于控制随机化中熵的位数。熵越高,使地址空间越稀疏,VMAR 和虚拟机映射就越分散。

物理内存管理器 (PMM)

物理内存管理器 (PMM) 将系统上的所有可用物理内存 (RAM) 划分为各个页面,并管理页面的情况。它负责在需要时为 VMO 提供免费物理页面。

VMO 是需求分页,即按需填充(已提交)其页面。页面会在写入时提交。在此之前,未提交的页面由系统上的单例物理零页面表示。这样可以避免为尚未访问过的页面或只被读取的页面分配内存,从而避免不必要的内存分配。

VMO 还可以由用户空间分页器提供支持,该分页器会按需填充 VMO 中的特定内容,例如,从磁盘上的文件中读取的内容。了解如何编写用户分页器

示例:映射 VMO

如需映射 VMO,请执行以下操作:

  1. 使用 zx_vmar_root_self() 获取进程的根 VMAR 的句柄,例如:

    zx_handle_t vmar = zx_vmar_root_self();
    
  2. 使用 zx_vmo_create(...) 创建 VMO。这将返回 VMO 句柄,例如:

    const size_t map_size = zx_system_get_page_size();
    zx_vmo_create(map_size, 0, &vmo);
    
  3. 使用 zx_vmar_allocate(...) 在父 VMAR 中创建子 VMAR。此操作会返回一个 VMAR 句柄及其起始地址,例如:

    const size_t region_size = zx_system_get_page_size() * 10;
    zx_vmar_allocate(vmar, ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE, 0,
                     region_size, &region, &region_addr);
    

    这是可选步骤;您还可以将 VMO 直接映射到父级。

  4. 使用 zx_vmar_map(...) 在 VMAR 内映射 VMO。这会返回现在映射到 VMAR 的 VMO 的起始地址,例如:

    zx_vmar_map(region, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0,
                map_size, &map_addr);
    

显示 VMO 到根 VMAR 的映射

示例:访问 VMO 映射

在前面的示例中,假设 zx_vmar_map() 返回了 map_addr 0x4000。如需访问映射的地址 0x4000,代码可能如下所示:

zx_vmar_map(...&addr); // addr = 0x4000
auto arr = reinterpret_cast<int*>(addr);
arr[0] = 1; // page fault

指针解引用会导致页面错误。 为了解决页面错误,内核会执行以下操作:

  1. 在进程的 VMAR 树中搜索地址 0x4000,从 root_vmar 开始,直到找到包含该地址的虚拟机映射。
  2. 虚拟机映射将包含对 VMO 的引用,以及 VMO 页面列表中对应于映射的第一个地址的偏移量。查找与地址 0x4000 对应的 VMO 偏移量。
  3. 如果 VMO 在该偏移量处没有页面,请分配新页面。 假设此页面的实际地址为 0xf000。
  4. 通过 ArchVmAspace 将虚拟地址 0x4000 到物理地址 0xf000 的转换添加到硬件页表,该页表表示架构特定的页面表,用于跟踪 MMU 使用的虚拟地址到物理地址的映射。

显示 VMO 地址在 VMAR 地址范围内