1. DPDK技術介紹
1) 簡介
DPDK全稱Intel Data Plane Development Kit啡浊,是intel提供的數(shù)據(jù)平面開發(fā)工具集卿嘲,為Intel architecture(IA)處理器架構下用戶空間高效的數(shù)據(jù)包處理提供庫函數(shù)和驅(qū)動的支持魂贬。通俗地說初狰,就是一個用來進行包數(shù)據(jù)處理加速的軟件庫魄揉。
DPDK不同于Linux系統(tǒng)以通用性設計為目的,而是專注于網(wǎng)絡應用中數(shù)據(jù)包的高性能處理金赦。具體體現(xiàn)在DPDK應用程序是運行在用戶空間上利用自身提供的數(shù)據(jù)平面庫來收發(fā)數(shù)據(jù)包,繞過了Linux內(nèi)核協(xié)議棧對數(shù)據(jù)包處理過程对嚼。它不是一個用戶可以直接建立應用程序的完整產(chǎn)品夹抗,不包含需要與控制層(包括內(nèi)核和協(xié)議堆棧)進行交互的工具。
相比原生 Linux(Native Linux)纵竖,采用Intel DPDK技術后能夠大幅提升IPV4的轉(zhuǎn)發(fā)性能漠烧,可以讓用戶在遷移包處理應用時(從基于NPU的硬件遷移到基于Intel x86的平臺上),獲得更好的成本和性能優(yōu)勢靡砌。同時可以采用統(tǒng)一的平臺部署不同的服務已脓,如應用處理,控制處理和包處理服務通殃。
2) 技術優(yōu)點
? 通過UIO技術將報文拷貝到應用空間處理度液,規(guī)避不必要的內(nèi)存拷貝和系統(tǒng)調(diào)用,便于快速迭代優(yōu)化画舌。
? 通過大頁內(nèi)存HUGEPAGE堕担,降低cache miss(訪存開銷),利用內(nèi)存多通道交錯訪問提高內(nèi)存訪問有效帶寬曲聂,即提高命中率霹购,進而提高cpu訪問速度。
? 通過CPU親和性朋腋,綁定網(wǎng)卡和線程到固定的core齐疙,減少cpu任務切換膜楷。特定任務可以被指定只在某個核上工作,避免線程在不同核間頻繁切換贞奋,保證更多的cache命中赌厅。
? 通過無鎖隊列,減少資源競爭忆矛。cache行對齊察蹲,預取數(shù)據(jù),多元數(shù)據(jù)批量操作催训。
? 通過輪詢可在包處理時避免中斷上下文切換的開銷洽议。
3) DPDK、網(wǎng)卡漫拭、用戶應用程序亚兄、內(nèi)核之間的關系
? PMD:Pool Mode Driver,輪詢模式驅(qū)動采驻,通過非中斷审胚,以及數(shù)據(jù)幀進出應用緩沖區(qū)內(nèi)存的零拷貝機制,提高發(fā)送/接受數(shù)據(jù)幀的效率礼旅。
? 流分類:Flow Classification膳叨,為N元組匹配和LPM(最長前綴匹配)提供優(yōu)化的查找算法。
? 環(huán)隊列:Ring Queue痘系,針對單個或多個數(shù)據(jù)包生產(chǎn)者菲嘴、單個數(shù)據(jù)包消費者的出入隊列提供無鎖機制,有效減少系統(tǒng)開銷汰翠。
? MBUF緩沖區(qū)管理:分配內(nèi)存創(chuàng)建緩沖區(qū)龄坪,并通過建立MBUF對象,封裝實際數(shù)據(jù)幀复唤,供應用程序使用健田。
? EAL:Environment Abstract Layer,環(huán)境抽象(適配)層佛纫,PMD初始化妓局、CPU內(nèi)核和DPDK線程配置/綁定、設置HugePage大頁內(nèi)存等系統(tǒng)初始化呈宇。
2. 源程序包組成1) Makefile &&CONFIG
MakeFile文件主要位于位于 $(RTE_SDK)/mk 中跟磨。此處留在后面第5節(jié)進行討論
配置模板位于 $(RTE_SDK)/config。這些模板描述了為每個目標啟用的選項攒盈。 配置文件許多可以為DPDK庫啟用或禁用的選項抵拘,包括調(diào)試選項。用戶應該查看配置文件并熟悉這些選項型豁。配置文件同樣也用于創(chuàng)建頭文件僵蛛,創(chuàng)建的頭文件將位于新生成的目錄中尚蝌。一般可以根據(jù)用戶編譯的編譯器和操作系統(tǒng)來直接選擇配置項。
2) Lib庫
庫文件源碼位于目錄$(RTE_SDK)/lib中充尉。按照慣例飘言,庫指的是為應用程序提供API的任何代碼。通常驼侠,它會生成一個(.a)文件姿鸿,這個目錄中可能也保存一些內(nèi)核模塊。
Lib常用庫文件包含以下內(nèi)容
lib
+-- librte_cmdline # 命令行接口
+-- librte_distributor # 報文分發(fā)器
+-- librte_eal # 環(huán)境抽象層
+-- librte_ether # PMD通用接口
+-- librte_hash # 哈希庫
+-- librte_ip_frag # IP分片庫
+-- librte_kni # 內(nèi)核NIC接口
+-- librte_kvargs # 參數(shù)解析庫
+-- librte_lpm # 最長前綴匹配庫
+-- librte_mbuf # 報文及控制緩沖區(qū)操作庫
+-- librte_mempool # 內(nèi)存池管理器
+-- librte_meter # QoS metering 庫
+-- librte_net # IP相關的一些頭部
+-- librte_power # 電源管理庫
+-- librte_ring # 軟件無鎖環(huán)形緩沖區(qū)
+-- librte_sched # QoS調(diào)度器和丟包器庫
+-- librte_timer # 定時器庫
3) 應用程序
應用程序是包含 main() 函數(shù)的源文件倒源。 他們位于 $(RTE_SDK)/app 和 $(RTE_SDK)/examples 目錄中苛预。
常用示例文件:
examples
+-- cmdline # Example of using the cmdline library
+-- exception_path # Sending packets to and from Linux TAP device
+-- helloworld # Basic Hello World example
+-- ip_reassembly # Example showing IP reassembly
+-- ip_fragmentation # Example showing IPv4 fragmentation
+-- ipv4_multicast # Example showing IPv4 multicast
+-- kni # Kernel NIC Interface (KNI) example
+-- l2fwd # L2 forwarding with and without SR-IOV
+-- l3fwd # L3 forwarding example
+-- l3fwd-power # L3 forwarding example with power management
+-- l3fwd-vf # L3 forwarding example with SR-IOV
+-- link_status_interrupt # Link status change interrupt example
+-- load_balancer # Load balancing across multiple cores/sockets
+-- multi_process # Example apps using multiple DPDK processes
+-- qos_meter # QoS metering example
+-- qos_sched # QoS scheduler and dropper example
+-- timer # Example of using librte_timer library
+-- vmdq_dcb # Example of VMDQ and DCB receiving
+-- vmdq # Example of VMDQ receiving
+-- vhost # Example of userspace vhost and switch
3. DPDK架構分析
4. Dpdk基礎庫介紹
1) EAL 環(huán)境適配層
環(huán)境抽象層為底層資源如硬件和內(nèi)存空間的訪問提供了接口。 這些通用的接口為APP和庫隱藏了不同環(huán)境的特殊性笋熬。 EAL負責初始化及分配資源(內(nèi)存热某、PCI設備、定時器胳螟、控制臺等等)昔馋。
典型函數(shù):rte_eal_init
抄自dpdk網(wǎng)站:
EAL提供的典型服務有:
? DPDK的加載和啟動:DPDK和指定的程序鏈接成一個獨立的進程,并以某種方式加載
? CPU親和性和分配處理:DPDK提供機制將執(zhí)行單元綁定到特定的核上糖耸,就像創(chuàng)建一個執(zhí)行程序一樣秘遏。
? 系統(tǒng)內(nèi)存分配:EAL實現(xiàn)了不同區(qū)域內(nèi)存的分配,例如為設備接口提供了物理內(nèi)存嘉竟。
? PCI地址抽象:EAL提供了對PCI地址空間的訪問接口
? 跟蹤調(diào)試功能:日志信息邦危,堆棧打印、異常掛起等等周拐。
? 公用功能:提供了標準libc不提供的自旋鎖铡俐、原子計數(shù)器等凰兑。
? CPU特征辨識:用于決定CPU運行時的一些特殊功能妥粟,決定當前CPU支持的特性,以便編譯對應的二進制文件吏够。
? 中斷處理:提供接口用于向中斷注冊/解注冊回掉函數(shù)勾给。
? 告警功能:提供接口用于設置/取消指定時間環(huán)境下運行的毀掉函數(shù)。
2) Ring 庫
環(huán)形緩沖區(qū)支持隊列管理锅知。rte_ring并不是具有無限大小的鏈表播急,它具有如下屬性:
先進先出(FIFO)
最大大小固定,指針存儲在表中
無鎖實現(xiàn)
多消費者或單消費者出隊操作
多生產(chǎn)者或單生產(chǎn)者入隊操作
批量出隊 - 如果成功售睹,將指定數(shù)量的元素出隊桩警,否則什么也不做
批量入隊 - 如果成功,將指定數(shù)量的元素入隊昌妹,否則什么也不做
突發(fā)出隊 - 如果指定的數(shù)目出隊失敗捶枢,則將最大可用數(shù)目對象出隊
突發(fā)入隊 - 如果指定的數(shù)目入隊失敗握截,則將最大可入隊數(shù)目對象入隊
單生產(chǎn)者入隊:
單消費者出隊
3) Mempool 庫
DPDK提供了內(nèi)存池機制,使得內(nèi)存的管理的使用更加簡單安全烂叔。在設計大的數(shù)據(jù)結構時谨胞,都可以使用mempool分配內(nèi)存,同時蒜鸡,mempool也提供了內(nèi)存的獲取和釋放等操作接口胯努。對于數(shù)據(jù)包mempool甚至提供了更加詳細的接口-rte_pktmbuf_pool_create()
? mempool的創(chuàng)建
內(nèi)存池的創(chuàng)建使用的接口是rte_mempool_create()。在仔細分析代碼之前逢防,先說明一下mempool的設計思路:在DPDK中叶沛,總體來說,mempool的組織是通過3個部分實現(xiàn)的
? mempool頭結構胞四。mempool由名字區(qū)分恬汁,掛接在struct rte_tailq_elem rte_mempool_tailq全局隊列中,可以根據(jù)mempool的名字進行查找辜伟,使用rte_mempool_lookup()接口即可氓侧。這只是個mempool的指示結構,mempool分配的內(nèi)存區(qū)并不在這里面导狡,只是通過物理和虛擬地址指向?qū)嶋H的內(nèi)存地址约巷。
? mempool的實際空間。這就是通過內(nèi)存分配出來的地址連續(xù)的空間旱捧,用來存儲mempool的obj對象独郎。主要利用rte_mempool_populate_default()進行創(chuàng)建
? ring隊列。其作用就是存放mempool中的對象指針枚赡,提供了方便存取使用mempool的空間的辦法氓癌。
? mempool的常見使用是獲取元素空間和釋放空間。
? rte_mempool_get可以獲得池中的元素贫橙,其實就是從ring取出可用元素的地址贪婉。
? rte_mempool_put可以釋放元素到池中。
? rte_mempool_in_use_count查看池中已經(jīng)使用的元素個數(shù)
? rte_mempool_avail_count 查看池中可以使用的元素個數(shù)
4) 定時器庫
定時器庫為DPDK執(zhí)行單元提供定時器服務卢肃,使得執(zhí)行單元可以為異步操作執(zhí)行回調(diào)函數(shù)疲迂。定時器庫的特性如下:
定時器可以周期執(zhí)行,也可以執(zhí)行一次莫湘。
need-to-insert-img
定時器可以在一個核心加載并在另一個核心執(zhí)行尤蒿。但是必須在調(diào)用rte_timer_reset()中指定它。
定時器提供高精度(取決于檢查本地核心的定時器到期的rte_timer_manage()的調(diào)用頻率)幅垮。
如果應用程序不需要腰池,可以在編譯時禁用定時器,并且程序中不調(diào)用rte_timer_manage()來提高性能。
具體使用參考:http://blog.csdn.net/linzhaolover/article/details/9410529
5. 庫的編譯(Makefile)及使用(API使用方案)
need-to-insert-img
makefile位于目錄: /examples/xxx/Makefile示弓。 文件中真正必須的為兩個變量APP和SRCS-y演怎,前者為示例程序編譯生成的目標文件名稱,后者為要編譯的源文件避乏。
另外兩個必須的為makefile文件rte.vars.mk和rte.extapp.mk爷耀。前者定義一些全局的編譯打包鏈接用到的選項,如CFLAGS拍皮、ASFLAGS歹叮、LDFLAGS等變量,頭文件位置和庫路徑等和體系架構相關編譯選項铆帽。后者rte.extapp.mk內(nèi)部又包含了重要的mk/rte.app.mk文件咆耿,首先變量LDLIBS初始化為DPDK核心編譯生成的所有靜態(tài)庫文件。
makefile編寫
在DPDK中,Makefiles的套路是
1.在文件開頭,包含$(RTE_SDK)/mk/rte.vars.mk
2.設置RTE構建系統(tǒng)的變量,比如設置RTE_SDK和RTE_TARGET環(huán)境變量
3.包含指定的 $(RTE_SDK)/mk/rte.XYZ.mk,其中XYZ 可以填寫app, lib, extapp, extlib, obj,依賴構建的目標類型.
3.1 應用程序類型(application)
- rte.app.mk
- rte.extapp.mk
- rte.hostapp.mk
3.2 庫類型 (library)
- rte.lib.mk
- rte.extlib.mk
- rte.hostlib.mk
3.3 安裝類型(install)
rte.install.mk
沒有生成任何文件,僅僅用于創(chuàng)建鏈接和將文件拷貝到安裝目錄.
3.4 內(nèi)核模塊(Kernel Module )
rte.module.mk
用于構建內(nèi)核模塊,在dpdk開發(fā)包框架中.
3.5 Objects類型
- rte.obj.mk
- rte.extobj.mk
3.6 Misc類型
- rte.doc.mk
- rte.gnuconfigure.mk
- rte.subdir.mk
4.包含用戶自定義的規(guī)則和變量
常用的變量
系統(tǒng)構建變量
RTE_SDK
RTE_TARGET
編譯變量(庫文件,庫目錄,C編譯標志,C++編譯標志)
CFLAGS: C編譯標志. 使用 += 為變量添加內(nèi)容
LDFLAGS: 鏈接標志
need-to-insert-img
CPPFLAGS: C++編譯標志. 使用 += 為變量添加內(nèi)容
LDLIBS: 待添加的庫文件的列表
SRC-y: 源文件列表
警告變量
WERROR_CFLAGS:在dpdk示例makefile中是加上此標志的.
CFLAGS += $(WERROR_CFLAGS)
6. 性能優(yōu)化1) 內(nèi)存
? 內(nèi)存拷貝:不要在數(shù)據(jù)面程序中使用libc
通過Linux應用程序環(huán)境爹橱,DPDK中可以使用許多l(xiāng)ibc函數(shù)萨螺。 這可以簡化應用程序的移植和控制平面的開發(fā)。 但是愧驱,這些功能中有許多不是為了性能而設計的慰技。 諸如memcpy() 或 strcpy() 之類的函數(shù)不應該在數(shù)據(jù)平面中使用。 要復制小型結構體组砚,首選方法是編譯器可以優(yōu)化一個更簡單的技術吻商。
對于經(jīng)常調(diào)用的特定函數(shù),提供一個自制的優(yōu)化函數(shù)也是一個好主意糟红,該函數(shù)應聲明為靜態(tài)內(nèi)聯(lián)艾帐。DPDK API提供了一個優(yōu)化的rte_memcpy() 函數(shù)。
? 內(nèi)存申請
need-to-insert-img
libc的其他功能盆偿,如malloc()柒爸,提供了一種靈活的方式來分配和釋放內(nèi)存。 在某些情況下事扭,使用動態(tài)分配是必要的捎稚,但是建議不要在數(shù)據(jù)層面使用類似malloc的函數(shù),因為管理碎片堆可能代價高昂句旱,并且分配器可能無法針對并行分配進行優(yōu)化阳藻。
如果您確實需要在數(shù)據(jù)平面中進行動態(tài)分配晰奖,最好使用固定大小對象的內(nèi)存池谈撒。 這個API由librte_mempool提供。 這個數(shù)據(jù)結構提供了一些提高性能的服務匾南,比如對象的內(nèi)存對齊啃匿,對對象的無鎖訪問,NUMA感知,批量get/put和percore緩存溯乒。 rte_malloc() 函數(shù)對mempools使用類似的概念夹厌。
? 內(nèi)存區(qū)域的并發(fā)訪問
need-to-insert-img
幾個lcore對同一個內(nèi)存區(qū)域進行的讀寫(RW)訪問操作可能會產(chǎn)生大量的數(shù)據(jù)高速緩存未命中,這代價非常昂貴裆悄。 通趁疲可以使用per-lcore變量來解決這類問題。例如光稼,在統(tǒng)計的情況下或南。 至少有兩個解決方案:
使用 RTE_PER_LCORE 變量。注意艾君,在這種情況下采够,處于lcore x的數(shù)據(jù)在lcore y上是無效的。
使用一個表結構(每個lcore一個)冰垄。在這種情況下蹬癌,每個結構都必須緩存對齊。
如果在同一緩存行中沒有RW變量虹茶,那么讀取主要變量可以在不損失性能的情況下在內(nèi)核之間共享逝薪。
? NUMA
need-to-insert-img
在NUMA系統(tǒng)上,由于遠程內(nèi)存訪問速度較慢蝴罪,所以最好訪問本地內(nèi)存翼闽。 在DPDK中,memzone洲炊,ring感局,rte_malloc和mempool API提供了在特定內(nèi)存槽上創(chuàng)建內(nèi)存池的方法。
有時候暂衡,復制數(shù)據(jù)以優(yōu)化速度可能是一個好主意询微。 對于經(jīng)常訪問的大多數(shù)讀取變量,將它們保存在一個socket中應該不成問題狂巢,因為數(shù)據(jù)將存在于緩存中撑毛。
? 跨存儲器通道分配
need-to-insert-img
現(xiàn)代內(nèi)存控制器具有許多內(nèi)存通道,可以支持并行數(shù)據(jù)讀寫操作唧领。 根據(jù)內(nèi)存控制器及其配置藻雌,通道數(shù)量和內(nèi)存在通道中的分布方式會有所不同。 每個通道都有帶寬限制斩个,這意味著如果所有的存儲器訪問都在同一通道上完成胯杭,則存在潛在的性能瓶頸。
默認情況下受啥, Mempool Library 分配對象在內(nèi)存通道中的地址做个。
2) lcore之間的通信
need-to-insert-img
為了在內(nèi)核之間提供基于消息的通信鸽心,建議使用提供無鎖環(huán)實現(xiàn)的DPDK ring API。
該環(huán)支持批量訪問和突發(fā)訪問居暖,這意味著只需要一次昂貴的原子操作即可從環(huán)中讀取多個元素(請參閱 Ring 庫 )顽频。
使用批量訪問操作時,性能會大大提高太闺。
出隊消息的代碼算法可能類似于以下內(nèi)容:
#define MAX_BULK 32
while (1) {
/* Process as many elements as can be dequeued. */
count = rte_ring_dequeue_burst(ring, obj_table, MAX_BULK, NULL);
if (unlikely(count == 0))
continue;
my_process_bulk(obj_table, count);
}
3) PMD 驅(qū)動
DPDK輪詢模式驅(qū)動程序(PMD)也能夠在批量/突發(fā)模式下工作糯景,允許在發(fā)送或接收功能中對每個呼叫的一些代碼進行分解。
避免部分寫入省骂。 當PCI設備通過DMA寫入系統(tǒng)存儲器時莺奸,如果寫入操作位于完全緩存行而不是部分寫入操作,則其花費較少冀宴。 在PMD代碼中灭贷,已采取了盡可能避免部分寫入的措施。
? 低報文延遲
need-to-insert-img
傳統(tǒng)上略贮,吞吐量和延遲之間有一個折衷甚疟。 可以調(diào)整應用程序以實現(xiàn)高吞吐量,但平均數(shù)據(jù)包的端到端延遲通常會因此而增加逃延。 類似地览妖,可以將應用程序調(diào)整為平均具有低端到端延遲,但代價是較低的吞吐量揽祥。
為了實現(xiàn)更高的吞吐量讽膏,DPDK嘗試通過突發(fā)處理數(shù)據(jù)包來合并單獨處理每個數(shù)據(jù)包的成本。
以testpmd應用程序為例拄丰,突發(fā)大小可以在命令行上設置為16(也是默認值)府树。 這允許應用程序一次從PMD請求16個數(shù)據(jù)包。 然后料按,testpmd應用程序立即嘗試傳輸所有接收到的數(shù)據(jù)包奄侠,在這種情況下是全部16個數(shù)據(jù)包。
在網(wǎng)絡端口的相應的TX隊列上更新尾指針之前载矿,不發(fā)送分組垄潮。 當調(diào)整高吞吐量時,這種行為是可取的闷盔,因為對RX和TX隊列的尾指針更新的成本可以分布在16個分組上弯洗, 有效地隱藏了寫入PCIe 設備的相對較慢的MMIO成本。 但是逢勾,當調(diào)優(yōu)為低延遲時牡整,這不是很理想,因為接收到的第一個數(shù)據(jù)包也必須等待另外15個數(shù)據(jù)包才能被接收敏沉。 直到其他15個數(shù)據(jù)包也被處理完畢才能被發(fā)送果正,因為直到TX尾指針被更新,NIC才知道要發(fā)送數(shù)據(jù)包盟迟,直到所有的16個數(shù)據(jù)包都被處理完畢才被發(fā)送秋泳。
為了始終如一地實現(xiàn)低延遲,即使在系統(tǒng)負載較重的情況下攒菠,應用程序開發(fā)人員也應避免處理數(shù)據(jù)包迫皱。 testpmd應用程序可以從命令行配置使用突發(fā)值1。 這將允許一次處理單個數(shù)據(jù)包辖众,提供較低的延遲卓起,但是增加了較低吞吐量的成本凹炸。
4) 鎖和原子操作
need-to-insert-img
原子操作意味著在指令之前有一個鎖定前綴啤它,導致處理器的LOCK#信號在執(zhí)行下一條指令時被斷言变骡。 這對多核環(huán)境中的性能有很大的影響。
可以通過避免數(shù)據(jù)平面中的鎖定機制來提高性能渊胸。 它通臭崦停可以被其他解決方案所取代办成,比如percore變量迂卢。 而且而克,一些鎖定技術比其他鎖定技術更有效率怔毛。 例如,Read-Copy-Update(RCU)算法可以經(jīng)常替換簡單的rwlock
7. 遇到的問題及解決方法
https://www.xuebuyuan.com/1149388.html
https://blog.csdn.net/hz5034/article/details/78811445