内存管理是个比较大的话题,本文只做个简单的介绍。建议先Magenta自带的文档memory,会对内存管理有个直观的认识。
涉及到的对象
Virtual memory address space(Vmas)
vmas表示处理器层面的地址空间,和具体的cpu的arch有关。一般分为2大类:kernel space和process space。在Magneta下输入命令“k vmm aspaces”会枚举出当前所有的vmas,缩减后的结果如下:
as 0xffffffff8024db20 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 fl 0x1 ref 72 'kernel'
as 0xffffff8001690060 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 fl 0 ref 31 'proc:1263'
as 0xffffff8001691498 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 fl 0 ref 66 'proc:1283'
as 0xffffff80016a89c8 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 fl 0 ref 38 'proc:1539'
as 0xffffff80016aee28 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 fl 0 ref 108 'proc:1611'
as 0xffffff80016b9928 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 fl 0 ref 45 'proc:1762'
第一行表示的是kernel space,其他是各个进程的process space。可见kernel的地址范围是
0xffffff8000000000 ~ 0xffffffffffffffff
而process的地址范围如下:
0x1000000 ~ 0x7fffffffefff
这些地址的定义请参见 kernel/arch/x86/rules.mk 中的宏:
KERNEL_ASPACE_BASE ?= 0xffffff8000000000UL # -512GB
KERNEL_ASPACE_SIZE ?= 0x0000008000000000UL
USER_ASPACE_BASE ?= 0x0000000001000000UL # 16MB
USER_ASPACE_SIZE ?= 0x00007ffffefff000UL
Magenta使用结构体 “class VmAspace” 表示vmas,其成员”ArchVmAspace arch_aspace_“表示具体arch下的地址访问,目前可以使用如下2者之一。
using ArchVmAspace = X86ArchVmAspace;
using ArchVmAspace = ArmArchVmAspace;
Virtual memory address region(Vmar)
vmar用于表示一段连续的虚拟地址空间,以数据结构class VmAddressRegion表示。kernel space和process space初始只有一整块vmar,以class VmAspace 的成员 mxtl::RefPtr< VmAddressRegion> root_vmar_表示。后续可以在root_vmar_上被分配子块或者回收;在子块上也可以做分配和回收。所以vmar是有层次结构的,class VmAddressRegion的成员ChildList subregions_ 和 VmAddressRegion* parent_分别用于表示其子块和父块。
命令“k vmm aspaces”的vmar部分缩减输出如下:
#kernel的vmar
vmar 0xffffffff8024d9a0 [0xffffff8000000000 0xffffffffffffffff] sz 0x8000000000 ref 1 'root'
vmar 0xffffff8001636e00 [0xffffff9000004000 0xffffff9000e04fff] sz 0xe01000 ref 2 'arena:handles'
#以下表示3个process的vmar
vmar 0xffffff8001690230 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 ref 2 'root'
vmar 0xffffff8001697f18 [0x1397428fe000 0x139742906fff] sz 0x9000 ref 1 'useralloc'
vmar 0xffffff800169aa58 [0x2f554d7d7000 0x2f554d817fff] sz 0x41000 ref 1 'useralloc'
vmar 0xffffff8001691668 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 ref 2 'root'
vmar 0xffffff800169d7a0 [0x71f0b8ea000 0x71f0b8ecfff] sz 0x3000 ref 1 'useralloc'
vmar 0xffffff8001694a18 [0x9ea73f6a000 0x9ea73faafff] sz 0x41000 ref 1 'useralloc'
vmar 0xffffff800169c148 [0x1869902a7000 0x1869902a9fff] sz 0x3000 ref 1 'useralloc'
vmar 0xffffff800169f3e0 [0x1000000 0x7fffffffefff] sz 0x7ffffefff000 ref 2 'root'
vmar 0xffffff80016b4bc8 [0x786f8c4d000 0x786f8c50fff] sz 0x4000 ref 1 'useralloc'
vmar 0xffffff80016ab6e8 [0x11f837604000 0x11f83760ffff] sz 0xc000 ref 1 'useralloc'
vmar 0xffffff80016abff0 [0x24ae539b6000 0x24ae539cefff] sz 0x19000 ref 1 'useralloc'
root_vmar_的范围一般设置为VmAspace的地址空间范围大小。所以kernel的vmar范围如下:
[0xffffff8000000000 0xffffffffffffffff]
而process的范围如下:
[0x1000000 0x7fffffffefff]
Virtual memory mapping(Vmm)
请参考vmar_map。mapping就是将vmo映射到vmar。
Virtual memory object(Vmo)
请参考vm_object。
vmo也表示一段连续的虚拟地址空间,但此空间指向物理页或memory-map的device区域。从某种意义上讲,vmo的行为更像文件,可以读取,可以被mapping。
Magenta以数据结构class VmObject表示vmo,而以其子类class VmObjectPaged表示指向物理页的vmo。那么paged vmo是什么时候分配物理页的呢?
在没有被mapping,直接调用mx_vmo_read/write时,如果还未分配物理页,在读的情况下,直接读取zero oage;在写的情况下,则分配物理页给vmo,后续可以写至此物理页。如果此vmo是从其他vmo通过clone而来,且父vmo对应于offset的物理页已分配,如是读,则从此物理页读取;如是写,则行为类似于COW,即先分配一个物理页,将父vmo的页copy此新page,然后作为vmo的物理页。 物理页由class VmObjectPaged的成员 VmPageList page_list_管理。
调用mx_vmar_map显式的映射vmo,将vmo映射于一段虚拟地址空间。后期在访问此虚拟空间时,会产生缺页中断,分配物理页并建立映射。调用流程如下:
x86_exception_handler
->x86_pfe_handler
->vmm_page_fault_handler
->VmAspace::PageFault
->VmAddressRegion::PageFault
-> 如果此段虚拟地址是VmMapping,则VmMapping::PageFault
当是读缺页时,依然是将zero page映射至vmo的虚拟空间,但此时映射为read only;后续如果要写此段虚拟空间,则依然会产生异常,从而可以分配新物理页并重新映射。
那么如果没有mapping,vmo是否会占虚拟地址空间? 此时vmo只占有物理页,读写物理页时,先将物理页的物理地址转变为kernel虚地址再读写。所以占有的是物理页的虚拟地址空间。
vmo的koid为0时,表示vmo没有被user引用,只有kernel指向此vmo。从代码看,在vmo创建时,koid默认值是0;后期在创建VmObjectDispatcher时,将VmObjectDispatcher的koid赋值给vmo的koid。
四者的关系
搬用memory中的例子,四者的topology如下:
$ vmaps 2470
/A ________01000000-00007ffffffff000 128.0T:sz 'proc:2470'
/R ________01000000-00007ffffffff000 128.0T:sz 'root'
R 00000187bc867000-00000187bc881000 104k:sz 'useralloc'
M 00000187bc867000-00000187bc87d000 r-x 88k:sz 0B:res 2535:vmo 'libmxio.so'
M 00000187bc87e000-00000187bc87f000 r-- 4k:sz 4k:res 2537:vmo 'libmxio.so'
M 00000187bc87f000-00000187bc881000 rw- 8k:sz 8k:res 2537:vmo 'libmxio.so'
...
M 0000246812b91000-0000246812d91000 rw- 2M:sz 76k:res 2542:vmo 'mmap-anonymous'
...
R 0000358923d92000-0000358923dd3000 260k:sz 'useralloc'
M 0000358923d93000-0000358923dd3000 rw- 256k:sz 16k:res 2538:vmo ''
...
"/A" -- Process address space
"/R" -- Root VMAR
"R" -- VMAR (R for Region)
"M" -- Mapping