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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章