• VPP创建主HEAP内存


    在文件src/vpp/vnet/main.c中,主函数首先临时创建1M大小的堆heap使用。最后,销毁临时heap,根据解析到的VPP所配置heap大小和页面大小,创建主heap。

    int
    main (int argc, char *argv[])
    {
      /* temporary heap */
      clib_mem_init (0, 1 << 20);
    
      /* destroy temporary heap and create main one */
      clib_mem_destroy ();
    
      if ((main_heap = clib_mem_init_with_page_size (main_heap_size,
                             main_heap_log2_page_sz)))
        {
          /* Figure out which numa runs the main thread */
          __os_numa_index = clib_get_current_numa_node ();
    
          if (default_log2_hugepage_sz != CLIB_MEM_PAGE_SZ_UNKNOWN)
        clib_mem_set_log2_default_hugepage_size (default_log2_hugepage_sz);
    
          /* and use the main heap as that numa's numa heap */
          clib_mem_set_per_numa_heap (main_heap);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    以上调用的两个内存初始化函数,实际上都是调用了clib_mem_init_internal函数,不同之处在于clib_mem_init使用了默认的页面大小。

    __clib_export void *
    clib_mem_init (void *memory, uword memory_size)
    {
      return clib_mem_init_internal (memory, memory_size,
                     CLIB_MEM_PAGE_SZ_DEFAULT);
    }
    
    __clib_export void *
    clib_mem_init_with_page_size (uword memory_size,
                      clib_mem_page_sz_t log2_page_sz)
    {
      return clib_mem_init_internal (0, memory_size, log2_page_sz);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    函数clib_mem_main_init获取系统的hugepage页面大小,以及NUMA信息。函数clib_mem_create_heap_internal创建主heap。

    /* Initialize CLIB heap based on memory/size given by user.
       Set memory to 0 and CLIB will try to allocate its own heap. */
    static void *
    clib_mem_init_internal (void *base, uword size,
                clib_mem_page_sz_t log2_page_sz)
    {
      clib_mem_heap_t *h;
    
      clib_mem_main_init ();
    
      h = clib_mem_create_heap_internal (base, size, log2_page_sz,
                         1 /*is_locked */ , "main heap");
    
      clib_mem_set_heap (h);
    
      if (mheap_trace_main.lock == 0)
        clib_spinlock_init (&mheap_trace_main.lock);
    
      return h;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以上的调用,首个参数base都是空,根据页面大小对齐要分配的内存大小,由函数clib_mem_vm_map_internal执行内存分配,成功的话返回分配的内存地址base。

    static clib_mem_heap_t *
    clib_mem_create_heap_internal (void *base, uword size,
                       clib_mem_page_sz_t log2_page_sz, int is_locked,
                       char *name)
    {
      clib_mem_heap_t *h;
      int sz = sizeof (clib_mem_heap_t);
      
      if (base == 0) 
        {
          log2_page_sz = clib_mem_log2_page_size_validate (log2_page_sz);
          size = round_pow2 (size, clib_mem_page_bytes (log2_page_sz));
          base = clib_mem_vm_map_internal (0, log2_page_sz, size, -1, 0,
                           "main heap");
      
          if (base == CLIB_MEM_VM_MAP_FAILED)
        return 0;
      
          flags = CLIB_MEM_HEAP_F_UNMAP_ON_DESTROY;
        }
      else
        log2_page_sz = CLIB_MEM_PAGE_SZ_UNKNOWN;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    base起始地址开头为clib_mem_heap_t结构,进行初始化,随后跳过此结构以及名称长度的空间,将随后空间生成mspace结构,以供mspace_malloc分配内存使用,例如pool_get的内存分配。

      h = base;
      h->base = base;
      h->size = size;
      h->log2_page_sz = log2_page_sz;
      h->flags = flags;
      sz = strlen (name);
      strcpy (h->name, name);
      sz = round_pow2 (sz + sizeof (clib_mem_heap_t), 16);
      h->mspace = create_mspace_with_base (base + sz, size - sz, is_locked);
    
      mspace_disable_expand (h->mspace);
    
      clib_mem_poison (mspace_least_addr (h->mspace),
               mspace_footprint (h->mspace));
    
      return h;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在函数clib_mem_vm_map_internal中,首先设置mmap标志MAP_SHARED,mmap的空间变动在其它进程中可见。

    void * 
    clib_mem_vm_map_internal (void *base, clib_mem_page_sz_t log2_page_sz,
                  uword size, int fd, uword offset, char *name)
    {                  
      clib_mem_main_t *mm = &clib_mem_main;
      clib_mem_vm_map_hdr_t *hdr;
      uword sys_page_sz = 1ULL << mm->log2_page_sz;
      int mmap_flags = MAP_FIXED, is_huge = 0;
    
      if (fd != -1)
        {
          mmap_flags |= MAP_SHARED;
          log2_page_sz = clib_mem_get_fd_log2_page_size (fd);
          if (log2_page_sz > mm->log2_page_sz)
        is_huge = 1;       
        }
      else
        {
          mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    根据log2_page_sz的值,决定是否使用巨页hugepage内存,使用巨页,mmap需要标志MAP_HUGETLB。

          if (log2_page_sz == mm->log2_page_sz)
        log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT;
    
          switch (log2_page_sz)
        {
        case CLIB_MEM_PAGE_SZ_UNKNOWN:
          /* will fail later */
          break;
        case CLIB_MEM_PAGE_SZ_DEFAULT:
          log2_page_sz = mm->log2_page_sz;
          break;
        case CLIB_MEM_PAGE_SZ_DEFAULT_HUGE:
          mmap_flags |= MAP_HUGETLB;
          log2_page_sz = mm->log2_default_hugepage_sz;
          is_huge = 1;
          break;
        default:
          mmap_flags |= MAP_HUGETLB;
          mmap_flags |= log2_page_sz << MAP_HUGE_SHIFT;
          is_huge = 1;
        }
        }
    
      if (log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN)
        return CLIB_MEM_VM_MAP_FAILED;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    根据页面大小log2_page_sz对齐要分配的内存size大小,保留size大小的内存映射,返回起始地址base。再次调用mmap将映射地址修改为读写权限,已经以上指定的mmap_flags标志属性。

    对于巨页的情况,mlock将映射的内存锁住,以防移动到swap区。

      size = round_pow2 (size, 1ULL << log2_page_sz);
    
      base = (void *) clib_mem_vm_reserve ((uword) base, size, log2_page_sz);
    
      if (base == (void *) ~0)
        return CLIB_MEM_VM_MAP_FAILED;
    
      base = mmap (base, size, PROT_READ | PROT_WRITE, mmap_flags, fd, offset);
    
      if (base == MAP_FAILED)
        return CLIB_MEM_VM_MAP_FAILED;
    
      if (is_huge && (mlock (base, size) != 0))
        {
          munmap (base, size);
          return CLIB_MEM_VM_MAP_FAILED;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    将base之前的sys_page_sz大小的内存单独映射在本进程内部,MAP_FIXED标志表明映射地址hdr需要等于指定地址(base - sys_page_sz)。如果last_map存在,将其权限变更为可读写,将以上映射的hdr赋值为其next指针,随后,将last_map的权限恢复为不可访问。

      hdr = mmap (base - sys_page_sz, sys_page_sz, PROT_READ | PROT_WRITE,
              MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
    
      if (hdr != base - sys_page_sz)
        {
          munmap (base, size);
          return CLIB_MEM_VM_MAP_FAILED;
        }
    
      map_lock ();
    
      if (mm->last_map)
        {
          mprotect (mm->last_map, sys_page_sz, PROT_READ | PROT_WRITE);
          mm->last_map->next = hdr;
          mprotect (mm->last_map, sys_page_sz, PROT_NONE);
        }
      else
        mm->first_map = hdr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    初始化clib_mem_vm_map_hdr_t结构的hdr,将其作为新的内存映射尾部赋值给last_map,初始化hdr的起始地址,页面大小和数量等信息。最后将尾部的sys_page_sz大小的hdr空间的权限设置为不可访问(PROT_NONE)。

      clib_mem_unpoison (hdr, sys_page_sz);
      hdr->next = 0;
      hdr->prev = mm->last_map;
      snprintf (hdr->name, CLIB_VM_MAP_HDR_NAME_MAX_LEN - 1, "%s", (char *) name);
      mm->last_map = hdr;
    
      hdr->base_addr = (uword) base;
      hdr->log2_page_sz = log2_page_sz;
      hdr->num_pages = size >> log2_page_sz;
      hdr->fd = fd;
      hdr->name[CLIB_VM_MAP_HDR_NAME_MAX_LEN - 1] = 0;
      mprotect (hdr, sys_page_sz, PROT_NONE);
    
      map_unlock ();
    
      clib_mem_unpoison (base, size);
      return base;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    以下VPP命令查看主heap的分配信息,以及使用情况。

    vpp# show memory main-heap verbose
    Thread 0 vpp_main
      base 0x7f7f69de9000, size 2g, locked, unmap-on-destroy, name 'main heap'
        page stats: page-size 4K, total 524288, mapped 39960, not-mapped 0, unknown 484328
          numa 0: 39960 pages, 156.09m bytes
        total: 1.99G, used: 93.37M, free: 1.91G, trimmable: 1.91G
          free chunks 764 free fastbin blks 0
          max total allocated 1.99G
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    pcl--基于区域增长的点云分割
    [激光原理与应用-18]:《激光原理与技术》-4- 粒子数反转与“光”强放大的基本原理
    使用element的过渡效果来做动效
    Promise,async,await 面试题
    Linux -- 面试考点要点,笔试不多
    Ansible-Playbook
    【Vue基础-数字大屏】自定义主题
    lvs dr+keepalived
    Hexagon_V65_Programmers_Reference_Manual (50)
    信息系统项目管理师(第四版)教材精读思维导图-第十三章项目资源管理
  • 原文地址:https://blog.csdn.net/sinat_20184565/article/details/127838374