RFC-0013:克隆 VMO 映射

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

系统调用会根据映射创建 VMO 的 CoW 克隆,而无需句柄。

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

总结

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

动机和问题陈述

Linux 版 Chromium 通过从 zygote 进程派生渲染程序来生成渲染程序,从而节省大量内存和 CPU。我们希望在 Fuchsia 上实现这些优惠。

为此,我们需要通过某种方式来实现“地址空间克隆”操作,在给定情况下:

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

将返回:

  • 一个新进程,其地址空间由输入进程中映射的 VMO 克隆体填充
  • 对于在输入进程中引用 VMO 的每个句柄,系统会生成一个新的句柄,以引用新进程中该 VMO 的相应克隆

通过添加此系统调用,我们可以轻松地在用户空间中实现此功能。 为此,使用 ZX_INFO_PROCESS_MAPS 获取地址空间布局,并使用 zx_vmar_create_vmo_child 为每个映射创建一个克隆(具有句柄的任何 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_vmar_create_vmo_child 的用户空间实现,具体方法是先根据映射创建一个句柄,然后调用 zx_vmo_create_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