收藏:DPDK內(nèi)存基本概念


作者簡(jiǎn)介:Anatoly Burakov,英特爾軟件工程師,目前在維護(hù)DPDK中的VFIO和內(nèi)存子系統(tǒng)。
引言
內(nèi)存管理是數(shù)據(jù)面開發(fā)套件(DPDK)的一個(gè)核心部分,以此為基礎(chǔ),DPDK的其他部分和用戶應(yīng)用得以發(fā)揮其最佳性能。本系列文章將詳細(xì)介紹DPDK提供的各種內(nèi)存管理的功能。
但在此之前,有必要先談一談為何DPDK中內(nèi)存管理要以現(xiàn)有的方式運(yùn)作,它背后又有怎樣的原理,再進(jìn)一步探討DPDK具體能夠提供哪些與內(nèi)存相關(guān)的功能。本文將先介紹DPDK內(nèi)存的基本原理,并解釋它們是如何幫助DPDK實(shí)現(xiàn)高性能的。
請(qǐng)注意,雖然DPDK支持FreeBSD,而且也會(huì)有正在運(yùn)行的Windows端口,但目前大多數(shù)與內(nèi)存相關(guān)的功能僅適用于Linux*。
標(biāo)準(zhǔn)大頁(yè)
現(xiàn)代CPU架構(gòu)中,內(nèi)存管理并不以單個(gè)字節(jié)進(jìn)行,而是以頁(yè)為單位,即虛擬和物理連續(xù)的內(nèi)存塊。這些內(nèi)存塊通常(但不是必須) 存儲(chǔ)在RAM中。在英特爾?64和IA-32架構(gòu)上,標(biāo)準(zhǔn)系統(tǒng)的頁(yè)面大小為4KB。
基于安全性和通用性的考慮,軟件的應(yīng)用程序訪問(wèn)的內(nèi)存位置使用的是操作系統(tǒng)分配的虛擬地址。運(yùn)行代碼時(shí),該虛擬地址需要被轉(zhuǎn)換為硬件使用的物理地址。這種轉(zhuǎn)換是操作系統(tǒng)通過(guò)頁(yè)表轉(zhuǎn)換來(lái)完成的,頁(yè)表在分頁(yè)粒度級(jí)別上(即4KB一個(gè)粒度)將虛擬地址映射到物理地址。為了提高性能,最近一次使用的若干頁(yè)面地址被保存在一個(gè)稱為轉(zhuǎn)換檢測(cè)緩沖區(qū)(TLB)的高速緩存中。每一分頁(yè)都占有TLB的一個(gè)條目。如果用戶的代碼訪問(wèn)(或最近訪問(wèn)過(guò))16 KB的內(nèi)存,即4頁(yè),這些頁(yè)面很有可能會(huì)在TLB緩存中。
如果其中一個(gè)頁(yè)面不在TLB緩存中,嘗試訪問(wèn)該頁(yè)面中包含的地址將導(dǎo)致TLB查詢失敗;也就是說(shuō),操作系統(tǒng)寫入TLB的頁(yè)地址必須是在它的全局頁(yè)表中進(jìn)行查詢操作獲取的。因此,TLB查詢失敗的代價(jià)也相對(duì)較高(某些情況下代價(jià)會(huì)非常高),所以最好將當(dāng)前活動(dòng)的所有頁(yè)面都置于TLB中以盡可能減少TLB查詢失敗。
然而,TLB的大小有限,而且實(shí)際上非常小,和DPDK通常處理的數(shù)據(jù)量(有時(shí)高達(dá)幾十GB)比起來(lái),在任一給定的時(shí)刻,4KB 標(biāo)準(zhǔn)頁(yè)面大小的TLB所覆蓋的內(nèi)存量(幾MB)微不足道。這意味著,如果DPDK采用常規(guī)內(nèi)存,使用DPDK的應(yīng)用會(huì)因?yàn)門LB頻繁的查詢失敗在性能上大打折扣。
為解決這個(gè)問(wèn)題,DPDK依賴于標(biāo)準(zhǔn)大頁(yè)。從名字中很容易猜到,標(biāo)準(zhǔn)大頁(yè)類似于普通的頁(yè)面,只是會(huì)更大。有多大呢?在英特爾?64和1A-32架構(gòu)上,目前可用的兩種大頁(yè)大小為2MB和1GB。也就是說(shuō),單個(gè)頁(yè)面可以覆蓋2 MB或1 GB大小的整個(gè)物理和虛擬連續(xù)的存儲(chǔ)區(qū)域。

圖1. TLB內(nèi)存覆蓋量比較
這兩種頁(yè)面大小DPDK都可以支持。有了這樣的頁(yè)面大小,就可以更容易覆蓋大內(nèi)存區(qū)域,也同時(shí)避免(同樣多的)TLB查詢失敗。反過(guò)來(lái),在處理大內(nèi)存區(qū)域時(shí),更少的TLB查詢失敗也會(huì)使性能得到提升,DPDK的用例通常如此。
將內(nèi)存固定到NUMA節(jié)點(diǎn)
當(dāng)分配常規(guī)內(nèi)存時(shí),理論上,它可以被分配到RAM中的任何位置。這在單CPU系統(tǒng)上沒有什么問(wèn)題,但是許多DPDK用戶是在支持非統(tǒng)一內(nèi)存訪問(wèn) (NUMA) 的多CPU系統(tǒng)上運(yùn)行應(yīng)用的。對(duì)于NUMA來(lái)說(shuō),所有內(nèi)存都是不同的:某一個(gè)CPU對(duì)一些內(nèi)存的訪問(wèn)(如不在該CPU所屬NUMA NODE上的內(nèi)存)將比其他內(nèi)存訪問(wèn)花費(fèi)更長(zhǎng)的時(shí)間,這是由于它們相對(duì)于執(zhí)行所述內(nèi)存訪問(wèn)的CPU所在的物理位置不同。進(jìn)行常規(guī)內(nèi)存分配時(shí),通常無(wú)法控制該內(nèi)存分配到哪里,因此如果DPDK在這樣的系統(tǒng)上使用常規(guī)內(nèi)存,就可能會(huì)導(dǎo)致以下的情況:在一個(gè)CPU上執(zhí)行的線程卻在無(wú)意中訪問(wèn)屬于非本地NUMA節(jié)點(diǎn)的內(nèi)存。

圖2. 理想的NUMA節(jié)點(diǎn)分配
雖然這種跨NUMA節(jié)點(diǎn)訪問(wèn)在所有現(xiàn)代操作系統(tǒng)上都比較少有,因?yàn)檫@樣的訪問(wèn)都是都是NUMA感知的,而且即使沒有DPDK還是有方法能對(duì)內(nèi)存實(shí)施NUMA定位。但是DPDK帶來(lái)的不僅僅是NUMA感知,事實(shí)上,整個(gè)DPDK API的構(gòu)建都旨在為每個(gè)操作提供明確的NUMA感知。如果不明確請(qǐng)求NUMA節(jié)點(diǎn)訪問(wèn)(其中所述結(jié)構(gòu)必須位于內(nèi)存中),通常無(wú)法分配給定的DPDK數(shù)據(jù)結(jié)構(gòu)。
DPDK API提供的這種明確的NUMA感知有助于確保用戶應(yīng)用在每個(gè)操作中都能考慮到NUMA感知;換句話說(shuō),DPDK API可以減少寫出編寫性能差的代碼的可能性。
硬件、物理地址和直接內(nèi)存存取(DMA)
DPDK被認(rèn)為是一組用戶態(tài)的網(wǎng)絡(luò)包輸入/輸出庫(kù),到目前為止,它基本上保持了最初的任務(wù)聲明。但是,電腦上的硬件不能處理用戶空間的虛擬地址,因?yàn)樗荒芨兄魏斡脩魬B(tài)的進(jìn)程和其所分配到的用戶空間虛擬地址。相反,它只能訪問(wèn)真實(shí)的物理地址上的內(nèi)存,也就是CPU、RAM和系統(tǒng)所有其他的部分用來(lái)相互通信的地址。
出于對(duì)效率的考量,現(xiàn)代硬件幾乎總是使用直接內(nèi)存存取(DMA)事務(wù)。通常,為了執(zhí)行一個(gè)DMA事務(wù),內(nèi)核需要參與創(chuàng)建一個(gè)支持DMA的存儲(chǔ)區(qū)域,將進(jìn)程內(nèi)虛擬地址轉(zhuǎn)換成硬件能夠理解的真實(shí)物理地址,并啟動(dòng)DMA事務(wù)。這是大多數(shù)現(xiàn)代操作系統(tǒng)中輸入輸出的工作方式;然而,這是一個(gè)耗時(shí)的過(guò)程,需要上下文切換、轉(zhuǎn)換和查找操作,這不利于高性能輸入/輸出。
DPDK的內(nèi)存管理以一種簡(jiǎn)單的方式解決了這個(gè)問(wèn)題。每當(dāng)一個(gè)內(nèi)存區(qū)域可供DPDK使用時(shí),DPDK就通過(guò)詢問(wèn)內(nèi)核來(lái)計(jì)算它的物理地址。由于DPDK使用鎖定內(nèi)存,通常以大頁(yè)的形式,底層內(nèi)存區(qū)域的物理地址預(yù)計(jì)不會(huì)改變,因此硬件可以依賴這些物理地址始終有效,即使內(nèi)存本身有一段時(shí)間沒有使用。然后,DPDK會(huì)在準(zhǔn)備由硬件完成的輸入/輸出事務(wù)時(shí)使用這些物理地址,并以允許硬件自己?jiǎn)?dòng)DMA事務(wù)的方式配置硬件。這使DPDK避免不必要的開銷,并且完全從用戶空間執(zhí)行輸入/輸出。
IOMMU和IOVA
默認(rèn)情況下,任何硬件都可以訪問(wèn)整個(gè)系統(tǒng),因此它可以在任何地方執(zhí)行DMA 事務(wù)。這有許多安全隱患。例如,流氓和/或不可信進(jìn)程(包括在VM (虛擬機(jī))內(nèi)運(yùn)行的進(jìn)程)可能使用硬件設(shè)備來(lái)讀寫內(nèi)核空間,和幾乎其他任何存儲(chǔ)位置。為了解決這個(gè)問(wèn)題,現(xiàn)代系統(tǒng)配備了輸入輸出內(nèi)存管理單元(IOMMU)。這是一種硬件設(shè)備,提供DMA地址轉(zhuǎn)換和設(shè)備隔離功能,因此只允許特定設(shè)備執(zhí)行進(jìn)出特定內(nèi)存區(qū)域(由IOMMU指定)的DMA 事務(wù),而不能訪問(wèn)系統(tǒng)內(nèi)存地址空間的其余部分。
由于IOMMU的參與,硬件使用的物理地址可能不是真實(shí)的物理地址,而是IOMMU分配給硬件的(完全任意的)輸入輸出虛擬地址(IOVA)。一般來(lái)說(shuō),DPDK社區(qū)可以互換使用物理地址和IOVA這兩個(gè)術(shù)語(yǔ),但是根據(jù)上下文,這兩者之間的區(qū)別可能很重要。例如,DPDK 17.11和更新的DPDK長(zhǎng)期支持(LTS)版本在某些情況下可能根本不使用實(shí)際的物理地址,而是使用用戶空間虛擬地址(甚至完全任意的地址)來(lái)實(shí)現(xiàn)DMA。IOMMU負(fù)責(zé)地址轉(zhuǎn)換,因此硬件永遠(yuǎn)不會(huì)注意到兩者之間的差異。

圖3 .IOMMU將物理地址重新映射到IOVA地址的示例
根據(jù)DPDK的初始化方式,IOVA地址可能代表也可能不代表實(shí)際的物理地址,但有一點(diǎn)始終是正確的:DPDK知道底層內(nèi)存布局,因此可以利用這一點(diǎn)。例如,它可以以創(chuàng)建IOVA連續(xù)虛擬區(qū)域的方式映射頁(yè)面,或者甚至利用IOMMU來(lái)重新排列內(nèi)存映射,以使內(nèi)存看起來(lái)IOVA連續(xù),即使底層物理內(nèi)存可能不連續(xù)。
因此,這種對(duì)底層物理內(nèi)存區(qū)域的感知是DPDK工具包中的又一個(gè)利器。大多數(shù)數(shù)據(jù)結(jié)構(gòu)不關(guān)心IOVA地址,但當(dāng)它們關(guān)心時(shí),DPDK為軟件和硬件提供了利用物理內(nèi)存布局的工具,并針對(duì)不同的用例進(jìn)行優(yōu)化。
請(qǐng)注意,IOMMU不會(huì)自行設(shè)置任何映射。相反,平臺(tái)、硬件和操作系統(tǒng)必須進(jìn)行配置,來(lái)使用IOMMU。這種配置說(shuō)明超出了本系列文章的范圍,但是在DPDK文檔和其他地方有相關(guān)說(shuō)明。一旦系統(tǒng)和硬件設(shè)置為使用IOMMU,DPDK就可以使用IOMMU為DPDK分配的任何內(nèi)存區(qū)域設(shè)置DMA映射。使用IOMMU是運(yùn)行DPDK的推薦方法,因?yàn)檫@樣做更安全,并且它提供了可用性優(yōu)勢(shì)。
內(nèi)存分配和管理
DPDK不使用常規(guī)內(nèi)存分配函數(shù),如malloc()。相反,DPDK管理自己的內(nèi)存。更具體地說(shuō),DPDK分配大頁(yè)并在此內(nèi)存中創(chuàng)建一個(gè)堆(heap)并將其提供給用戶應(yīng)用程序并用于存取應(yīng)用程序內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。
使用自定義內(nèi)存分配器有許多優(yōu)點(diǎn)。最明顯的一個(gè)是終端應(yīng)用程序的性能優(yōu)勢(shì):DPDK創(chuàng)建應(yīng)用程序要使用的內(nèi)存區(qū)域,并且應(yīng)用程序可以原生支持大頁(yè)、NUMA節(jié)點(diǎn)親和性、對(duì)DMA地址的訪問(wèn)、IOVA連續(xù)性等等性能優(yōu)勢(shì),而無(wú)需任何額外的開發(fā)。
DPDK內(nèi)存分配總是在CPU高速緩存行(cache line)的邊界上對(duì)齊,每個(gè)分配的起始地址將是系統(tǒng)高速緩存行大小的倍數(shù)。這種方法防止了許多常見的性能問(wèn)題,例如未對(duì)齊的訪問(wèn)和錯(cuò)誤的數(shù)據(jù)共享,其中單個(gè)高速緩存行無(wú)意中包含(可能不相關(guān)的)多個(gè)內(nèi)核同時(shí)訪問(wèn)的數(shù)據(jù)。對(duì)于需要這種對(duì)齊的用例(例如,分配硬件環(huán)結(jié)構(gòu)),也支持任何其他二次冪值 (當(dāng)然> =高速緩存行大小)。
DPDK中的任何內(nèi)存分配也是線程安全的。這意味著在任何CPU核心上發(fā)生的任何分配都是原子的,不會(huì)干擾任何其他分配。這可能看起來(lái)很無(wú)足輕重 (畢竟,常規(guī)glibc內(nèi)存分配例程通常也是線程安全的),但是一旦在多處理環(huán)境中考慮,它的重要性就會(huì)變得更加清晰。
DPDK支持特定風(fēng)格的協(xié)同多處理,其中主進(jìn)程管理所有DPDK資源,多個(gè)輔助進(jìn)程可以連接到主進(jìn)程,并共享由主進(jìn)程管理的資源的訪問(wèn)。
DPDK的共享內(nèi)存實(shí)現(xiàn)不僅通過(guò)映射不同進(jìn)程中的相同資源 (類似于shmget () 機(jī)制) 來(lái)實(shí)現(xiàn),還通過(guò)復(fù)制另一個(gè)進(jìn)程中主進(jìn)程的地址空間來(lái)實(shí)現(xiàn)。因此,由于兩個(gè)進(jìn)程中的所有內(nèi)容都位于相同的地址,指向DPDK內(nèi)存對(duì)象的任何指針都將跨進(jìn)程工作,無(wú)需任何地址轉(zhuǎn)換。這對(duì)于跨進(jìn)程傳遞數(shù)據(jù)時(shí)的性能非常重要。

表1. 操作系統(tǒng)和DPDK分配器的比較
內(nèi)存池
DPDK也有一個(gè)內(nèi)存池管理器,在整個(gè)DPDK中廣泛用于管理大型對(duì)象池,對(duì)象大小固定。它的用途很多——包輸入/輸出、加密操作、事件調(diào)度和許多其他需要快速分配或解除分配固定大小緩沖區(qū)的用例。DPDK內(nèi)存池針對(duì)性能進(jìn)行了高度優(yōu)化,并支持可選的線程安全(如果用戶不需要線程安全,則無(wú)需為之付費(fèi))和批量操作,所有這些都會(huì)導(dǎo)致每個(gè)緩沖區(qū)的分配或空閑操作周期計(jì)數(shù)達(dá)到兩位數(shù)以下。
也就是說(shuō),即使DPDK內(nèi)存池的主題出現(xiàn)在幾乎所有關(guān)于DPDK內(nèi)存管理的討論中,從技術(shù)上講,內(nèi)存池管理器是一個(gè)建立在常規(guī)DPDK內(nèi)存分配器之上的庫(kù)。它不是標(biāo)準(zhǔn)DPDK內(nèi)存分配工具的一部分,它的內(nèi)部工作與DPDK內(nèi)存管理例程完全分離 (并且非常不同) 。因此,這超出了本系列文章的范圍。但是,有關(guān)DPDK內(nèi)存池管理器庫(kù)的更多信息可以在DPDK文檔中找到。
結(jié)論
本文介紹了構(gòu)成DPDK內(nèi)存管理子系統(tǒng)基礎(chǔ)的許多核心原理,并證明了DPDK的高性能并不是偶然,而是其體系架構(gòu)的必然結(jié)果。
本系列接下來(lái)的文章將深入探討IOVA尋址及其在DPDK中的使用;以歷史的視角,回顧DPDK長(zhǎng)期支持(LTS)版本17.11及更早版本中提供的內(nèi)存管理功能;同時(shí)也會(huì)介紹18.11及更高版本DPDK版本中做出的更改和提供的新功能。
轉(zhuǎn)自:DPDK與SPDK開源社區(qū)
推薦閱讀:收藏: 詳解DPDK技術(shù)知識(shí)點(diǎn)

轉(zhuǎn)載申明:轉(zhuǎn)載本號(hào)文章請(qǐng)注明作者和來(lái)源,本號(hào)發(fā)布文章若存在版權(quán)等問(wèn)題,請(qǐng)留言聯(lián)系處理,謝謝。
推薦閱讀
更多架構(gòu)相關(guān)技術(shù)知識(shí)總結(jié)請(qǐng)參考“架構(gòu)師技術(shù)全聯(lián)盟書店”相關(guān)電子書(35本技術(shù)資料打包匯總詳情可通過(guò)“閱讀原文”獲取)。
內(nèi)容持續(xù)更新,現(xiàn)下單“架構(gòu)師技術(shù)全店打包匯總(全)”,后續(xù)可享全店內(nèi)容更新“免費(fèi)”贈(zèng)閱,格僅收188元(原總價(jià)270元)。
溫馨提示:
掃描二維碼關(guān)注公眾號(hào),點(diǎn)擊閱讀原文鏈接獲取“架構(gòu)師技術(shù)全店資料打包匯總(全)”電子書資料詳情。

