简短回答:
除非您随机分配大量的最小粒度(即4096字节)的非连续区域,否则不是这样。
长回答:
有点,
在现代体系结构中,您有几个层次的虚拟内存映射(或页表,无论您喜欢哪个术语),64位体系结构中,对于48位地址空间,通常有4个层次的内存映射(Intel即将推出的扩展将添加另一个层次,允许将4096字节的页面分割成256字节的页面)。每次在一个还没有映射的区域中分配一个页面时,内核都必须分配一个新的(通常
身体上
连续的;注意重点,相对而言,这是一个相当昂贵的操作)内存块,用于保存该内存空间区域的翻译映射。我将避免使用特定的术语,只需调用它们
L0 -> L1 -> L2 -> L3
具有
L0
是表示该虚拟内存空间的根映射。这将随页面大小的粒度和不同的操作系统或体系结构(例如Linux具有超级页面)而变化。
现在,如果新的映射处于L3级别,并且有一个L3页表,那么新的映射将只涉及更改该区域中的一个条目来指示转换。如果没有L3页表,则必须分配新的L3页表,并将其输入到L2页表中。这样一直到l0页的表格。
一些简要说明:
-
每次更改映射时,通常都会有一个TLB(Translation Lookaside Buffer;MMU用于VM->物理转换的硬件缓存)无效惩罚(手动或自动)。
-
有些页面可能不需要全部4个翻译阶段,翻译级别具有特定的大小,因此超级页面通常是一个页面,例如,使用二级页面表条目将该虚拟机空间的整个块映射到物理空间(这意味着只需要3个翻译级别)。
-
各种体系结构使用不同的方法来减少TLB破坏的惩罚(即x86 U64上的PCID;事实上,一些熔毁缓解措施(如KPTI)会导致性能回归而没有它)。
-
说到崩溃,一些内存范围可能有内核或蹦床映射或异常向量。这些是操作系统保留的。在64位系统的pre-spectre/meltdown中,内核通常会在每个页表中保持自己的映射。许多ARM处理器都有一种专用的机制,称为拆分页表(TTBR0/TTBR1;转换表基寄存器0/1)。
-
上面的一个例子是linux vdso(虚拟动态共享对象),它是由内核创建的映射。与之相对应的darwin(osx/ios)是commpage(公共页)。这通常具有系统中每个进程共享的只读代码,并且具有当前时间(以降低系统调用的成本,
gettimeofday
可以从vdso中读取,也可以使用vdso蹦床来读取)。
-
当然,以上所有内容都因您所使用的体系结构、操作系统以及所使用的操作系统版本而异,因为虚拟内存管理器通常使用各种技术来确保不会出现碎片。
但是,如果您随机请求许多小的固定映射,是的,您将有效地绕过许多映射,从而导致性能问题。