Linux运维进阶-Python,Docker,Shell

内存的三种分配方式以及hugepage

2020-08-12  本文已影响0人  My熊猫眼

在linux中经常会遇到的部分概念:

reserved memory: 这个是firmware/bios 占用的内存,以及kernel本身占用的部分内存. 这部分内存不能够被kernel 进行调度管理.
free memory: 这个表示还没有被kernel 接手管理的内存。但是这个和“可用内存”是不同的概念,kernel接手管理的内存也是有可以被回收利用的。
available memory: 这个表示 程序当前可以申请使用的内存大小,这个才表示真正的可用内存.
total memory: 这个是指”reserved memory“之外的内存,也就是受kernel 调度管理的内存。
page tables: 我们已经知道,程序运行时候访问的内存地址都是虚拟地址,而从物理地址到内存虚拟地址的映射关系,就是保存在page tables中, 那么这个mapping 关系的records 也随着增多,所以随着内存地址分配的增多,那么page tables 也随着变大,因此page tables的大小是动态变化的,比如oracle 服务器如果使用内存越来越多,那么相应的page table也会变大,从而导致page tables 占用了很大一部分内存空间. 查看page tables 占用的内存大小: grep PageTables /proc/meminfo
而另一个相似的概念”page frame“,与此有本质的不同,物理内存的最小单位是page frame, 每个物理内存页对应一个描述符,在内核的引导阶段就分配好,保存在mem_map[]数组中,这部分使用的内存是划在”reserved memory“中的。
hugepage与transparent hugepage: 内存是分页进行管理的,而虚拟地址到物理地址的映射关系是保存在page tables中的,因为随着分配内存的增大,那么page tables 也变大,从而导致内存的相当一部分资源都用于了内存的管理上,而cpu中有一个tlb, 这其实是一个cache, 缓存了 部分 pagetables的条目,从而用于提升虚拟地址到内存物理地址的寻址过程,可是随着page tables的增大,那么该tlb缓存的命中率就快速的下降.
为了解决page tables 不断增大以及 tlb命中率降低的问题,出现了Hugepage的概念。
hugepage有两种,一种是传统的hugepage, 另外一种是 transparent hugepage. 后一种其实是为了方便对传统Hugepage的复杂管理,而设置的一个抽象层,可以自动进行创建,管理以及使用传统hugepage的大多数方面, 其采用的是动态分配的方式。查看thp 的大小,通常是用:grep AnonHugePages /proc/meminfo ,而传统的hugepage, 管理起来比较麻烦,并且是 采用预分配的方式。
hugepage 是不会进行swap in /swap out的,也就是说没有swap的概念。在使用上,hugepage 和thp 通常不要混用,否则可能造成多种奇葩问题。所以一般要启用传统的huagepage, 那么就需要禁用thp, 而thp一般是系统自动enable的.
传统的hugepage的使用场景在哪里呢?
典型的是oracle服务器,通常oracle服务器会使用共享内存(SGA)来管理可以共享的一些资源,对这些资源的访问,其实是使用OS的API来访问内存资源的过程。在理论上,内存的操作都是非常快的,但是也可能出现性能问题,比如共享内存的一部分被swap到了swap 空间,也就是disk上,那么就会变慢,或者内存非常的大,那么访问的时间自然也会变长. 而使用 hugepage之后,首先是不存在swap 的问题,再次,因为page的size 变大了,所以需要记录的 虚拟内存地址到物理地址的映射条目也变少了,所以占用的page table 就变小了,而tlb 缓存的”虚拟地址到内存物理地址的mapping“ 命中率自然也就提高了,所以整体上来说,会解决一定的性能问题.

内存的分配有如下的几种方式:

  1. slab 分配:
    在linux的kernel中,也是需要动态进行内存分配的,而采用分页机制,导致内核的内存分配粒度过大。这时候就出现了slab分配,个人对slab的简单理解就是: slab由一个或者多个page组成,然后按照对象进行分类(其实就是基于 数据结构 进行分类),分类后,slab对每一个对象维护一个列表. 当申请特定类型的对象的时候,就从列表中分配相应数据结构,从而实现精确的内存分配,避免了内存碎片的产生.
    该方式分配的内存在Linux系统中会被统计: 在/proc/meminfo/{Slab,SReclaimable,SUnreclaim} 可以查看, 其中Slab的值是另外的两个值之和
  2. vmalloc:
    内核利用该方法来分配在虚拟内存中连续,但是在物理内存中可能不连续的内存,分配的单位是字节。 典型的应用是:内核对模块的实现, 因为模块可以在任何时候加载,而系统不一定有连续的内存可用,特别是系统运行了很久的情况下,这时候用该方式进行内存分配.
    该方式分配的内存在linux 系统中也会被统计:/proc/meminfo/{VmallocTotal, VmallocUsed,VmallocChunk},但是上述的值并不只是简单的vmalloc调用分配的内存,如果从物理内存分配的角度看,我们只关心vm_alloc的操作,所以需要从 /proc/vmallocinfo 中的vmallock记录中计算. 方法如下:
    grep vmalloc /proc/vmallocinfo | awk '{total+=$2};END{print total}'
  3. alloc_pags ,以page 为单位进行内存的分配.
    这种方式分配的内存在Linux系统中并不会被自动统计,所以在/proc/meminfo 中是看不到这种内存分配的大小的. 而一个典型的案例是:
    VMWare Esx 宿主机会通过guest机上的vmware_balloon module 占用guest的内存,但是因为采用的alloc_page的方式,所以看不到这一部分内存到哪里去了. Ballon driver 在VMWARE, KVM...等都是支持的,也就是为了让宿主机可以获取guest的memory, 从而避免宿主机因内存问题crash, 或者分配内存到其他的guest.

复杂的linux hugepage:

传统的hugepage,配置起来是比较复杂的,但是大致步骤如下:
A. 禁用THP, 这个是一个Kernel启动参数,必须通过grub 传递,重启后生效; 查看方法:

#通过kernel启动参数禁用THP: transparent_hugepage=never
cat /sys/kernel/mm/transparent_hugepage/enabled #如果结果为never,那么表示已经禁用了THP,如果为always, 表示THP是启用的状态; 如果启用了THP,那么可以看到/proc/meminfo 中的AnonHugePages 数值是大于0的

B. 设置默认的Hugepage size,默认大小是2MB, 如果需要调整,那么通过kernel启动参数进行调整, 如下设置适用于rhel8, 其他没有验证:

default_hugepagesz=4G

C. 设置Hugepage的数量: 这个参数通过/proc/sys/vm/nr_hugepages 来设置。
D. 验证设置生效: cat /proc/meminfo | grep Huge
E. 需要关注的注意点:
当app 申请使用hugepage 之后,那么HugePages_Rsvd 会增加,而HugePages_Free 并不会立马减少,真正使用hugepage之后,那么HugePages_Rsvd, HugePages_Free 会同时减少.
F. 如果需要禁用hugepage, 那么可以把nr_hugepages参数的值设置为0,这样huagepage 的数量为0,就表示禁用了. 需要注意的是:对THP 的设置和 传统hugepage的设置,两者之间并没有直接关系,只是如果两者都开启的话,那么可能会产生意想不到的后果

上一篇下一篇

猜你喜欢

热点阅读