前言
持續(xù)提升虛擬機(jī)性能是所有云計(jì)算工程師研究的方向,其中一個(gè)重要方式便是使用大頁(yè)內(nèi)存。本文從內(nèi)存映射出發(fā)為讀者淺析大頁(yè)內(nèi)存的相關(guān)概念和基礎(chǔ)操作,并以此為延伸,與各位分享華云產(chǎn)品技術(shù)中心計(jì)算團(tuán)隊(duì)在大頁(yè)內(nèi)存動(dòng)態(tài)分配方面的探索與實(shí)踐。
MMU 與內(nèi)存映射
在計(jì)算機(jī)系統(tǒng)結(jié)構(gòu)中,我們知道,CPU是通過(guò)尋址來(lái)訪問內(nèi)存的。例如,32 位 CPU 的尋址寬度是 0~0xFFFFFFFF ,通過(guò)換算可得為 4G,即可支持的物理內(nèi)存最大是 4G。
然而在實(shí)踐過(guò)程中會(huì)產(chǎn)生各種各樣的問題,若某個(gè)程序需要使用 4G 內(nèi)存,而此時(shí)可用的物理內(nèi)存小于 4G,這就導(dǎo)致程序不得不降低內(nèi)存的占用。因此為了解決此類問題,現(xiàn)代 CPU 引入了 MMU(Memory Management Unit 內(nèi)存管理單元)。MMU 的核心思想是利用虛擬地址替代物理地址,即CPU尋址時(shí)使用虛址,由 MMU 負(fù)責(zé)將虛址映射為物理地址。 MMU的引入,解決了對(duì)物理內(nèi)存的限制。
而內(nèi)存映射則是將虛擬內(nèi)存地址映射到物理內(nèi)存地址。為了完成內(nèi)存映射,內(nèi)核為每個(gè)進(jìn)程都維護(hù)了一張頁(yè)表,記錄虛擬地址與物理地址的映射關(guān)系。
頁(yè)表實(shí)際上存儲(chǔ)在 CPU 的內(nèi)存管理單元 MMU 中,這樣,CPU 就可以直接通過(guò)硬件,找出要訪問的內(nèi)存。
什么是 HugePages
大頁(yè)內(nèi)存(HugePages),顧名思義就是擁有更大頁(yè)面的內(nèi)存頁(yè),HugePages 提供了 4K 頁(yè)面大小的替代方案,即提供更大的頁(yè)面,在特定場(chǎng)景下對(duì)于性能有著非常明顯的提升。
HugePages 的查看與分配
在 Linux 中,可以使用如下命令查看大頁(yè)內(nèi)存
我們對(duì)其中的字段分別做一下解釋
AnonHugePages: 透明大頁(yè)的大小HugePages_Total: 大頁(yè)資源池中的大頁(yè)總量HugePages_Free: 池中未使用的大頁(yè)的數(shù)量HugePages_Rsvd: 已經(jīng)被分配預(yù)留但是還沒有使用的大頁(yè)數(shù)目HugePages_Surp: 池中大于/proc/sys/vm/nr_hugepages 中值的 HugePages 數(shù)量HugePagesize: 單塊大頁(yè)的大小
通常來(lái)說(shuō),我們會(huì)使用 2M 的大頁(yè),即 HugePagesize = 2048 KB。
請(qǐng)注意,這里關(guān)于大頁(yè)所有的單位都是數(shù)量(頁(yè)),而不是大小(KB)。舉個(gè)例子,如圖所示,HugePages_Total 的值為 100,那么在這臺(tái)機(jī)器上大頁(yè)會(huì)占用多少的內(nèi)存呢?
答案是 200M,即 HugePages_Total * HugePagesize,100 * 2048KB = 200M。
那么,如何分配 HugePages 呢?
分配 HugePages 主要有兩種方法。
一是修改 /etc/grub2.cfg 文件中啟動(dòng)菜單的內(nèi)核參數(shù),這樣做的好處在于,當(dāng)系統(tǒng)啟動(dòng)時(shí),擁有連續(xù)內(nèi)存的空間(用于 HugePages 分配)的機(jī)會(huì)要大得多。
二是通過(guò) sysctl vm.nr_hugepages 的命令進(jìn)行臨時(shí)分配,這種方式勝在分配靈活,而弊端則是,當(dāng)系統(tǒng)內(nèi)存被使用到一定程度時(shí),連續(xù)內(nèi)存頁(yè)的數(shù)量會(huì)變少,即在物理內(nèi)存有剩余時(shí),卻無(wú)法分配 HugePages 。
OpenStack 社區(qū)的方案
在 OpenStack 中,libvirt 的 driver 會(huì)去檢查用戶是否在 flavor 中配置大頁(yè)的相關(guān)選項(xiàng),并直接從已分配的資源池中劃分相關(guān)資源給虛擬機(jī)。
這就帶來(lái)了一個(gè)問題,即大頁(yè)資源池需要用戶提前分配。提前分配好的大頁(yè)資源池會(huì)直接占據(jù)這部分的內(nèi)存空間,且隨著資源池內(nèi)可用大頁(yè)數(shù)量的降低,用戶在一段時(shí)間后無(wú)法繼續(xù)創(chuàng)建大頁(yè)內(nèi)存的虛擬機(jī)。
歸根結(jié)底,OpenStack 本身并沒有提供一套用于管理和維護(hù)大頁(yè)資源池的機(jī)制。
ArStack 做了哪些嘗試
這就回到了這篇分享的主題,有什么辦法能夠盡可能多地創(chuàng)建高性能的大頁(yè)虛擬機(jī),同時(shí)又避免云平臺(tái)初期大頁(yè)資源池分配時(shí),過(guò)多地占用系統(tǒng)內(nèi)存呢?
基于這一出發(fā)點(diǎn),華云計(jì)算團(tuán)隊(duì)設(shè)計(jì)了一套動(dòng)態(tài)維護(hù)大頁(yè)資源池的系統(tǒng)。
用戶可以在系統(tǒng)初始化時(shí),以一個(gè)較低的值手動(dòng)設(shè)置初始大頁(yè)資源池的大小,Nova 的某個(gè)周期性定時(shí)任務(wù)會(huì)在一定時(shí)間間隔內(nèi)檢查各個(gè)計(jì)算節(jié)點(diǎn)的大頁(yè)資源池是否滿足預(yù)設(shè)的策略。
當(dāng)初始資源池消耗殆盡時(shí),Nova 會(huì)對(duì)大頁(yè)資源池進(jìn)行自動(dòng)擴(kuò)容,以維持一個(gè)資源相對(duì)富裕的狀態(tài)。同時(shí),Nova-Scheduler 在創(chuàng)建大頁(yè)虛擬機(jī)時(shí)也會(huì)對(duì)各個(gè)節(jié)點(diǎn)的計(jì)算資源進(jìn)行檢查,若都不滿足虛擬機(jī)所需資源, Nova-Scheduler 會(huì)計(jì)算相應(yīng)的資源缺口,并嘗試進(jìn)行擴(kuò)容。
在大頁(yè)虛擬機(jī)被刪除后,相應(yīng)的大頁(yè)資源會(huì)被釋放,并流入大頁(yè)資源池。此時(shí),Nova 檢測(cè)到該計(jì)算節(jié)點(diǎn)的大頁(yè)資源池存在過(guò)多未被分配的大頁(yè)時(shí),會(huì)進(jìn)行縮容操作,以釋放被占用的物理內(nèi)存,從而減少了物理內(nèi)存占用過(guò)多的情況。
基于這套動(dòng)態(tài)分配方案,最終實(shí)現(xiàn)了性能與靈活兼得的目標(biāo)。
后記
OpenStack 中很多邏輯都是牽一發(fā)而動(dòng)全身的,在實(shí)踐過(guò)程中,Nova-Scheduler 、NUMA 以及 ArStack 的內(nèi)存預(yù)分配都針對(duì) HugePages 相關(guān)場(chǎng)景進(jìn)行了對(duì)應(yīng)的調(diào)整和優(yōu)化。
在此感謝計(jì)算團(tuán)隊(duì)所有同學(xué)的辛勤付出。未來(lái),ArStack 會(huì)繼續(xù)努力,持續(xù)為用戶提供更加優(yōu)質(zhì)的私有云產(chǎn)品。