Linux各层级内存管理

注:转载于https://www.cnblogs.com/jimbo17/p/8987443.html,部分内容有修改

1、NUMA(Non-Uniform Memory Access) 非一致内存访问及NODE

该模型假定给定CPU对不同内存的单元访问时间不一样。NUMA架构下,CPU平均划分为多个Node,每个Node有自己的内存控制器及内存插槽。CPU访问自己Node上的内存时速度快,而访问其他CPU所关联Node的内存的速度比较慢。

通过以下方式查看NUMA系统信息,

[root@localhost ~]# numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 24 25 26 27 28 29 30 31 32 33 34 35
node 0 size: 130539 MB
node 0 free: 11053 MB
node 1 cpus: 12 13 14 15 16 17 18 19 20 21 22 23 36 37 38 39 40 41 42 43 44 45 46 47
node 1 size: 131072 MB
node 1 free: 14420 MB
node distances:
node   0   1 
  0:  10  21 
  1:  21  10 

由上可见,系统有两个node,其中node distances即表示node间内存访问的速度级别。跨node的访问速度比本node慢两倍多。

NUMA内存分配策略:

  • default:仅在本地节点分配,即程序运行节点
  • bind:仅在指定节点上分配
  • interleavel:在所有节点交叉分配
  • preferred:先在指定节点上分配,失败则可在其他节点上分配

NUMA存在的问题:

来源:https://www.cnblogs.com/klb561/p/9053692.html
MySQL是单进程多线程架构数据库,当numa采用默认内存分配策略时,MySQL进程会被并且仅仅会被分配到numa的一个节点上去。假设这个节点的本地内存为10GB,而MySQL配置20GB内存,超出节点本地内存部分(20GB-10GB)Linux会使用swap而不是使用其他节点的物理内存。在这种情况下,能观察到虽然系统总的可用内存还未用完,但是MySQL进程已经开始在使用swap了。这就导致数据库性能变差。
如果单机只运行一个MySQL实例,可以选择关闭numa,关闭nuam有两种方法:
1.硬件层,在BIOS中设置关闭;
2.OS内核,启动时设置numa=off。

不过最新的MySQL已经从代码层面解决了该问题,
https://www.kancloud.cn/taobaomysql/monthly/67064

2、ZONE

ZONE的出现是由于硬件约束:

  1. ISA总线的DMA只能范围内存的前16M
  2. X86-32系统由于线性地址空间太少,无法访问所有物理内存(2^32=4GB),需要借助HIGHMEM机制

因此有以下ZONE,

  1. ZONE_DMA,地址最低的16M内存,归DMA设备使用
  2. ZONE_DMA32,该Zone用于支持32-bits地址总线的DMA设备,只在64-bits系统里才有效
  3. ZONE_NORMAL,该Zone的内存被内核直接映射为线性地址并可以直接使用。在X86-32架构下,该Zone对应的地址范围为16MB~896MB。在X86-64架构下,DMA和DMA32之外的内存全部在NORMAL的Zone里管理
  4. ZONE_HIGHMEM,该Zone只在32位系统才有,通过建立临时页表的方式映射超过896MB的内存空间

查看系统当前各个node的ZONE管理区

[root@localhost ~]# cat /proc/zoneinfo |grep -E "zone| free|managed"
Node 0, zone      DMA
  pages free     3973
        managed  3975
Node 0, zone    DMA32
  pages free     127782
        managed  314664
Node 0, zone   Normal
  pages free     1710485
        managed  32506279
Node 1, zone   Normal
  pages free     2688883
        managed  33028971

可见,Node0上有DMA、DMA32和Normal三个Zone,Node1上只有一个Normal Zone。

3、Page(页)

Page是Linux底层内存管理的基本单位,大小为4KB。一个Page映射为一段连续的物理内存,内存的分配和释放都要以Page为单位进行。进程虚拟地址到物理地址的映射也是通过Page Table页表进行,页表的每一项记录一个Page的虚拟地址对应的物理地址。

4、TLB(转换检测缓冲区)——硬件级缓存

内存访问时需要查找地址对应的Page结构,这个数据记录在页表里。所有对内存地址的访问都要先查询页表,因此页表的访问次数是频率最高的。为了提高对页表的访问速度,引入了TLB(Translation Lookaside Buffer)机制,将访问较多页表缓存在CPU的cache里。因此CPU的性能统计里很重要的一项就是L1/L2 cache的TLB miss统计项。在内存较大的系统里,如256GB内存全量的页表项有256GB/4KB=67108864条,每个条目占用16字节的话,需要1GB,显然是CPU cache无法全量缓存的。这时候如果访问的内存范围较广很容易出现TLB miss导致访问延时的增加。

5、Hugepages(大页)

为了降低TLB miss的概率,Linux引入了Hugepages机制,可以设定Page大小为2MB或者1GB。2MB的Hugepages机制下,同样256GB内存需要的页表项降低为256GB/2MB=131072,仅需要2MB。因此Hugepages的页表可以全量缓存在CPU cache中。 通过sysctl -w vm.nr_hugepages=1024可以设置hugepages的个数为1024,总大小为4GB。需要注意是,设置huagepages会从系统申请连续2MB的内存块并进行保留(不能用于正常内存申请),如果系统运行一段时间导致内存碎片较多时,再申请hugepages会失败。
如下所示为hugepages的设置和mount方法,mount之后应用程序需要在mount路径下通过mmap进行文件映射来使用这些hugepages。

sysctl -w vm.nr_hugepages=1024
mkdir -p /mnt/hugepages
mount -t hugetlbfs hugetlbfs /mnt/hugepages

6、Buddy System(伙伴系统)

Linux Buddy System是为了解决以Page为单位的内存分配导致外内存碎片问题:即系统缺少连续的Page页导致需要连续Page页的内存申请无法得到满足。原理很简单,将不同个数的连续Pages组合成Block进行分配,Block按2的幂次方个Pages划分为11个Block链表,分别对应1,2,4,8,16,32,64,128,256,512和1024个连续的Pages。调用Buddy System进行内存分配时,根据申请的大小找最合适的Block。

如下所示为各个Zone上的Buddy System基本信息,后面11列为11个Block链表里可用的Block个数。

[root@localhost ~]# cat /proc/buddyinfo 
Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3 
Node 0, zone    DMA32    769   3912   1663    368    949    339    249    139    195      0      0 
Node 0, zone   Normal  46347  63606  19435 267024  28762    522      5      0      0      0      0 
Node 1, zone   Normal  47237  62462 144525 345185  21462    375     23      3      0      0      0 

7、Slab

Buddy System的内存都是大块申请,但是大多数应用需要的内存都很小,比如常见的几百个Bytes的数据结构,如果也申请一个Page,将会非常浪费。为了满足小而不规则的内存分配需求,Linux设计了Slab分配器。原理简单说就是为特定的数据结构建立memcache,从Buddy System里申请Pages,将每个Page按数据结构的大小划分为多个Objects,使用者从memcache里申请数据结构时分配一个Object。

可以通过/proc/slabinfo查看系统slab信息,

[root@localhost ~]# cat /proc/slabinfo |head 
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
wstcplog_time          0      0   3392    1    1 : tunables    0    0    0 : slabdata      0      0      0
wstcplog_req           0      0   3264    1    1 : tunables    0    0    0 : slabdata      0      0      0
wstcplog_fmt           0      0   1280    3    1 : tunables    0    0    0 : slabdata      0      0      0
ws_get_snd_item   391514 396646   1472    2    1 : tunables    0    0    0 : slabdata 198323 198323      0
wip_recver             0      0   2176    1    1 : tunables    0    0    0 : slabdata      0      0      0
wip_sender             0      0    320   12    1 : tunables    0    0    0 : slabdata      0      0      0
wip_sock               0      0    832    4    1 : tunables    0    0    0 : slabdata      0      0      0
ext4_groupinfo_4k  60937  61080    136   30    1 : tunables    0    0    0 : slabdata   2036   2036      0

也可通过slabtop查看排序后的slab信息,

[root@localhost ~]# slabtop -s c -o | head -n15
 Active / Total Objects (% used)    : 93008521 / 111244867 (83.6%)
 Active / Total Slabs (% used)      : 8164760 / 8164760 (100.0%)
 Active / Total Caches (% used)     : 72 / 104 (69.2%)
 Active / Total Size (% used)       : 24807655.46K / 27493165.03K (90.2%)
 Minimum / Average / Maximum Object : 0.01K / 0.25K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
14600118 14596164  99%    1.01K 4866706        3  19466824K ext4_inode_cache       
27971412 16389388  58%    0.19K 1331972       21   5327888K dentry                 
34800831 31395601  90%    0.10K 892329       39   3569316K buffer_head            
3002482 2996544  99%    0.57K 428926        7   1715704K radix_tree_node        
21322182 18353309  86%    0.04K 209041      102    836164K ext4_extent_status     
387656 384687  99%    1.44K 193828        2    775312K ws_get_snd_item        
6908715 6908715 100%    0.05K  81279       85    325116K shared_policy_node     
290706 280743  96%    0.64K  48451        6    193804K proc_inode_cache

8、kmalloc

和glibc的malloc()一样,内核也提供kmalloc()用于分配任意大小的内存空间。同样,如果放任应用程序随意从Page里申请任意大小的内存也会导致Page内存碎片化。为了解决内部碎片问题,Linux使用Slab机制来实现kmalloc内存分配。原理和Buddy System类似,即创建2的幂次方的Slab池用于kmalloc根据大小适配最佳的Slab进行分配。

如下所示为用于kmalloc分配的Slabs:

[root@localhost ~]# cat /proc/slabinfo | grep kmalloc | grep -v dma
kmalloc-8192         236    245   8192    1    2 : tunables    0    0    0 : slabdata    245    245      0
kmalloc-4096        3619   3627   4096    1    1 : tunables    0    0    0 : slabdata   3627   3627      0
kmalloc-2048        7030   7060   2048    2    1 : tunables    0    0    0 : slabdata   3530   3530      0
kmalloc-1024       12560  12768   1024    4    1 : tunables    0    0    0 : slabdata   3192   3192      0
kmalloc-512        13922  15088    512    8    1 : tunables    0    0    0 : slabdata   1886   1886      0
kmalloc-256       111151 135504    256   16    1 : tunables    0    0    0 : slabdata   8469   8469      0
kmalloc-192       126473 134337    192   21    1 : tunables    0    0    0 : slabdata   6397   6397      0
kmalloc-128        47745  56160    128   32    1 : tunables    0    0    0 : slabdata   1755   1755      0
kmalloc-96         56495  56742     96   42    1 : tunables    0    0    0 : slabdata   1351   1351      0
kmalloc-64        296432 301440     64   64    1 : tunables    0    0    0 : slabdata   4710   4710      0
kmalloc-32         93781  94976     32  128    1 : tunables    0    0    0 : slabdata    742    742      0
kmalloc-16         68352  68352     16  256    1 : tunables    0    0    0 : slabdata    267    267      0
kmalloc-8          37376  37376      8  512    1 : tunables    0    0    0 : slabdata     73     73      0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章