本文档介绍了 Sysmem 的 VMO 的层次结构, 用于管理内存的方法本文档假定您熟悉虚拟 内存和非系统内存的计算 在 Fuchsia 上。
堆
所有 VMO 都是从堆分配的。当前的堆为:
名称 | 固定泳池 | 说明 |
---|---|---|
系统核心 | 否 | 通用主内存 |
SysmemAmlogicProtectedPool | 是 | 无法从 CPU 访问内存 |
SysmemContiguousPool | 是 | 物理连续的主内存 |
tee_secure | 是 | 适用于 Amlogic 解密编码视频的特殊用途保护内存 |
系统内存外部堆(可能多个) | 否 | 目前用于金鱼 |
系统-康核 | 否 | 连续主内存;仅用于没有 SysmemContiguousPool 的系统 |
并非所有堆都会从固定池中进行子分配;例如“核心”和“Contig Core”
可以从主内存分配某些堆直接对应于
fuchsia.sysmem.HeapType 值,但所选的堆也可能是
依赖于 is_physically_contiguous
等 BufferMemorySettings 成员。
系统核心
系统核心从主内存分配。如果不存在 向内存添加约束条件
SysmemContiguousPool
CPU 和系统上的一些其他设备只需要几乎连续的 内存。他们可以选择具有任意实际地址的任意网页,并且依靠 MMU 硬件,用于分配新的连续虚拟地址。这样,您就可以 因为任何物理页面都可以使用
但是,某些硬件不具备 MMU 或分散-收集功能。这样 硬件需要物理上连续的地址空间 在内存中的顺序是相同的。在系统运行时,主内存 变得越来越分化, 因为其他分配的页面恰好 随机分布在内存周围。
为了避免这个问题,Sysmem 提供了一个单独的连续池。它 在启动后不久分配一个大型内存池, 然后再向应用提供较小的部分。从理论上讲, 内存仍可能碎片化,但实际上它能发挥作用, 更大的内存块从该池中分配,而同一集群中 数据块会同时释放回数据池。
SysmemAmlogicProtectedPool
在使用 Amlogic SoC 的系统中,DRM(受保护的内存)视频需要 通过访问控制机制分配给特殊区域 无法读取解密的视频。这些区域必须 并且只能由 GPU 和其他硬件在特殊模式下访问, 硬件以有线方式连接,以确保其不会泄露内存。
不能将内存任意标记为受保护或不受保护。硬件 只能将少量 (< 32) 区域标记为受保护。为此, 系统内存可以在启动后不久分配受保护的池(类似于 并告知固件保护该池中的所有内存 区域。然后它可以从这个受保护的内存池中进行子分配。
tee_secure
tee_secure 用于另一种类型的受保护的内存, 数据类型。固件分配此区域, ZBI 必须告诉 zircon 从内存中分配,也绝不接触它其他驾驶员 可以检索有关该存储器的信息及其所在位置 然后告知 sysmem。系统可以通过以下方式从此堆进行子分配: 所需的资源。
系统内存外部堆
外部堆不一定使用实际内存。例如,金鱼 堆是外部堆,表示 FEMU 之外的视频内存 虚拟机。客户端可以传递 VMO 句柄,但不应传递 直接写入内存;而是让 goldfish 驱动程序查找主机 使用 VMO KOID 管理资源
VMO 层次结构
系统使用 VMO 的层次结构来跟踪 客户。让 VMO 保持活跃状态的因素有三个:
VMO 的句柄。
VMO 到进程地址空间的映射。
表示已映射到设备的 PMT。
出于安全考虑,sysmem 无法回收内存供 VMO 使用 直到所有这些类型的引用都消失普通 对于 VMO,内核通过仅在所有引用对象被销毁后销毁 VMO 都消失了。不过,sysmem 会从较大的物理地址子分配 VMO 因此它需要了解 VMO 是否被销毁, 决定要重复使用哪些内存范围。
内核支持 ZX_VMO_ZERO_CHILDREN
信号来帮助完成这些工作
用例 - 如果 VMO 的所有子项都已关闭,则
ZX_VMO_ZERO_CHILDREN
将在父级 VMO 上收到信号。
客户端叶 VMO
这些是分发给客户端的 VMO;调用
BufferCollection.SetName,然后再分配 VMO。客户
也可以直接在 VMO 上设置 ZX_PROP_NAME
,但我们不建议这样做
因为 sysmem 驱动程序无法访问该名称。
系统还会保留对这些 VMO 的引用,但前提是 BufferCollection 会继续引用它们,即使当前没有子级 VMO 句柄
中间 VMO
每个叶 VMO 都有一个中间 VMO 作为父 VMO。两者之间存在 1-1 映射关系 叶和中间 VMO。名称由堆设置,通常固定不变 来自堆的所有 VMO例如,对于 来自连续池的 VMO。
系统使用这些 VMO 检测是否清除了对叶 VMO 的所有引用 输出;一旦收到 ZX_VMO_ZERO_CHILDREN 信号,便知道它是安全的 让它删除 VMO 并可能重复使用该空间中间 VMO 从未 会传递到 sysmem 进程之外,因此客户端永远无法引用它们 。
堆 VMO
它们表示为堆中的 VMO 分配的整个内存池 。它们通常会在启动后不久进行分配,以确保有足够的内存 可用。堆 VMO 也可能表示 地址 - 例如 tee_secure 覆盖特定的物理范围 由引导加载程序分配。
中间 VMO 作为堆 vmo 中的切片分配,因此 每个中间 VMO 表示堆中不同的内存范围
如果堆不代表物理内存池,则不需要 堆 VMO。在这种情况下,中间 VMO 是在没有父 VMO 的情况下分配的。
报告内存
检查
Sysmem 会提供 Inspect 层次结构,以将其内存用量报告给 快照和其他客户端应用下面是一个简单的层次结构示例:
root:
sysmem:
collections:
logical-collection-0:
allocator_id = 1
heap = 0
min_coded_height = 1024
min_coded_width = 600
name = vc-framebuffer
pixel_format = 101
pixel_format_modifier = 0
size_bytes = 2490368
vmo_count = 1
collection-5:
channel_koid = 20048
debug_id = 5498
debug_name = driver_host
collection-6:
channel_koid = 20050
debug_id = 5498
debug_name = driver_host
collection-at-allocation-7:
debug_id = 19829
debug_name = virtual-console.cm
min_buffer_count = 1
collection-at-allocation-8:
debug_id = 5498
debug_name = driver_host
collection-at-allocation-9:
debug_id = 5498
debug_name = driver_host
vmo-20085:
koid = 20085
heaps:
SysmemContiguousPool:
allocations_failed = 0
allocations_failed_fragmentation = 0
free_at_high_water_mark = 37498880
high_water_mark = 2490368
id = 1
is_ready = true
last_allocation_failed_timestamp_ns = 0
max_free_at_high_water = 37498880
size = 39989248
used_size = 2490368
vmo-20085:
koid = 20085
size = 2490368
SysmemRamMemoryAllocator:
id = 0
系统通过检查层次结构来报告其内存视图,
/dev/diagnostics/class/sysmem/XXX.inspect
文件(其中 XXX 是伪随机文件,
3 位数标识符)。显示的每个逻辑集合都代表一组相同的
缓冲区。这些逻辑集合包含列表
该合集中由直播中间 VMO 组成的组合。
并且可用于唯一标识系统中的系统内存虚拟机
memgraph 输出。
所有堆还具有检查节点。其中包括 所有子 VMO 以及堆的满满程度以及 它的任何分配失败。有些堆只有名称和 ID 属性, 而不是从它们分配的 VMO 的信息。
逻辑集合的 allocator_id
与所用堆的 id
匹配
来分配其内存。
检查数据受到限制,因为系统内存无法查看其他 进程。例如,它不知道还有哪些其他进程, 保留对其 VMO 的引用,只不过至少有一个进程。 它也不知道创建了 VMO 的客户端进程的确切名称。 系统客户端应使用其 但并不强制执行,也无法保证 客户端设置的名称正确无误
不过,有些信息
检查数据。例如,客户端进程可以保留某个通道
而无需保留 VMO 的任何句柄。仅限
sysmem 知道 BufferCollection 通道与内部 VMO 之间的映射
过程。channel_koid
属性提供有关服务器上的信息
频道 ID。
ZX_INFO_PROCESS_VMOs
此系统调用由 memgraph
和 mem
工具使用。它可以确定
进程引用 VMO,这对于将内存归因于指定的
安全地向流程发送消息
sysmem 使用的 VMO 层次结构可能会导致这些工具出现问题。对于
例如,mem
会忽略不含任何已提交的 VMO
内存(分配的内存,有物理内存支持),以免杂乱无章
输出内容。这会导致 mem 忽略叶 VMO,因为它是 Cloud Storage 中的根 VMO
实际分配内存的树。Mem 有一些可传播的技巧
作为内部 IP 地址的子项的 VMO 的
SysmemContiguousPool
和 SysmemAmlogicProtectedPool
- 查看
"size" [尺寸]叶 VMO 的一部分,并假设已分配所有内存。这个
仅适用于分配没有重叠(即没有重叠)的固定大小池
为什么它被限制在一组硬编码的池中。
外部堆 VMO 也很复杂,因为它们实际上不会占用 客户机虚拟机内的内存。因此,内存负责执行 正确做法是不报告这些错误(其承诺的内存大小为 0), 这意味着很难将主机系统上的内存归因于 内部环境
memgraph -v
对内存信息的处理较少,但用户
需要自行处理以确定内存用量。它还可以是
难以确定 VMO 有哪些来源,因为它们
必须有一致的名称
统一的方法
任何想要完整而准确地了解系统内存 VMO 的实用程序都必须
合成检查和 ZX_INFO_PROCESS_VMOS
信息。系统检查
数据应该是存在哪些系统内存 VMO 的可信来源,而内核
是进程保留对 VMO 的引用的可信来源。这会
需要遍历逻辑缓冲区集合条目并列出它们
然后通过ZX_INFO_PROCESS_VMOS
查找它们的尺码
哪些进程引用了其子项。
实用程序可以为每个进程截取 ZX_INFO_HANDLE_TABLE
快照。然后它可以
使用该表在 channel_koid
中查找 koid,以确定哪个
即保留该 BufferCollection
在某些情况下,无法正确计算内存。通过 主要问题是,系统不会报告频道消息中保留的标识名 这就使我们无法对这些引用进行解释客户 可以将 VMO 标识名推送到某个频道,并且永远不会从该频道读取数据; 就连内核也不知道将内存归因于谁。调试 在这种情况下,您可以使用客户端信息作为后备。
未来可能发生的变化
为每个客户端创建一个中间 VMO,以便系统内存可以自行确定 客户端仍然有对 VMO 的引用。
让组件框架改为向 sysmem 传递不可伪造的标识符 就是让客户端传递可伪造的调试名称