dpdk内存管理之内存初始化(内存收集)

来源:互联网 发布:中国经济数据网 编辑:程序博客网 时间:2024/04/30 12:14

初始化之前的内存layout

dpdk得到的原始内存是通过mmap大页获得的,而这样的原始内存分布可以用下图表示(NUMA架构)。关于页表相关的知识,可以参加我的另外一篇文章(虚拟存储器)。

其中页a、a+1类似的表示连续的物理页。

dpdk通过事先申请的大页,mmap对应的大页dir获得内存。可以看出,程序mmap每个页时,虽然物理页可能连续,但返回的虚拟地址却不一定是连续的。而且,返回的内存不会固定在某个socket上。

初始化之后的内存layout

为了高效的使用内存,需要重新组织内存,并对其进行管理。

组织的方式:

  • 把物理地址连续的页映射到进程中时,对应的虚拟地址也连续。
  • 把物理地址连续,并且在同一个socket node上的连续页,组织成一个段(segment)统一管理。
  • 对于相同的物理页,每个进程映射的虚拟地址都相同。

经过重新组织内存后的layout,如图:

组织内存用到的技术

dpdk使用linux提供的获取大页、页表、numa节点表、mmap功能重新组织内存。

大页:最多可以同时存在3中大页。一般只是用2M的大页。linux中获取大页相关信息是通过访问解析/sys/kernel/mm/hugepages、/proc/meminfo、/proc/mounts等内容得到的。

页表:linux中每个进程的页表对应的文件是/proc/pid/pagemap,本进程的页表是/proc/self/pagemap。

/proc/pid/pagemap中的内容(每一个页表项有64位)如下:

* Bits 0-54   page framenumber (PFN) if present

* Bits 0-4    swap type ifswapped

* Bits5-54   swap offset if swapped

*Bit  55    pte is soft-dirty (see Documentation/vm/soft-dirty.txt)

*Bit  56   page exclusively mapped (since 4.2)

* Bits57-60  zero

*Bit  61   page is file-page or shared-anon (since 3.5)

*Bit  62    page swapped

* Bit  63    page present

注意:大页是不会被swap的,是常驻内存的,所以每个大页对应的页表项中PFN都对应物理内存,不会对应到swap区或者外设内存中。

numa节点表:linux中每个物理页所在的numa socket表对应的文件是/proc/pid/numa_maps,本进程的numa socket表是/proc/self/numa_maps。

/proc/pid/numa_maps中的内容大致如下:

address   policy   mapping details

00400000 defaultfile=/usr/local/bin/app mapped=1 active=0 N3=1 kernelpagesize_kB=4

00600000 defaultfile=/usr/local/bin/app anon=1 dirty=1 N3=1 kernelpagesize_kB=4

3206000000 default file=/lib64/ld-2.12.so mapped=26 mapmax=6 N0=24 N3=2kernelpagesize_kB=4

320621f000 default file=/lib64/ld-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4

3206220000 default file=/lib64/ld-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4

3206221000 default anon=1 dirty=1 N3=1 kernelpagesize_kB=4

3206800000 default file=/lib64/libc-2.12.so mapped=59 mapmax=21 active=55 N0=41 N3=18kernelpagesize_kB=4

320698b000 default file=/lib64/libc-2.12.so

3206b8a000 default file=/lib64/libc-2.12.so anon=2 dirty=2 N3=2 kernelpagesize_kB=4

3206b8e000 default file=/lib64/libc-2.12.so anon=1 dirty=1 N3=1 kernelpagesize_kB=4

3206b8f000 default anon=3 dirty=3 active=1 N3=3 kernelpagesize_kB=4

7f4dc10a2000 default anon=3 dirty=3 N3=3 kernelpagesize_kB=4

7f4dc10b4000 default anon=2 dirty=2 active=1 N3=2 kernelpagesize_kB=4

7f4dc1200000 default file=/anon_hugepage\040(deleted) huge anon=1 dirty=1 N3=1kernelpagesize_kB=2048

7fff335f0000 default stack anon=3 dirty=3 N3=3 kernelpagesize_kB=4

7fff3369d000 default mapped=1 mapmax=35 active=0 N3=1 kernelpagesize_kB=4

Where:"address" is the starting address for the mapping;

"policy"reports the NUMA memory policy set for the mapping (seevm/numa_memory_policy.txt);

"mapping details" summarizes mappingdata such as mapping type, page usage counters, node locality page counters (N0== node0, N1 == node1, ...) and the kernel page size, in KB, that is backingthe mapping up.

主进程初始化内存流程

  1. 获取大页信息并存放在internal_config.hugepage_info[i]结构中
  2. 把所有可用的大页都映射进程中,并且每个大页对应一个文件(格式:mountdir/rtesmap_%d),后续可以通过open(hugedir),再mmap(fd)把大页映射进程中使用。

    把每页信息都放到结构struct hugepage_file tmp_hp[]中。

    /**

     * Structure used to storeinformations about hugepages that we mapped

     * through the files inhugetlbfs.

     */

    struct hugepage_file {

            void*orig_va;      /**< virtual addr offirst 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每种大页都从0开始*/

    int memseg_id;      /**< the memory segment to which pagebelongs */

    char filepath[MAX_HUGEPAGE_PATH]; /**< pathto backing file on filesystem */

    };
  3. 通过find_physaddrs函数,根据映射得到的虚拟地址orig_va和/proc/self/pagemap中的信息,找到每个大页虚拟地址对应的物理地址,并添到struct hugepage_file结构中的physaddr字段。
  4. 通过find_numasocket函数,根据映射得到的虚拟地址orig_va和/proc/self/numa_maps中的信息,找到每个大页虚拟地址对应的numa_maps项,解析对应的numa socket节点,赋值到对应的页信息结构struct hugepage_file中的socket_id字段。
  5. 现在,得到了2M(假设使用2M大页)大页的所有可用页的信息,并放到tmp_hp数组中。然后,按物理地址physaddr从小到大排序每个大页。排序后tmp_hp数组中是按物理地址递增的顺序放置大页信息的。
  6. 再一次mmap所有的可用页。这次mmap,是把物理地址连续的页,映射成虚拟地址也连续的页(通过指定void*mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)函数的addr值)。并且,这次映射的虚拟地址放到final_va字段中。
  7. 解除第一次映射的va虚拟地址orig_va。munmap(orig_va)之后使orig_va对应的虚拟地址空间可用。
  8. dpdk默认是把所有的页平分到每个socket(根据每个socket上core的数量)上。
  9. 创建共享内存/var/run/.rte_hugepage_info,把重新组织的页信息struct hugepage_file数组放到.rte_hugepage_info中,以便其他进程共享。
  10. 最后,把物理地址连续(现在虚拟地址肯定也连续了)、同一个socket上的连续的页,组织成一个段memseg,放到rte_mem_config结构的memseg字段中。进程间共享。

次进程使用

次进程主要是把重新组织的内存映射到进程中。

  1. 根据共享的mem_config,获得memseg段信息。memseg中包括每个段的信息(段大小、虚拟和物理地址等)。再检查是否能获得每个段对应的虚拟地址空间。
  2.  然后,再根据共享的.rte_hugepage_info获得每个页信息。
  3. 根据.rte_hugepage_info中每个页的信息(dir、memseg_id),把每个页映射到次进程中,映射的虚拟地址和主进程中的一样。

0 0
原创粉丝点击