Linux内存管理(TLB和HugePage)

在前文Linux进程内存管理之虚拟内存中,我们学习了虚拟内存的概念,也了解了程序刚malloc成功时,操作系统并不会立即为其分配真实物理内存,而是在访问时才延迟分配。本文中,我们就将来学习下操作系统是如何为程序分配物理内存的。

进程访问虚拟内存地址,操作系统转换找到正确的物理内存地址。两者要对应起来就必然会有一个类似map的地址对照表。而内存的最小分配单位是页(Page),故操作系统在内存中维护了一个PageTable,里面就维护了虚拟内存页和物理内存页之间的对照关系。CPU中有一个硬件组件”内存管理单元”(MMU),它负责维护上面提到的PageTable。

多级页表

现代操作系统至少都是32位的操作系统,进程使用的虚拟地址也是32位的,一个页面4K,也就是占用了低12位的偏移地址,那么进程最大可访问的虚拟内存空间是220=1048576个页面。

如果页表是个一维数组的形式来存储的,即使程序从头到尾可能只访问了最高地址空间的那个页,进程还是得在一开始维护2的20次方个表项,这种组织关系下,除了最高那个页对应的表项,所有其他表项对应的有效位都是0,白白浪费了很多空间。

因此,真实场景下Linux系统采用了多级页表的机制。以一个32位地址0x12345678为例,假设采用了2级页表,一级页表占用8位,二级页表占用12位(注意,页表内偏移地址占用最低的12位)。

可以把多级页表结果想象成一个树结构,若页表项中的有效位为0,则代表其没有子树,否则指向挂载在其下面的二级页表。而二级页表中的每一项则指向一个真实的物理内存Page页。虚拟地址0x12345678中的低地址0x678即代表其在Page页中的偏移。

这种内存组织形式下,可以节约大量的页表内存占用。如上图,由于一级页表中只有两项的有效位为1,那么也就只有这两项下挂的二级页表被创建。内存占用“sizeof(pageitem)*0xFF + 2*sizeof(pageitem)*0xFFF”。

这是一个典型的用时间换空间的案例,并且上面的例子举的例子是32位地址空间,对于64位系统来说,页表的级数会更多。一般会达到四级页表,也就意味着从虚拟地址最终找到对应的物理地址,需要经过四次内存访问,对性能有较大影响。为了解决这个问题,CPU中维护了一个页表缓存TLB。

 

页表缓存(Translation Lookaside Buffer

TLB是一块高速缓存硬件,用于缓存最近被访问到的页表项。它的访问速度非常快,由于成本的关系,其可以存储的内容非常有限。里面存储着虚拟地址和物理地址页表项的对应关系。MMU执行虚拟地址向物理地址转换时,首先会检查TLB中是否已存在这块虚拟地址的对应页表项,若存在则跳过多级页表的寻址过程,不存在就通过多级页表进行寻址对应,同时根据一定的规则刷新TLB中的缓存。

假设TLB的大小仅能存储对应512个页表项,若程序访问的内存在512个page页,即512*4KB=2048KB=2M内的,可以保证每个page页都落入TLB中的页表缓存,这样后续程序访问任一内存地址都可以跳过多级页表寻址。如果程序使用的内存量进一步增大,则势必有一部分page页无法纳入TLB中的页表缓存。通过TLB中的页表缓存直接访问到物理page,相比多级页表寻址访问到物理page,性能上要好很多。那么我们可以怎么优化让尽可能多的内存访问走到TLB方式呢。

  • 一种是硬件方式,增大TLB的空间,这种方式最为直接,但是成本太高,受限于硬件,提升有限。
  • 一种是软件方式,增大单个Page对应的内存,如果TLB只能对应512个页表项,那么能否每个page对应1G的内存呢,这样程序访问512个G的内存,也能保证都利用到TLB缓存了。

Linux已经支持了第二种软件方式,它支持HugePage,将单个Page的大小从4KB扩充到2MB甚至更大

 

HugePage大页

在Linux下通过命令“cat /proc/meminfo”查看内存使用信息,结果如下图所示。可以从中找到大页的相关信息。目前系统中定义的大页的大小Hugepagesize为2MB,总的大页数HugePages_Total为0,说明系统目前未分配任何大页内存。

我们需要通过操作系统配置手动的将部分内存分配给大页使用,这部分内存一旦分配给了大页。程序也只有使用特殊的系统调用接口才能使用到这部分大页内存,一般的程序通过普通的malloc接口是无法申请到这部分内存的。由于这个隔离性,就对我们分配多少内存给大页使用带来了一定的挑战。还有,大页内存也是无法被Swap到磁盘中的,所以其中的数据访问性能会比较稳定,这也是它的一个优势。

由于大页内存的配置和使用都需要额外的工作量,Linux操作系统又推出了一个透明大页(transparent_hugepage)机制。顾名思义,它自动为应用程序提供大页支持,不需要特殊配置。透明大页通过在后台扫描(使用khugepaged 内核线程)内存映射,尝试找到或者创建(通过移动相邻的内存)总共2MB 的连续4KB 映射,用一个大页来替换这一段内存映射。因此,从cat /proc/meminfo 查看的结果中,有AnonHugePages这一项,其占用了202MB的内存,对应101个大页。

然而,实际使用中大多推荐不启用透明大页机制(虽然高版本redhat系统默认是启用了透明大页,可以通过命令“cat /sys/kernel/mm/transparent_hugepage/enabled”查看透明大页启用情况)。原因就是这种动态调整机制可能带来较大的性能毛刺。如果存在频繁映射内存的情况,或者存在生命周期很短的进程,khugepaged 会进行大量的拆分/合并内存区域的工作,毫无意义,存活时间很短。这会引起很高的CPU 使用率,以及较长的卡顿,因为内核被迫得先把2MB 的页拆分成4KB 的页,才能执行原本在单页上效率很高的操作。

综上所述,建议关闭操作系统的透明大页机制,开启内存大页,为特定程序提供大页内存访问机制,进一步提高内存访问效率。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值