zoukankan      html  css  js  c++  java
  • 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数据结构如下:

    1 struct hugepage_info {
    2   size_t hugepage_sz; /**< size of a huge page */
    3   const char *hugedir; /**< dir where hugetlbfs is mounted */
    4   uint32_t num_pages[RTE_MAX_NUMA_NODES];
    5   /**< number of hugepages of that size on each socket */
    6   int lock_descriptor; /**< file descriptor for hugepage dir */
    7 };

      具体赋值如下,

      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的首地址;

    1 struct rte_config {
    2     uint32_t master_lcore;       /**< Id of the master lcore */
    3 
    4         ... ...
    5     
    6     struct rte_mem_config *mem_config;
    7 } __attribute__((__packed__));

    struct rte_mem_config数据结构如下:

     1 struct rte_mem_config {
     2     volatile uint32_t magic;   /**< Magic number - Sanity check. */
     3 
     4     /* memory topology */
     5     uint32_t nchannel;    /**< Number of channels (0 if unknown). */
     6     uint32_t nrank;       /**< Number of ranks (0 if unknown). */
     7 
     8     /**
     9      * current lock nest order
    10      *  - qlock->mlock (ring/hash/lpm)
    11      *  - mplock->qlock->mlock (mempool)
    12      * Notice:
    13      *  *ALWAYS* obtain qlock first if having to obtain both qlock and mlock
    14      */
    15     rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */
    16     rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */
    17     rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */
    18 
    19     uint32_t memzone_idx; /**< Index of memzone */
    20 
    21     /* memory segments and zones */
    22     struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */
    23     struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */
    24 
    25     /* Runtime Physmem descriptors. */
    26     struct rte_memseg free_memseg[RTE_MAX_MEMSEG];
    27 
    28     struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */
    29 
    30     /* Heaps of Malloc per socket */
    31     struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];
    32 } __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数据结构如下:

     1 struct hugepage_file {
     2     void *orig_va;      /**< virtual addr of first mmap() */
     3     void *final_va;     /**< virtual addr of 2nd mmap() */
     4     uint64_t physaddr;  /**< physical addr */
     5     size_t size;        /**< the page size */
     6     int socket_id;      /**< NUMA socket ID */
     7     int file_id;        /**< the '%d' in HUGEFILE_FMT */
     8     int memseg_id;      /**< the memory segment to which page belongs */
     9 #ifdef RTE_EAL_SINGLE_FILE_SEGMENTS
    10     int repeated;       /**< number of times the page size is repeated */
    11 #endif
    12     char filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */
    13 };

    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中。

    1 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数据结构如下:

     1 struct rte_memseg {
     2     phys_addr_t phys_addr;      /**< Start physical address. */
     3     union {
     4         void *addr;         /**< Start virtual address. */
     5         uint64_t addr_64;   /**< Makes sure addr is always 64 bits */
     6     };
     7 #ifdef RTE_LIBRTE_IVSHMEM
     8     phys_addr_t ioremap_addr; /**< Real physical address inside the VM */
     9 #endif
    10     size_t len;               /**< Length of the segment. */
    11     size_t hugepage_sz;       /**< The pagesize of underlying memory */
    12     int32_t socket_id;          /**< NUMA socket ID. */
    13     uint32_t nchannel;          /**< Number of channels. */
    14     uint32_t nrank;             /**< Number of ranks. */
    15 #ifdef RTE_LIBRTE_XEN_DOM0
    16      /**< store segment MFNs */
    17     uint64_t mfn[DOM0_NUM_MEMBLOCK];
    18 #endif
    19 } __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、总结

    如下图:

  • 相关阅读:
    PAT (Advanced Level) 1010. Radix (25)
    PAT (Advanced Level) 1009. Product of Polynomials (25)
    PAT (Advanced Level) 1008. Elevator (20)
    PAT (Advanced Level) 1007. Maximum Subsequence Sum (25)
    PAT (Advanced Level) 1006. Sign In and Sign Out (25)
    PAT (Advanced Level) 1005. Spell It Right (20)
    PAT (Advanced Level) 1004. Counting Leaves (30)
    PAT (Advanced Level) 1001. A+B Format (20)
    PAT (Advanced Level) 1002. A+B for Polynomials (25)
    PAT (Advanced Level) 1003. Emergency (25)
  • 原文地址:https://www.cnblogs.com/MerlinJ/p/4074391.html
Copyright © 2011-2022 走看看