【PCIe与包处理I/0】mbuf和mempool

mbuf是报文中的描素的结构体,是整个转发过程中最核心的数据结构之一。主要针对于mbuf的常用API与基本原理做一个简单的介绍。

1、mbuf:报文内存存储结构,存储在mempool中
2、mempool:使用环形缓冲区保存空闲对象

rte_mbuf内存结构

首先我们看一下rte_mbuf的数据结构的定义:先主要说明几个跟数据有关的变量

struct rte_mbuf {
	void *buf_addr; /**< Virtual address of segment buffer. */
	uint16_t data_off;
	uint32_t pkt_len; /**< Total pkt len: sum of all segments. */
	uint16_t data_len; /**< Amount of data in segment buffer. */
	uint16_t buf_len
	......
}

既然叫mbuf,其实就是一种buf管理的结构体:

这里写图片描述

rte_mbuf整个用来存数据的buf就是上图所示的内容,一般数据都会有分3个区域:headroom、data、 tailroom。其结构与linux内核协议栈的skb_buf相似,在保存报文的内存块前后分别保留headroom和tailroom,以方便应用解封报文。Headroom默认128字节,可以通过宏RTE_PKTMBUF_HEADROOM调整。整个buf的大小,也就是数据结构中的buf_len的大小,一般是4096

  • headroom:
    保留区域headroom:一般用来存放用户自己针对于mbuf的一些描素信息,一般保留给用户使用,可以通过修改mbuf头文件,来实现headroom的大小;
    data_off 的默认值就是 mbuf的headroom的大小;默认就是128。如果要定义超过这个范围的私有字段,请自行修改 RTE_PKTMBUF_HEADROOM

  • 数据字段:data。
    data区域一般指的是地址区间在 buf_addr + data_offbuf_add + data_off + data_len 即,data_len就是这段数据的长短,这个data_len一般都是通过mbuf的几个基本操作,或者通过赋值来实现的。

  • tailroom:
    一般指的是,data_len还未包含的东西。默认其实data_len是0。所以说默认来说tailroom应该是占了很大的空间的;

其实mbuf的控制,就是不断的控制这个几个区域的大小,永远记住,我们的报文数据永远是存放在data中的;主要控制的就是data_off 与data_len

至于pkt_len在普通情况下,就是和data_len是一个大小,在大报文的时候,就是两个mbuf通过链表组合起来的。本文假定报文的长度不超过rte_mbuf->buf_len的长度

rte_mbuf分配与释放


wike:http://doc.dpdk.org/api/rte__mbuf_8h.html#ad4d1c289d8cffc831dfb77c64f52447b

Mbuf由缓冲池rte_mempool管理,rte_mempool在初始化时一次申请多个mbuf,申请的mbuf个数和长度都由用户指定。宏MBUF_SIZE是例子程序中使用的mbuf长度:

#define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)

以l2fwd二层转发例子来说明,通过rte_mempool_create创建了一个内存池,内存池中有NB_MBUF个元素。内存池创建好后,都会调用rte_pktmbuf_init初始化每一个元素

l2fwd_pktmbuf_pool = rte_mempool_create("mbuf_pool", NB_MBUF,  MBUF_SIZE, 32,
				        sizeof(struct rte_pktmbuf_pool_private),
				        rte_pktmbuf_pool_init, NULL, 
                              rte_pktmbuf_init, NULL,rte_socket_id(), 0);

用下面函数向rte_mempool申请一个mbuf:

struct rte_mbuf *rte_pktmbuf_alloc(struct rte_mempool *mp);

当mbuf不再使用了,那就需要释放他所占用的内存空间,rte_pktmbuf_free接口用于释放一个mbuf空间,内部最终调用rte_mempool_put将已经申请的空间放回到rte_mempool内存池中,相当于回收以便这个空间后续可以被使用:

void rte_pktmbuf_free(struct rte_mbuf *m);

关于rte_mempool的更多细节,详见:
https://blog.csdn.net/qq_15437629/article/details/78149983

rte_mbuf基本操作

wiki:http://doc.dpdk.org/api/rte__mbuf_8h.html#aadf5bef4ceb0b76dfafff0895f285ab0

  • 在帧数据前插入一段内容
    rte_pktmbuf_prepend(struct rte_mbuf *m, uint16_t len)

报文向前扩容,例如报文从应用层往下,一层一层的封装就用这个。
移动data_off指针,注意:需要查看返回值,如果已经偏移完headroom的时候,会返回NULL。
rte_pktmbuf_prepend通过以下操作为IP报文封装二层头部:

m->data_off = (uint16_t)(m->data_off - len);
m->data_len = (uint16_t)(m->data_len + len);
m->pkt_len  = (m->pkt_len + len);
  • 在帧数据后增加一段内容
    rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len)

先有首部再填数据字段,就可以用这个。向后扩容,改变data_len的长度 ,返回改变前的尾地址。
rte_pktmbuf_append通过下列实现在tailroom 中加入长度为len个字节数据:

tail = (char *)m_last->buf_addr + m_last->data_off + m_last->data_len;
m_last->data_len = (uint16_t)(m_last->data_len + len);
m->pkt_len  = (m->pkt_len + len);
  • 在帧数据前删除一段内容
    rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len)

从二层到三层转发,去二层头就可以用这个。首部向后缩小空间, 改变data_off的值。
rte_pktmbuf_adj通过以下一系列操作剥去报文的二层头部:

m->data_len = (uint16_t)(m->data_len - len);
m->data_off = (uint16_t)(m->data_off + len);
m->pkt_len  = (m->pkt_len - len);
  • 将帧数据后截掉一段内容
    rte_pktmbuf_trim(struct rte_mbuf *m, uint16_t len)

尾部向前缩小空间, 移动data_len减少buf_len;(预分配的内容太大,数据没那么大可以用这个)

m_last->data_len = (uint16_t)(m_last->data_len - len);
m->pkt_len  = (m->pkt_len - len);
  • 获得指向数据的指针
    rte_pktmbuf_mtod

rte_pktmbuf_mtod、rte_pktmbuf_mtod_offset 这两个API就是就是返回buf_addr+data_off +useroff 然后再强制类型转换一下。

只是mtod这个API默认是useroff ==0,就是把数据data部分的首指针返回。

rte_mbuf打印

最后提供一个gdb打印mbuf的方法:

vim ~/.gdbinit

handle SIGPIPE nostop noprint

define pmbuf
    set $m = (struct rte_mbuf *)$arg0
    printf "dump mbuf at %p, buf_addr:%p, buf_len=%hu, data_off=%u\n", $m, $m->buf_addr, $m->buf_len, $m->data_off
    printf "data_len=%hu, pkt_len=%d\n", $m->data_len, $m->pkt_len
    printf "ol_flags=0x%x, nb_segs=%u\n", $m->ol_flags, $m->nb_segs
    while $m
        set $data_len = $m->data_len
        set $data = $m->buf_addr + $m->data_off

        dump binary memory /tmp/dump.bin $data $data+$data_len
        shell hexdump -n 100 -C /tmp/dump.bin
        set $m = $m->next
    end
end
document pmbuf
    print mbuf
    usage: pmbuf batch->packets[0]
end

打印出的内容如下:

(gdb) pmbuf in_batch->packets[0]
dump mbuf at 0x4b1685812e80, buf_addr:0x4b1685813180, buf_len=2304, data_off=128
data_len=92, pkt_len=92
ol_flags=0x2, nb_segs=1
00000000  0e 94 9d a5 da 3d 0a 25  50 15 f8 0b 08 00 45 00  |.....=.%P.....E.|
00000010  00 4e 00 00 40 00 40 11  9a 9c 50 00 00 01 50 00  |.N..@.@...P...P.|
00000020  00 02 a8 2c 12 b5 00 3a  00 00 08 00 00 00 08 90  |...,...:........|
00000030  11 00 fa 16 3e 01 01 40  fa 16 3e 01 01 20 08 00  |....>..@..>.. ..|
00000040  45 00 00 1c 89 a0 00 00  ff 11 ae 7f c0 a8 01 20  |E.............. |
00000050  c0 a8 01 40 27 10 4e 20  00 08 06 fd              |...@'.N ....|
0000005c

将数据部分保存到txt文件,打开wireshark,文件-> 从Hex转储导入既可
在这里插入图片描述

l2fwd代码解读

DPDK内存管理-l2fwd代码解读

  • 9
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值