1 前言

DPDK通过使用hugetlbfs,减少CPU TLB表的Miss次数,提高性能。

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

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

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

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

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

ret_mbuf 报文描述符

2 初始化

DPDK的内存初始化工作,主要是将hugetlbfs的配置的大内存页,根据其映射的物理地址是否连续、属于哪个Socket等,有效的组织起来,为后续管理提供便利。

2.1 eal_hugepage_info_init()

eal_hugepage_info_init()主要是获取配置好的Hugetlbfs的相关信息,并将其保存在struct internal_config数据结构中。

主要工作如下:

  1、读取/sys/kernel/mm/hugepages目录下的各个子目录,通过判断目录名称中包含"hugepages-"字符串,获取hugetlbfs的相关子目录,并获取hugetlbfs配置的内存页大小。例如:  

  [root@YMOS_DEFAULT ~]# ls -ltr /sys/kernel/mm/hugepages/total 0drwxr-xr-x 2 root root 0 2014-11-04 15:54 hugepages-2048kB

  2、通过读取/proc/mounts信息,找到hugetlbfs的挂载点。例如:    

root@Ubuntu:~# cat /proc/mounts rootfs / rootfs rw 0 0sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0udev /dev devtmpfs rw,relatime,size=1016836k,nr_inodes=254209,mode=755 0 0devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=205128k,mode=755 0 0/dev/disk/by-uuid/fd1dbca3-ac30-4bac-b93a-0d89b0fd152c / ext4 rw,relatime,errors=remount-ro,user_xattr,barrier=1,data=ordered 0 0none /sys/fs/fuse/connections fusectl rw,relatime 0 0none /sys/kernel/debug debugfs rw,relatime 0 0none /sys/kernel/security securityfs rw,relatime 0 0none /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0none /run/shm tmpfs rw,nosuid,nodev,relatime 0 0none /media/sf_F_DRIVE vboxsf rw,nodev,relatime 0 0gvfs-fuse-daemon /home/chuanxinji/.gvfs fuse.gvfs-fuse-daemon rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0/dev/sr0 /media/VBOXADDITIONS_4.3.10_93012 iso9660 ro,nosuid,nodev,relatime,uid=1000,gid=1000,iocharset=utf8,mode=0400,dmode=0500 0 0none /mnt/huge hugetlbfs rw,relatime 0 0root@Ubuntu:~#

  3、通过读取/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages,获取配置的hugepages个数。

 root@Ubuntu:~# cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages64root@Ubuntu:~#

  4、以打开文件的方式,打开挂载点目录,为其FD设置互斥锁,Why??

上述所有获取的信息,都保存在internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]中,hugepage_info数据结构如下:

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 */
};
具体赋值如下,
  hpi->hugepage_sz = 2M;hpi->hugedir = /mnt/huge;hpi->num_pages[0] = 64; // 由于此时还不知道哪些内存页分处在哪个socket上,故,都先放在socket-0上。hpi->lock_descriptor = open(hpi->hugedir, O_RONLY); // 在读取hugetlbfs配置的时候,需要锁住整个目录。当所有hugepage都mmap完成后,会解锁。

  5、将internal_config.hugepage_info[MAX_HUGEPAGES_SIZE]按内存页的大小排序。

2.2 rte_eal_config_create()

rte_eal_config_create()主要是初始化rte_config.mem_config。如果是以root用户运行dpdk程序的话,rte_config.mem_config指向/var/run/.rte_config文件mmap的一段sizeof(struct rte_mem_config)大小的内存。

rte_config.mem_config = /var/run/.rte_config文件mmap的首地址;

struct rte_config {uint32_t master_lcore;       /**< Id of the master lcore */... ...struct rte_mem_config *mem_config;
} __attribute__((__packed__));

struct rte_mem_config数据结构如下:

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__));

2.3 rte_eal_hugepage_init()

rte_eal_hugepage_init()主要是在/mnt/huge目录下创建hugetlbfs配置的内存页数(在本文中就是64)的rtemap_xx文件,并为每个rtemap_xx文件做mmap映射,保证mmap后的虚拟地址与实际的物理地址是一样的。

具体如下:

1、创建nr_hugepages个struct hugepage_file数组,即有多少个内存页,创建多少个struct hugepage_file数据结构。struct 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_SEGMENTSint repeated;       /**< number of times the page size is repeated */#endifchar filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */};

2、有多少个内存页,在挂载点目录下创建多少个rtemap_xx文件,如下所示,并为每一个文件mmap一个hugepage_sz大小的内存区域。其中,

     hugepage_file->orig_va = 记录每个rtemap_xx文件mmap的首地址;hugepage_file->file_id = 创建的rtemap_xx的顺序,就是xx的值;hugepage_file->filepath = /mnt/huge/rtemap_xx;hugepage_file->size = hugepage_sz,也就是2M;root@Ubuntu:~# ls -tlr /mnt/huge/total 131072-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_2-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_1-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_0-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_8-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_7-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_6... ...-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_60-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_59-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_58-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_63-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_62-rwxr-xr-x 1 root root 2097152 Nov  5 14:53 rtemap_61root@Ubuntu:~# 

3、通过读取/proc/self/pagemap页表文件,得到本进程中虚拟地址与物理地址的映射关系。使用上一步中,每个rtemap_xx文件mmap得到的虚拟地址,除以操作系统内存页的大小4k,得到一个偏移量。根据这个偏移量,在/prox/self/pagemap中,得到物理地址的页框,假设为page,那么,物理页框page乘以操作系统内存页的大小4K,再加上虚拟地址的页偏移,就是物理地址。每个rtemap_xx映射的物理地址保存在对应的hugepage_file->physaddr中。

physaddr = ((page & 0x7fffffffffffffULL) * page_size) + ((unsigned long)virtaddr % page_size);

4、读取/proc/self/numa_maps,得到每个rtemap_xx文件mmap得到的虚拟地址在哪个Socket上,即,哪个CPU上。其socketid保存在对应的hugepage_file->socket_id中。

root@Ubuntu:~# cat /proc/self/numa_maps
00400000 default file=/bin/cat mapped=7 mapmax=2 N0=7
0060a000 default file=/bin/cat anon=1 dirty=1 N0=1
0060b000 default file=/bin/cat anon=1 dirty=1 N0=1
025c1000 default heap anon=3 dirty=3 active=0 N0=3
7fdf0222c000 default file=/usr/lib/locale/locale-archive mapped=10 mapmax=61 N0=10
7fdf0290f000 default file=/lib/x86_64-linux-gnu/libc-2.15.so mapped=82 mapmax=128 N0=82
7fdf02ac4000 default file=/lib/x86_64-linux-gnu/libc-2.15.so
7fdf02cc3000 default file=/lib/x86_64-linux-gnu/libc-2.15.so anon=4 dirty=4 N0=4
7fdf02cc7000 default file=/lib/x86_64-linux-gnu/libc-2.15.so anon=2 dirty=2 N0=2
7fdf02cc9000 default anon=3 dirty=3 active=1 N0=3
7fdf02cce000 default file=/lib/x86_64-linux-gnu/ld-2.15.so mapped=27 mapmax=122 N0=27
7fdf02ed7000 default anon=3 dirty=3 N0=3
7fdf02eee000 default anon=2 dirty=2 N0=2
7fdf02ef0000 default file=/lib/x86_64-linux-gnu/ld-2.15.so anon=1 dirty=1 N0=1
7fdf02ef1000 default file=/lib/x86_64-linux-gnu/ld-2.15.so anon=2 dirty=2 N0=2
7fff09be1000 default stack anon=3 dirty=3 N0=3
7fff09cc2000 default
root@Ubuntu:~#

5、在hugepage_file数组中,根据物理地址,按从小到大的顺序,将hugepage_file排序。

6、根据按物理地址排序后的结果,判断物理地址是否连续,重新mmap /mnt/huge/retmap_xx文件,使得物理地址等于第二次mmap后的虚拟地址。第二次mmap得到的虚拟地址保存在对应的hugepage_file->final_va中。

7、munmap释放第一步中各个rtemap_xx文件首次mmap得到的内存地址。

8、计算每个socket上包含多少个hugepage,信息保存在internal_config.hugepage_info[0].num_pages[socket]中。

9、calc_num_pages_per_socket(),目的是什么???

10、为/var/run/.rte_hugepage_info文件mmap一段nr_hugepages * sizeof(struct hugepage_file)大小的内存块,并将第一步中创建的hugepage_file数组中的所有内容,都copy到这一块内存中。

11、rte_config.mem_config->memseg[]数组记录hugepage_file映射后物理地址连续的块数,hugepage_file->memseg_id为该huepage_file的物理地址在哪个rte_config.mem_config->memseg[]数组中。struct rte_memseg数据结构如下:

struct rte_memseg {phys_addr_t phys_addr;      /**< Start physical address. */union {void *addr;         /**< Start virtual address. */uint64_t addr_64;   /**< Makes sure addr is always 64 bits */};
#ifdef RTE_LIBRTE_IVSHMEMphys_addr_t ioremap_addr; /**< Real physical address inside the VM */
#endifsize_t len;               /**< Length of the segment. */size_t hugepage_sz;       /**< The pagesize of underlying memory */int32_t socket_id;          /**< NUMA socket ID. */uint32_t nchannel;          /**< Number of channels. */uint32_t nrank;             /**< Number of ranks. */
#ifdef RTE_LIBRTE_XEN_DOM0/**< store segment MFNs */uint64_t mfn[DOM0_NUM_MEMBLOCK];
#endif
} __attribute__((__packed__));

  rte_config.mem_config->memseg[j].phys_addr = 各物理地址是连续的内存块的首地址。

  rte_config.mem_config->memseg[j].addr = 各个物理地址是连续的内存块对应的虚拟地址的首地址。由于物理地址和虚拟地址是相同的,这个值应该等于phys_addr。

  rte_config.mem_config->memseg[j].len = 各个物理地址是连续的内存块的大小。

  rte_config.mem_config->memseg[j].socket_id = 内存块在哪个socket上。。

  rte_config.mem_config->memseg[j].hugepage_sz = hugepage内存页的大小。本文中是2M。

2.4 rte_eal_memdevice_init()

rte_eal_memdevice_init()初始化rte_config.mem_config->nchannel和rte_config.mem_config->nrank。

  rte_config.mem_config->nchannel = 启动参数中“-n”指定的值,不能为0,不能大于4。

  rte_config.mem_config->nrank = 启动参数中“-r”指定的值。不能为0,不能大于16。

2.5 rte_eal_memzone_init()

rte_eal_memzone_init()主要负责初始化rte_config.mem_config->free_memseg[]及rte_config.mem_config->memzone[]。其中,rte_config.mem_config->free_memseg[]记录空闲的rte_config.mem_config->memseg[]。

3、总结

如下图:

(转)DPDK内存管理 01 -----初始化相关推荐

  1. dpdk内存管理——内存初始化

    *说明:本系列博文源代码均来自dpdk17.02* 1.1内存初始化 1.1.1 hugepage技术 hugepage(2M/1G..)相对于普通的page(4K)来说有几个特点: (1) huge ...

  2. dpdk内存管理分析

    dpdk内存管理分析 文章目录 dpdk内存管理分析 1.1 简述 1.2 `rte_config_init`分析 1.3 `eal_hugepage_info_init`的分析 1.4 `rte_e ...

  3. DPDK 内存管理---malloc_heap和malloc_elem

    博文是基于dpdk20.5代码阅读所写,如理解有错误或不当之处,烦请指正,不甚感激.也可以私信我一起探讨. 两种数据结构体介绍 Malloc 库内部使用了两种数据结构类型(可以参考dpdk官方文档3. ...

  4. iOS - 内存管理 01

    iOS - 内存管理 01 一.概述 内部管理简单来说就是计算机内部存储的管理,我们从冯·诺依曼结构说起,冯·诺依曼结构指出了计算机由运算器.控制器.存储器.输入和输出设备几大部件组成.拿 iPhon ...

  5. 启动期间的内存管理之初始化过程概述----Linux内存管理(九)

    转载地址:https://blog.csdn.net/gatieme/article/details/52403148 日期 内核版本 架构 作者 GitHub CSDN 2016-09-01 Lin ...

  6. dpdk内存管理之rte_eal_hugepage_init()函数分析

    dpdk版本:dpdk-stable-16.11.11 今天我们来看一下rte_eal_hugepage_init() 函数都干了哪些事. 1.计算大页总数 在调用rte_eal_hugepage_i ...

  7. DPDK内存管理二:初始化

    DPDK 内存的初始化主要在rte_eal_init()函数中进行: eal_hugepage_info_init() /* 获取系统中hugepage种类以及数量信息到internal_config ...

  8. DPDK内存管理总结

    1.前言 本文基于DPDK-17.05.2分析总结, DPDK通过使用hugetlbfs,减少CPU TLB表的Miss次数,提高性能. 2.Hugetlbfs初始化 DPDK的内存初始化工作,主要是 ...

  9. android 设置setmultichoiceitems设置初始化勾选_Linux内核启动:虚拟盘空间设置和内存管理结构初始化...

    1. 设置虚拟盘并初始化 接下来main函数将对外设中的虚拟盘区进行设置. 检查makefile文件中"虚拟盘使用标志"是否设置, 以此确定系统是否使用了虚拟盘(假设有虚拟盘,大小 ...

  10. dpdk 内存管理 原理剖析

    dpdk 的内存管理层次结构 物理巨页的管理 虚拟地址空间的管理 heap管理(变长buffer) mempool管理(定长buffer) 物理巨页的管理 dpdk中通过 数组 hugepg_tbl[ ...

最新文章

  1. javascript如何实现功能的分析
  2. Matlab与线性代数 -- 数组的乘法与除法
  3. 实施ITIL十个需要知道的事情
  4. 进阶学习(4.2) JVM 常用配置参数, GC 参数
  5. mysql如何算值_如何计算MySQL中具有特定值的列数?
  6. 【个人总结】知识晶体的前中后台
  7. 枚举类型是怎样定义的?有什么用途?_新型合金材料——什么是液态金属、液态金属的定义、发展以及用途...
  8. Ajax-jsonp
  9. 【面试】排序算法整理
  10. C++ STL set详解
  11. 麦子学院3天带你学会Apple Watch开发
  12. 小程序无法获取用户头像的原因
  13. vm虚拟化服务器杀毒,VMware服务器虚拟化平台应急方案
  14. 2022年R2移动式压力容器充装操作证考试题库及答案
  15. python3+selenium实现126邮箱登陆并发送邮件
  16. 【Opencv实战】刷爆朋友圈的特效“人物动漫化”是什么鬼?今天教你涨姿势—网友狂喜:我要学习,不许拦我
  17. 应届生如何做好一份简历?
  18. SQLserver提权
  19. android高仿微信的图片查看
  20. Esp32+Dht11+MQTT+Mysql实现测温数据传至数据库

热门文章

  1. Visual Studio 2012安装VASSISTX插件后导致CPU高的解决的方法
  2. zjufantasy.com开发日记(1)
  3. update与fixedupdate差别
  4. php中include包含文件路径查找过程
  5. ASP.NET数据分页技术(4)
  6. Java NIO 详解(一)
  7. Python文档学习笔记(1)--使用Python 解释器
  8. Spring MVC 实现文件的上传和下载
  9. Spring的p标签
  10. 利用OpenSSL创建自签名的SSL证书备忘