DPDK常用API合集二

网络数据包缓冲管理(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 的初始化流程完成

    // 从内存池中分配一个新的 mbuf
    mbuf = rte_pktmbuf_alloc(mbuf_pool);
    if (mbuf == NULL) {
        // 分配失败
        printf("Failed to allocate mbuf\n");
        return -1;
    }

    // 在这里可以对分配的 mbuf 进行操作,如填充数据、设置头部信息等

    // 释放 mbuf
    rte_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 的初始化流程完成

    // 从内存池中分配一个新的 mbuf
    mbuf = rte_pktmbuf_alloc(mbuf_pool);
    if (mbuf == NULL) {
        // 分配失败
        printf("Failed to allocate mbuf\n");
        return -1;
    }

    // 在这里可以对分配的 mbuf 进行操作,如填充数据、设置头部信息等

    // 使用完毕后释放 mbuf
    rte_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_mbuf
    cloned_mbuf = rte_pktmbuf_clone(original_mbuf, mbuf_pool);
    if (cloned_mbuf == NULL) {
        // 克隆失败
        printf("Failed to clone mbuf\n");
        return -1;
    }

    // 对克隆的 mbuf 进行处理

    // 释放克隆的 mbuf
    rte_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 的初始化流程完成

    // 从内存池中分配一个新的 mbuf
    mbuf = 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); // 释放 mbuf
        return -1;
    }

    // 获取外部数据在物理内存中的地址(IOVA)
    data_iova = rte_malloc_virt2iova(data_ptr);

    // 将外部数据关联到 mbuf 上
    rte_pktmbuf_attach(mbuf, data_ptr, data_iova, data_len);

    // 现在可以使用 mbuf 来处理关联的数据了

    // 释放外部数据缓冲区
    free(data_ptr);

    // 释放 mbuf
    rte_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);

    // 创建一个 mbuf
    struct 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");

    // 释放 mbuf
    rte_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);

    // 创建一个 mbuf
    struct 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

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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); // 释放 mbuf
        return -1;
    }

    // 在新的 mbuf 数据区域中填充数据,这里简单地填充了 0x01
    uint8_t *data = rte_pktmbuf_mtod_offset(new_mbuf, uint8_t *, 0);
    for (int i = 0; i < data_len; i++) {
        data[i] = 0x01;
    }

    // 现在 mbuf 中已经包含了附加的数据

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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); // 释放 mbuf
        return -1;
    }

    // 在新的 mbuf 开头填充数据,这里简单地填充了 0x01
    uint8_t *data = rte_pktmbuf_mtod_offset(new_mbuf, uint8_t *, 0);
    for (int i = 0; i < prepend_len; i++) {
        data[i] = 0x01;
    }

    // 现在 mbuf 中已经在开头预留了一定长度的空间,并填充了数据

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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); // 释放 mbuf
        return -1;
    }

    // 现在 mbuf 中已经移除了指定长度的数据

    // 访问 mbuf 中的数据
    uint16_t new_len = rte_pktmbuf_data_len(new_mbuf);
    printf("New length of mbuf: %d\n", new_len);

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_pktmbuf_free(mbuf);

    return 0;
}

#include <rte_mbuf.h>
#include <stdio.h>

int main() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_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 4

int main() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_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() {
    // 创建一个 mbuf
    struct 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);

    // 释放 mbuf
    rte_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 64

int 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.1
    uint32_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, &eth_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.1
    uint32_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 10

int 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 10

int 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 10

int 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 128

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;
    }

    // 设置数据包缓冲区的内容(分片)
    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;
}

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 要生成DPDK 19.11的API手册,需要进行以下步骤: 1. 下载DPDK 19.11源代码:首先,需要从DPDK官方网站下载DPDK 19.11的源代码,并解压到本地目录。 2. 安装依赖:在生成API手册之前,需要先确保系统中已经安装了必要的依赖库,如gcc,doxygen等。可以通过运行DPDK提供的脚本(如usertools/dpdk-setup.sh)来安装依赖。 3. 配置编译选项:在源代码目录中,通过运行config脚本来配置编译选项。可以选择性地启用或禁用某些组件或驱动,并确保已开启生成API文档的选项。 4. 编译源代码:运行make命令来编译DPDK源代码。这会自动编译生成相应的API文档。 5. 生成API手册:在编译完成后,通过运行make doc命令来生成DPDK 19.11的API手册。这个命令会调用doxygen工具来分析源代码,并生成API文档。 6. 查看API手册:生成的API手册位于doc/api目录下。可以使用浏览器访问该目录下的index.html文件来查看API手册。通过索引、搜索等功能,可以快速定位和查找所需的API文档。 需要注意的是,生成API手册需要一定的系统配置和依赖库支持,且会占用一定的系统资源和时间。根据机器配置和网络状况的不同,生成API手册的时间可能会有所不同。 ### 回答2: 要生成DPDK 19.11版本的API手册,可以按照以下步骤进行操作: 1. 确保已经安装了必要的软件和工具,如doxygen和git。 * 安装doxygen:可以通过在操作系统中运行适当的管理器或从doxygen官方网站下载和安装程序进行安装。 * 安装git:根据所使用的操作系统,可以使用适当的管理器或从git官方网站下载和安装程序。 2. 在终端或命令提示符中,使用git命令从DPDK的官方Git仓库中克隆最新版本的源代码。 * 执行以下命令:git clone http://dpdk.org/git/dpdk 3. 进入克隆的dpdk目录。 * 执行以下命令:cd dpdk 4. 切换到DPDK 19.11版本的稳定分支。 * 执行以下命令:git checkout v19.11 5. 使用doxygen生成API手册。 * 执行以下命令:doxygen doc/guides/doxyfile-guides 6. 生成的API手册将保存在doc/api目录中。 通过以上步骤,你就可以生成DPDK 19.11版本的API手册。可以通过打开doc/api/index.html文件在浏览器中查看手册。手册将DPDK库中的各个模块和函数的详细说明,以及使用说明和示例代码,帮助开发人员了解和使用DPDK软件库。 ### 回答3: 要生成DPDK 1911版本的API手册,你可以按照以下步骤进行操作: 1. 确保你的开发环境已经配置好,DPDK的安装和设置。 2. 下载DPDK 1911版本的源代码,可以到DPDK官方网站上获取。 3. 进入DPDK源代码的根目录。 4. 打开终端,输入以下命令来生成API手册: ``` make doc-api-doc ``` 5. 等待编译和生成过程完成。这步可能需要一些时间,取决于你的机器性能和DPDK代码的大小。 6. 执行完上述命令后,你可以在源代码根目录下的`build/doc/api`目录中找到生成的API手册。手册以HTML格式显示。 7. 可以使用Web浏览器来打开手册,以查看和导航DPDK的各种API。 8. 如果你想将API手册导出为其他格式(如PDF),你可以使用相关工具将HTML转换为所需的格式。 请注意,生成API手册需要有适当的开发环境以及一些编译工具和依赖项。如果你遇到了问题,请参考DPDK的官方文档或向社区寻求帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写一封情书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值