1. 单段 mbuf 的结构示意
An mbuf with One Segment:
整个 mbuf 可分为两块:rte_mbuf 结构体和数据缓冲区(注意数据缓冲区(图中mbuf struct后面全部)和实际发送的数据包区域(图中红色部分)的区别)
mbuf struct
rte_mbuf
结构体,存放数据包的元信息priv_data
私有数据区域,在mbuf struct
后面,一般为 0,图中未展示.headroom
数据缓冲区开头预留的一段空间(默认为RTE_PKTMBUF_HEADROOM = 128
个字节)rte_pktmbuf_datalen(m)
实际的数据包长度,即mbuf->data_len
。单段 mbuf 的情况,pktlen
等于datalen
tailroom
数据缓冲区剩余还未使用的空间
默认大小:
#define RTE_PKTMBUF_HEADROOM 128
#define RTE_MBUF_DEFAULT_DATAROOM 2048
#define RTE_MBUF_DEFAULT_BUF_SIZE \
(RTE_MBUF_DEFAULT_DATAROOM + RTE_PKTMBUF_HEADROOM)
2. rte_mbuf
结构体中的部分成员变量含义
struct rte_mbuf {
void *buf_addr; /** mbuf 缓冲区的虚拟地址 */
rte_iova_t buf_iova __rte_aligned(sizeof(rte_iova_t)); /** mbuf 缓冲区的物理地址 */
uint16_t data_off; /** 缓冲区内实际数据的偏移量,即headromm的长度 */
uint16_t nb_segs; /** 段数量,只有第一段 mbuf 的 nb_segs 有效 */
uint32_t pkt_len; /** 整个数据包的数据字节数 */
uint16_t data_len; /** 当前 mbuf 的段内实际存储的数据字节数 */
uint16_t buf_len; /** 当前 mbuf 所分配的缓冲区总大小(包括 headroom + data + tailroom)*/
struct rte_mempool *pool; /** 当前 mbuf 是由哪个mempool分配的 */
struct rte_mbuf *next; /** 当前 mbuf 的下一个段 */
uint16_t priv_size; /** 当前 mbuf 附带的私有数据区大小,介于 rte_mbuf struct 和 buffer(或者说 headroom)之间*/
}
mbuf->buf_addr
是整个缓冲区的起始地址
mbuf->data_off
是headroom
的长度
实际用户发送的数据包起始地址是(uint8-t *)mbuf->buf_addr + mbuf->data_off
也可以使用函数 rte_pktmbuf_mtod(mbuf, uint8_t *)
来获取,等价于(uint8-t *)mbuf->buf_addr + mbuf->data_off
注意:
rte_mbuf
结构体并不包含实际数据,只是描述和指向数据,所有数据实际存在mbuf->buf_addr
指向的 buffer 中;- 为了性能,mbuf 在分配和释放时最好避免跨 NUMA 节点。
3. 向 mbuf 中写入数据的方法
✅推荐方法
void *data_ptr = rte_pktmbuf_append(mbuf, data_len);
memcpy(data_ptr, your_data, data_len);
❌不推荐方法
char *pkt_data = rte_pktmbuf_mtod(mbuf, char *);
memcpy(pkt_data, my_data, data_len);
// 必须自己更新长度字段
mbuf->data_len = data_len;
mbuf->pkt_len = data_len;
使用该方式,可能会忘记更新data_len
和pkt_len
,导致异常。
4. headroom
作用
主要用于协议栈预留空间,比如添加额外的协议头。
默认情况下,网卡实际发送的数据范围是起始地址为(uint8-t *)mbuf->buf_addr + mbuf->data_off
,长度为mbuf->data_len
的部分。
如果添加了额外的协议头,并希望被网卡发送出去,则需要:
- 把协议头写入
headroom
区域 - 调整
mbuf->data_off
,增加mbuf->data
和mbuf->pkt_len
的值,确保新加的数据在发送范围之内
✅推荐方法
// 在 mbuf 前部预留 20 字节空间
void *hdr = rte_pktmbuf_prepend(mbuf, 20); // 最好判断一下 hdr 是否为空
// 将 header 内容写入新预留空间
memcpy(hdr, my_header_data, 20);
❌不推荐方法
// 向前移动 data_off
mbuf->data_off -= 20;
// 把你的 header 数据写入 headroom 中对应位置
memcpy((char *)mbuf->buf_addr + mbuf->data_off, my_header, 20);
// 更新长度
mbuf->data_len += 20;
mbuf->pkt_len += 20;
【参考】