一、DPDK的内存模式
DPDK存在两种内存模式:
1、legacy mode : 这一种为静态内存模式,即在初始化过程就根据配置分配所有的内存,并且这些内存页在application结束之前不会归还给OS;而且也不会再向OS申请更多的内存。并且application使用DPDK的rte_malloc库申请内存时,若剩余可用的内存不足,则直接返回错误。这种模式的优点是若一段内存空间的虚拟内存地址是连续的,那么其物理内存地址也是连续的。
2、dynamic mode:这一种为动态内存模式,在初始化过程根据配置分配所需要的内存,并且这些内存页在application结束之前不会归还给OS。若application使用DPDK的rte_malloc库申请内存时,若剩余可用的内存不足,则会先向操作系统申请内存,再将这些内存分配给application。由于会动态地向OS申请内存,所以虚拟内存连续并不意味着物理内存连续。(对于向操作系统动态申请的内存,在application释放这些内存时,DPDK系统会将其归还给OS,不必等到application结束)
二、相关结构体的说明
下面先对一些结构体的字段进行说明(参考后面的图例能更容易的理解),能够帮助对后面的理解:
**********************************malloc_elem.h*****************************
struct malloc_elem {
struct malloc_heap *heap;
struct malloc_elem *volatile prev; //指向上一个malloc_elem
/**< points to prev elem in memseg */
struct malloc_elem *volatile next; // 指向下一个malloc_elem
/**< points to next elem in memseg */
LIST_ENTRY(malloc_elem) free_list; //指向下一个free malloc_elem
/**< list of free elements in heap */
struct rte_memseg_list *msl;
volatile enum elem_state state; //表明malloc_elem的状态: free, 或者busy
uint32_t pad; //填充无效的内存区域
size_t size; // 表示malloc_elem所表示的内存区域的大小(包含struct malloc_elem), 以字节为单位
struct malloc_elem *orig_elem; //指向最初的malloc_elem(即内存初始化时的malloc_elem)
size_t orig_size; //最初的malloc_elem的内存区域的大小
#ifdef RTE_MALLOC_DEBUG
uint64_t header_cookie; /* Cookie marking start of data */
/* trailer cookie at start + size */
#endif
} __rte_cache_aligned;
**********************************rte_malloc_heap.h*****************************
struct malloc_heap {
rte_spinlock_t lock;
LIST_HEAD(, malloc_elem) free_head[RTE_HEAP_NUM_FREELISTS];
/*
free_head用于存放free malloc_elem的位置
free_head[0] 存放由大小为(0, 2^8]的free malloc_elem组成的链表头指针
free_head[1] 存放由大小为(2^8, 2^10]的free malloc_elem组成的链表头指针
free_head[2] 存放由大小为(2^10, 2^12]的free malloc_elem组成的链表头指针
free_head[3] 存放由大小为(2^12, 2^14]的free malloc_elem组成的链表头指针
······
以此类推
*/
struct malloc_elem *volatile first; //指向heap的第一个malloc_elem
struct malloc_elem *volatile last; // 指向heap的最后一个malloc_elem
unsigned alloc_count; //已经分配的malloc_elem的数量
unsigned int socket_id;
size_t total_size; //表示此heap所包含的内存(以字节为单位)
char name[RTE_HEAP_NAME_MAX_LEN];
} __rte_cache_aligned;
三、DPDK内存管理结构
在这里,我们抛弃struct rte_memseg_list, struct rte_memseg的观点。单纯地从struct malloc_heap和struct malloc_elem的角度来看DPDK的内存管理,如下图所示 :
这是一个刚初始化完成的heap,只包含free malloc_elem。
可以看出DPDK的内存管理主要由一个双向链表组成:malloc_heap包含first, last指针,分别指向第一个malloc_elem和最后一个malloc_elem;每一个malloc_elem有一个prev和next指针(如图中黑色的线条所示)。图中蓝色的线表示的是由malloc_heap中的 free_head 和malloc_elem中的 free_list 构成的free malloc_elem的链表。
DPDK中的rte_malloc库中,application使用 rte_malloc() 方法向DPDK的运行时系统申请内存,rte_free() 释放内存。其它向heap申请或释放内存的方法基本也是调用这两个方法 :
1,rte_malloc() : 这个方法主要是调用了 malloc_heap_alloc()(位于malloc_heap.c) 进行分配内存。并且每次分配时,都会从一个free malloc_elem的末尾开始分配。
2,rte_free() : 这个方法主要是调用了 malloc_heap_free()(位于malloc_heap.c) 将内存释放回DPDK系统。
下面用一个场景说明内存的分配和释放过程:
一, rte_malloc()分配一块内存后,malloc_heap变化如下 :
分配完后的结果如上图所示,每一次分配的基本步骤如下:
1, 根据需要分配的内存的大小从对应的free_head中查找到合适的free malloc_elem,如果没有,则向OS申请额外的内存(通过eal_memalloc_alloc_seg_bulk() 申请),作为新的free malloc_elem加入到heap中,对其进行扩展。
2, 从free malloc_elem的末尾开始分配内存,然后将free malloc_elem划分为两个malloc_elem, 后面一个标记为busy malloc_elem。
3, 修改malloc_elem的prev, next指针,并且将剩余的free malloc_elem加入到合适的free链表的头部。
二,执行rte_free()释放刚才分配的内存,malloc_heap的变化如下:
释放完后的结果如上图所示,每一次释放的基本步骤如下:
1, 将要进行释放的busy malloc_elem的状态标记为free
2, 检查有没有在内存上并且也是free的malloc_elem, 存在的话就合并为一个大的free malloc, 然后加入到合适的free链表的头部。
3, 检查能否归还向OS申请的额外内存,如果可以的话,就将其归还给OS(通过 eal_memalloc_free_seg_bulk() 归还)。
四,底层实现
DPDK在memalloc时有两种模式single-file-segments, page-per-file。 每一种都在hugetlbfs的挂载点上有相应的文件形式(即存在于内存中的文件),这样在内存分配时可以使用对file descriptor操作的系统调用对内存进行操作。
下面对这两种模式进行进一步的解释 :
single-file-segment的映射关系如下:
蓝色表示的是内存文件所占用的物理内存,对于一个rte_memseg_list会创建一个文件,rte_memseg_list中的mem_segment会和文件的其中一部分进行映射。
page-per-file的映射关系如下:
蓝色表示的是内存文件所占用的物理内存,对于一个mem_segment会创建一个文件,并且mem_segment会和对应的文件进行映射。
1,对于legacy mode,DPDK系统会在hugetlbfs的挂载点上创建相应的内存文件。
2,对于dynamic mode,关于内存文件有两种形式:
若internal_config.in_memory(位于eal_internal_cfg.h)为true,则使用memfd_create(),或MAP_ANONYMOUS | MAP_HUGETLB 创建anonymous file。
若internal_config.in_memory(位于eal_internal_cfg.h)为false, 则在hugetlbfs的挂载点上创建相应的内存文件。
这样,DPDK系统能够通过一系列和文件相关的接口(比如open, close, fallocate, ftruncate等)对内存文件进行操作。
所以在dynamic mode下,DPDK向系统申请内存,其实就是通过fallocate, 或者ftruncate修改内存文件的大小;
从而获取更多的物理内存。而将内存归还给OS,就是使用unlink(), 删除对应的内存文件,释放文件所占用的物理内存。
从application的角度看,每次使用rte_malloc()申请内存时,DPDK会分配一个合适的malloc_elem管理这一段申请的内存(一个hugepage中可能包含一个或多个malloc_elem)。
而使用rte_free()释放内存时,就是将malloc_elem的状态设置为free,然后合并相邻的free malloc_elem即可。
五、总结
这篇文章简单地介绍了DPDK的内存管理和分配:
1、每一个malloc_heap都由一个由malloc_elem为节点的双向链表组成。
2、DPDK根据free malloc_elem所管理内存的大小将free malloc_elem划分到不同的free_head中。每次在分配内存时,可以快速地找到合适的free malloc_elem, 而不用遍历所有的free malloc_elem。采取的策略是首次适应算法(First Fit):从合适的free_head的头节点开始查找该表,把最先能够满足要求的空闲区分配给用户。
3、DPDK的内存管理的底层实现是通过memory-file,和相关的文件接口实现的。
六、函数调用图
下面给出的是内存管理和分配相关的代码的函数调用图:
1、malloc_elem : http://naotu.baidu.com/file/cda0a195d0f53d9a1c01052cedd91a3b?token=7100881a9cdcaf7e
2、malloc_heap :http://naotu.baidu.com/file/0617659be9481990d1e5c18986b0af32?token=227982f46fc62c42
3、eal_memalloc :http://naotu.baidu.com/file/39af99626733c35dad7850de8b4f11f9?token=07d7fb5bf46d49f6
————————————————
版权声明:本文为CSDN博主「pcokk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pcokk/article/details/89775431