內存是計算機中的珍貴的稀有資源,所以為了精細管理,內存管理非常復雜的,一台計算機會同時運行很多應用,為了防止這些應用程序爭搶內存,內存的管理是通過操作系統來管理的,操作系統為了方便管理內存,也為了降低應用使用內存的復雜性,引入了虛擬內存的概念( 還是那句話,解決不了的問題引入一個中間層)。
一 虛擬內存
虛擬內存,可以看成內存和磁盤的抽象,通過中斷,地址翻譯,內存,磁盤文件,內核軟件等交互,為每個應用程序均提供一個私有的大的地址空間。
在32位系統上,虛擬內存可以訪問的存儲空間為:232; 64位系統中虛擬存儲地址空間范圍不是264,而是一般為:248,以為現在還用不了這么大的空間,可以通過命令:
[root@izbp14xswj2tx6qgnz9dllz ~]# cat /proc/cpuinfo
......
address sizes : 46 bits physical, 48 bits virtual
power management:
結果中:
address sizes : 46 bits physical, 48 bits virtual
表示物理地址為:46位,虛擬地址為48位。
這么大的空間是如何划分的那:
虛擬內存采用頁為單位進行存儲管理的,典型的頁面大小為4KB或1MB,linux下可以通過getconf命令查看。頁面大小順便說一句,有些特殊的場景喜歡設置超級大頁,比如在DPDK這種高性能網絡處理庫中,常設置大頁為1GB,目的是為了減少頁表的條目,可以讓頁表完全保存的高速緩存中,提升內存分配效率。
虛擬內存畢竟是虛的,在使用的時候系統會判斷對應此虛擬內存頁是否有映射的物理內存,如果沒有對應的物理內存頁,就會發生缺頁中斷,操作系統就會給這個虛擬內存頁分配真正的物理內存,建立映射關系;如果現在物理內存也使用緊張,操作系統就會將不活躍的頁換出存到磁盤的swap空間,將物理內存頁釋放出來,以供使用;如果下次換出的頁面需要使用了,又會產生缺頁中斷,被換入到物理內存中,就這樣來回倒騰,由於應用具有局部性,所以一般情況下,應用程序只在少量頁面上工作,效率並不低。
[root@localhost ~]# cat /proc/swaps
Filename Type Size Used Priority
/dev/dm-1 partition 2097148 0 -1
[root@localhost ~]# free -h
total used free shared buff/cache available
Mem: 2.8G 155M 2.4G 9.8M 234M 2.4G
Swap: 2.0G 0B 2.0G
[root@localhost ~]# swapon -s
文件名 類型 大小 已用 權限
/dev/dm-1 partition 2097148 0 -1
二 地址翻譯
剛才說了,我們應用使用的是虛擬內存,真正使用的時候才會通過操作系統,MMU(內存管理單元)和存在內存中的頁表結合來完成虛擬地址和物理地址的翻譯工作。頁表將虛擬地址映射為物理地址,如下圖:
實際情況要更復雜,比如分了多級頁表,多級頁表可以減少內存的使用,比如在x86的32位地址上通過二級頁表完成地址的翻譯。
這些頁表是保存在內存中的,如果每次都要這么翻譯,內存訪問的性能肯定是受到一定影響的,數據都可以緩存,頁表頁同樣可以進一步緩存放在SRAM中,MMU中有關於頁表的緩存,小的緩存稱為TLB(后備緩沖器),加入TLB后,翻譯就如下圖:
好了問題來了,TLB是一個小的緩存,保存的映射頁表項畢竟是有限的。如果4GB虛擬內存空間,每個頁面大小位4K,那就有220個頁,如果需要4個字節來標識一個頁表項的數據的話那么頁表的大小就為4B*1M即4MB大小的空間。
頁表大小= 頁表項個數*頁表項大小
按照上圖,如果我們的整個頁表都可以保存到TLB中,將提升內存的訪問速度,從幾十到幾百個時鍾周期,降低到1到2個時鍾周期,頁表項的大小一般是固定不變的,因為TLB的大小受限,所以我們只能想辦法減少頁表項的個數,頁表項的個數= 總內存/一個內存頁大小
,顯然,如果我們增加內存頁的大小,比如我們設置內存頁大小為1GB,那么4GB的內存,只需要4個頁表項,很容易將整個頁表都保存在內存中,從而提升內存的訪問速度。
三 大頁內存
上面提到,我們為了提升內存訪問速度,我們對於耗費很大的應用比如Oracle等,可以采用大頁內存方式,如果程序本身使用的內存很少,采用大頁內存效果不明顯,還浪費內存。
3.1 查看是否支持
Linux系統采用hugetlbfs 的特殊文件系統來加入對2MB和1GB的大頁內存的支持,為了配置,先查看cpu是否支持:
cat /proc/cpuinfo |grep --color pse
cat /proc/cpuinfo |grep --color pdpe1gb
查看內核是否支持:
grep -i hugetlb /boot/config-4.18.0-193.el8.x86_64
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
CONFIG_CGROUP_HUGETLB=y
# 以下兩種都為y則標識支持
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
cpu的功能列表中含有pse標識支持2MB的內存大頁,含有pdpe1gb支持1GB的內存大頁。
3.2 查看大頁內存使用情況
grep Huge /proc/meminfo
AnonHugePages: 4089856 kB
ShmemHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
這里面說明,一個大頁為:2MB(Hugepagesize: 2048 kB)
總共有的內存大頁數量為:HugePages_Total: 0
NUMA架構的查看:
[root@localhost ~]# cat /sys/devices/system/node/node*/meminfo|fgrep Huge
Node 0 AnonHugePages: 153600 kB
Node 0 HugePages_Total: 0
Node 0 HugePages_Free: 0
Node 0 HugePages_Surp: 0
Node 1 AnonHugePages: 30720 kB
Node 1 HugePages_Total: 0
Node 1 HugePages_Free: 0
Node 1 HugePages_Surp: 0
可以看到這個NUMA機器上並沒有分配大頁內存:
HugePages_Total: 0
3.3 分配大頁內存
如果分配2MB的大頁內存比較簡單,可以通過命令預留:
echo 1024> /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
預留1024個2MB的大頁即預留2GB的大頁內存。
如果是NUMA架構,在每個node節點上預留:
echo 1024> /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 1024> /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
如果要分配超過1GB的大頁內存,需要在linux的啟動項中設置和掛載。 1) 安裝
yum install libhugetlbfs
2) 分配 更改啟動文件,添加:
transparent_hugepage=never default_hugepagesz=1G hugepagesz=1G hugepages=4
分配4個大頁內存,每個為1G,在centos6中是修改
/etc/grub.conf
centos7是修改/etc/grub2.cfg
文件. 3) mount 將大頁內存映射到空目錄:
mkdir /mnt/myhuge
mount -t hugetlbfs nodev /mnt/myhuge
如果要開機自動設置:
vim /etc/fstab
nodev /mnt/myhuge hugetlbfs pagesize=1GB 0 0
像DPDK等有專門的設置工具,開機的時候立刻設置防止內存不夠,大頁內存需要連續的空間。
3.4 程序使用
#這個是看資料查的,並沒有實踐過
HUGETLB_MORECORE=yes LD_PRELOAD=libhugetlbfs.so ./your_program
四 大頁優缺點
優點: 1) 大頁內存TLB miss 很少,缺頁中斷也很少,對於內存的訪問性能更好,對於占用大量內存的程序,性能提升比較明顯,可以提升達到50%左右。 2) 大頁內存的內存頁不會swap到磁盤上。
缺點: 1) 必須使用特定的方式使用,比如采用mmap映射或者通過上面方式指定。 2) 程序使用內存小,卻申請了大頁內存,會造成內存浪費,因為內存分配最小單位是頁。
五 大頁內存引起的杯具
說了半天,還沒有說到大頁內存引起了什么杯具那,是這樣的,在一台機器上,內存本來就很小,只有4GB內存,查看程序占用內存一直沒多少,當時剩余內存也很少就很奇怪。 分析下內存分配:
# cat /proc/meminfo|grep Huge
HugePages_Total: 1035
HugePages_Free: 1033
HugePages_Rsvd: 62
HugePages_Surp: 0
Hugepagesize: 2048 kB
更好的工具是用atop查看,發現大頁內存占用了了高達2GB的內存,卻沒怎么使用。
解決辦法
#禁用大頁緩存
vi /etc/sysctl.conf
vm.nr_hugepages = 0
vm.nr_hugepages_mempolicy = 0
# 生效
sysctl -p
立竿見影的效果,一下釋放了2GB的內存,這可是占系統的內存一半啊,所以大頁內存是否使用,還要斟酌,不然白白浪費了大量內存。
如果程序內存占用大,TLB的miss又很多的情況下,可以使用,具體如何查看TLB的miss多那,可以通過perf工具來進行分析:
perf record -e dTLB-loads -e faults -p pid
perf report
六 詩詞欣賞
竹里館 - - [王維]
獨坐幽篁里,彈琴復長嘯。
深林人不知,明月來相照。