3. 内存池创建与对象分配

任务目标

使用DPDK的rte_mempool_create创建内存池,从池中分配/释放rte_mbuf结构,验证内存池的统计计数变化,理解DPDK内存池的无锁环形缓冲区管理机制。

环境依赖

DPDK已编译并绑定igb_uio或vfio-pci驱动,具备hugepages(建议2MB页,至少64MB)。编译需链接-lrte_mempool -lrte_mbuf -lrte_eal。

核心实现

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

#define POOL_SIZE   4096
#define CACHE_SIZE  256
#define BUF_SIZE    2048

int main(int argc, char **argv) {
    // 初始化EAL(需传入--file-prefix等参数)
    int ret = rte_eal_init(argc, argv);
    if (ret < 0) {
        printf("EAL init failed: %d\n", ret);
        return -1;
    }

    // 1. 创建内存池:每个对象为mbuf + 私有数据区
    struct rte_mempool *mp = rte_mempool_create(
            "my_pool",                  // 池名称
            POOL_SIZE,                  // 对象数量(含per-lcore缓存)
            BUF_SIZE,                   // 每个对象大小(含mbuf头)
            CACHE_SIZE,                 // per-lcore缓存大小
            sizeof(struct rte_pktmbuf_pool_private), // 私有数据区
            rte_pktmbuf_pool_init,      // 池初始化回调
            NULL,
            rte_pktmbuf_init,           // mbuf初始化回调
            NULL,
            rte_socket_id(),            // 在本地NUMA节点分配
            0);                         // 无额外标志
    if (mp == NULL) {
        printf("mempool create failed\n");
        return -1;
    }
    printf("Pool created: %s, objects=%u, size=%u\n",
           mp->name, mp->size, mp->elt_size);

    // 2. 从池中分配一个mbuf
    struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mp);
    if (mbuf == NULL) {
        printf("mbuf alloc failed\n");
        return -1;
    }
    printf("Allocated mbuf: pool=%s, buf_addr=%p, data_len=%u\n",
           mbuf->pool->name, mbuf->buf_addr, mbuf->data_len);

    // 3. 写入数据并调整长度
    char *data = rte_pktmbuf_append(mbuf, 64);
    if (data == NULL) {
        printf("append failed\n");
        return -1;
    }
    snprintf(data, 64, "DPDK mempool test");
    printf("Data written: %s\n", rte_pktmbuf_mtod(mbuf, char *));

    // 4. 释放mbuf回池
    rte_pktmbuf_free(mbuf);
    printf("mbuf freed back to pool\n");

    // 5. 验证池统计(需编译时开启CONFIG_RTE_MEMPOOL_STATS)
    printf("Pool stats: allocated_objs=%u, in_pool=%u\n",
           rte_mempool_avail_count(mp),   // 当前可用对象数
           rte_mempool_in_use_count(mp)); // 已分配对象数

    // 清理
    rte_mempool_free(mp);
    rte_eal_cleanup();
    return 0;
}

编译与运行

# 编译(假设DPDK已安装到/usr/local)
gcc -o mempool_demo mempool_demo.c \
    $(pkg-config --cflags --libs libdpdk) \
    -lrte_mempool -lrte_mbuf -lrte_eal

# 运行(需root权限,指定hugepage内存)
sudo ./mempool_demo --file-prefix=pg --huge-dir=/mnt/huge \
    -m 64 --no-pci

验证方法

程序正常输出以下内容即通过:

Pool created: my_pool, objects=4096, size=2176
Allocated mbuf: pool=my_pool, buf_addr=0x7f..., data_len=0
Data written: DPDK mempool test
mbuf freed back to pool
Pool stats: allocated_objs=4095, in_pool=1

关键判定点:

  • 池创建成功:objects=4096,且elt_size大于BUF_SIZE(因含mbuf头结构);
  • 分配/释放闭环:分配后in_pool=1,释放后avail_count恢复为4095(因per-lcore缓存中仍有对象);
  • 数据写入正确:读取到DPDK mempool test字符串。

内存池管理机制说明

DPDK内存池基于无锁环形队列(rte_ring)实现,每个核访问自己的per-lcore cache(大小为CACHE_SIZE)以减少锁竞争。分配时优先从本地缓存取对象,缓存不足时从全局池批量获取;释放时先放回本地缓存,缓存满后批量归还全局池。上述输出中avail_count=4095而非4096,是因为释放的mbuf进入了当前核的缓存而非立即回到全局池,这正是缓存机制的正常表现。

常见问题

  • EAL初始化失败:确认hugepage已挂载且--huge-dir路径正确,或改用--socket-mem指定内存;
  • 池创建返回NULL:检查POOL_SIZE是否超过hugepage可用内存,或BUF_SIZE是否过小(需大于sizeof(struct rte_mbuf));
  • 统计计数不变化:需在DPDK编译配置中开启CONFIG_RTE_MEMPOOL_STATS=y,否则avail_count仍可用但in_use_count恒为0。