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_off
到buf_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转储导入既可