虚拟内存映射碎片会导致性能问题吗?

时间:2019-01-24 09:03:37

标签: c linux memory-management linux-kernel virtual-memory

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

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

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

1 个答案:

答案 0 :(得分:2)

简短的回答:除非您要随机分配大量的最小粒度(即4096字节)的非连续区域,否则不会这样。


长答案:种,

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

现在,如果您的新映射处于L3级别,并且具有L3页面表,则新映射将只涉及更改该区域中的条目以指示翻译。如果没有L3页表,则必须分配一个新的L3页表,并将其输入到L2页表中。依此类推,直到L0页面表。

一些注意事项:

  • 每次更改映射时,通常都会有一个TLB(转换后备缓冲区; 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蹦床来读取它)。
  • 当然,以上所有内容均会根据所使用的体系结构和操作系统以及所使用的操作系统版本而有所不同,因为虚拟内存管理器通常会使用多种技术来确保不会发生碎片。 但是,如果您随机请求许多小的固定映射,是的,您将有效地绕过很多固定映射,从而导致性能问题。