rte_mbuf

內存結構
這裏寫圖片描述

分配操作

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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章