| RFC-0280:VMO 引用和切片归因 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | 建议更改 VMO 的引用和切片子项的内存归因。 |
| 问题 | |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2026-03-31 |
| 审核日期(年-月-日) | 2026-03-31 |
问题陈述
引用和切片是 VMO 子项类型,可将读取和写入等操作转发到父 VMO。操作的行为就像是在父 VMO 的重复句柄上执行一样。
目前,当使用 zx_object_get_info 查询时,引用和切片会针对所有字段报告零个归因字节 。
此外,无法查询 VMO 是引用还是切片,因此很难判断零个归因字节是否意味着没有内容。API 用户依赖于黑客攻击来检测和归因这些克隆类型。
此方案导致了一些问题。
Fxfs
Fxfs 使用引用来向 blob 和可变文件提供句柄。这存在问题,因为在使用现有 API 时,无法检索文件保持打开状态的客户端的相关信息。为了归因于 fxfs,内存监控器目前依赖于一种黑客攻击,该黑客攻击将总字节数为 0 的所有 VMO 视为引用。
IOBuffers
IOBuffers 使用引用来跟踪端点上的对等方。 在 IOBuffer 实现中,根 VMO 位于内核中,因此无法从用户空间查询归因。这种情况还包括嵌套引用,这进一步使情况复杂化。
孤立内存
如果 VMO 在仅具有引用和切片克隆时被丢弃,则原始 VMO 的 VmCowPages 将保持活动状态,但这些页面不会归因于任何 VMO。由于 API 限制,无法检索有关原始 VMO 或其克隆的任何信息,因此内存监控器会将此视为“孤立内存”。
摘要
此 RFC 建议通过 zx_object_get_info 更改 VMO 的引用和切片子项的内存归因。引用和切片子项现在将报告内存,就像查询是在父项上执行的一样。切片只会报告其可见范围内的字节。
此外,zx_info_vmo::flags 中将添加两个新标志,以便用户可以查询 VMO 是引用 (ZX_INFO_IS_REFERENCE) 还是切片 (ZX_INFO_VMO_IS_SLICE)。
这些更改应为 API 用户提供足够的信息,以便他们自行管理引用归因。
利益相关方
协调人:
jamesr@google.com
审核人:
etiennej@google.com、rashaeqbal@google.com
咨询对象:
adanis@google.com
社交化:
一份概述现有归因方案的问题、建议的解决方案以及考虑过的替代解决方案的文档已发送至 fuchsia-memory-wg 和 zircon-discuss 邮件列表以征求意见。
要求
为了使内存监控器和一般内存记账功能正常运行(包括模拟 Linux 内存归因 API),仍应能够根据 zx_info_vmo_t 中提供的信息计算这些标准指标:
- 总内存(相当于 RSS/常驻集大小)
- “此 VMO 引用的总内存”
- 专用内存(相当于 USS/唯一集大小)
- “如果此 VMO 被销毁,将释放的内存”
- 缩放内存(相当于 PSS/比例集大小)
- “此 VMO 在总内存中所占的份额”
- 必须与系统内存总用量相加
设计
从
zx_object_get_info返回的归因信息将发生更改
,以便引用和切片报告字节,就像它们是父项一样。
zx_info_vmo::flags 中将添加两个标志,以便用户可以查询 VMO 是引用 (ZX_INFO_IS_REFERENCE) 还是切片 (ZX_INFO_VMO_IS_SLICE)。
这样,API 用户就可以决定如何为引用归因字节。总内存可以报告为引用是父项。新标志可用于为专用内存返回 0,并正确计算总内存。
引用示例
以下是引用归因更改的示例:假设有一个 VMO,其中包含两个已提交的页面和一个引用子项。假设页面大小为 4096,以下是 API 查询的更改方式。
现有归因:
zx_info_vmo_t info;
reference.get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr)
info.committed_bytes = 0
info.populated_bytes = 0
info.committed_private_bytes = 0
info.populated_private_bytes = 0
info.committed_scaled_bytes = 0
info.populated_scaled_bytes = 0
info.committed_fractional_scaled_bytes = 0
info.populated_fractional_scaled_bytes = 0
建议的归因:
zx_info_vmo_t info;
reference.get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr)
info.committed_bytes = 8192
info.populated_bytes = 8192
info.committed_private_bytes = 8192
info.populated_private_bytes = 8192
info.committed_scaled_bytes = 8192
info.populated_scaled_bytes = 8192
info.committed_fractional_scaled_bytes = 0
info.populated_fractional_scaled_bytes = 0
info.flags & ZX_INFO_IS_REFERENCE = true;
info.flags & ZX_INFO_IS_SLICE = false;
切片示例
如果我们有一个 VMO,其中包含两个已提交的页面和一个单页切片,假设页面大小为 4096,以下是新 API 将报告归因的方式。它的行为就像是在父项上查询切片可见范围的归因一样。
建议的归因:
zx_info_vmo_t info;
slice.get_info(ZX_INFO_VMO, &info, sizeof(info), nullptr, nullptr)
info.committed_bytes = 4096
info.populated_bytes = 4096
info.committed_private_bytes = 4096
info.populated_private_bytes = 4096
info.committed_scaled_bytes = 4096
info.populated_scaled_bytes = 4096
info.committed_fractional_scaled_bytes = 0
info.populated_fractional_scaled_bytes = 0
info.flags & ZX_INFO_IS_REFERENCE = false;
info.flags & ZX_INFO_IS_SLICE = true;
实现
此更改的内核实现将很简单,但需要与内存监控器协调,以确保它们不会报告来自引用和切片的错误值。
性能
此更改将引入对 cow-pages 树的遍历,否则我们将返回零,因此在查询某些引用时,zx_object_get_info 可能会变慢。
安全注意事项
通过查询“*_scaled”字段或将引用中的专用字节与非专用字节进行比较,可以推断出有关原始 VMO 的写入时复制克隆的边信道信息,这在旧归因方案中是不可能的。这不会给当前引用用户带来任何问题。
预计这不会在未来的使用中造成问题,因为拥有引用在概念上类似于拥有 VMO 的额外句柄。
隐私注意事项
不适用
测试
需要更新任何查询引用或切片归因的现有测试。
文档
zx_object_get_info 系统调用 的文档将更新。
缺点、替代方案和未知事项
此设计的一个缺点是,如果父 ID 用于确定拥有页面的 VMO,则可能会引入嵌套引用的重复计数 bug。
另一种方法是扩展共享,以包含引用和切片以及写入时复制子项。这将对系统进行更大的更改,因为它会更改写入时复制克隆的归因,并且需要为所有 VMO 修改 USS 和 PSS 的计算。