dpdk是通過許多不同的緯度來加速包處理的,其中主要包括:
hugepage大頁內(nèi)存(進(jìn)程使用的是虛擬地址,一般頁表(4k)能映射的虛擬地址空間有限,使用大頁能減少換頁次數(shù)提高cache命中,通過mmap把大頁映射到用戶態(tài)的虛擬地址空間有用過mmap的都知道這是實現(xiàn)共享內(nèi)存的手段,所以dpdk還支持多進(jìn)程共享內(nèi)存)
cache預(yù)取 (每次預(yù)讀當(dāng)前數(shù)據(jù)相鄰前后的數(shù)據(jù)),批量操作數(shù)據(jù),cache line對齊(通過浪費一點內(nèi)存將要操作的數(shù)據(jù)對齊)
接管了網(wǎng)卡用戶態(tài)驅(qū)動使用輪詢而不是網(wǎng)卡中斷
將網(wǎng)卡rx tx隊列映射到用戶態(tài)空間實現(xiàn)真正的零拷貝(傳統(tǒng)堆棧至少也得一次拷貝,因為隊列空間在內(nèi)核而內(nèi)核和用戶態(tài)使用不同的地址空間)(傳統(tǒng)堆棧為了支持通用性,例如ipx等其他網(wǎng)絡(luò),將包處理過程分了很多層次,層之間的接口標(biāo)準(zhǔn)統(tǒng)一數(shù)據(jù)結(jié)構(gòu)就需要轉(zhuǎn)換,無形中帶來了巨大的成本,如osi七層模型而實用的就是tcp/ip四層模型)
線程綁定cpu
支持NUMA,不同的core屬于不同的node,每個node有自己的mempool減少沖突
無鎖環(huán)形隊列(沖突發(fā)生時也是一次cas的開銷)
dpdk通過tools/dpdk-setup.sh的腳本,通過編譯、掛載內(nèi)核模塊, 綁定網(wǎng)卡(先把網(wǎng)卡ifconfig down),設(shè)置hugepage后就可以使用了。
在內(nèi)核模塊igb加載時,會注冊pci設(shè)備驅(qū)動
static struct pci_driver igbuio_pci_driver = {
.name = "igb_uio",
.id_table = NULL,
.probe = igbuio_pci_probe,
.remove = igbuio_pci_remove,
};
在綁定網(wǎng)卡時,會調(diào)用igbuio_pci_probe,使用用戶態(tài)驅(qū)動uio接管網(wǎng)卡(中斷處理、mmap映射設(shè)備內(nèi)存到用戶空間)
系統(tǒng)啟動時,bios會將設(shè)備總線地址信息記錄在/sys/bus/pci/devices,dpdk程序啟動時會去這里掃描pci設(shè)備,根據(jù)不同類型的NIC有對應(yīng)的初始化流程。在后面配置隊列的時候會把用戶態(tài)的隊列內(nèi)存地址通過硬件指令交給NIC,從而實現(xiàn)零拷貝。

如果NIC收到包,會做標(biāo)記,輪詢的時候通過標(biāo)記取數(shù)據(jù)包
while (nb_rx < nb_pkts) {
/*
* The order of operations here is important as the DD status
* bit must not be read after any other descriptor fields.
* rx_ring and rxdp are pointing to volatile data so the order
* of accesses cannot be reordered by the compiler. If they were
* not volatile, they could be reordered which could lead to
* using invalid descriptor fields when read from rxd.
*/
rxdp = &rx_ring[rx_id];
staterr = rxdp->wb.upper.status_error;
if (! (staterr & rte_cpu_to_le_32(E1000_RXD_STAT_DD)))
break;
rxd = *rxdp;
發(fā)包的輪詢就是輪詢發(fā)包結(jié)束的硬件標(biāo)志位,硬件發(fā)包完成會寫回標(biāo)志位,驅(qū)動發(fā)現(xiàn)后再釋放對應(yīng)的描述符和緩沖塊。
KNI
通過創(chuàng)建一個虛擬網(wǎng)卡,將收到的包丟給協(xié)議棧
/* 發(fā)送skb到協(xié)議棧 */
/* Call netif interface */
netif_receive_skb(skb);
POWER
在負(fù)載小的時候沒有必要使用輪詢模式,這時可以打開網(wǎng)卡中斷 使用eventfd epoll通知用戶層
Ring
無鎖環(huán)形隊列的核心就是操作頭尾索引,先將頭尾索引賦給臨時變量,再把尾索引往后跳n個位置,利用cas判斷頭如果還是在原來的位置就指向尾否則就重復(fù)這個過程,然后在操作中間跳過的n個元素就是安全的了,此時頭尾索引應(yīng)該指向同一個位置,如果不同應(yīng)該是有別的線程也在操作,重復(fù)等待即可。(這里有個細(xì)節(jié),索引是只加不減的,因為是環(huán)形隊列索引又是unsigned 32bits,所以每次取數(shù)據(jù)前把索引模隊列長度-1, uint32_t mask; /**< Mask (size-1) of ring. */即可)
Windows下使用vmware虛擬機的時候出現(xiàn)EAL: Error reading from file descriptor,根據(jù)網(wǎng)上的說法打了patch還是不行,后來嘗試掛載內(nèi)核模塊的時候不加載vfio模塊就可以了