2. 大页内存配置与验证

任务目标

本案例目标是在Linux系统上配置大页内存(HugePages),使DPDK能够正确识别并使用大页,同时通过DPDK内存池(mempool)的创建来验证大页配置是否生效。

环境与依赖

  • Linux系统(建议内核 ≥ 3.8,x86_64架构)
  • DPDK库(版本 ≥ 20.11,本案例使用DPDK 22.11验证)
  • GCC编译器、libnuma-dev(可选,用于NUMA感知)
  • root权限(配置大页需要)

大页配置步骤

首先,在系统启动时预留大页内存。编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 中添加:

default_hugepagesz=1G hugepagesz=1G hugepages=4

然后更新GRUB并重启系统:

sudo update-grub
sudo reboot

重启后,验证大页是否成功预留:

grep Huge /proc/meminfo

预期输出(示例):

HugePages_Total:       4
HugePages_Free:        4
Hugepagesize:       1048576 kB

如果系统不支持1G大页,可以使用2M大页作为替代:

default_hugepagesz=2M hugepagesz=2M hugepages=1024

DPDK大页挂载与配置

DPDK运行时需要访问大页文件系统。执行以下命令挂载hugetlbfs并设置DPDK环境变量:

sudo mkdir -p /mnt/huge
sudo mount -t hugetlbfs pagesize=1G /mnt/huge
echo 4 | sudo tee /sys/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages

如果使用2M大页,挂载命令为:

sudo mount -t hugetlbfs pagesize=2M /mnt/huge

完整代码:DPDK大页验证程序

以下代码初始化DPDK EAL环境,创建内存池并输出大页使用信息:

#include <rte_eal.h>
#include <rte_mempool.h>
#include <rte_memory.h>
#include <stdio.h>
#include <stdint.h>

#define MEMPOOL_SIZE 8192
#define CACHE_SIZE   256

int main(int argc, char **argv) {
    int ret;
    struct rte_mempool *mp;
    unsigned int i;
    const struct rte_memseg *ms;
    uint64_t huge_total = 0, huge_used = 0;

    /* 初始化EAL,必须传入 --huge-dir 或使用默认挂载点 */
    ret = rte_eal_init(argc, argv);
    if (ret < 0) {
        printf("EAL初始化失败: %d\n", ret);
        return -1;
    }

    /* 遍历所有内存段,统计大页使用情况 */
    for (i = 0; i < rte_memory_get_nmemsegs(); i++) {
        ms = rte_memseg_get(i);
        if (ms == NULL) continue;
        huge_total += ms->len;
        if (ms->state == RTE_MEMSEG_IN_USE)
            huge_used += ms->len;
    }

    printf("DPDK检测到大页内存总量: %lu MB\n", huge_total / (1024*1024));
    printf("已使用大页内存: %lu MB\n", huge_used / (1024*1024));

    /* 创建内存池,验证大页分配 */
    mp = rte_mempool_create("test_pool", MEMPOOL_SIZE,
                            RTE_MBUF_DEFAULT_BUF_SIZE, CACHE_SIZE,
                            0, NULL, NULL, NULL, NULL,
                            SOCKET_ID_ANY, 0);
    if (mp == NULL) {
        printf("内存池创建失败: %s\n", rte_strerror(rte_errno));
        rte_eal_cleanup();
        return -1;
    }

    printf("内存池创建成功: %s\n", mp->name);
    printf("内存池对象数: %u\n", rte_mempool_avail_count(mp));
    printf("内存池大小: %zu bytes\n", rte_mempool_mem_size(mp));

    /* 验证内存池内存是否来自大页 */
    struct rte_mempool_memhdr *memhdr;
    int hugepage_count = 0;
    STAILQ_FOREACH(memhdr, &mp->mem_list, next) {
        if (memhdr->hugepage_sz > 0) {
            hugepage_count++;
            printf("内存块 %d: 大页大小 %lu KB\n",
                   hugepage_count, memhdr->hugepage_sz / 1024);
        }
    }
    if (hugepage_count == 0) {
        printf("警告: 内存池未使用大页!\n");
    } else {
        printf("验证通过: 内存池使用 %d 个大页内存块\n", hugepage_count);
    }

    /* 释放资源 */
    rte_mempool_free(mp);
    rte_eal_cleanup();
    return 0;
}

编译与运行

使用pkg-config编译:

gcc -o hugepage_test hugepage_test.c $(pkg-config --cflags --libs libdpdk)

运行程序(需要root权限):

sudo ./hugepage_test --huge-dir=/mnt/huge

验证方法与判定标准

程序运行后,观察以下输出以确认大页配置成功:

  1. 大页总量检测:程序输出的“DPDK检测到大页内存总量”应大于0,且数值与系统配置的大页总量一致(例如配置4个1G大页,则总量约为4096 MB)。
  2. 内存池创建:输出“内存池创建成功”且对象数为8192,说明DPDK成功从大页分配内存。
  3. 大页使用确认:输出“验证通过: 内存池使用 N 个大页内存块”,N ≥ 1,证明内存池内存确实来自大页。

如果程序输出“内存池创建失败”或“警告: 内存池未使用大页”,请检查:

  • 大页是否成功预留(cat /proc/meminfo 查看HugePages_Free)
  • hugetlbfs是否正确挂载到 /mnt/huge
  • EAL参数是否正确指定 --huge-dir

常见问题

  • EAL初始化失败:通常是因为没有root权限或大页目录不可访问。使用 sudo 运行,并确认 /mnt/huge 存在且已挂载。
  • 内存池创建失败(ENOMEM):大页内存不足。增加系统大页数量或减小 MEMPOOL_SIZE。
  • NUMA节点问题:如果系统有多个NUMA节点,确保每个节点都有足够大页,或使用 --socket-mem 指定内存分配。