DPDK内存管理-----(一)初始化
1 前言
DPDK通过使用hugetlbfs,减少CPU TLB表的Miss次数,提高性能。
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 0
drwxr-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 0
sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
udev /dev devtmpfs rw,relatime,size=1016836k,nr_inodes=254209,mode=755 0 0
devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /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 0
none /sys/fs/fuse/connections fusectl rw,relatime 0 0
none /sys/kernel/debug debugfs rw,relatime 0 0
none /sys/kernel/security securityfs rw,relatime 0 0
none /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0
none /run/shm tmpfs rw,nosuid,nodev,relatime 0 0
none /media/sf_F_DRIVE vboxsf rw,nodev,relatime 0 0
gvfs-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 0
none /mnt/huge hugetlbfs rw,relatime 0 0
root@Ubuntu:~#
3、通过读取/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages,获取配置的hugepages个数。
- root@Ubuntu:~# cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
64
root@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_SEGMENTS
- int repeated; /**< number of times the page size is repeated */
- #endif
- char 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_61
root@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_IVSHMEM
- phys_addr_t ioremap_addr; /**< Real physical address inside the VM */
- #endif
- size_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内存管理-----(一)初始化的更多相关文章
- 启动期间的内存管理之初始化过程概述----Linux内存管理(九)
在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检 ...
- DPDK内存管理(1)
1 前言 DPDK将利用hugepage预留的物理内存统一的组织管理起来,然后以库的方式对外提供使用的接口.下图展示了DPDK中内存有关的模块的相互关系. rte_eal 是统一 ...
- DPDK内存管理-----(二)rte_mempool内存管理
DPDK以两种方式对外提供内存管理方法,一个是rte_mempool,主要用于网卡数据包的收发:一个是rte_malloc,主要为应用程序提供内存使用接口.本文讨论rte_mempool.rte_me ...
- DPDK内存管理(1)(转)
1 前言 DPDK将利用hugepage预留的物理内存统一的组织管理起来,然后以库的方式对外提供使用的接口.下图展示了DPDK中内存有关的模块的相互关系. rte_eal 是统一 ...
- DPDK内存管理-----(三)rte_malloc内存管理
rte_malloc()为程序运行过程中分配内存,模拟从堆中动态分配内存空间. void * rte_malloc(const char *type, size_t size, unsigned al ...
- DPDK内存管理-----rte_mbuf(转)
本文主要介绍rte_mbuf与rte_mempool数据结构之间的组织关系.以及网卡接收到的数据是如何存储在rte_mbuf中的. 一.rte_mbuf.rte_mempool及网卡收到的数据包在内存 ...
- Linux0.11内核--内存管理之1.初始化
[版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5597705.html ] Linux内核因为使用了内存分页机制,所以相对来说好理解些.因为内存 ...
- linux内存管理初始化
内存管理子系统是linux内核最核心最重要的一部分,内核的其他部分都需要在内存管理子系统的基础上运行.而对其初始化是了解整个内存管理子系统的基础.对相关数据结构的初始化是从全局启动例程start_ke ...
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...
随机推荐
- JAVA如何将PDF转换SWF格式的FLASH
1. 需要用到的工具 SWFTools 下载地址 http://www.swftools.org/download.html,下载完成以后,直接安装就行 2.下面就是重点喽,详见Java代码解析 附 ...
- 109、FragmentPagerAdapter与FragmentStatePagerAdapter区别
x 在一个 Android 应用中,我使用 FragmentPagerAdapter 来处理多 Fragment 页面的横向滑动.不过我碰到了一个问题,即当 Fragment 对应的数据集发生改变时, ...
- C++学习18 派生类的析构函数
和构造函数类似,析构函数也是不能被继承的. 创建派生类对象时,构造函数的调用顺序和继承顺序相同,先执行基类构造函数,然后再执行派生类的构造函数.但是对于析构函数,调用顺序恰好相反,即先执行派生类的析构 ...
- Java SE 第二十四讲----static与final使用陷阱关键字
1.对于final类型成员变量,一般来说有两种赋值方式: a)在声明final类型的成员变量时就附上初值 package com.cl.staticandfinal; public class Fin ...
- PS 查看选定图层的高宽
1. 选中图层,然后按键盘"Ctrl+T(菜单中点编辑->自由变换)",然后上面工具条会出现调整比率,宽W:100% H:100%,然后在100%的位置点鼠标右键,选择 ...
- Codeforces 519E A and B and Lecture Rooms [倍增法LCA]
题意: 给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的.树上所有边的边权是1. 思路: 很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分 ...
- HYSBZ 1036 【树链剖分】
思路: 裸裸的树链剖分.... 树链剖分就是把一棵树分成若干重链和轻链...然后保证形成的线段树上每条链是连续存储的.然后这样就能用线段树进行维护了. 但是每次一定要保证是在同一条链里边....思路就 ...
- hbase的regionserver宕机
错误日志: regionserver.HRegionServer: Failed deleting my ephemeral node zookeeper.RecoverableZooKeeper: ...
- 日志挖掘Logmnr
日志挖掘 9.1 日志中数据用途 所有对用户数据以及数据字典的改变全部被保存在联机日志中.当然nologging,insert/*+append+/情况比较特殊除外,因此归档日志可以用来做数据库的恢复 ...
- php mysql 中文乱码解决方法
本文章向码农们介绍php mysql 中文乱码解决方法,对码农们非常实用,需要的码农可以参考一下. 从MySQL 4.1开始引入多语言的支持,但是用PHP插入的中文会出现乱码.无论用什么编码也不行 解 ...