RFC-0013:克隆 VMO 映射

RFC-0013:克隆 VMO 映射
状态已接受
区域
  • 内核
说明

一种系统调用,用于根据映射创建 VMO 的 CoW 克隆,而无需对其使用句柄。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2020-10-26
审核日期(年-月-日)2020-12-09

摘要

目前,只有在您拥有 VMO 的句柄时,才能创建 VMO 的 CoW 克隆。这对于 zygote 用例来说还不够,因为它需要 CoW 克隆整个地址空间,包括所有 VMO 句柄已关闭的映射。此 RFC 提出了一种新的系统调用来解决此问题。

动机和问题陈述

Linux 上的 Chromium 通过从 zygote 进程中分叉来生成渲染程序,从而显著节省内存和 CPU。我们希望在 Fuchsia 上实现这些节省。

为此,我们需要某种方法来实现“地址空间克隆”操作,该操作在以下情况下会执行:

  • 进程根 VMAR
  • 一些引用地址空间中映射的 VMO 的句柄

将返回:

  • 一个新进程,其地址空间由输入进程中映射的 VMO 的克隆填充
  • 对于引用输入进程中 VMO 的每个句柄,都有一个引用新进程中该 VMO 的相应克隆的新句柄

通过添加此系统调用,我们可以在用户空间中轻松实现此操作。其工作原理是使用 ZX_INFO_PROCESS_MAPS 获取地址空间布局,并使用 zx_vmar_create_vmo_child 创建每个映射的克隆,但具有句柄的任何 VMO 除外,这些 VMO 使用 zx_vmo_create_child 进行处理。

设计

zx_vmar_create_vmo_child

zx_status_t zx_vmar_create_vmo_child(zx_handle_t handle,
                                     uint32_t options,
                                     zx_vaddr_t addr,
                                     size_t size,
                                     zx_handle_t* out)

创建 handle 引用的 VMAR 中在 addraddr+size 范围内映射的页面的 CoW 克隆。该范围必须是单个 VMO 映射的子范围,即不得跨两个映射或包含任何未映射的页面。结果是新的 VMO,它是范围内映射的 VMO 的子项。

允许的选项包括 ZX_VMO_CHILD_SNAPSHOTZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITEZX_VMO_CHILD_NO_WRITE。它们的解读方式与 zx_vmo_create_child 中的解读方式相同。(目前不允许使用 ZX_VMO_CHILD_SLICE,因为存在安全问题:必须特别注意,确保修改父 VMO 的功能不会破坏任何安全边界。)

在以下情况下返回 ZX_ERR_INVALID_ARGS

  • addraddr+size 的范围不是单个 VMO 映射的子范围
  • addrsize 未对齐到页面

在以下情况下返回 ZX_ERR_ACCESS_DENIED

  • 使用新标志 ZX_VM_CANNOT_CREATE_VMO_CHILD 完成映射,或者 vmar 选项包含此标志
  • handle 没有 ZX_RIGHT_INSPECT

如果 handle 不是有效的句柄,则返回 ZX_ERR_BAD_HANDLE;如果 handle 不是 VMAR,则返回 ZX_ERR_WRONG_TYPE;如果 VMAR 已被销毁,则返回 ZX_ERR_BAD_STATE。如果记录的原因适用,还可以返回 zx_vmo_create_child 返回的任何错误。

返回的 VMO 句柄将具有 ZX_DEFAULT_VMO_RIGHTS,并进行以下更改:

  • 如果指定了 ZX_VMO_CHILD_NO_WRITE,系统会移除 ZX_RIGHT_WRITE
  • 如果映射可执行且未指定 ZX_VMO_CHILD_NO_WRITE,系统会添加 ZX_RIGHT_EXECUTE。这意味着,可以创建可执行映射的可执行克隆,但它们必须是只读的。

ZX_VM_CANNOT_CREATE_VMO_CHILD

ZX_VM_CANNOT_CREATE_VMO_CHILD 是一个新的 zx_vm_options_t 标志,可为 VMO 映射或 VMAR 指定。这样,映射 VMO 或创建 VMAR 的代码便可禁止对它们使用 zx_vmar_create_vmo_child

实现

添加系统调用并不是一项复杂的更改;它可以在一个 CL 中完成。 克隆算法的实现不在本 RFC 的讨论范围内。

性能

由于此系统调用仅在 zygote 实现上下文中使用,因此不值得单独对其进行基准测试。我们应衡量 zygote 实现的整体性能。

安全注意事项

目前无法使用 VMAR 引用(而非句柄)创建 VMO 子项,但此 RFC 可让您实现这一点。这有点像凭空授予新功能。

在这种情况下,这种做法似乎并不那么冒险,因为最终结果与创建新的 VMO 并复制数据(但内存用量更少)相同,这在之前是可行的。如果需要禁止这种情况,此 RFC 建议使用 zx_vm_options_t 标志 (ZX_VM_CANNOT_CREATE_VMO_CHILD)。

测试

系统会对该系统调用进行单元测试。 用于未来实现地址空间克隆的测试还将用作系统调用的集成测试。

文档

本 RFC 是 zx_vmar_create_vmo_child 文档的起点。

缺点、替代方案和未知情况

还有哪些策略可以解决同一问题?

我们可以添加一个系统调用来执行整个地址空间克隆操作。这将是一个非常复杂的系统调用,无论是接口还是实现。最好将复杂性推送到用户空间。

我们可以添加一个系统调用,以允许从映射中铸造 VMO 句柄。这样一来,便可通过先从映射中创建句柄,然后调用 zx_vmo_create_child,实现 zx_vmar_create_vmo_child 的用户空间实现。不过,为了让接口更易于推理,Zircon API 通常只允许铸造新创建对象的句柄。此系统调用必须凭空创建句柄。

您可以使用各种技巧来避免关闭任何 VMO 句柄,例如修改用于创建和映射 VMO 的所有代码以避免关闭它们,或拦截 zx_handle_close。不过,这不足以实现地址空间克隆,因为加载器服务会在进程开始运行之前将某些 VMO 映射到进程,而这无法帮助您获取这些 VMO 的句柄。我们可以修改加载器服务,将必要的句柄传递给进程,但这比添加此系统调用的工作量要大。

在先技术和参考文档

https://chromium.googlesource.com/chromium/src/+/master/docs/linux/zygote.md http://neugierig.org/software/chromium/notes/2011/08/zygote.html