內存結構
分配操作
Mbuf由緩衝池rte_mempool管理,rte_mempool在初始化時一次申請多個mbuf,申請的mbuf個數和長度都由用戶指定。宏MBUF_SIZE是例子程序中使用的mbuf長度:
#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
用下面函數向rte_mempool申請一個mbuf:
struct rte_mbuf *rte_pktmbuf_alloc(struct rte_mempool *mp);
拷貝操作
dpdk接收報文並把報文上送上層應用的過程中,報文傳輸是“零拷貝”,即不需要拷貝報文內容,只需要傳送mbuf地址。然而在一個報文上送給多個應用時,仍然需要對報文做拷貝並送給不同的應用。Librte_mbuf採用“複製rte_mbuf,共享data數據域”的方式實現報文的拷貝函數rte_pktmbuf_clone(),函數原型如下:
struct rte_mbuf *rte_pktmbuf_clone(struct rte_mbuf *md, struct rte_mempool *mp)
rte_pktmbuf_clone()函數首先申請一個新的rte_mbuf,我們稱這個mbuf爲indirect buffer,用mi表示,參數md稱爲direct buffer。函數將md的各結構體成員(引用計數refcnt除外)一一複製給mi,同時將md的引用計數refcnt增1。此時,mi->pkt.data指向md的data數據域。
Rte_pktmbuf_clone()要求參數md必須是direct buffer,我們可以通過判斷md->buf_addr – sizeof(struct rte_mbuf) == md 是否爲真,確定md是否爲direct buffer,該功能由宏RTE_MBUF_DIRECT(mb)實現。
釋放操作
用下面函數釋放一個mbuf,釋放過程即把mbuf歸還到rte_mempool中:
void rte_pktmbuf_free(struct rte_mbuf *m);
根據m的引用計數和m的indirect/direct類型,rte_pktmbuf_free()分以下方式釋放m:
如果m的引用計數大於1,則只將m的引用計數減1,函數返回;
如果m的引用計數是1且m是direct類型,則將m的引用計數置0,然後把m歸還mempool,函數返回;
如果m的引用計數是1且m是indirect類型,則rte_pktmbuf_free()將m引用計數置0,同時將m對應的direct buffer的引用計數減1(減1後引用計數爲0則把direct buffer歸還mempool),把m歸還mempool,函數返回;
Rte_pktmbuf_free()通過宏RTE_MBUF_FROM_BADDR(m->buf_addr)找到m對應的direct buffer,宏實現如下:
#define RTE_MBUF_FROM_BADDR(ba) (((struct rte_mbuf *)(ba)) - 1)
Rte_pktmbuf_free()通過判斷m != RTE_MBUF_FROM_BADDR(m->buf_addr)是否爲真判斷m的indirect/direct類型。
解封裝操作
Rte_mbuf的結構與linux內核協議棧的skb_buf相似,在保存報文的內存塊前後分別保留headroom和tailroom,以方便應用解封報文。Headroom默認128字節,可以通過宏RTE_PKTMBUF_HEADROOM調整。
我們可以通過m->pkt.data – m->buf_addr計算出headroom長度,通過m->buf_len – m->pkt.data_len – headroom_size計算出tailroom長度。這些計算過程都由以下函數實現:
uint16_t rte_pktmbuf_headroom(const struct rte_mbuf *m)
uint16_t rte_pktmbuf_tailroom(const struct rte_mbuf *m)
假設m->pkt.data指向報文的二層首地址,我們可以通過以下一系列操作剝去報文的二層頭部:
m->pkt.data += 14;
m->pkt.data_len -= 14;
m->pkt.pkt_len -= 14;
這些操作已經由rte_pktmbuf_adj()實現,函數原型如下:
char *rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len)
我們可以通過以下一系列操作爲IP報文封裝二層頭部:
m->pkt.data -= 14;
m->pkt.data_len += 14;
m->pkt.pkt_len += 14;
這些操作由rte_pktmbuf_prepend()實現,函數原型如下:
char *rte_pktmbuf_prepend(struct rte_mbuf *m, uint16_t len)
如果需要在tailroom 中加入N個字節數據,我們可以通過以下操作完成:
tail = m->pkt.data + m->pkt.data_len; // tail記錄tailroom首地址
m->pkt.data_len += N;
m->pkt.pkt_len += N;
這些操作由rte_pktmbuf_append()實現,函數原型如下:
char *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)
librte_mbuf還提供了rte_pktmbuf_trim()函數,用來移除mbuf中data數據域的最後N個字節,函數實現如下:
m->pkt.data_len -= N;
m->pkt.pkt_len -= N;
函數原型如下:
int rte_pktmbuf_trim(struct rte_mbuf *m, uint16_t len)