c-虚拟内存映射碎片是否会导致性能问题



我有一个应用程序,在该应用程序中,我首先使用linuxmmap系统调用创建大型匿名映射(大约4MB)。

然后在进程执行过程中,为了尽快释放内存,我考虑取消映射较小的内存块,这样最初是大块的虚拟内存映射将导致碎片化。

这可能会由于虚拟内存转换表碎片而导致性能问题,还是内核使用智能策略来避免这种情况?我可以不介意虚拟内存映射的碎片化吗?

简短的回答:除非你随机分配了成吨又成吨的最小粒度(即4096字节)的非连续区域,否则不会。


长答案:类型,

在现代体系结构中,您有几个级别的虚拟内存映射(或页表,无论您喜欢哪个术语),在64位体系结构中的48位地址空间中,通常有4级内存映射(英特尔即将推出的扩展将添加另一个级别,允许将4096字节的页分割为256字节的页)。每次在还不存在映射的区域中分配页面时,内核都必须分配一个新的内存块(通常是物理上连续的;请注意,这是一个相对昂贵的操作),以保存该内存空间的该区域的转换映射。我将避免使用特定于arch的术语,只将它们称为L0 -> L1 -> L2 -> L3,其中L0是表示该虚拟内存空间的根映射。这将随着页面大小的粒度以及不同的操作系统或体系结构而变化(例如,Linux具有超级页面)。

现在,如果您的新映射是L3级别的,并且有一个L3页面表,那么新映射只需要更改该区域中的一个条目来指示转换。如果没有L3页表,则必须分配一个新的L3页表并将其输入L2页表。依此类推,直到L0页表。

几个快速提示:

  • 每次更改映射时,通常都会有TLB(Translation Lookaside Buffer;MMU用于VM->Phys翻译的硬件缓存)无效惩罚(无论是手动还是自动)
  • 有些页面可能不需要所有4个阶段的翻译,翻译级别有特定的大小,因此超级页面通常是一个页面,例如使用L2页面表条目将VM空间的整个块映射到物理空间(这意味着只需要3个级别的翻译)
  • 不同的体系结构使用不同的方法来减少TLB破坏的代价(例如x86_64上的PCID;事实上,KPTI等一些熔毁缓解措施在没有它的情况下会导致性能退化)
  • 说到崩溃,一些内存范围可能具有内核或蹦床映射或异常向量。这些由操作系统保留。在Spectre/Meltdown之前的64位系统上,内核通常将自己映射到每个页面表中。许多ARM处理器都有一种专用的机制,称为拆分页表(TTBR0/TTBR1;转换表基址寄存器0/1)
  • 上面的一个例子是Linux VDSO(虚拟动态共享对象),它是由内核创建的映射。Darwin(OSX/iOS)与之相对应的是commpage(公共页面)。这通常具有由系统中的每个进程共享的只读代码,并且具有当前时间(为了降低系统调用的成本,gettimeofday可以从VDSO读取或使用VDSO蹦床读取)
  • 当然,由于虚拟内存管理器通常使用各种技术来确保不会出现碎片,因此以上所有内容都因体系结构、您使用的操作系统以及您使用的OS版本而异但是,如果您随机请求许多小的固定映射,是的,您将有效地绕过其中的许多映射,从而导致性能问题

最新更新