网络数据包缓冲管理(librte_mbuf)
1.1 rte_pktmbuf_alloc 是 DPDK(数据平面开发工具包)中的一个函数,用于在内存池中分配一个新的 mbuf(内存缓冲区)
struct rte_mbuf *rte_pktmbuf_alloc(struct rte_mempool *mp);
- mp 是一个指向用于分配 mbuf 的内存池的指针。
#include <rte_mbuf.h>
#include <rte_mempool.h>int main() {struct rte_mempool *mbuf_pool; // 内存池指针struct rte_mbuf *mbuf; // mbuf 指针// 初始化 DPDK 环境和内存池// 具体初始化过程不在此展示,需要按照 DPDK 的初始化流程完成// 从内存池中分配一个新的 mbufmbuf = rte_pktmbuf_alloc(mbuf_pool);if (mbuf == NULL) {// 分配失败printf("Failed to allocate mbuf\n");return -1;}// 在这里可以对分配的 mbuf 进行操作,如填充数据、设置头部信息等// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
1.2 rte_pktmbuf_free 是 DPDK 中用于释放 mbuf(内存缓冲区)的函数
mbuf 是 DPDK 中存储网络数据包的数据结构。使用 rte_pktmbuf_alloc 分配的 mbuf 在使用完毕后应当通过 rte_pktmbuf_free 函数进行释放,以便重用内存。该函数的原型如下:
void rte_pktmbuf_free(struct rte_mbuf *m);
- 参数 m 是一个指向要释放的 mbuf 的指针
#include <rte_mbuf.h>int main() {struct rte_mempool *mbuf_pool; // 内存池指针struct rte_mbuf *mbuf; // mbuf 指针// 初始化 DPDK 环境和内存池// 具体初始化过程不在此展示,需要按照 DPDK 的初始化流程完成// 从内存池中分配一个新的 mbufmbuf = rte_pktmbuf_alloc(mbuf_pool);if (mbuf == NULL) {// 分配失败printf("Failed to allocate mbuf\n");return -1;}// 在这里可以对分配的 mbuf 进行操作,如填充数据、设置头部信息等// 使用完毕后释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
1.3 rte_pktmbuf_clone 是 DPDK 中用于克隆(复制)一个 mbuf(内存缓冲区)的函数
克隆操作会复制原始 mbuf 的数据和元数据,生成一个新的 mbuf。这在需要对同一个数据包进行多次处理时非常有用,避免了对原始数据包进行修改可能带来的问题。该函数的原型如下:
struct rte_mbuf *rte_pktmbuf_clone(const struct rte_mbuf *md, struct rte_mempool *mp);
- 参数 md 是一个指向要克隆的原始 mbuf 的指针,
- 参数 mp 则是用于分配新 mbuf 的内存池的指针
#include <rte_mbuf.h>
#include <rte_mempool.h>int main() {struct rte_mempool *mbuf_pool; // 内存池指针struct rte_mbuf *original_mbuf; // 原始 mbuf 指针struct rte_mbuf *cloned_mbuf; // 克隆 mbuf 指针// 初始化 DPDK 环境和内存池// 具体初始化过程不在此展示,需要按照 DPDK 的初始化流程完成// 假设 original_mbuf 已经被填充了数据// 克隆 original_mbufcloned_mbuf = rte_pktmbuf_clone(original_mbuf, mbuf_pool);if (cloned_mbuf == NULL) {// 克隆失败printf("Failed to clone mbuf\n");return -1;}// 对克隆的 mbuf 进行处理// 释放克隆的 mbufrte_pktmbuf_free(cloned_mbuf);return 0;
}
首先需要确保 DPDK 环境和内存池已经正确初始化。然后假设 original_mbuf 已经被填充了数据。接着调用 rte_pktmbuf_clone 函数克隆 original_mbuf,并将结果存储在 cloned_mbuf 中。然后可以对 cloned_mbuf 进行处理,而不会影响到 original_mbuf。最后通过调用 rte_pktmbuf_free 函数释放克隆的 mbuf。
1.4 rte_pktmbuf_attach 是 DPDK 中用于将一个 mbuf(内存缓冲区)的数据指针和长度与外部数据进行关联的函数
通过这种方式,可以将已有的数据关联到 mbuf 上,而不需要额外地进行数据复制。这在需要处理已有数据的情况下,可以提高处理效率。该函数的原型如下
void rte_pktmbuf_attach(struct rte_mbuf *m, void *buf_addr, rte_iova_t buf_iova, uint16_t data_len);
- 参数 m 是一个指向要关联数据的 mbuf 的指针;
- buf_addr 是一个指向要关联的数据的起始地址的指针;
- buf_iova 是要关联的数据在物理内存中的地址;
- data_len 则是要关联的数据的长度。
#include <rte_mbuf.h>
#include <rte_mempool.h>int main() {struct rte_mempool *mbuf_pool; // 内存池指针struct rte_mbuf *mbuf; // mbuf 指针void *data_ptr; // 外部数据指针rte_iova_t data_iova; // 外部数据在物理内存中的地址uint16_t data_len = 2048; // 外部数据长度,这里假设为2048字节// 初始化 DPDK 环境和内存池// 具体初始化过程不在此展示,需要按照 DPDK 的初始化流程完成// 从内存池中分配一个新的 mbufmbuf = rte_pktmbuf_alloc(mbuf_pool);if (mbuf == NULL) {// 分配失败printf("Failed to allocate mbuf\n");return -1;}// 假设已经有一个包含数据的外部缓冲区 data_ptr,这里简单地使用 malloc 分配data_ptr = malloc(data_len);if (data_ptr == NULL) {// 分配失败printf("Failed to allocate memory for external data\n");rte_pktmbuf_free(mbuf); // 释放 mbufreturn -1;}// 获取外部数据在物理内存中的地址(IOVA)data_iova = rte_malloc_virt2iova(data_ptr);// 将外部数据关联到 mbuf 上rte_pktmbuf_attach(mbuf, data_ptr, data_iova, data_len);// 现在可以使用 mbuf 来处理关联的数据了// 释放外部数据缓冲区free(data_ptr);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
首先分配了一个 mbuf,然后使用标准的 malloc 函数分配了一个外部数据缓冲区 data_ptr。接着使用 rte_malloc_virt2iova 函数将 data_ptr 的虚拟地址转换为 IOVA 地址,并将其与 mbuf 关联起来。最后,释放了外部数据缓冲区和 mbuf。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 假设有一个外部数据缓冲区uint8_t external_data[] = {0x00, 0x11, 0x22, 0x33, 0x44};rte_iova_t data_iova; // 外部数据在物理内存中的地址uint16_t data_len = sizeof(external_data);// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 将外部数据关联到 mbuf 上rte_pktmbuf_attach(mbuf, external_data, data_iova, data_len);// 访问外部数据uint8_t *mbuf_data = rte_pktmbuf_mtod(mbuf, uint8_t *);printf("External data: ");for (int i = 0; i < data_len; i++) {printf("%02X ", mbuf_data[i]);}printf("\n");// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
首先创建了一个外部数据缓冲区 external_data,然后分配了一个 mbuf,并将外部数据关联到该 mbuf 上。接着通过 rte_pktmbuf_mtod 函数可以获取指向 mbuf 数据区域的指针,通过该指针可以访问外部数据。最后释放了 mbuf
1.5 rte_pktmbuf_detach 函数用于从 mbuf 中分离关联的外部数据
当调用 rte_pktmbuf_attach 函数将外部数据与 mbuf 关联后,如果在后续的处理中不再需要使用外部数据,可以通过 rte_pktmbuf_detach 函数将其从 mbuf 中分离,以便释放或重新关联其他数据。函数原型如下:
void rte_pktmbuf_detach(struct rte_mbuf *m);
- 参数 m 是一个指向要操作的 mbuf 的指针。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 假设有一个外部数据缓冲区uint8_t external_data[] = {0x00, 0x11, 0x22, 0x33, 0x44};rte_iova_t data_iova; // 外部数据在物理内存中的地址uint16_t data_len = sizeof(external_data);// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 将外部数据关联到 mbuf 上rte_pktmbuf_attach(mbuf, external_data, data_iova, data_len);// 在此处可以使用 mbuf 访问外部数据// 处理完成后分离外部数据rte_pktmbuf_detach(mbuf);// 现在 mbuf 不再关联外部数据,可以进行其他操作,例如释放 mbuf// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
首先创建了一个外部数据缓冲区 external_data,然后分配了一个 mbuf,并将外部数据关联到该 mbuf 上。在需要使用外部数据时,可以通过 mbuf 来访问外部数据。处理完成后,通过调用 rte_pktmbuf_detach 函数将外部数据从 mbuf 中分离,此时 mbuf 不再关联外部数据,可以进行其他操作,例如释放 mbuf。
1.6 rte_pktmbuf_append 函数用于将数据附加到 mbuf 的末尾,扩展 mbuf 的数据区域
这在构建网络数据包时非常有用,可以逐步地将数据添加到 mbuf 中,而无需提前分配足够大的内存空间。函数原型如下:
struct rte_mbuf *rte_pktmbuf_append(struct rte_mbuf *m, uint16_t len);
- 参数 m 是一个指向要操作的 mbuf 的指针,而 len 则是要附加的数据长度
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 将数据附加到 mbuf 的末尾uint16_t data_len = 10;struct rte_mbuf *new_mbuf = rte_pktmbuf_append(mbuf, data_len);if (new_mbuf == NULL) {printf("Failed to append data to mbuf\n");rte_pktmbuf_free(mbuf); // 释放 mbufreturn -1;}// 在新的 mbuf 数据区域中填充数据,这里简单地填充了 0x01uint8_t *data = rte_pktmbuf_mtod_offset(new_mbuf, uint8_t *, 0);for (int i = 0; i < data_len; i++) {data[i] = 0x01;}// 现在 mbuf 中已经包含了附加的数据// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
以下是一些典型的使用场景:
数据包组装: 在发送数据包之前,需要将数据组装成一个完整的数据包。使用 rte_pktmbuf_append 函数可以逐步地将数据添加到 mbuf 中,直到构建完整的数据包。
数据包处理: 在接收数据包后,可能需要对数据包进行一些处理,例如解析报头、修改报头信息等。在处理过程中,可能需要动态地添加一些额外的数据或信息到数据包中,这时就可以使用 rte_pktmbuf_append 函数。
数据包重组: 在一些特定的网络处理场景中,可能需要对接收到的数据包进行重组或合并。例如,当收到一个分片数据包时,需要将多个分片的数据合并成一个完整的数据包。在重组过程中,可以使用 rte_pktmbuf_append 函数逐步地将分片数据添加到 mbuf 中。
数据包转发: 在数据包转发或路由过程中,可能需要修改数据包的一些信息,例如修改源地址、目标地址等。使用 rte_pktmbuf_append 函数可以方便地将额外的信息添加到数据包中,以便进行转发或路由操作。
1.7 rte_pktmbuf_prepend 函数用于在 mbuf 的开头预留一定长度的空间,以便将数据添加到 mbuf 的开头
这在一些网络处理场景中非常有用,例如在数据包头部添加额外的选项、修改头部信息等。函数原型如下
struct rte_mbuf *rte_pktmbuf_prepend(struct rte_mbuf *m, uint16_t len);
- 参数 m 是一个指向要操作的 mbuf 的指针,而 len 则是要预留的空间长度
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 开头预留空间uint16_t prepend_len = 10;struct rte_mbuf *new_mbuf = rte_pktmbuf_prepend(mbuf, prepend_len);if (new_mbuf == NULL) {printf("Failed to prepend space to mbuf\n");rte_pktmbuf_free(mbuf); // 释放 mbufreturn -1;}// 在新的 mbuf 开头填充数据,这里简单地填充了 0x01uint8_t *data = rte_pktmbuf_mtod_offset(new_mbuf, uint8_t *, 0);for (int i = 0; i < prepend_len; i++) {data[i] = 0x01;}// 现在 mbuf 中已经在开头预留了一定长度的空间,并填充了数据// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
1.8 rte_pktmbuf_adj 函数用于从 mbuf 的开头移除指定长度的数据,即调整 mbuf 的数据区域的起始位置和长度
这在一些网络处理场景中非常有用,例如在处理数据包时需要跳过数据包的头部信息,或者从数据包中截取部分数据。函数原型如下:
struct rte_mbuf *rte_pktmbuf_adj(struct rte_mbuf *m, uint16_t len);
- 参数 m 是一个指向要操作的 mbuf 的指针,
- len 则是要移除的数据长度
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 开头填充一些数据uint16_t initial_len = 20;for (int i = 0; i < initial_len; i++) {rte_pktmbuf_append(mbuf, 1); // 每次向 mbuf 中附加一个字节的数据}// 调整 mbuf 的数据区域,移除指定长度的数据uint16_t adj_len = 10;struct rte_mbuf *new_mbuf = rte_pktmbuf_adj(mbuf, adj_len);if (new_mbuf == NULL) {printf("Failed to adjust mbuf\n");rte_pktmbuf_free(mbuf); // 释放 mbufreturn -1;}// 现在 mbuf 中已经移除了指定长度的数据// 访问 mbuf 中的数据uint16_t new_len = rte_pktmbuf_data_len(new_mbuf);printf("New length of mbuf: %d\n", new_len);// 释放 mbufrte_pktmbuf_free(new_mbuf);return 0;
}
首先分配了一个 mbuf,并向其中附加了一些初始数据。然后调用 rte_pktmbuf_adj 函数从 mbuf 的开头移除了指定长度的数据。函数会返回一个新的 mbuf,该 mbuf 包含了原始 mbuf 中剩余的数据。可以通过 rte_pktmbuf_data_len 函数获取新的 mbuf 中的数据长度。最后释放了 mbuf
1.9 rte_pktmbuf_trim 函数用于从 mbuf 的末尾截取指定长度的数据,即调整 mbuf 的数据区域的长度
与 rte_pktmbuf_adj 函数相对应,rte_pktmbuf_trim 函数主要用于截取数据包的尾部数据。这在一些网络处理场景中非常有用,例如在处理数据包时需要丢弃数据包的尾部信息,或者从数据包中截取部分数据。函数原型如下:
void rte_pktmbuf_trim(struct rte_mbuf *m, uint16_t len);
- 参数 m 是一个指向要操作的 mbuf 的指针,
- len 则是要截取的数据长度
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 末尾填充一些数据uint16_t initial_len = 20;for (int i = 0; i < initial_len; i++) {rte_pktmbuf_append(mbuf, 1); // 每次向 mbuf 中附加一个字节的数据}// 调整 mbuf 的数据区域,截取指定长度的数据uint16_t trim_len = 10;rte_pktmbuf_trim(mbuf, trim_len);// 现在 mbuf 中已经截取了指定长度的数据// 访问 mbuf 中的数据uint16_t new_len = rte_pktmbuf_data_len(mbuf);printf("New length of mbuf: %d\n", new_len);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
首先分配了一个 mbuf,并向其中附加了一些初始数据。然后调用 rte_pktmbuf_trim 函数从 mbuf 的末尾截取了指定长度的数据。可以通过 rte_pktmbuf_data_len 函数获取 mbuf 中的新的数据长度。最后释放了 mbuf。
1.10 rte_pktmbuf_pkt_len 函数用于获取 mbuf 中网络数据包的总长度(packet length),即整个数据包的大小,包括所有的数据和头部信息
这在处理数据包时需要获取数据包的完整长度时非常有用。函数原型如下:
uint32_t rte_pktmbuf_pkt_len(const struct rte_mbuf *m);
- 参数 m 是一个指向要操作的 mbuf 的指针
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 中附加一些数据uint16_t data_len = 20;for (int i = 0; i < data_len; i++) {rte_pktmbuf_append(mbuf, 1); // 每次向 mbuf 中附加一个字节的数据}// 获取 mbuf 中网络数据包的总长度uint32_t pkt_len = rte_pktmbuf_pkt_len(mbuf);printf("Packet length: %u\n", pkt_len);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
首先分配了一个 mbuf,并向其中附加了一些数据。然后调用 rte_pktmbuf_pkt_len 函数获取 mbuf 中网络数据包的总长度,并打印出来。最后释放了 mbuf。
1.11 rte_pktmbuf_data_len 函数用于获取 mbuf 中有效数据的长度(data length),即除去头部信息后剩余数据的大小
这在处理数据包时需要知道实际有效数据的长度时非常有用。函数原型如下:
uint16_t rte_pktmbuf_data_len(const struct rte_mbuf *m);
- 参数 m 是一个指向要操作的 mbuf 的指针
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 中附加一些数据uint16_t data_len = 20;for (int i = 0; i < data_len; i++) {rte_pktmbuf_append(mbuf, 1); // 每次向 mbuf 中附加一个字节的数据}// 获取 mbuf 中有效数据的长度uint16_t data_len_result = rte_pktmbuf_data_len(mbuf);printf("Data length: %u\n", data_len_result);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 在 mbuf 中附加一些数据uint16_t data_len = 20;for (int i = 0; i < data_len; i++) {rte_pktmbuf_append(mbuf, 1); // 每次向 mbuf 中附加一个字节的数据}// 在 mbuf 中附加一些头部信息(假设每个头部信息占用10个字节)uint16_t header_len = 10;for (int i = 0; i < header_len; i++) {rte_pktmbuf_prepend(mbuf, 1); // 每次在 mbuf 头部添加一个字节的数据,模拟添加头部信息}// 获取 mbuf 中有效数据的长度(不包括头部信息)uint16_t data_len_result = rte_pktmbuf_data_len(mbuf);printf("Data length: %u\n", data_len_result);// 获取 mbuf 中整个数据包的总长度(包括头部信息)uint32_t pkt_len_result = rte_pktmbuf_pkt_len(mbuf);printf("Packet length: %u\n", pkt_len_result);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
在这个例子中,我们首先向 mbuf 中添加了20个字节的数据,然后在数据前面添加了10个字节的
头部信息。因此,实际有效数据的长度为20,而整个数据包的总长度为30。因此,
rte_pktmbuf_data_len 函数返回20,而 rte_pktmbuf_pkt_len 函数返回30
1.12 rte_pktmbuf_headroom 函数用于获取 mbuf 中头部区域的剩余空间大小
头部区域是指在 mbuf 的数据区域之前的未使用空间,通常用于添加头部信息或者在数据包处理过程中需要临时保存一些数据。这个函数返回的是头部区域的剩余空间大小,即还能向头部区域中添加多少字节的数据。函数原型如下:
uint16_t rte_pktmbuf_headroom(const struct rte_mbuf *m);
- 参数 m 是一个指向要操作的 mbuf 的指针
#include <rte_mbuf.h>
#include <stdio.h>#define VLAN_TAG_SIZE 4int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 获取 mbuf 中头部区域的剩余空间大小uint16_t headroom_size = rte_pktmbuf_headroom(mbuf);printf("Initial headroom size: %u\n", headroom_size);// 检查是否有足够的头部区域空间来添加 VLAN 标签if (headroom_size < VLAN_TAG_SIZE) {printf("Not enough headroom to add VLAN tag\n");rte_pktmbuf_free(mbuf);return -1;}// 在 mbuf 中头部区域添加 VLAN 标签(假设 VLAN 标签长度为4字节)rte_pktmbuf_prepend(mbuf, VLAN_TAG_SIZE);// 更新头部区域的剩余空间大小headroom_size = rte_pktmbuf_headroom(mbuf);printf("Headroom size after adding VLAN tag: %u\n", headroom_size);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
我们首先创建了一个 mbuf,并获取了头部区域的剩余空间大小。然后检查是否有足够的空间来添加 VLAN 标签,如果有足够的空间,则向头部区域添加 VLAN 标签。最后释放了 mbuf。
1.13 rte_pktmbuf_tailroom 函数用于获取 mbuf 中尾部区域的剩余空间大小
尾部区域是指在 mbuf 的数据区域之后的未使用空间,通常用于在数据包处理过程中向数据包中添加额外的数据或者头部信息。这个函数返回的是尾部区域的剩余空间大小,即还能向尾部区域中添加多少字节的数据。函数原型如下:
uint16_t rte_pktmbuf_tailroom(const struct rte_mbuf *m);
- 参数 m 是一个指向要操作的 mbuf 的指针。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建一个 mbufstruct rte_mbuf *mbuf = rte_pktmbuf_alloc(NULL);if (mbuf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 获取 mbuf 中尾部区域的剩余空间大小uint16_t tailroom_size = rte_pktmbuf_tailroom(mbuf);printf("Tailroom size: %u\n", tailroom_size);// 释放 mbufrte_pktmbuf_free(mbuf);return 0;
}
1.14 rte_pktmbuf_reset是DPDK(Data Plane Development Kit)中的一个函数,用于重置rte_mbuf结构体中的字段以及释放相关资源
void rte_pktmbuf_reset(struct rte_mbuf *m)
- 参数 m 是指向 rte_mbuf 结构体的指针,表示待重置的数据包缓冲区。
#include <rte_mbuf.h>
#include <stdio.h>int main() {struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(); // 分配一个数据包缓冲区// 使用数据包缓冲区...// 使用完后重置数据包缓冲区rte_pktmbuf_reset(pkt_buf);// 打印重置后的数据包缓冲区信息printf("After resetting the packet buffer:\n");printf("Data len: %u\n", pkt_buf->data_len);printf("Pkt len: %u\n", pkt_buf->pkt_len);return 0;
}
1.15 rte_pktmbuf_init函数用于初始化DPDK中的rte_mbuf结构体,设置相关的字段值
通常,这个函数在分配内存用于rte_mbuf结构体后立即调用,以确保结构体的正确初始化。以下是函数的原型:
struct rte_mbuf *rte_pktmbuf_init(struct rte_mempool *mp)
- 参数 mp 是指向rte_mempool结构体的指针,表示用于分配rte_mbuf结构体的内存池。
这个函数的主要作用是为rte_mbuf结构体的字段赋予合适的初始值,以便后续使用。通常情况下,你不需要自己手动调用这个函数,因为DPDK中的其他函数,如rte_pktmbuf_alloc会自动调用它来初始化新分配的rte_mbuf结构体。
#include <rte_mbuf.h>
#include <rte_mempool.h>
#include <stdio.h>int main() {struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 初始化一个rte_mbuf结构体struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 打印初始化后的rte_mbuf结构体信息printf("After initializing the packet buffer:\n");printf("Data len: %u\n", pkt_buf->data_len);printf("Pkt len: %u\n", pkt_buf->pkt_len);return 0;
}
我们首先创建了一个rte_mempool结构体 mbuf_pool,用于分配rte_mbuf结构体。然后,我们使用rte_pktmbuf_alloc函数从这个内存池中分配了一个rte_mbuf结构体 pkt_buf。由于rte_pktmbuf_alloc函数内部会自动调用rte_pktmbuf_init函数来初始化分配的rte_mbuf结构体,所以我们无需手动调用。最后,我们打印了初始化后的rte_mbuf结构体的信息,包括数据长度和数据包长度。
1.16 rte_pktmbuf_mtod函数用于获取rte_mbuf结构体中数据的指针,即数据的起始地址
通常情况下,它用于访问rte_mbuf中存储的数据,并进行数据处理。以下是函数的原型:
void *rte_pktmbuf_mtod(const struct rte_mbuf *mbuf, void *type)
- 参数 mbuf 是指向rte_mbuf结构体的指针,表示待获取数据指针的数据包缓冲区。
- 参数 type 是一个指针类型,用于指示所需数据的类型。在大多数情况下,可以将其设置为 void * 类型,并将其值设置为NULL。
#include <rte_mbuf.h>
#include <stdio.h>// 定义以太网帧的结构体
struct ethernet_frame {unsigned char dest_mac[6];unsigned char src_mac[6];uint16_t ethertype;// 其他字段...
};int main() {// 假设有一个已经初始化的rte_mbuf结构体 pkt_buf,其中存储了一段以太网帧数据struct rte_mbuf pkt_buf;// 此处省略数据的初始化过程...// 使用 rte_pktmbuf_mtod 函数获取数据指针struct ethernet_frame *eth_frame = rte_pktmbuf_mtod(&pkt_buf, struct ethernet_frame *);// 提取源MAC地址并打印printf("Source MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",eth_frame->src_mac[0], eth_frame->src_mac[1], eth_frame->src_mac[2],eth_frame->src_mac[3], eth_frame->src_mac[4], eth_frame->src_mac[5]);return 0;
}
在这个示例中,我们假设已经有一个初始化好的rte_mbuf结构体 pkt_buf,其中存储了一段以太网帧的数据。我们使用rte_pktmbuf_mtod函数获取数据指针,并将其转换为指向以太网帧结构体的指针。然后,我们通过该指针访问以太网帧结构体中的源MAC地址,并将其打印出来
1.17 rte_pktmbuf_mtod_offset函数类似于rte_pktmbuf_mtod,但是它允许指定一个偏移量,以便从指定位置开始获取数据的指针
void *rte_pktmbuf_mtod_offset(const struct rte_mbuf *mbuf, uint16_t offset, void *type)
- 参数 mbuf 是指向rte_mbuf结构体的指针,表示待获取数据指针的数据包缓冲区。
- 参数 offset 是一个16位无符号整数,表示从数据包缓冲区的起始位置开始的偏移量。
- 参数 type 是一个指针类型,用于指示所需数据的类型。在大多数情况下,可以将其设置为 void * 类型,并将其值设置为NULL
#include <rte_mbuf.h>
#include <stdio.h>// 定义IPv4头部的结构体
struct ipv4_header {uint8_t version_ihl;uint8_t tos;uint16_t total_length;uint16_t id;uint16_t fragment_offset;uint8_t ttl;uint8_t protocol;uint16_t checksum;uint32_t src_addr;uint32_t dest_addr;
};int main() {// 假设有一个已经初始化的rte_mbuf结构体 pkt_buf,其中存储了一个IPv4数据包struct rte_mbuf pkt_buf;// 此处省略数据的初始化过程...// 使用 rte_pktmbuf_mtod_offset 函数获取IPv4头部的起始地址struct ipv4_header *ipv4_hdr = rte_pktmbuf_mtod_offset(&pkt_buf, sizeof(struct ether_hdr), struct ipv4_header *);// 提取源IP地址并打印char src_ip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET, &(ipv4_hdr->src_addr), src_ip_str, INET_ADDRSTRLEN);printf("Source IP Address: %s\n", src_ip_str);return 0;
}
1.18 rte_pktmbuf_mtod_offset_update函数是DPDK中的一个函数,用于获取rte_mbuf结构体中数据的指针,并且可以指定一个偏移量来更新数据指针的位置
这个函数通常用于处理数据包中的不同部分,特别是在处理变长协议头(例如VLAN标签、IPv4选项等)时非常有用。以下是函数的原型:
void *rte_pktmbuf_mtod_offset_update(struct rte_mbuf *mbuf, uint16_t offset)
-
参数 mbuf 是指向rte_mbuf结构体的指针,表示待获取数据指针的数据包缓冲区。
-
参数 offset 是一个16位无符号整数,表示从数据包缓冲区的起始位置开始的新的偏移量。
这个函数返回指向更新后的数据包缓冲区中指定偏移量处数据的指针。
#include <rte_mbuf.h>
#include <stdio.h>// 定义IPv4头部的结构体
struct ipv4_header {uint8_t version_ihl;uint8_t tos;uint16_t total_length;uint16_t id;uint16_t fragment_offset;uint8_t ttl;uint8_t protocol;uint16_t checksum;uint32_t src_addr;uint32_t dest_addr;
};int main() {// 假设有一个已经初始化的rte_mbuf结构体 pkt_buf,其中存储了一个IPv4数据包struct rte_mbuf pkt_buf;// 此处省略数据的初始化过程...// 使用 rte_pktmbuf_mtod_offset_update 函数更新数据指针的位置,跳过以太网头部struct ipv4_header *ipv4_hdr = rte_pktmbuf_mtod_offset_update(&pkt_buf, sizeof(struct ether_hdr));// 打印IPv4头部的源IP地址printf("Source IP Address: %u.%u.%u.%u\n",(ipv4_hdr->src_addr >> 24) & 0xFF,(ipv4_hdr->src_addr >> 16) & 0xFF,(ipv4_hdr->src_addr >> 8) & 0xFF,ipv4_hdr->src_addr & 0xFF);return 0;
}
使用场景差异:
rte_pktmbuf_mtod_offset 函数适用于当你需要获取数据包缓冲区中指定偏移量处的数据指针,但不需要修改原始数据包缓冲区的指针位置时。
rte_pktmbuf_mtod_offset_update 函数适用于当你需要获取数据包缓冲区中指定偏移量处的数据指针,并且希望在获取指针的同时更新原始数据包缓冲区的指针位置时。
1.19 rte_pktmbuf_pkt_num_segs函数用于获取给定数据包缓冲区(rte_mbuf)所包含的分片数量(即链表中的节点数)
在DPDK中,数据包可能由多个分片(segments)组成,这些分片通过链表连接在一起。这个函数可以帮助你了解数据包的组成情况。以下是函数的原型:
uint16_t rte_pktmbuf_pkt_num_segs(const struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示待获取分片数量的数据包缓冲区。
这个函数返回数据包缓冲区所包含的分片数量。
#include <rte_mbuf.h>
#include <stdio.h>#define DATA_LEN 64int main() {// 创建一个数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()));if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 添加两个分片到数据包缓冲区struct rte_mbuf *seg1 = rte_pktmbuf_alloc(pkt_buf->pool);struct rte_mbuf *seg2 = rte_pktmbuf_alloc(pkt_buf->pool);if (seg1 == NULL || seg2 == NULL) {printf("Failed to allocate segments\n");return -1;}// 初始化分片的数据rte_pktmbuf_append(pkt_buf, DATA_LEN);rte_pktmbuf_append(seg1, DATA_LEN);rte_pktmbuf_append(seg2, DATA_LEN);// 将分片添加到数据包缓冲区rte_pktmbuf_chain(pkt_buf, seg1);rte_pktmbuf_chain(pkt_buf, seg2);// 使用 rte_pktmbuf_pkt_num_segs 函数获取数据包所包含的分片数量uint16_t num_segs = rte_pktmbuf_pkt_num_segs(pkt_buf);// 打印分片数量printf("Number of segments in the packet buffer: %u\n", num_segs);return 0;
}
我们首先创建了一个数据包缓冲区 pkt_buf,然后分别创建了两个分片 seg1 和 seg2。接着,我们为每个分片分配了一定长度的数据。然后,我们使用rte_pktmbuf_chain函数将这两个分片添加到数据包缓冲区中。最后,我们使用rte_pktmbuf_pkt_num_segs函数来获取数据包所包含的分片数量,并将结果打印出来
1.20 rte_pktmbuf_pkt_type函数用于获取数据包的类型,即数据包的协议类型(例如IPv4、IPv6、ARP等)
这个函数可以帮助你在处理数据包时根据其类型采取不同的处理逻辑。以下是函数的原型:
enum rte_net_hdr_lens rte_pktmbuf_pkt_type(const struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示待获取类型的数据包缓冲区。
#include <rte_mbuf.h>
#include <stdio.h>// 枚举类型定义了不同的数据包类型
enum pkt_type {UNKNOWN,IPV4,IPV6,ARP,VLAN,// 可以根据需要添加更多类型
};int main() {// 假设有一个已经初始化的rte_mbuf结构体 pkt_buf,其中存储了一个数据包struct rte_mbuf pkt_buf;// 此处省略数据包的初始化过程...// 使用 rte_pktmbuf_pkt_type 函数获取数据包的类型enum rte_net_hdr_lens pkt_type = rte_pktmbuf_pkt_type(&pkt_buf);// 根据不同的数据包类型执行不同的处理逻辑switch (pkt_type) {case RTE_PTYPE_L2_ETHER:printf("Ethernet frame detected\n");// 执行与以太网帧相关的处理逻辑...break;case RTE_PTYPE_L3_IPV4:printf("IPv4 packet detected\n");// 执行与IPv4数据包相关的处理逻辑...break;case RTE_PTYPE_L3_IPV6:printf("IPv6 packet detected\n");// 执行与IPv6数据包相关的处理逻辑...break;case RTE_PTYPE_L2_ETHER_VLAN:printf("VLAN frame detected\n");// 执行与VLAN帧相关的处理逻辑...break;case RTE_PTYPE_L2_ETHER_ARP:printf("ARP packet detected\n");// 执行与ARP数据包相关的处理逻辑...break;default:printf("Unknown packet type\n");// 执行未知数据包类型的处理逻辑...break;}return 0;
}
1.21 rte_pktmbuf_pull函数用于移除数据包缓冲区中的数据
它类似于消耗数据包的一部分,使得数据包的长度减少。这个函数通常用于移除数据包头部的一部分,例如以太网头部或者IP头部。以下是函数的原型:
struct rte_mbuf *rte_pktmbuf_pull(struct rte_mbuf *m, uint16_t len)
-
参数 m 是指向rte_mbuf结构体的指针,表示待处理的数据包缓冲区。
-
参数 len 是要移除的数据的长度,单位为字节。
这个函数返回一个指向处理后的数据包缓冲区的指针
#include <rte_mbuf.h>
#include <stdio.h>#define DATA_LEN 64
#define ETH_HDR_LEN sizeof(struct rte_ether_hdr)
#define IPV4_HDR_LEN sizeof(struct rte_ipv4_hdr)int main() {// 初始化数据包缓冲区struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 创建一个数据包缓冲区,假设包含了一个完整的IPv4数据包struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 初始化IPv4数据包内容uint32_t src_ip = rte_cpu_to_be_32(0xC0A80101); // 192.168.1.1uint32_t dest_ip = rte_cpu_to_be_32(0xC0A80102); // 192.168.1.2// 设置以太网头部struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(pkt_buf, struct rte_ether_hdr *);rte_eth_dev_mac_addr_get(0, ð_hdr->s_addr); // 获取本机MAC地址eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4); // 设置以太网类型// 设置IPv4头部struct rte_ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod_offset(pkt_buf, ETH_HDR_LEN, struct rte_ipv4_hdr *);ipv4_hdr->version_ihl = 0x45;ipv4_hdr->type_of_service = 0;ipv4_hdr->total_length = rte_cpu_to_be_16(DATA_LEN - ETH_HDR_LEN); // 假设数据长度为64字节,减去以太网头部长度ipv4_hdr->packet_id = 0;ipv4_hdr->fragment_offset = 0;ipv4_hdr->time_to_live = 64;ipv4_hdr->next_proto_id = IPPROTO_UDP;ipv4_hdr->src_addr = src_ip;ipv4_hdr->dst_addr = dest_ip;ipv4_hdr->hdr_checksum = 0; // 计算校验和时自动填充// 使用 rte_pktmbuf_pull 函数移除以太网头部和IPv4头部struct rte_mbuf *new_pkt_buf = rte_pktmbuf_pull(pkt_buf, ETH_HDR_LEN + IPV4_HDR_LEN);if (new_pkt_buf == NULL) {printf("Failed to pull packet\n");return -1;}// 打印处理后的数据包长度printf("Packet length after pulling Ethernet header and IPv4 header: %u\n", new_pkt_buf->data_len);// 释放数据包缓冲区rte_pktmbuf_free(new_pkt_buf);return 0;
}
1.22 rte_pktmbuf_push函数用于在数据包缓冲区的开头插入数据。
它通常用于在数据包前面添加一些新的数据,比如添加新的头部信息。以下是函数的原型:
struct rte_mbuf *rte_pktmbuf_push(struct rte_mbuf *m, uint16_t len)
-
参数 m 是指向rte_mbuf结构体的指针,表示待处理的数据包缓冲区。
-
参数 len 是要插入的数据的长度,单位为字节。
#include <rte_mbuf.h>
#include <stdio.h>
#include <string.h>#define DATA_LEN 64
#define IPV4_HDR_LEN sizeof(struct rte_ipv4_hdr)
#define IPV6_HDR_LEN sizeof(struct rte_ipv6_hdr)int main() {// 初始化数据包缓冲区struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 创建一个数据包缓冲区,假设包含了一个完整的IPv4数据包struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 初始化IPv4数据包内容uint32_t src_ip = rte_cpu_to_be_32(0xC0A80101); // 192.168.1.1uint32_t dest_ip = rte_cpu_to_be_32(0xC0A80102); // 192.168.1.2// 设置IPv4头部struct rte_ipv4_hdr *ipv4_hdr = rte_pktmbuf_mtod(pkt_buf, struct rte_ipv4_hdr *);ipv4_hdr->version_ihl = 0x45;ipv4_hdr->type_of_service = 0;ipv4_hdr->total_length = rte_cpu_to_be_16(DATA_LEN); // 假设数据长度为64字节ipv4_hdr->packet_id = 0;ipv4_hdr->fragment_offset = 0;ipv4_hdr->time_to_live = 64;ipv4_hdr->next_proto_id = IPPROTO_UDP;ipv4_hdr->src_addr = src_ip;ipv4_hdr->dst_addr = dest_ip;ipv4_hdr->hdr_checksum = 0; // 计算校验和时自动填充// 使用 rte_pktmbuf_push 函数在数据包前面插入IPv6头部struct rte_mbuf *new_pkt_buf = rte_pktmbuf_push(pkt_buf, IPV6_HDR_LEN);if (new_pkt_buf == NULL) {printf("Failed to push packet\n");return -1;}// 在新插入的IPv6头部中填充一些数据内容struct rte_ipv6_hdr *ipv6_hdr = rte_pktmbuf_mtod(new_pkt_buf, struct rte_ipv6_hdr *);memset(ipv6_hdr, 0, IPV6_HDR_LEN); // 清空IPv6头部ipv6_hdr->vtc_flow = rte_cpu_to_be_32(0x60000000); // 设置版本号和流标识ipv6_hdr->payload_len = rte_cpu_to_be_16(DATA_LEN - IPV6_HDR_LEN); // 设置负载长度ipv6_hdr->hop_limits = 64; // 设置跳数限制// 假设源和目的IPv6地址struct in6_addr src_addr = {.s6_addr = {0xfe, 0x80, 0, 0, 0, 0, 0, 0x1}};struct in6_addr dest_addr = {.s6_addr = {0xfe, 0x80, 0, 0, 0, 0, 0, 0x2}};ipv6_hdr->src_addr = src_addr;ipv6_hdr->dst_addr = dest_addr;// 打印插入后的数据包长度printf("Packet length after pushing new IPv6 header: %u\n", new_pkt_buf->data_len);// 释放数据包缓冲区rte_pktmbuf_free(new_pkt_buf);return 0;
}
1.23 rte_pktmbuf_alloc_bulk函数用于从内存池中批量分配一组数据包缓冲区
这在需要处理多个数据包的场景下非常有用,可以减少内存分配的开销。以下是函数的原型:
unsigned int rte_pktmbuf_alloc_bulk(struct rte_mempool *pool, struct rte_mbuf **mbufs, unsigned int count)
- 参数 pool 是指向内存池的指针,从这个内存池中分配数据包缓冲区。
- 参数 mbufs 是一个指向rte_mbuf指针数组的指针,用于保存分配的数据包缓冲区。
- 参数 count 是要分配的数据包缓冲区的数量。
#include <rte_mbuf.h>
#include <stdio.h>#define NUM_MBUFS 10int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *mbufs[NUM_MBUFS];unsigned int num_allocated = rte_pktmbuf_alloc_bulk(mbuf_pool, mbufs, NUM_MBUFS);if (num_allocated != NUM_MBUFS) {printf("Failed to allocate all mbufs\n");return -1;}// 打印分配结果printf("Allocated %u mbufs\n", num_allocated);// 释放数据包缓冲区for (int i = 0; i < num_allocated; ++i) {rte_pktmbuf_free(mbufs[i]);}return 0;
}
1.24 rte_pktmbuf_free_bulk函数用于释放一组数据包缓冲区,与rte_pktmbuf_free类似,但是可以一次性释放多个数据包缓冲区
这在需要释放多个数据包缓冲区的场景下非常有用,可以提高效率并减少内存管理的开销。以下是函数的原型
void rte_pktmbuf_free_bulk(struct rte_mbuf **mbufs, unsigned int count)
- 参数 mbufs 是一个指向rte_mbuf指针数组的指针,其中包含要释放的数据包缓冲区。
- 参数 count 是要释放的数据包缓冲区的数量。
#include <rte_mbuf.h>
#include <stdio.h>#define NUM_MBUFS 10int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *mbufs[NUM_MBUFS];unsigned int num_allocated = rte_pktmbuf_alloc_bulk(mbuf_pool, mbufs, NUM_MBUFS);if (num_allocated != NUM_MBUFS) {printf("Failed to allocate all mbufs\n");return -1;}// 打印分配结果printf("Allocated %u mbufs\n", num_allocated);// 释放数据包缓冲区rte_pktmbuf_free_bulk(mbufs, num_allocated);// 打印释放结果printf("Freed %u mbufs\n", num_allocated);return 0;
}
1.25 rte_pktmbuf_clone_bulk函数用于对一组数据包缓冲区进行批量克隆操作
克隆操作会创建一组新的数据包缓冲区,这些新的缓冲区与原始数据包缓冲区具有相同的数据内容。这在需要对一组数据包进行操作而又不想修改原始数据包时非常有用。以下是函数的原型:
unsigned int rte_pktmbuf_clone_bulk(struct rte_mbuf **src, struct rte_mbuf **dst, unsigned int count, struct rte_mempool *pool)
- 参数 src 是一个指向原始数据包缓冲区的指针数组,其中包含要克隆的数据包缓冲区。
- 参数 dst 是一个指向rte_mbuf指针数组的指针,用于保存克隆后的数据包缓冲区。
- 参数 count 是要克隆的数据包缓冲区的数量。
- 参数 pool 是指向用于存储新数据包缓冲区的内存池的指针。
#include <rte_mbuf.h>
#include <stdio.h>#define NUM_MBUFS 10int main() {// 创建源数据包缓冲区内存池struct rte_mempool *src_pool = rte_pktmbuf_pool_create("SRC_MB_POOL", NUM_MBUFS, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (src_pool == NULL) {printf("Failed to create source mbuf pool\n");return -1;}// 创建目标数据包缓冲区内存池struct rte_mempool *dst_pool = rte_pktmbuf_pool_create("DST_MB_POOL", NUM_MBUFS, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (dst_pool == NULL) {printf("Failed to create destination mbuf pool\n");return -1;}// 分配源数据包缓冲区struct rte_mbuf *src_mbufs[NUM_MBUFS];unsigned int num_allocated = rte_pktmbuf_alloc_bulk(src_pool, src_mbufs, NUM_MBUFS);if (num_allocated != NUM_MBUFS) {printf("Failed to allocate all source mbufs\n");return -1;}// 打印源数据包缓冲区分配结果printf("Allocated %u source mbufs\n", num_allocated);// 克隆数据包缓冲区struct rte_mbuf *dst_mbufs[NUM_MBUFS];unsigned int num_cloned = rte_pktmbuf_clone_bulk(src_mbufs, dst_mbufs, NUM_MBUFS, dst_pool);if (num_cloned != NUM_MBUFS) {printf("Failed to clone all mbufs\n");return -1;}// 打印克隆结果printf("Cloned %u mbufs\n", num_cloned);// 释放源数据包缓冲区for (int i = 0; i < num_allocated; ++i) {rte_pktmbuf_free(src_mbufs[i]);}// 释放目标数据包缓冲区for (int i = 0; i < num_cloned; ++i) {rte_pktmbuf_free(dst_mbufs[i]);}return 0;
}
1.26 rte_pktmbuf_attach_extbuf函数用于将外部内存缓冲区附加到数据包缓冲区上,以便在外部内存上操作数据包
这在需要处理大型数据包或者需要避免内存复制的情况下非常有用。以下是函数的原型:
int rte_pktmbuf_attach_extbuf(struct rte_mbuf *m, void *buf_addr, rte_iova_t buf_iova, uint16_t buf_len, rte_mbuf_extbuf_free_callback_t free_cb, void *cb_arg)
- 参数 m 是指向rte_mbuf结构体的指针,表示要附加外部内存缓冲区的数据包缓冲区。
- 参数 buf_addr 是指向外部内存缓冲区的指针。
- 参数 buf_iova 是外部内存缓冲区的物理地址。
- 参数 buf_len 是外部内存缓冲区的长度,单位为字节。
- 参数 free_cb 是一个回调函数,用于释放外部内存缓冲区。函数原型为 void free_cb(void *addr, void *opaque),其中 addr 是外部内存缓冲区的起始地址,opaque 是回调函数的不透明指针参数。
- 参数 cb_arg 是传递给回调函数 free_cb 的不透明指针参数。
函数返回值为 0 表示成功,返回负值表示失败。
#include <rte_mbuf.h>
#include <stdio.h>#define BUF_LEN 2048// 定义释放外部缓冲区的回调函数
void free_cb(void *addr, void *opaque) {// 在此处释放外部缓冲区,这里简单地打印释放信息printf("Freeing external buffer at address: %p\n", addr);
}int main() {// 分配外部内存缓冲区char ext_buf[BUF_LEN];// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()));if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 将外部内存缓冲区附加到数据包缓冲区上int ret = rte_pktmbuf_attach_extbuf(pkt_buf, ext_buf, rte_malloc_virt2phy(ext_buf), BUF_LEN, free_cb, NULL);if (ret < 0) {printf("Failed to attach external buffer\n");return -1;}// 打印附加结果printf("External buffer attached successfully\n");// 执行其他操作...return 0;
}
1.27rte_pktmbuf_detach_extbuf函数用于从数据包缓冲区上分离已附加的外部内存缓冲区
这在需要释放外部内存缓冲区或者需要将数据包恢复为完全内部存储的情况下非常有用。以下是函数的原型:
void rte_pktmbuf_detach_extbuf(struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示要分离外部内存缓冲区的数据包缓冲区
#include <rte_mbuf.h>
#include <stdio.h>// 定义释放外部缓冲区的回调函数
void free_cb(void *addr, void *opaque) {// 在此处释放外部缓冲区,这里简单地打印释放信息printf("Freeing external buffer at address: %p\n", addr);
}int main() {// 分配外部内存缓冲区char ext_buf[2048];// 分配数据包缓冲区并附加外部内存缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()));if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}rte_pktmbuf_attach_extbuf(pkt_buf, ext_buf, rte_malloc_virt2phy(ext_buf), sizeof(ext_buf), free_cb, NULL);// 打印附加结果printf("External buffer attached successfully\n");// 执行其他操作...// 分离外部内存缓冲区rte_pktmbuf_detach_extbuf(pkt_buf);// 打印分离结果printf("External buffer detached successfully\n");return 0;
}
1.28 rte_pktmbuf_is_contiguous函数用于检查数据包缓冲区是否具有连续的数据内存布局
这在需要处理连续内存数据的场景下非常有用。以下是函数的原型:
int rte_pktmbuf_is_contiguous(const struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示要检查的数据包缓冲区。
在DPDK中,数据包缓冲区的连续性通常指的是数据区域的连续性,即数据包中的数据是否存储在连续的内存块中。连续的数据包缓冲区可能有以下情况:
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 检查数据包缓冲区的内存布局是否连续int is_contiguous = rte_pktmbuf_is_contiguous(pkt_buf);// 打印检查结果if (is_contiguous) {printf("Packet buffer has contiguous memory layout\n");} else {printf("Packet buffer does not have contiguous memory layout\n");}// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
单一段内存块分配:当使用rte_pktmbuf_alloc函数从内存池中分配数据包缓冲区时,并且内存池中有足够的空闲连续内存块时,分配的数据包缓冲区可能会是连续的。这通常发生在对较小的数据包进行分配时,以至于数据包的整个数据区域都可以存储在一个连续的内存块中。
外部内存缓冲区附加:当使用rte_pktmbuf_attach_extbuf函数将外部内存缓冲区附加到数据包缓冲区时,如果外部内存缓冲区是连续的,那么数据包缓冲区也会变得连续。这种情况下,外部内存缓冲区的连续性决定了数据包缓冲区的连续性。
在以上情况下,数据包缓冲区可能具有连续的数据内存布局。然而,当数据包的数据区域跨越多个内存块或者存在非连续的外部内存缓冲区时,数据包缓冲区就不会是连续的。
1.29 rte_pktmbuf_refcnt_read函数用于读取数据包缓冲区的引用计数值
引用计数表示有多少个指针引用了数据包缓冲区,这对于跟踪数据包缓冲区的使用情况非常有用。以下是函数的原型:
uint16_t rte_pktmbuf_refcnt_read(const struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示要读取引用计数的数据包缓冲区
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 读取数据包缓冲区的引用计数uint16_t refcnt = rte_pktmbuf_refcnt_read(pkt_buf);// 打印引用计数值printf("Packet buffer reference count: %u\n", refcnt);// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
引用计数(Reference Count)
是一种内存管理技术,用于跟踪共享资源的引用情况。在数据包缓冲区的情境中,引用计数表示有多少个指针指向了同一个数据包缓冲区。每当有一个新的指针指向数据包缓冲区时,引用计数就会增加;相应地,当指针不再引用数据包缓冲区时,引用计数会减少。当引用计数为零时,表示没有任何指针指向数据包缓冲区,可以安全地释放或回收该数据包缓冲区。
引用计数在多种情况下都非常有用,例如:
共享资源管理:
当多个对象需要共享同一个资源时,可以使用引用计数来管理资源的生命周期,确保资源在不再被引用时能够被及时释放或回收。
内存管理:
在动态内存分配中,可以使用引用计数来跟踪动态分配的内存块的引用情况,从而实现自动内存管理和垃圾回收。
数据结构管理:
在一些数据结构中,如智能指针、共享指针和数据包缓冲区等,引用计数可以用于跟踪数据结构的引用情况,防止内存泄漏和悬挂指针的出现。
总的来说,引用计数是一种简单而有效的内存管理技术,可以帮助程序员更轻松地管理共享资源的生命周期,并防止内存泄漏和悬挂指针等问题的出现
1.30 rte_pktmbuf_metadata_read函数用于读取数据包缓冲区的元数据
元数据是一些与数据包相关的附加信息,可以用于传递额外的信息或配置给数据包的处理函数。以下是函数的原型:
uint64_t rte_pktmbuf_metadata_read(const struct rte_mbuf *m)
- 参数 m 是指向rte_mbuf结构体的指针,表示要读取元数据的数据包缓冲区。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 设置元数据uint64_t metadata_value = 0x123456789abcdef0;rte_pktmbuf_metadata_write(pkt_buf, metadata_value);// 读取元数据uint64_t read_metadata_value = rte_pktmbuf_metadata_read(pkt_buf);// 打印读取的元数据值printf("Metadata value: %" PRIx64 "\n", read_metadata_value);// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
在 DPDK 中,元数据存储在数据包缓冲区的私有区域(private area)。私有区域是数据包缓冲区的一部分,用于存储与数据包相关的额外信息,如元数据、RSS 配置等。私有区域位于数据包缓冲区的尾部,紧随数据包的实际数据部分。
DPDK 中的数据包缓冲区结构 rte_mbuf 中定义了一个字段 priv_size,表示私有区域的大小。在初始化数据包池时,可以通过指定私有区域的大小来设置 priv_size。在调用 rte_pktmbuf_alloc 函数分配数据包缓冲区时,会根据私有区域的大小在数据包缓冲区的尾部分配一段空间用于存储私有数据,其中也包括元数据。
因此,元数据存储在数据包缓冲区的尾部,紧随数据包的实际数据部分。要读取或写入元数据,只需使用 rte_pktmbuf_metadata_read 和 rte_pktmbuf_metadata_write 函数即可,DPDK 会自动处理元数据的存储和读取操作,无需关心具体的存储位置。
1.31 rte_pktmbuf_linearize 函数用于将非连续的数据包缓冲区转换为连续的数据包缓冲区,以便于进行线性访问
当数据包缓冲区分散在多个分片中时,线性化操作可以提高数据包处理的效率。以下是函数的原型:
int rte_pktmbuf_linearize(struct rte_mbuf *m)
参数 m 是指向 rte_mbuf 结构体的指针,表示要进行线性化的数据包缓冲区
#include <rte_mbuf.h>
#include <stdio.h>#define MAX_DATA_LEN 128int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 创建数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 设置数据包缓冲区的内容(分片)const char *data1 = "Hello, ";const char *data2 = "world!";rte_pktmbuf_append(pkt_buf, strlen(data1));rte_memcpy(rte_pktmbuf_append(pkt_buf, strlen(data2)), data1, strlen(data1));rte_memcpy(rte_pktmbuf_append(pkt_buf, strlen(data2)), data2, strlen(data2));// 打印线性化前的数据包缓冲区内容printf("Before linearization:\n");struct rte_mbuf *cur_seg = pkt_buf;while (cur_seg != NULL) {printf("%.*s", (int)cur_seg->data_len, rte_pktmbuf_mtod(cur_seg, char *));cur_seg = cur_seg->next;}printf("\n");// 线性化数据包缓冲区int ret = rte_pktmbuf_linearize(pkt_buf);if (ret != 0) {printf("Linearization failed\n");rte_pktmbuf_free(pkt_buf);return -1;}// 打印线性化后的数据包缓冲区内容printf("After linearization:\n");printf("%.*s\n", (int)pkt_buf->pkt_len, rte_pktmbuf_mtod(pkt_buf, char *));// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
在这个示例中,我们首先创建了一个名为 MBUF_POOL 的内存池,然后使用 rte_pktmbuf_alloc 函数从内存池中分配了一个数据包缓冲区 pkt_buf。接着,我们使用 rte_pktmbuf_append 函数向数据包缓冲区中添加了两个分片,分别存储字符串 "Hello, " 和 “world!”。然后,我们打印了线性化前的数据包缓冲区内容。
1.32 rte_pktmbuf_chain 函数用于将两个数据包缓冲区链成一个链表
这在处理需要多个数据包的场景中很有用,例如在处理分片数据包时。
struct rte_mbuf *rte_pktmbuf_chain(struct rte_mbuf *head, struct rte_mbuf *tail)
- 参数 head 是指向第一个数据包缓冲区的指针,它将成为链表的头部。
- 参数 tail 是指向第二个数据包缓冲区的指针,它将成为链表的尾部。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 2, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配第一个数据包缓冲区struct rte_mbuf *pkt_buf1 = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf1 == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 分配第二个数据包缓冲区struct rte_mbuf *pkt_buf2 = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf2 == NULL) {printf("Failed to allocate mbuf\n");rte_pktmbuf_free(pkt_buf1);return -1;}// 将两个数据包缓冲区链成一个链表struct rte_mbuf *chain_head = rte_pktmbuf_chain(pkt_buf1, pkt_buf2);// 计算链表的长度uint16_t chain_length = 0;struct rte_mbuf *m = chain_head;while (m != NULL) {chain_length++;m = m->next;}// 打印链表的长度printf("Length of the packet chain: %u\n", chain_length);// 释放链表中的数据包缓冲区rte_pktmbuf_free(chain_head);return 0;
}
1.33 rte_pktmbuf_linearize_seg 函数用于线性化数据包缓冲区的指定片段,即将片段中的分散数据复制到一个连续的线性缓冲区中
这在处理分片数据包时很有用,因为分片数据通常会分散存储在不同的数据包缓冲区中,而一些处理操作可能要求数据是连续的
uint32_t rte_pktmbuf_linearize_seg(struct rte_mbuf *pkt, uint32_t seg_len_offset, uint32_t data_off, uint32_t data_len)
- 参数 pkt 是要线性化的数据包缓冲区。
- 参数 seg_len_offset 是片段长度字段的偏移量,通常为RTE_PKTMBUF_HEADROOM。
- 参数 data_off 是要线性化的数据片段的偏移量。
- 参数 data_len 是要线性化的数据片段的长度。
函数返回线性化后的数据包长度,如果失败则返回0
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 向数据包缓冲区填充一些数据uint32_t data_len = 100;rte_pktmbuf_append(pkt_buf, data_len);// 打印数据包缓冲区原始状态printf("Original packet length: %u\n", rte_pktmbuf_pkt_len(pkt_buf));// 线性化数据包缓冲区的指定片段uint32_t seg_len_offset = RTE_PKTMBUF_HEADROOM;uint32_t data_off = 10;uint32_t linearized_len = rte_pktmbuf_linearize_seg(pkt_buf, seg_len_offset, data_off, data_len);// 打印线性化后的数据包长度printf("Linearized packet length: %u\n", linearized_len);// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
在这个示例中,我们首先创建了一个名为 MBUF_POOL 的内存池,然后使用 rte_pktmbuf_alloc 函数从内存池中分配了一个数据包缓冲区 pkt_buf。接着,我们向数据包缓冲区填充了一些数据。然后,我们调用 rte_pktmbuf_linearize_seg 函数线性化数据包缓冲区的指定片段。最后,我们打印线性化后的数据包长度,并释放了数据包缓冲区。
1.34 rte_pktmbuf_free_seg 函数用于释放数据包缓冲区的指定片段
这在处理数据包时需要释放部分数据时很有用,例如在数据包重组或数据包剪裁时。
void rte_pktmbuf_free_seg(struct rte_mbuf *m, uint32_t offset)
- 参数 m 是要释放的数据包缓冲区。
- 参数 offset 是要释放的片段的偏移量。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 向数据包缓冲区填充一些数据uint32_t data_len = 100;rte_pktmbuf_append(pkt_buf, data_len);// 打印数据包缓冲区原始状态printf("Original packet length: %u\n", rte_pktmbuf_pkt_len(pkt_buf));// 释放数据包缓冲区的指定片段uint32_t offset = 10;rte_pktmbuf_free_seg(pkt_buf, offset);// 打印释放片段后的数据包长度printf("Packet length after freeing segment: %u\n", rte_pktmbuf_pkt_len(pkt_buf));// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
1.35 rte_pktmbuf_data_iova 函数用于获取数据包缓冲区的数据物理地址(IOVA),以便在DMA等硬件操作中使用
在一些需要 DMA 的场景下,例如网络包的发送和接收,获取数据包缓冲区的物理地址是必要的。
phys_addr_t rte_pktmbuf_data_iova(const struct rte_mbuf *m)
- 参数 m 是要获取数据物理地址的数据包缓冲区。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 获取数据包缓冲区的数据物理地址phys_addr_t data_iova = rte_pktmbuf_data_iova(pkt_buf);// 打印数据物理地址printf("Data physical address: %#lx\n", data_iova);// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}
1.36 rte_pktmbuf_data_mz 函数用于获取数据包缓冲区的内存区域的物理地址,该内存区域可能属于DPDK的hugepage内存区(即大页内存)。
通常在需要使用硬件加速的场景下,需要将数据包缓冲区的内存区域分配在hugepage内存中以获得更好的性能。
phys_addr_t rte_pktmbuf_data_mz(const struct rte_mbuf *m)
- 参数 m 是要获取内存区域物理地址的数据包缓冲区。
#include <rte_mbuf.h>
#include <stdio.h>int main() {// 创建内存池struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 1, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if (mbuf_pool == NULL) {printf("Failed to create mbuf pool\n");return -1;}// 分配数据包缓冲区struct rte_mbuf *pkt_buf = rte_pktmbuf_alloc(mbuf_pool);if (pkt_buf == NULL) {printf("Failed to allocate mbuf\n");return -1;}// 获取数据包缓冲区的内存区域的物理地址phys_addr_t data_mz = rte_pktmbuf_data_mz(pkt_buf);// 打印内存区域的物理地址printf("Memory zone physical address: %#lx\n", data_mz);// 释放数据包缓冲区rte_pktmbuf_free(pkt_buf);return 0;
}