在操作系统中 内存管理 提供了为进程动态分配部分内存的方法, 以便在不再需要时将其释放,以便重复使用。 现代操作系统使用地址空间来隔离进程内存。
地址空间 表示进程用来引用内存的一组虚拟地址。 虚拟地址直接映射到实际地址。Fuchsia 使用 VMAR (虚拟内存地址区域)来表示地址空间。
VMAR、映射和 VMO
在 Fuchsia 中,每个进程都有一个根 VMAR,并且可以划分为 VMAR 和映射。映射指向 VMO(虚拟内存对象):
VMAR 和虚拟机映射
通过 VMAR 是特定进程中连续的虚拟地址范围”地址 空格:
- VMAR 可以有非重叠的子 VMAR 和/或虚拟机映射 次级区域
- 它们会将保护位应用于一部分内存(读写、 可执行文件等)
- 它们有一个 WAVL 树, 提高搜索效率
虚拟机映射表示“已映射”即虚拟地址空间中的 由实体页面支持的地址:
- 虚拟机映射没有子级
- 他们从 VMO 映射一系列页面
- 成功的网页搜索到此结束
以下是可用的 VMAR 和虚拟机映射系统调用:
zx_vmar_allocate()
- 创建新的子级 VMARzx_vmar_map()
- 将 VMO 映射到进程zx_vmar_unmap()
- 从进程中取消映射内存区域zx_vmar_protect()
- 调整内存访问权限zx_vmar_destroy()
- 销毁 VMAR 及其所有子级
另请参阅虚拟内存地址区域参考。
VMOS
VMO 是内存字节的容器。 它们包含可通过虚拟机映射映射到地址空间的物理页面。 以下是 VMO 系统调用:
zx_vmo_create()
- 创建新的 VMOzx_vmo_create_child()
- 创建新的子 VMOzx_vmo_create_physical()
- 创建新的物理 VMOzx_vmo_get_size()
- 获取 VMO 的大小zx_vmo_op_range()
- 对一系列 VMO 执行操作zx_vmo_read()
- 从 VMO 读取zx_vmo_replace_as_executable()
- 创建 VMO 的可执行版本zx_vmo_set_cache_policy()
- 为 VMO 保留的页面设置缓存政策zx_vmo_set_size()
- 调整 VMO 的大小zx_vmo_write()
- 写入 VMO
另请参阅虚拟内存对象参考。
虚拟内存管理器 (VMM)
虚拟内存管理器 (VMM) 负责维护进程地址 包括:
- 为 已映射。
- 确保为地址范围设置了正确的访问保护位。
为此,它会管理 VMAR、虚拟机映射、VMO 和 硬件页面表格。另请参阅 VMM 源代码。
当进程开始时,其整个地址空间表示为一个 VMAR。 由于地址空间的不同部分已映射,因此 VMAR 层次结构树 填充。树中的节点以 VMAR 和 VM 的形式创建并销毁 系统会创建和销毁映射。
节点表示 VMAR 或虚拟机映射:
- VMAR 指向一系列子项,这些子项是其他 VMAR 和虚拟机 在父级 VMAR 的地址范围内的映射。
- 虚拟机映射指向一个 VMO 内映射到该 指定地址范围
详细了解 直观呈现根 VMAR 中的内存用量
VMM 使用
地址空间布局随机化
用于控制在地址空间内创建新 VMAR 的地址范围。
通过
aslr.entropy_bits
内核命令行选项可用于控制熵的位数
随机生成。更高的熵会产生更加稀疏的地址空间,
更加广泛地使用 VMAR 和虚拟机映射。
物理内存管理器 (PMM)
通过 物理内存管理器 (PMM) 将系统上的所有可用物理内存 (RAM) 划分到页面中, 管理它们的后续情况 当出现以下情况时,它负责为 VMO 提供免费的物理页面 他们需要它们。
VMO 是按需求调出的,即系统会按需填充(提交)其网页。 页面会在写入时被提交。在此之前,未提交的页面 由系统上的单例物理零页面表示。这样可以避免 不必要地为尚未访问的页面或仅 所有来源。
VMO 也可以由 用户空间分页器, 它会按需填充 VMO 中的特定内容, 例如从磁盘上的文件中读取的内容 了解如何编写用户寻呼机。
示例:映射 VMO
如需映射 VMO,请执行以下操作:
使用
zx_vmar_root_self()
获取进程根 VMAR 的句柄。 例如:zx_handle_t vmar = zx_vmar_root_self();
使用
zx_vmo_create(...)
创建 VMO。 这将返回 VMO 句柄,例如:const size_t map_size = zx_system_get_page_size(); zx_vmo_create(map_size, 0, &vmo);
使用
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, ®ion, ®ion_addr);
这是可选步骤;您还可以将 VMO 直接映射到父级。
使用
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 映射
在前面的示例中,
假设 zx_vmar_map()
返回了 map_addr
0x4000。
要访问映射地址 0x4000,请执行以下操作:
代码可能如下所示:
zx_vmar_map(...&addr); // addr = 0x4000
auto arr = reinterpret_cast<int*>(addr);
arr[0] = 1; // page fault
指针解引用会导致页面错误。 要解决页面错误,内核将执行以下操作:
- 在启动进程的 VMAR 树中搜索地址 0x4000
在
root_vmar
处,直到找到包含该地址的虚拟机映射。 - 虚拟机映射将包含对 VMO 的引用, 以及 VMO 页面列表中的偏移量 (对应于映射的第一个地址)。 查找与地址 0x4000 对应的 VMO 偏移量。
- 如果 VMO 在该偏移量处没有页面,则分配一个新页面。 假设此网页的实际地址是 0xf000。
- 添加从虚拟地址 0x4000 到实际地址 0xf000 的转换
通过
ArchVmAspace
、 表示针对特定架构的页表 用于跟踪 MMU。