DPDK是一個技術(shù)棧,主要用于Intel架構(gòu)的服務(wù)器領(lǐng)域,其主要目的就是提升x86標準服務(wù)器的轉(zhuǎn)發(fā)性能。因此,本文只重點介紹DPDK平臺部分技術(shù)在電信云中的最佳實踐。
1、為什么需要DPDK?
在IA上,網(wǎng)絡(luò)數(shù)據(jù)包處理遠早于DPDK而存在。從商業(yè)版的Windows到開源的Linux操作系統(tǒng),所有跨主機通信幾乎都會涉及網(wǎng)絡(luò)協(xié)議棧以及底層網(wǎng)卡驅(qū)動對于數(shù)據(jù)包的處理。然而,低速網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)發(fā)與高速網(wǎng)絡(luò)數(shù)據(jù)轉(zhuǎn)發(fā)的處理對系統(tǒng)的要求完全不一樣。以Linux為例,傳統(tǒng)網(wǎng)絡(luò)設(shè)備驅(qū)動包處理的動作可以概括如下:
數(shù)據(jù)包到達網(wǎng)卡設(shè)備。
網(wǎng)卡設(shè)備依據(jù)配置進行DMA操作。
網(wǎng)卡發(fā)送中斷,喚醒處理器。
驅(qū)動軟件填充讀寫緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)。
數(shù)據(jù)報文達到內(nèi)核協(xié)議棧,進行高層處理。
如果最終應(yīng)用在用戶態(tài),數(shù)據(jù)從內(nèi)核搬移到用戶態(tài)。
如果最終應(yīng)用在內(nèi)核態(tài),在內(nèi)核繼續(xù)進行。
隨著網(wǎng)絡(luò)接口帶寬從千兆向萬兆邁進,原先每個報文就會觸發(fā)一個中斷,中斷帶來的開銷變得突出,大量數(shù)據(jù)到來會觸發(fā)頻繁的中斷開銷,導(dǎo)致系統(tǒng)無法承受。
在網(wǎng)絡(luò)包高性能轉(zhuǎn)發(fā)技術(shù)領(lǐng)域,有兩個著名的技術(shù)框架NAPI和Netmap。NAPI策略用于高吞吐的場景,其策略是系統(tǒng)被中斷喚醒后,盡量使用輪詢的方式一次處理多個數(shù)據(jù)包,直到網(wǎng)絡(luò)再次空閑重新轉(zhuǎn)入中斷等待,其目的就是解決數(shù)據(jù)包在轉(zhuǎn)發(fā)過程過程中頻繁中斷引入的大量系統(tǒng)開銷。Netmap就是采用共享數(shù)據(jù)包池的方式,減少內(nèi)核到用戶空間的包復(fù)制,從而解決大多數(shù)場景下需要把包從內(nèi)核的緩沖區(qū)復(fù)制到用戶緩沖區(qū)引入大量系統(tǒng)開銷問題。
NAPI與Netmap兩方面的努力其實已經(jīng)明顯改善了傳統(tǒng)Linux系統(tǒng)上的包處理能力,但是,Linux作為分時操作系統(tǒng),要將CPU的執(zhí)行時間合理地調(diào)度給需要運行的任務(wù)。相對于公平分時,不可避免的就是適時調(diào)度。早些年CPU核數(shù)比較少,為了每個任務(wù)都得到響應(yīng)處理,進行充分分時,用效率換響應(yīng),是一個理想的策略。現(xiàn)今CPU核數(shù)越來越多,性能越來越強,為了追求極端的高性能高效率,分時就不一定總是上佳的策略。以Netmap為例,即便其減少了內(nèi)核到用戶空間的內(nèi)存復(fù)制,但內(nèi)核驅(qū)動的收發(fā)包處理和用戶態(tài)線程依舊由操作系統(tǒng)調(diào)度執(zhí)行,除去任務(wù)切換本身的開銷,由切換導(dǎo)致的后續(xù)cache替換(不同任務(wù)內(nèi)存熱點不同),對性能也會產(chǎn)生負面的影響。為此,Intel針對IA架構(gòu)的這些問題,就提出了DPDK技術(shù)棧的架構(gòu),其根本目的就是盡量采用用戶態(tài)驅(qū)動能力來替代內(nèi)核態(tài)驅(qū)動,從而減少內(nèi)核態(tài)的開銷,提升轉(zhuǎn)發(fā)性能。
2、鳥瞰DPDK
什么是DPDK?在《DPDK深入淺出》一書中,有以下一段描述:
針對不同的對象,其定義并不相同。對于普通用戶來說,它可能是一個性能出色的包數(shù)據(jù)處理加速軟件庫;對于開發(fā)者來說,它可能是一個實踐包處理新想法的創(chuàng)新工場;對于性能調(diào)優(yōu)者來說,它可能又是一個絕佳的成果分享平臺。當下火熱的網(wǎng)絡(luò)功能虛擬化,則將DPDK放在一個重要的基石位置。
DPDK最初的動機很簡單,就是為了證明IA多核處理器能夠支撐高性能數(shù)據(jù)包處理。隨著早期目標的達成和更多通用處理器體系的加入,DPDK逐漸成為通用多核處理器高性能數(shù)據(jù)包處理的業(yè)界標桿。
目前,DPDK技術(shù)主要應(yīng)用于計算領(lǐng)域的硬件加速器、通信領(lǐng)域的網(wǎng)絡(luò)處理器和IT領(lǐng)域的多核處理器。隨著軟件(例如,DPDK)在I/O性能提升上的不斷創(chuàng)新,將多核處理器的競爭力提升到一個前所未有的高度。在SDN/NFV領(lǐng)域,DPDK技術(shù)得到了空前應(yīng)用,產(chǎn)生了不少最佳實踐案例。
DPDK提出的目的就是為IA上的高速包處理。下圖所示的DPDK主要模塊分解展示了以基礎(chǔ)軟件庫的形式,為上層應(yīng)用的開發(fā)提供一個高性能的基礎(chǔ)I/O開發(fā)包。主要利用了有助于包處理的軟硬件特性,如大頁、緩存行對齊、線程綁定、預(yù)取、NUMA、IA最新指令的利用、Intel DDIO、內(nèi)存交叉訪問等。
核心庫Core Libs,提供系統(tǒng)抽象、大頁內(nèi)存、緩存池、定時器及無鎖環(huán)等基礎(chǔ)組件。
PMD庫,提供全用戶態(tài)的驅(qū)動,以便通過輪詢和線程綁定得到極高的網(wǎng)絡(luò)吞吐,支持各種本地和虛擬的網(wǎng)卡。
Classify庫,支持精確匹配(Exact Match)、最長匹配(LPM)和通配符匹配(ACL),提供常用包處理的查表操作。
QoS庫,提供網(wǎng)絡(luò)服務(wù)質(zhì)量相關(guān)組件,如限速(Meter)和調(diào)度(Sched)。
除了這些組件,DPDK 還提供了幾個平臺特性,比如節(jié)能考慮的運行時頻率調(diào)整(POWER),與Linux kernel stack建立快速通道的 KNI(Kernel Network Interface)。而Packet Framework和DISTRIB為搭建更復(fù)雜的多核流水線處理模型提供了基礎(chǔ)的組件。
DPDK軟件包內(nèi)有一個最基本的三層轉(zhuǎn)發(fā)實例(l3fwd),可用于測試雙路服務(wù)器整系統(tǒng)的吞吐能力,通過現(xiàn)場實驗,可以達到220Gbit/s的數(shù)據(jù)報文吞吐能力。除了通過硬件或者軟件提升性能之外,如今DPDK整系統(tǒng)報文吞吐能力上限已經(jīng)不再受限于CPU的核數(shù),當前瓶頸在于PCIe(IO總線)的LANE數(shù)。換句話說,系統(tǒng)性能的整體I/O天花板不再是CPU,而是系統(tǒng)所提供的所有PCIe LANE的帶寬,也就是能插入多少個高速以太網(wǎng)接口卡。
在這樣的性能基礎(chǔ)上,網(wǎng)絡(luò)節(jié)點的軟化(NFV)就成為可能。對于網(wǎng)絡(luò)節(jié)點上運轉(zhuǎn)的不同形態(tài)的網(wǎng)絡(luò)功能,通過軟化并適配到一個通用的硬件平臺,就是軟硬件解耦。解耦正是NFV的一個核心思想,而硬件解耦的多個網(wǎng)絡(luò)功能在單一通用節(jié)點上的隔離共生問題,就是另一個核心思想---虛擬化。
3、電信云中數(shù)據(jù)包轉(zhuǎn)發(fā)性能提升中的DPDK
cache的作用
在當今服務(wù)器領(lǐng)域,一個處理器通常包含多個核心(Core),集成Cache子系統(tǒng),內(nèi)存子系統(tǒng)通過內(nèi)部或外部總線與其通信。在經(jīng)典計算機系統(tǒng)中一般都有兩個標準化的部分:北橋(North Bridge)和南橋(SouthBridge)。它們是處理器和內(nèi)存以及其他外設(shè)溝通的渠道。在這類系統(tǒng)中,北橋就是真?zhèn)€架構(gòu)的瓶頸,一旦北橋處理不過來或故障,整個系統(tǒng)的處理效率就會變低或癱瘓。因此,后來計算機系統(tǒng)中只存在南橋芯片,而北橋部分就被全部移植到CPU的SoC中,其中最重要的部分就是內(nèi)存控制器,并在此基礎(chǔ)上進一步衍生出NUMA和MPP架構(gòu),這個放在后面會講。
我們在本科學習計算機基礎(chǔ)課程時,都知道計算機的內(nèi)存分為SRAM、DRAM、SDRAM和DDR(1/2/3/4)等不同類型。在早期的PC系統(tǒng)中,主要使用DRAM和SDRAM來作為內(nèi)存,相比SRAM在成本、功耗方面有不小的優(yōu)勢,而且速度也還可以。后來在現(xiàn)今的PC系統(tǒng)中,利用SDRAM在一個時鐘周期的上下邊沿進行數(shù)據(jù)讀寫,整體數(shù)據(jù)吞吐率帶寬翻倍,也就是DDR RAM,DDR根據(jù)不同的主頻,又分為DDR1/DDR2/DDR3/DDR4。而SRAM,由于其功耗高、成本高,速度很快,一般都作為CPU的cache使用,目前都被封裝的CPU的SoC中。
一般來說,Cache由三級組成,之所以對Cache進行分級,也是從成本和生產(chǎn)工藝的角度考慮的。一級(L1)最快,但是容量最??;三級(LLC,Last Level Cache)最慢,但是容量最大。
一級Cache:一般分為數(shù)據(jù)Cache和指令Cache,數(shù)據(jù)Cache用來存儲數(shù)據(jù),而指令Cache用于存放指令。這種Cache速度最快,一般處理器只需要3~5個指令周期就能訪問到數(shù)據(jù),因此成本高,容量小,一般都只有幾十KB。
二級Cache:和一級Cache分為數(shù)據(jù)Cache和指令Cache不同,數(shù)據(jù)和指令都無差別地存放在一起。速度相比一級Cache慢一些,處理器大約需要十幾個處理器周期才能訪問到數(shù)據(jù),容量也相對來說大一些,一般有幾百KB到幾MB不等。
三級Cache:速度更慢,處理器需要幾十個處理器周期才能訪問到數(shù)據(jù),容量更大,一般都有幾MB到幾十個MB。在多核處理器內(nèi)部,三級Cache由所有的核心所共有。這樣的共享方式,其實也帶來一個問題,有的處理器可能會極大地占用三級Cache,導(dǎo)致其他處理器只能占用極小的容量,從而導(dǎo)致Cache不命中,性能下降。因此,Intel公司推出了Intel® CAT技術(shù),確保有一個公平,或者說軟件可配置的算法來控制每個核心可以用到的Cache大小。
為了將cache與內(nèi)存進行關(guān)聯(lián),需要對cache和內(nèi)存進行分塊,并采用一定的映射算法進行關(guān)聯(lián)。分塊就是將Cache和內(nèi)存以塊為單位進行數(shù)據(jù)交換,塊的大小通常以在內(nèi)存的一個存儲周期中能夠訪問到的數(shù)據(jù)長度為限。當今主流塊的大小都是64字節(jié),因此一個Cache line就是指64個字節(jié)大小的數(shù)據(jù)塊。而映射算法是指把內(nèi)存地址空間映射到Cache地址空間。具體來說,就是把存放在內(nèi)存中的內(nèi)容按照一定規(guī)則裝入到Cache中,并建立內(nèi)存地址與Cache地址之間的對應(yīng)關(guān)系。當CPU需要訪問這個數(shù)據(jù)塊內(nèi)容時,只需要把內(nèi)存地址轉(zhuǎn)換成Cache地址,從而在Cache中找到該數(shù)據(jù)塊,最終返回給CPU。
根據(jù)Cache和內(nèi)存之間的映射關(guān)系的不同,Cache可以分為三類:第一類是全關(guān)聯(lián)型Cache(full associative cache),第二類是直接關(guān)聯(lián)型Cache(direct mapped cache),第三類是組關(guān)聯(lián)型Cache(N-ways associative cache)。
全關(guān)聯(lián)型cache:需要在cache中建立一個目錄表,目錄表的每一項由內(nèi)存地址、cache塊號和一個有效位組成。當CPU需要訪問某個內(nèi)存地址時,首先查詢該目錄表判斷該內(nèi)容是否緩存在Cache中,如果在,就直接從cache中讀取內(nèi)容;如果不在,就去通過內(nèi)存地址轉(zhuǎn)換去內(nèi)存沖讀取。具體原理如下:
首先,用內(nèi)存的塊地址A在Cache的目錄表中進行查詢,如果找到等值的內(nèi)存塊地址,檢查有效位是否有效,只有有效的情況下,才能通過Cache塊號在Cache中找到緩存的內(nèi)存,并且加上塊內(nèi)地址B,找到相應(yīng)數(shù)據(jù),這時則稱為Cache命中,處理器拿到數(shù)據(jù)返回;否則稱為不命中,CPU則需要在內(nèi)存中讀取相應(yīng)的數(shù)據(jù)。使用全關(guān)聯(lián)型Cache,塊的沖突最小(沒有沖突),Cache的利用率也高,但是需要一個訪問速度很快的相聯(lián)存儲器。隨著Cache容量的增加,其電路設(shè)計變得十分復(fù)雜,因此一般只有TLB cache才會設(shè)計成全關(guān)聯(lián)型。
直接關(guān)聯(lián)型Cache:是指將某一塊內(nèi)存映射到Cache的一個特定的塊,即Cache line中。假設(shè)一個Cache中總共存在N個Cache line,那么內(nèi)存就被分成N等分,其中每一等分對應(yīng)一個Cache line。比如:Cache的大小是2K,而一個Cache line的大小是64B,那么就一共有2K/64B=32個Cache line,那么對應(yīng)我們的內(nèi)存,第1塊(地址0~63),第33塊(地址64*32~64*33-1),以及第(N*32+1)塊都被映射到Cache第一塊中;同理,第2塊,第34塊,以及第(N*32+2)塊都被映射到Cache第二塊中;可以依次類推其他內(nèi)存塊。直接關(guān)聯(lián)型Cache的目錄表只有兩部分組成:區(qū)號和有效位。具體原理如下:
首先,內(nèi)存地址被分成三部分:區(qū)號A、塊號B和塊內(nèi)地址C。根據(jù)區(qū)號A在目錄表中找到完全相等的區(qū)號,并且在有效位有效的情況下,說明該數(shù)據(jù)在Cache中,然后通過內(nèi)存地址的塊號B獲得在Cache中的塊地址,加上塊內(nèi)地址C,最終找到數(shù)據(jù)。如果在目錄表中找不到相等的區(qū)號,或者有效位無效的情況下,則說明該內(nèi)容不在Cache中,需要到內(nèi)存中讀取??梢钥闯?,直接關(guān)聯(lián)是一種很“死”的映射方法,當映射到同一個Cache塊的多個內(nèi)存塊同時需要緩存在Cache中時,只有一個內(nèi)存塊能夠緩存,其他塊需要被“淘汰”掉。因此,直接關(guān)聯(lián)型命中率是最低的,但是其實現(xiàn)方式最為簡單,匹配速度也最快。
組關(guān)聯(lián)型Cache:是目前Cache中用的比較廣泛的一種方式,是前兩種Cache的折中形式。在這種方式下,內(nèi)存被分為很多組,一個組的大小為多個Cache line的大小,一個組映射到對應(yīng)的多個連續(xù)的Cache line,也就是一個Cache組,并且該組內(nèi)的任意一塊可以映射到對應(yīng)Cache組的任意一個??梢钥闯?,在組外,其采用直接關(guān)聯(lián)型Cache的映射方式,而在組內(nèi),則采用全關(guān)聯(lián)型Cache的映射方式。比如:有一個4路組關(guān)聯(lián)型Cache,其大小為1M,一個Cache line的大小為64B,那么總共有16K個Cache line,但是在4路組關(guān)聯(lián)的情況下,就擁有了4K個組,每個組有4個Cache line。一個內(nèi)存單元可以緩存到它所對應(yīng)的組中的任意一個Cache line中去。具體原理如下:
目錄表由三部分組成:“區(qū)號+塊號”、Cache塊號和有效位。一個內(nèi)存地址被分成四部分:區(qū)號A、組號B、塊號C和塊內(nèi)地址D。首先,根據(jù)組號B按地址查找到一組目錄表項;然后,根據(jù)區(qū)號A和塊號C在該組中進行關(guān)聯(lián)查找(即并行查找,為了提高效率),如果匹配且有效位有效,則表明該數(shù)據(jù)塊緩存在Cache中,得到Cache塊號,加上塊內(nèi)地址D,可以得到該內(nèi)存地址在Cache中映射的地址,得到數(shù)據(jù);如果沒有找到匹配項或者有效位無效,則表示該內(nèi)存塊不在Cache中,需要處理器到內(nèi)存中讀取。
Cache之所以能夠提高系統(tǒng)性能,主要是因為程序執(zhí)行存在局部性現(xiàn)象,即時間局部性(程序中指令和數(shù)據(jù)在時間上的關(guān)聯(lián)性,比如:循環(huán)體中的變量和指令)和空間局部性(程序中指令和數(shù)據(jù)在空間上的關(guān)聯(lián)性,比如:列表數(shù)據(jù)結(jié)構(gòu)中的元素)。cache就可以根據(jù)程序的局部性特點,以及當前執(zhí)行狀態(tài)、歷史執(zhí)行過程、軟件提示等信息,然后以一定的合理方法,在數(shù)據(jù)/指令被使用前取入Cache,也就是cache預(yù)取。
內(nèi)存的數(shù)據(jù)被加載進cache后,最終還是需要寫回到內(nèi)存中,這個寫回的過程存在兩種策略:
直寫(write-through):在CPU對Cache寫入的同時,將數(shù)據(jù)寫入到內(nèi)存中。這種策略保證了在任何時刻,內(nèi)存的數(shù)據(jù)和Cache中的數(shù)據(jù)都是同步的,這種方式簡單、可靠。但由于CPU每次對Cache更新時都要對內(nèi)存進行寫操作,總線工作繁忙,內(nèi)存的帶寬被大大占用,因此運行速度會受到影響。
回寫(write-back):回寫相對于直寫而言是一種高效的方法?;貙懴到y(tǒng)通過將Cache line的標志位字段添加一個Dirty標志位,當處理器在改寫了某個Cache line后,并不是馬上把其寫回內(nèi)存,而是將該Cache line的Dirty標志設(shè)置為1。當處理器再次修改該Cache line并且寫回到Cache中,查表發(fā)現(xiàn)該Dirty位已經(jīng)為1,則先將Cache line內(nèi)容寫回到內(nèi)存中相應(yīng)的位置,再將新數(shù)據(jù)寫到Cache中。
除了上述這兩種寫策略,還有WC(write-combining)和UC(uncacheable)。這兩種策略都是針對特殊的地址空間來使用的,這里不做詳細討論,有興趣的可以參考Intel官方社區(qū)。
在采用回寫策略的架構(gòu)中,如果多個CPU同時對一個cache line進行修改后的寫回操作,就存在“臟”數(shù)據(jù)區(qū)域的問題,這就是cache一致性問題。其本質(zhì)原因是存在多個處理器獨占的Cache,而不是多個處理器。解決Cache一致性問題的機制有兩種:基于目錄的協(xié)議(Directory-based protocol)和總線窺探協(xié)議(Bus snooping protocol)。這里因為篇幅問題,不再展開討論,有興趣的可參見《深入淺出DPDK》一書相關(guān)內(nèi)容。
事實上,Cache對于絕大多數(shù)程序員來說都是透明不可見的,cache完成數(shù)據(jù)緩存的所有操作都是硬件自動完成的。但是,硬件也不是完全智能的。因此,Intel體系架構(gòu)引入了能夠?qū)ache進行預(yù)取的指令,使一些對程序執(zhí)行效率有很高要求的程序員能夠一定程度上控制Cache,加快程序的執(zhí)行。DPDK對cache進行預(yù)取操作如下:
while (nb_rx < nb_pkts)
{
rxdp = &rx_ring[rx_id];
//讀取接收描述符
staterr = rxdp->wb.upper.status_error;
//檢查是否有報文收到
if (!(staterr & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD)))
break;
rxd = *rxdp;
//分配數(shù)據(jù)緩沖區(qū)
nmb = rte_rxmbuf_alloc(rxq->mb_pool);
nb_hold++;
//讀取控制結(jié)構(gòu)體
rxe = &sw_ring[rx_id];
……
rx_id++;
if (rx_id == rxq->nb_rx_desc) rx_id = 0;
//預(yù)取下一個控制結(jié)構(gòu)體mbuf rte_ixgbe_prefetch(sw_ring[rx_id].mbuf);
//預(yù)取接收描述符和控制結(jié)構(gòu)體指針 if ((rx_id & 0x3) == 0) { rte_ixgbe_prefetch(&rx_ring[rx_id]); rte_ixgbe_prefetch(&sw_ring[rx_id]); }
……
//預(yù)取報文 rte_packet_prefetch((char *)rxm->buf_addr + rxm->data_off);
//把接收描述符讀取的信息存儲在控制結(jié)構(gòu)體mbuf中
rxm->nb_segs = 1;
rxm->next = NULL;
rxm->pkt_len = pkt_len;
rxm->data_len = pkt_len;
rxm->port = rxq->port_id;
……
rx_pkts[nb_rx++] = rxm;
}
同時,DPDK在定義數(shù)據(jù)結(jié)構(gòu)或者數(shù)據(jù)緩沖區(qū)時就申明cache line對齊,代碼如下:
#define RTE_CACHE_LINE_SIZE 64
#define __rte_cache_aligned __attribute__((__aligned__(RTE_CACHE_LINE_SIZE)))
struct rte_ring_debug_stats
{
uint64_t enq_success_bulk;
uint64_t enq_success_objs;
uint64_t enq_quota_bulk;
uint64_t enq_quota_objs;
uint64_t enq_fail_bulk;
uint64_t enq_fail_objs;
uint64_t deq_success_bulk;
uint64_t deq_success_objs;
uint64_t deq_fail_bulk;
uint64_t deq_fail_objs;
} __rte_cache_aligned;
大頁內(nèi)存
在前文《x86架構(gòu)基礎(chǔ)》一文中提到了TLB的概念,其主要用來緩存內(nèi)存地址轉(zhuǎn)換中的頁表項,其本質(zhì)上也是一個cache,稱之為TLB cache。TLB和cache的區(qū)別是:TLB緩存內(nèi)存地址轉(zhuǎn)換用的頁表項,而cache緩存程序用到的數(shù)據(jù)和指令。
TLB中保存著程序線性地址前20位[31:12]和頁框號的對應(yīng)關(guān)系,如果匹配到線性地址就可以迅速找到頁框號,通過頁框號與線性地址后12位的偏移組合得到最終的物理地址。TLB使用虛擬地址進行搜索,直接返回對應(yīng)的物理地址,相對于內(nèi)存中的多級頁表需要多次訪問才能得到最終的物理地址,TLB查找大大減少了CPU的開銷。如果需要的地址在TLB Cache中,就會迅速返回結(jié)果,然后CPU用該物理地址訪問內(nèi)存,這樣的查找操作也稱為TLB命中;如果需要的地址不在TLB Cache中,也就是不命中,CPU就需要到內(nèi)存中訪問多級頁表,才能最終得到物理地址。但是,TLB的大小是有限的,因此TLB不命中的概率很大,為了提高內(nèi)存地址轉(zhuǎn)換效率,減少CPU的開銷,就提出了大頁內(nèi)存的概念。
在x86架構(gòu)中,一般都分成以下四組TLB:
第一組:緩存一般頁表(4KB頁面)的指令頁表緩存(Instruction-TLB)。
第二組:緩存一般頁表(4KB頁面)的數(shù)據(jù)頁表緩存(Data-TLB)。
第三組:緩存大尺寸頁表(2MB/4MB頁面)的指令頁表緩存(Instruction-TLB)。
第四組:緩存大尺寸頁表(2MB/4MB頁面)的數(shù)據(jù)頁表緩存(Data-TLB)
如果采用常規(guī)頁(4KB)并且使TLB總能命中,需要尋址的內(nèi)容都在該內(nèi)容頁內(nèi),那么至少需要在TLB表中存放兩個表項。如果一個程序使用了512個內(nèi)容頁也就是2MB大小,那么需要512個頁表表項才能保證不會出現(xiàn)TLB不命中的情況。但是,如果采用2MB作為分頁的基本單位,那么只需要一個表項就可以保證不出現(xiàn)TLB不命中的情況;對于消耗內(nèi)存以GB為單位的大型程序,可以采用1GB為單位作為分頁的基本單位,減少TLB不命中的情況。需要注意的是:系統(tǒng)能否支持大頁,支持大頁的大小為多少是由其使用的處理器決定的。
在Linux啟動之后,如果想預(yù)留大頁,則可以使用以下的方法來預(yù)留內(nèi)存。在非NUMA系統(tǒng)中,可以使用以下方法預(yù)留2MB大小的大頁。
# 預(yù)留1024個大小為2MB的大頁,也就是預(yù)留了2GB內(nèi)存。
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
系統(tǒng)未開啟大頁內(nèi)存的狀態(tài)
系統(tǒng)開啟大頁內(nèi)存后的狀態(tài)
如果是在NUMA系統(tǒng)中,假設(shè)有兩個NODE的系統(tǒng)中,則可以用以下的命令:
# 在NODE0和NODE1上各預(yù)留1024個大小為2MB的大頁,總共預(yù)留了4GB大小。
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages
而對于大小為1GB的大頁,則必須在Linux的GRUB配置文件中進行修改,并重啟系統(tǒng)生效,不能動態(tài)預(yù)留。
DPDK中也是使用HUGETLBFS來使用大頁。首先,它需要把大頁mount到某個路徑,比如/mnt/huge,以下是命令:
mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge
需要注意的是:在mount之前,要確保之前已經(jīng)成功預(yù)留內(nèi)存,否則會失敗。該命令只是臨時的mount了文件系統(tǒng),如果想每次開機時省略該步驟,可以修改/etc/fstab文件,加上如下一行:
nodev /mnt/huge hugetlbfs defaults 0 0
對于1GB大小的大頁,則必須用如下的命令:
nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0
然后,在DPDK運行的時候,會使用mmap()系統(tǒng)調(diào)用把大頁映射到用戶態(tài)的虛擬地址空間,然后就可以正常使用了。
未完待續(xù)......