DPDK内存管理 原文地址:http://www.cnblogs.com/jintianfree/p/4018043.html

本文深入剖析DPDK如何利用hugepage预留的物理内存,并通过rte_eal进行统一管理,最终形成可供各组件调用的内存接口。文章详细介绍了rte_eal内部的初始化过程,包括获取hugepage信息、内存映射、物理地址获取等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 前言

 

DPDK将利用hugepage预留的物理内存统一的组织管理起来,然后以库的方式对外提供使用的接口。下图展示了DPDK中内存有关的模块的相互关系。

rte_eal            是统一的组织管理者(当然rte_eal不只是做内存的工作)

rte_malloc       对外提供分配释放内存的API,分配的内存都是rte_eal中管理的内存

rte_ring          提供无锁队列,他之间使用了rte_eal管理的内存

rte_mempool  利用rte_eal中的内存和rte_ring提供内存池的功能

ret_mbuf       

 

下面我们一个一个分析各个模块是怎样处理及使用内存的(重点诉说流程,不会详细的介绍代码),DPDK管理的内存有个很有用的特点--同一块物理内存在不同的进程地址空间中,虚拟地址是一样的。下面会详细的说明DPDK是怎样实现这个功能的。

首先hugepage这里就不描述了,要理解DPDK的内存管理必须知道hugepage,不了解的自行补齐吧,linux内核中有个文档介绍的很详细。可以点击这里查看。

2 rte_eal 对内存的组织与管理

这块是在rte_eal中实现的。有关的函数有:

rte_eal_init -> eal_hugepage_info_init

rte_eal_init -> rte_eal_memory_init

rte_eal_init -> rte_eal_memzone_init

rte_eal_init -> eal_check_mem_on_local_socket

 

2.1 eal_hugepage_info_init

首先DPDK管理的内存都是通过hugepage预留的,这个函数主要获取预留的内存的信息,获取方法是读取 /sys/kernel/mm/hugepages 下的文件,具体还是参考hugepage的文档吧,这里不详细描述。

预留的内存按页大小分类( i386 architecture supports 4K and 4M (2M in PAE mode) page sizes, ia64 architecture supports multiple page sizes 4K, 8K, 64K, 256K, 1M, 4M, 16M, 256M and ppc64 supports 4K and 16M. ),可能会有多类,DPDK会把这些信息全部读取,使用struct hugepage_info进行保存,每一类对应一个struct hugepage_info类型的对象,所有的这些对象保存在struct internal_config的数组中。DPDK有个internal_config的全局变量,记录这些信息。

 

复制代码
struct hugepage_info {
    size_t hugepage_sz;   /**< size of a huge page */
    const char *hugedir;    /**< dir where hugetlbfs is mounted */
    uint32_t num_pages[RTE_MAX_NUMA_NODES];
                /**< number of hugepages of that size on each socket */
    int lock_descriptor;    /**< file descriptor for hugepage dir */
};
复制代码

 

struct internal_config {
   ...
    unsigned num_hugepage_sizes;      /**< how many sizes on this system */
    struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES];
};

 

 

struct internal_config internal_config;

 

总结 eal_hugepage_info_init,就是获取了系统中预留的内存信息,有几类(相同页大小的是一类),将每一类的信息(页大小、页数、挂载点)存储在internal_config.hugepage_info数组中。hugepage_info数组也进行了排序,大页的信息放前面,小页的信息放后面。

每一类所有内存页,也分处在哪个 socket上(不明白的查看NUMA相关知识补齐)的,hugepage_info中统计内存页数会按属于处在哪个socket上进行统计,但在这一步(eal_hugepage_info_init)中,还区分不了每个页处在哪个socket上,因此这里还没有按socket统计页数,将内存页数直接记录到num_pages[0]里面了。

 

2.2 rte_eal_memory_init

复制代码
int
rte_eal_memory_init(void)
{
    RTE_LOG(INFO, EAL, "Setting up memory...\n");
    const int retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
            rte_eal_hugepage_init() :
            rte_eal_hugepage_attach();
    if (retval < 0)
        return -1;

    if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
        return -1;

    return 0;
}
复制代码

 

DPDK多进程状态下,分为RTE_PROC_PRIMARY进程及RTE_PROC_SECONDARY进程,RTE_PROC_PRIMARY负责初始化内存,RTE_PROC_SECONDARY获取 RTE_PROC_PRIMARY 内存映射的信息,创建与RTE_PROC_PRIMARY一样的内存映射。这是DPDK多进程共享内存的方式。此处先不展开描述。随着流程的展开,自然会明白。

2.2.1 rte_eal_mem_init -> rte_eal_hugepage_init 

DPDK有个全局变量

static struct rte_config rte_config

该变量的成员 struct rte_mem_config *mem_config;  保存了DPDK管理的内存的所有信息。DPDK将内存统一的管理,就是将所有内存保存到mem_config:memseg[]中。

复制代码
struct rte_mem_config {
    volatile uint32_t magic;   /**< Magic number - Sanity check. */

    /* memory topology */
    uint32_t nchannel;    /**< Number of channels (0 if unknown). */
    uint32_t nrank;       /**< Number of ranks (0 if unknown). */

    /**
     * current lock nest order
     *  - qlock->mlock (ring/hash/lpm)
     *  - mplock->qlock->mlock (mempool)
     * Notice:
     *  *ALWAYS* obtain qlock first if having to obtain both qlock and mlock
     */
    rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */
    rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */
    rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */

    uint32_t memzone_idx; /**< Index of memzone */

    /* memory segments and zones */
    struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */
    struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */

    /* Runtime Physmem descriptors. */
    struct rte_memseg free_memseg[RTE_MAX_MEMSEG];

    struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */

    /* Heaps of Malloc per socket */
    struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];
} __attribute__((__packed__));
复制代码

详细流程:

函数先根据 struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES]; (eal_hugepage_info_init中获取的信息)得到了内存页的总数。然后为struct hugepage_file *tmp_hp申请了内存,有多少个内存页,就分配了多少个hugepage_file。

 

复制代码
struct hugepage_file {
    void *orig_va;      /**< virtual addr of first mmap() */
    void *final_va;     /**< virtual addr of 2nd mmap() */
    uint64_t physaddr;  /**< physical addr */
    size_t size;        /**< the page size */
    int socket_id;      /**< NUMA socket ID */
    int file_id;        /**< the '%d' in HUGEFILE_FMT */
    int memseg_id;      /**< the memory segment to which page belongs */
#ifdef RTE_EAL_SINGLE_FILE_SEGMENTS
    int repeated;        /**< number of times the page size is repeated */
#endif
    char filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */
};
复制代码

接着针对每一类内存(相同页大小的内存算一类)进行处理,

for(每一类) {

  for(这一类的每一页) {         

    每个页对应一个hugepage_file,file_id,size,filepath初始化。其中file_id是页的序号,这个序号是给filepath用的,是为了给每个页对应的文件起一个独立的文件名(与其他页不会重复)。

    建立文件<filepath>,这个文件是因为使用hugepage的内存必须按这样的方式(先建立文件,再mmap,得到内存虚拟地址使用)使用。

    执行mmap获得页的虚拟地址,赋给hugepage_file:orig_va。

  } (map_all_hugepages())

 

  所有hugepage预留的物理内存都映射完成,可以根据hugepage_file:orig_va进行访问了。

  for(这一类的每一页) {

    获取物理地址,赋给hugepage_file:physaddr。

    获取物理地址的方式,是linux内核提供的功能,不了解的可以点击这里补齐。

  }(find_physaddrs())

  

  for(这一类的每一页) {

     获取每个页所在的socket,赋给hugepage_file:socket_id。 

     获取的方法也是linux内核提供的功能,具体不详述。

  }(find_numasocket())

 

  对这一类的页的hugepage_file[]按页物理地址进行排序。

 

  for(这一类的每一页) {

    重新执行mmap,并将获取的虚拟地址赋给hugepage_file:final_va。    

  }(map_all_hugepages())

  本次重新mmap,与第一次的不同是,会主动先申请一块足够映射本类所有内存页的虚拟地址空间,然后将本类的所有页连续的映射的该虚拟地址空间中。

  如果找不到这么大的虚拟地址空间,还是会一页一页的映射,让内核去决定虚拟地址空间。

  第二次映射用代码的注释解释 The second mapping tries to  map continguous physical blocks in contiguous virtual blocks.

 

  将第一次的映射unmap掉

 

继续

更新 internal_config.hugepage_info[].num_pages[],我们知道bugepage_info在eal_hugepage_info_init中初始化时,不知道每个页是属于哪个socket的,因此直接使用num_pages[0]记录了所有的页数,但在上一步中,已经知道了每个页属于哪个socket,因此这里更新了num_pages,准确的记录了哪个socket上有多少个页。

 

这块我还没具体看 unmap pages that we won't need (looks at used_hp),接下来再研究。

接下来,建立了共享内存,将hugepage_file拷贝到共享内存中,这样RTE_PROC_SECONDARY启动后可以在共享内存中读取这些信息,然后创建与RTE_PROC_PRIMARY一致的映射。

 

最后,使用mem_config->memseg[]将所有内存管理起来。

一个struct rte_memsg对象,保存一块相同socket的、相同页大小的、物理地址连续的、虚拟地址连续的内存。因此整个内存由多个memseg组成。

 

总结rte_eal_hugepage_init,它获取hugepge的方式预留的所有物理内存,统一的映射到虚拟地址空间中,保存到memseg中。至此,理论上可以通过memseg获取虚拟地址使用这些内存了。但DPDK提供了统一的方式来使用,不是谁用就可以来拿的。

 

 2.2.2 rte_eal_mem_init -> rte_eal_hugepage_attach

 /*
 * This creates the memory mappings in the secondary process to match that of
 * the server process. It goes through each memory segment in the DPDK runtime
 * configuration and finds the hugepages which form that segment, mapping them
 * in order to form a contiguous block in the virtual memory space
 */

 

3 rte_malloc

 

4 rte_ring

 

5 rte_mempool

<think>好的,用户现在遇到了在CentOS 7.9上安装DPDK的问题,但看起来问题不是出在DPDK本身,而是在安装依赖时遇到了yum仓库配置错误。用户提供的错误信息显示,每次执行yum命令时都会报错:“File contains no section headers. file: file:///etc/yum.repos.d/CentOS-Base.repo, line: 1 '<html>\r\n'”。这说明CentOS-Base.repo文件的内容被替换成了HTML,而不是正确的仓库配置。这通常是因为用户可能不小心下载了一个网页而不是正确的仓库文件,或者镜像站点有问题,导致下载的文件不正确。 首先,我需要确认这个问题的根源。用户可能尝试过手动修改仓库文件,或者在配置镜像时出现了错误。CentOS-Base.repo是CentOS系统默认的仓库配置文件,如果这个文件被损坏或者格式错误,yum就无法正确解析,导致无法安装任何软件包。错误信息中的“<html>”提示说明文件内容被替换成了HTML页面,这可能是由于下载仓库文件时发生了重定向到某个网页,或者镜像站点不可用,导致返回了错误的响应。 接下来,解决这个问题的步骤应该是恢复正确的CentOS-Base.repo文件。用户需要手动下载正确的仓库配置文件,并替换掉当前错误的文件。CentOS官方提供了标准的仓库配置文件,用户可以通过wget或curl命令直接从官方镜像下载。例如,使用wget下载CentOS 7的Base.repo文件,并将其覆盖到/etc/yum.repos.d/CentOS-Base.repo。完成后,运行yum clean all清除缓存,再更新元数据,确保仓库配置正确。 此外,用户可能还需要检查其他仓库文件,如epel.repo是否也存在同样的问题。如果用户之前尝试安装epel-release但失败,可能是因为CentOS-Base.repo的问题导致无法连接到正确的仓库。修复Base.repo后,再重新安装epel-release应该可以解决问题。同时,用户安装python3和pip3失败的问题,也是由于仓库配置错误,无法下载软件包。修复仓库后,这些安装命令应该可以正常执行。 另外,用户提到的“pip3: 未找到命令”是因为在安装python3-pip失败后,pip3没有被正确安装。一旦仓库配置修复,重新安装python3-pip即可解决。最后,用户可能在安装开发工具和依赖库时同样遇到了仓库配置问题,导致所有yum命令失败。因此,解决CentOS-Base.repo的问题是关键,之后其他安装步骤才能顺利进行。 总结来说,用户的问题核心在于yum仓库配置文件损坏,导致无法安装任何软件包。解决方案是手动恢复正确的仓库配置,然后继续执行之前的安装步骤。需要确保用户有权限修改系统文件,并且在下载仓库文件时选择正确的镜像源。可能还需要检查网络连接,确保能够访问CentOS的官方镜像站点。修复仓库后,用户应能顺利安装所有依赖,继续DPDK的安装过程。</think>从错误信息看,主要问题是 **CentOS-Base.repo 文件被篡改成了HTML格式内容**,导致所有 `yum` 命令失效。以下是问题分析和解决方案: --- ### 问题分析 1. **错误特征** `File contains no section headers` 提示表明 `/etc/yum.repos.d/CentOS-Base.repo` 文件格式损坏: ```bash file: file:///etc/yum.repos.d/CentOS-Base.repo, line: 1 '<html>\r\n' # 文件内容被替换成了HTML ``` 2. **常见原因** - 手动下载仓库文件时误操作(如用浏览器下载导致保存为HTML) - 镜像站点配置错误,返回了网页内容 - 病毒/恶意脚本篡改 --- ### 解决方案 #### 步骤 1:恢复仓库文件 ```bash # 备份原有错误文件 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak # 从官方镜像下载正确的仓库文件(选择适合你的CentOS版本) sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 清理缓存并重建元数据 sudo yum clean all sudo yum makecache ``` #### 步骤 2:修复EPEL仓库(可选) 如果之前尝试安装 `epel-release` 失败: ```bash sudo rm -f /etc/yum.repos.d/epel*.repo # 删除残留的EPEL配置 sudo yum install -y epel-release # 重新安装EPEL ``` #### 步骤 3:验证修复 ```bash sudo yum repolist # 应显示正常仓库列表 sudo yum update -y # 测试更新操作 ``` --- ### 后续操作 完成仓库修复后,重新执行原始安装命令: ```bash sudo yum -y install python3 python3-pip sudo yum groupinstall "Development Tools" -y sudo yum install kernel-devel numactl-devel pciutils -y sudo yum install libibverbs-devel elfutils-libelf-devel -y ``` --- ### 关键注意事项 1. **镜像源选择** 示例使用了阿里云镜像,也可替换为: - 清华大学镜像:`https://mirrors.tuna.tsinghua.edu.cn/repo/` - 华为云镜像:`https://repo.huaweicloud.com/` 2. **网络连通性验证** 若仍失败,检查防火墙和DNS: ```bash ping mirrors.aliyun.com # 测试网络连通性 dig mirrors.aliyun.com +short # 检查DNS解析 ``` 3. **文件权限问题** 确保操作时有 `sudo` 权限,且 `/etc/yum.repos.d/` 目录可写。 --- ### 附:正常CentOS-Base.repo文件片段 ```ini [base] name=CentOS-$releasever - Base baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 ``` 修复后系统应恢复正常软件包管理功能,之后可继续DPDK的安装流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值