此文件包含有关 Zircon 中的内存管理和诊断的信息,并讨论了检查进程和系统内存使用情况的方法。
进程可以通过 3 种方式使用内存:
- 以堆、线程堆栈、可执行代码 + 数据的形式映射内存。 此内存由 VMAR 表示,VMAR 反过来又保存对 VMO 的引用。程序员通常通过内存地址与此内存连接。
- 独立的 VMO。这些是不通过 VMAR 映射的内存页集。程序员通过句柄(通常是发出 vmo_read 和 vmo_write)与此内存连接。
- 内核对象句柄形式的内核内存。
Fuchsia 遵循过度使用模型:进程可以分配的内存量超出给定时刻能够满足的需求,并且在写入内存页面时,内核会动态地进行物理分配(有线)。
用户空间内存
哪些进程占用了全部内存?
转储进程总内存用量
使用 ps
工具:
$ ps
TASK PSS PRIVATE SHARED NAME
j:1028 32.9M 32.8M root
p:1043 1386.3k 1384k 28k bin/devmgr
j:1082 30.0M 30.0M zircon-drivers
p:1209 774.3k 772k 28k /boot/bin/acpisvc
p:1565 250.3k 248k 28k driver_host
p:1619 654.3k 652k 28k driver_host
p:1688 258.3k 256k 28k driver_host
p:1867 3878.3k 3876k 28k driver_host
p:1916 24.4M 24.4M 28k driver_host
j:1103 1475.7k 1464k zircon-services
p:1104 298.3k 296k 28k crashlogger
p:1290 242.3k 240k 28k netsvc
p:2115 362.3k 360k 28k sh:console
p:2334 266.3k 264k 28k sh:vc
p:2441 306.3k 304k 28k /boot/bin/ps
TASK PSS PRIVATE SHARED NAME
PSS(比例共享状态)是一个字节数,用于估算进程消耗的进程中映射物理内存量。其值为 PRIVATE +
(SHARED / sharing-ratio)
,其中 sharing-ratio
取决于共享此进程中每个页面的进程数量。
这样做的目的是,例如,如果四个进程共享一个页面,则该页面上有 1/4 的字节将分别包含在这四个进程的 PSS
中。如果两个进程共享不同的页面,则每个进程都会获得该页面的 1/2 字节。
PRIVATE 是仅由此进程映射的字节数。也就是说,没有其他进程映射此内存。请注意,这并未考虑未映射的专用 VMO。
SHARED 是此进程和至少一个其他进程映射的字节数。请注意,这未考虑未映射的共享 VMO。它也没有指明共享内存的进程数量:可能为 2,也可能为 50。
直观呈现内存用量
如果您有 Fuchsia build,则可以使用树状图来直观呈现系统的内存用量。
在主机上,从 Fuchsia 检出的根目录运行以下命令:
./scripts/fx shell memgraph -vt | ./scripts/memory/treemap.py > mem.html
在浏览器中打开
mem.html
。
memgraph
工具会生成系统任务和内存信息的 JSON 说明,然后由 treemap.py
脚本解析。-vt
表示在输出中包含 VMO 和线程。
转储进程的详细内存映射
如果您想了解特定进程为何使用如此多的内存,可以对其 koid 运行 vmaps
工具(koid 是运行 ps 时显示的 ID),以查看它映射到内存中的内容。
$ vmaps help
Usage: vmaps <process-koid>
Dumps a process's memory maps to stdout.
First column:
"/A" -- Process address space
"/R" -- Root VMAR
"R" -- VMAR (R for Region)
"M" -- Mapping
Indentation indicates parent/child relationship.
列标记:
:sz
:条目的虚拟大小(以字节为单位)。并非所有页面都必须由物理内存提供支持。:res
:条目中“常驻”的内存量(以字节为单位);即支持该条目的物理内存量。此内存可能是私有的(只能由此进程访问),也可能由多个进程共享。:vmo
:映射到此区域的 VMO 的koid
。
$ vmaps 2470
/A ________01000000-00007ffffffff000 128.0T:sz 'proc:2470'
/R ________01000000-00007ffffffff000 128.0T:sz 'root'
...
# This 'R' region is a dynamic library. The r-x section is .text, the r--
# section is .rodata, and the rw- section is .data + .bss.
R 00000187bc867000-00000187bc881000 104k:sz 'useralloc'
M 00000187bc867000-00000187bc87d000 r-x 88k:sz 0B:res 2535:vmo 'libfdio.so'
M 00000187bc87e000-00000187bc87f000 r-- 4k:sz 4k:res 2537:vmo 'libfdio.so'
M 00000187bc87f000-00000187bc881000 rw- 8k:sz 8k:res 2537:vmo 'libfdio.so'
...
# This 2MB anonymous mapping is probably part of the heap.
M 0000246812b91000-0000246812d91000 rw- 2M:sz 76k:res 2542:vmo 'mmap-anonymous'
...
# This region looks like a stack: a big chunk of virtual space (:sz) with a
# slightly-smaller mapping inside (accounting for a 4k guard page), and only a
# small amount actually committed (:res).
R 0000358923d92000-0000358923dd3000 260k:sz 'useralloc'
M 0000358923d93000-0000358923dd3000 rw- 256k:sz 16k:res 2538:vmo ''
...
# The stack for the initial thread, which is allocated differently.
M 0000400cbba84000-0000400cbbac4000 rw- 256k:sz 4k:res 2513:vmo 'initial-stack'
...
# The vDSO, which only has .text and .rodata.
R 000047e1ab874000-000047e1ab87b000 28k:sz 'useralloc'
M 000047e1ab874000-000047e1ab87a000 r-- 24k:sz 24k:res 1031:vmo 'vdso/stable'
M 000047e1ab87a000-000047e1ab87b000 r-x 4k:sz 4k:res 1031:vmo 'vdso/stable'
...
# The main binary for this process.
R 000059f5c7068000-000059f5c708d000 148k:sz 'useralloc'
M 000059f5c7068000-000059f5c7088000 r-x 128k:sz 0B:res 2476:vmo '/boot/bin/sh'
M 000059f5c7089000-000059f5c708b000 r-- 8k:sz 8k:res 2517:vmo '/boot/bin/sh'
M 000059f5c708b000-000059f5c708d000 rw- 8k:sz 8k:res 2517:vmo '/boot/bin/sh'
...
您还可以使用 zxdb 中的
aspace
命令显示内存映射。
转储与进程关联的所有 VMO
vmos <pid>
这还会显示未映射的 VMO(ps
和 vmaps
当前均不考虑这些 VMO)。
此图还会显示给定 VMO 是否是子级,以及其父级的 koid。
$ vmos 1118
rights koid parent #chld #map #shr size alloc name
rwxmdt 1170 - 0 1 1 4k 4k stack: msg of 0x5a
r-xmdt 1031 - 2 28 14 28k 28k vdso/stable
- 1298 - 0 1 1 2M 68k jemalloc-heap
- 1381 - 0 3 1 516k 8k self-dump-thread:0x12afe79c8b38
- 1233 1232 1 1 1 33.6k 4k libbacktrace.so
- 1237 1233 0 1 1 4k 4k data:libbacktrace.so
...
- 1153 1146 1 1 1 883.2k 12k ld.so.1
- 1158 1153 0 1 1 16k 12k data:ld.so.1
- 1159 - 0 1 1 12k 12k bss:ld.so.1
rights koid parent #chld #map #shr size alloc name
列数:
rights
:如果进程通过句柄指向 VMO,则此列将显示该句柄具有的零或多项权限:r
:ZX_RIGHT_READ
w
:ZX_RIGHT_WRITE
x
:ZX_RIGHT_EXECUTE
m
:ZX_RIGHT_MAP
d
:ZX_RIGHT_DUPLICATE
t
:ZX_RIGHT_TRANSFER
- 注意:非标识名条目在此列中只有一个“-”。
koid
:VMO 的 koid(如果有)。否则为 0。没有 Koid 的 VMO 是由内核创建的,从未具有用户空间句柄。parent
:VMO 的父级的子级(如果是子级)。#chld
:VMO 的活跃子级数量。#map
:VMO 当前映射到 VMAR 的次数。#shr
:映射(共享)VMO 的进程数量。size
:VMO 的当前大小(以字节为单位)。alloc
:分配给 VMO 的物理内存量,以字节为单位。- 注意:如果此列包含值
phys
,则表示 VMO 指向原始物理地址范围,如内存映射设备。phys
个 VMO 不会消耗 RAM。
- 注意:如果此列包含值
name
:VMO 的名称,如果名称为空,则为-
。
为了将其与 ps
相关联:每个 VMO 对其映射部分有所贡献(因为并非所有或 VMO 的任何页面都会被映射):
PRIVATE = #shr == 1 ? alloc : 0
SHARED = #shr > 1 ? alloc : 0
PSS = PRIVATE + (SHARED / #shr)
您还可以使用 zxdb 中的
handle
命令显示 VMO 信息。
转储“隐藏”(未映射和内核)VMO
k zx vmos hidden
与 vmos <pid>
类似,但会转储系统中未映射到任何进程的所有 VMO:
- 用户空间有句柄但未进行映射的 VMO
- 仅映射到内核空间的 VMO
- 仅限内核、未映射且没有句柄的 VMO
koid
值为零表示只有内核具有对该 VMO 的引用。
#map
值为零表示 VMO 未映射到任何地址空间。
另请参阅:k zx vmos all
,用于转储系统中的所有 VMO。注意:由于内核控制台缓冲区限制,此输出被截断的情况很常见,因此通常最好将 k zx vmos hidden
输出与每个用户进程的 vmaps
转储结合使用。
限制
ps
和 vmaps
目前均不会计入:
- 未映射的 VMO 或 VMO 子范围。例如,您可以创建 VMO,向其中写入 1G 数据,这不会在此处显示。
所有进程转储工具均不会考虑以下事项:
成倍映射的网页。如果您使用 VMO 的同一范围创建多个映射,则 VMO 的所有已提交页面都将按照这些页面的映射次数进行计数。这可以在同一进程内,也可以位于进程之间(如果这些进程共享 VMO)。
请注意,“多重映射页面”包括写入时复制。
进程分配的资源的底层内核内存开销。 例如,一个进程可以有一百万个打开的句柄,而这些句柄会消耗内核内存。
您可以使用
k zx ps
命令查看进程句柄消耗情况;运行k zx ps help
可查看其列的说明。写入时复制 (COW) 克隆 VMO。克隆的干净(非脏、非复制)页面不会计入映射克隆的进程的“共享”页面,而相同的页面可能会错误地计入映射父级(克隆)VMO 的进程的“私有”页面。
TODO(dbort):解决此问题;工具是在 COW 克隆存在之前编写的。
内核内存
转储系统内存区域和内核堆使用情况
运行 kstats -m
将持续转储有关物理内存用量和可用性的信息。
$ kstats -m
--- 2017-06-07T05:51:08.021Z ---
mem total free VMOs kheap kfree wired mmu ipc other
2048M 1686.4M 317.8M 5.1M 0.9M 17.8M 20.0M 0.1M 0.0M
--- 2017-06-07T05:51:09.021Z ---
...
字段:
-t
选项以 ISO 8601 字符串的形式显示收集统计信息时的时间戳2017-06-07T05:51:08.021Z
。total
:系统可用的物理内存总量。free
:未分配内存量。VMOs
:提交给 VMO(包括内核和用户)的内存量。所有用户空间内存的超集。不包括属于wired
的特定 VMO。kheap
:标记为分配的内核堆内存量。kfree
:标记为可用的内核堆内存量。wired
:出于此结构体中的其他字段未涵盖的原因,预留并映射到内核的内存量。通常用于 RAM 磁盘和内核映像等只读数据以及前期启动动态内存。mmu
:用于架构专用的 MMU 元数据(如页面表格)的内存量。ipc
:进程间通信使用的内存量。other
:other
中的其他所有实体。
转储内核地址空间
k zx asd kernel
转储内核的 VMAR/映射/VMO 层次结构,类似于用户进程的 vmaps
工具。
$ k zx asd kernel
as 0xffffffff80252b20 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 fl 0x1 ref 71 'kernel'
vmar 0xffffffff802529a0 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 ref 1 'root'
map 0xffffff80015f89a0 [0xffffff8000000000 0xffffff8fffffffff] sz 0x1000000000 mmufl 0x18 vmo 0xffffff80015f8890/k0 off 0 p ages 0 ref 1 ''
vmo 0xffffff80015f8890/k0 size 0 pages 0 ref 1 parent k0
map 0xffffff80015f8b30 [0xffffff9000000000 0xffffff9000000fff] sz 0x1000 mmufl 0x18 vmo 0xffffff80015f8a40/k0 off 0 pages 0 ref 1 ''
object 0xffffff80015f8a40 base 0x7ffe2000 size 0x1000 ref 1
map 0xffffff80015f8cc0 [0xffffff9000001000 0xffffff9000001fff] sz 0x1000 mmufl 0x1a vmo 0xffffff80015f8bd0/k0 off 0 pages 0 ref 1 ''
object 0xffffff80015f8bd0 base 0xfed00000 size 0x1000 ref 1
...