dpdk 的内存管理层次结构
- 物理巨页的管理
- 虚拟地址空间的管理
- heap管理(变长buffer)
- mempool管理(定长buffer)
物理巨页的管理
dpdk中通过 数组 hugepg_tbl[LEN] 组织所有的巨页。巨页管理的主要需求是:
- 在用户空间 获取物理巨页
- 规则化 虚拟地址 与 物理巨页的映射 关系。
- 约束1:保证 连续的va对应连续的pa
- 约束2:不连续的va则pa不连续。
约束1 可以方便用户态网卡驱动进行dma映射。dma映射要求 网卡视角的物理地址是连续的。
约束2 方便heap堆管理的rte_free()
进行合并,同时保持约束1成立。即两块连续的{va, len}可以合并,合并后其对应 物理地址区域 也应该是连续的。
物理巨页的获取 - 第一次mmap()
在用户空间,我们如何申请利用巨页呢? 通过在 hugetblfs文件系统类型的 /dev/hugepages
先创建文件/dev/hugepages/rte_hugepage_%d
,然后 mmap()
系统调用 获取1G的巨页。有别于普通文件系统的内存申请,这里无需设置文件大小,如ftruncate(fd)
。
但此时的物理巨页的位置 是用户无法控制的,需后续进一步的排序处理。
// lib/eal/linux/eal_memory.c
map_all_hugepages()
// 拼接文件名 /dev/hugepages/rte_hugepage_%s
eal_get_hugefile_path(hf->filepath, sizeof(hf->filepath),
hpi->hugedir, hf->file_id);
// 在/dev/hugepages/ 目录下 新建文件
fd = open(hf->filepath, O_CREAT | O_RDWR, 0600);
// 获取物理巨页。注意:多次mmap(),该文件仍 只有1g大小
virtaddr = mmap(NULL, hugepage_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd, 0);
物理巨页的排序 - 第二次mmap()
由上小节可知,用户通过mmap()系统调用获取的物理巨页的位置是不可控的,该现象可由用户 通过读写 /proc/self/pagemaps
来查看当前进程的va所对应的pa。依靠/proc/self/pagemaps
获取的pa信息,进行排序,然后通过mmap() 重新分配 递增的va给排序后的 物理巨页。同时,连续的物理巨页,其va也是连续的。
// 涉及函数
虚拟地址空间的管理
当用户需要 一片虚拟地址区域时,可通过eal_get_virtual_area()
进行获取,其从管理的va空闲区域头部获取 虚拟地址区域。dpdk 会让 虚拟地址区域 连续。
heap(变长buffer) 的管理
这部分的实现 遵循 常见的变长buffer管理方式,即将 不同长度的空闲区域 通过 链表组织。在连续的物理巨页 头部添加 malloc_elem结构,进行管理。
mempool管理(定长buffer)
在heap的基础上,dpdk针对定长buffer的管理 进行优化。步骤如下:
- 先在heap中malloc一个 定长buffer的大数组。
- 将 空闲的定长元素 的指针 加入 rte_ring进行管理。
其主要优点是,rte_ring无锁队列,通过原子操作,可减低 malloc的锁争用开销,且引入per-cpu 栈buffer cache的快分配流程。
总体视角
主要数据结构的关联图
数据结构 通过文件共享
dpdk 多进程模式下,进程间通过 文件来共享文件,通过文件锁来同步。
已知的 文件 与 数据的映射如下:
/var/run/dpdk/rte/hugepage_data => hugepage_file []
/dev/hugepage/rte_hugepage_0 => huge page
/var/run/dpdk/rte/fbarray_0 => rte_fbarray->data
/var/run/dpdk/rte/fbarray_memzone
/var/run/dpdk/rte/config => rte_mem_config
附录
https://upload-images.jianshu.io/upload_images/8087815-492c366e5f913d4c.png