DPDK18.11.11 内存管理和分配的解析

一、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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值