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、总结

如下图:

 




DPDK以两种方式对外提供内存管理方法,一个是rte_mempool,主要用于网卡数据包的收发;一个是rte_malloc,主要为应用程序提供内存使用接口。本文讨论rte_mempool。rte_mempool由函数rte_mempool_create()负责创建,从rte_config.mem_config->free_memseg[]中取出合适大小的内存,放到rte_config.mem_config->memzone[]中。

本文中,以l2fwd为例,说明rte_mempool的创建及使用。

一、rte_mempool的创建

复制代码
1 l2fwd_pktmbuf_pool =
2     rte_mempool_create("mbuf_pool", NB_MBUF,
3                MBUF_SIZE, 32,
4                sizeof(struct rte_pktmbuf_pool_private),
5                rte_pktmbuf_pool_init, NULL,
6                rte_pktmbuf_init, NULL,
7                rte_socket_id(), 0);
复制代码

“mbuf_pool”:创建的rte_mempool的名称。

NB_MBUF:rte_mempool包含的rte_mbuf元素的个数。

MBUF_SIZE:每个rte_mbuf元素的大小。

1 #define RTE_PKTMBUF_HEADROOM    128
2 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
3 #define NB_MBUF   8192
1 struct rte_pktmbuf_pool_private {
2     uint16_t mbuf_data_room_size; /**< Size of data space in each mbuf.*/
3 };

rte_mempool由函数rte_mempool_create()负责创建。首先创建rte_ring,再创建rte_mempool,并建立两者之间的关联。

   

1、rte_ring_create()创建rte_ring无锁队列

1 r = rte_ring_create(rg_name, rte_align32pow2(n+1), socket_id, rg_flags);

  具体步骤如下:

  a、需要保证创建的队列数可以被2整除,即,count = rte_align32pow2(n + 1);

  b、计算需要为count个队列分配的内存空间,即,ring_size = count * sizeof(void *) + sizeof(struct rte_ring);

  struct rte_ring的数据结构如下,

复制代码
 1 struct rte_ring {
 2     TAILQ_ENTRY(rte_ring) next;      /**< Next in list. */
 3 
 4     char name[RTE_RING_NAMESIZE];    /**< Name of the ring. */
 5     int flags;                       /**< Flags supplied at creation. */
 6 
 7     /** Ring producer status. */
 8     struct prod {
 9         uint32_t watermark;      /**< Maximum items before EDQUOT. */
10         uint32_t sp_enqueue;     /**< True, if single producer. */
11         uint32_t size;           /**< Size of ring. */
12         uint32_t mask;           /**< Mask (size-1) of ring. */
13         volatile uint32_t head;  /**< Producer head. */
14         volatile uint32_t tail;  /**< Producer tail. */
15     } prod __rte_cache_aligned;
16 
17     /** Ring consumer status. */
18     struct cons {
19         uint32_t sc_dequeue;     /**< True, if single consumer. */
20         uint32_t size;           /**< Size of the ring. */
21         uint32_t mask;           /**< Mask (size-1) of ring. */
22         volatile uint32_t head;  /**< Consumer head. */
23         volatile uint32_t tail;  /**< Consumer tail. */
24 #ifdef RTE_RING_SPLIT_PROD_CONS
25     } cons __rte_cache_aligned;
26 #else
27     } cons;
28 #endif
29 
30 #ifdef RTE_LIBRTE_RING_DEBUG
31     struct rte_ring_debug_stats stats[RTE_MAX_LCORE];
32 #endif
33 
34     void * ring[0] __rte_cache_aligned; /**< Memory space of ring starts here.
35                                          * not volatile so need to be careful
36                                          * about compiler re-ordering */
37 };
复制代码

  c、调用rte_memzone_reserve(),在rte_config.mem_config->free_memseg[]中查找一个合适的free_memseg(查找规则是free_memseg中剩余内存大于等于需要分配的内存,但是多余的部分是最小的),从该free_memseg中分配指定大小的内存,然后将分配的内存记录在rte_config.mem_config->memzone[]中。

  d、初始化新分配的rte_ring。

复制代码
 1 r->flags = flags;
 2 r->prod.watermark = count;
 3 r->prod.sp_enqueue = !!(flags & RING_F_SP_ENQ);
 4 r->cons.sc_dequeue = !!(flags & RING_F_SC_DEQ);
 5 r->prod.size = r->cons.size = count;
 6 r->prod.mask = r->cons.mask = count-1;
 7 r->prod.head = r->cons.head = 0;
 8 r->prod.tail = r->cons.tail = 0;
 9 
10 TAILQ_INSERT_TAIL(ring_list, r, next); // 挂到rte_config.mem_config->tailq_head[RTE_TAILQ_RING]队列中
复制代码

2、创建并初始化rte_mempool

  a、计算需要为rte_mempool申请的内存空间。包含:sizeof(struct rte_mempool)、private_data_size,以及n * objsz.total_size。

1 mempool_size = MEMPOOL_HEADER_SIZE(mp, pg_num) + private_data_size;
2 if (vaddr == NULL)
3     mempool_size += (size_t)objsz.total_size * n;

  objsz.total_size = objsz.header_size + objsz.elt_size + objsz.trailer_size; 其中,

  objsz.header_size = sizeof(struct rte_mempool *);

  objsz.elt_size = MBUF_SIZE;

  objsz.trailer_size = ????

  b、调用rte_memzone_reserve(),在rte_config.mem_config->free_memseg[]中查找一个合适的free_memseg,在该free_memseg中分配mempool_size大小的内存,然后将新分配的内存记录到rte_config.mem_config->memzone[]中。

  c、初始化新创建的rte_mempool,并调用rte_pktmbuf_pool_init()初始化rte_mempool的私有数据结构。

复制代码
 1 /* init the mempool structure */
 2 mp = mz->addr;
 3 memset(mp, 0, sizeof(*mp));
 4 snprintf(mp->name, sizeof(mp->name), "%s", name);
 5 mp->phys_addr = mz->phys_addr;
 6 mp->ring = r;
 7 mp->size = n;
 8 mp->flags = flags;
 9 mp->elt_size = objsz.elt_size;
10 mp->header_size = objsz.header_size;
11 mp->trailer_size = objsz.trailer_size;
12 mp->cache_size = cache_size;
13 mp->cache_flushthresh = (uint32_t)
14     (cache_size * CACHE_FLUSHTHRESH_MULTIPLIER);
15 mp->private_data_size = private_data_size;
16 
17 /* calculate address of the first element for continuous mempool. */
18 obj = (char *)mp + MEMPOOL_HEADER_SIZE(mp, pg_num) +
19     private_data_size;
20 
21 /* populate address translation fields. */
22 mp->pg_num = pg_num;
23 mp->pg_shift = pg_shift;
24 mp->pg_mask = RTE_LEN2MASK(mp->pg_shift, typeof(mp->pg_mask));
25 
26 /* mempool elements allocated together with mempool */
27 mp->elt_va_start = (uintptr_t)obj;
28 mp->elt_pa[0] = mp->phys_addr +
29     (mp->elt_va_start - (uintptr_t)mp);
30 
31 mp->elt_va_end = mp->elt_va_start;
32 
33 RTE_EAL_TAILQ_INSERT_TAIL(RTE_TAILQ_MEMPOOL, rte_mempool_list, mp); //挂到rte_config.mem_config->tailq_head[RTE_TAILQ_MEMPOOL]队列中
复制代码

  d、调用mempool_populate(),以及rte_pktmbuf_init()初始化rte_mempool的每个rte_mbuf元素。

3、总结

相关数据结构的关联关系如下图:

二、rte_mempool的调用

 




rte_malloc()为程序运行过程中分配内存,模拟从堆中动态分配内存空间。

1 void *
2 rte_malloc(const char *type, size_t size, unsigned align)
3 {
4     return rte_malloc_socket(type, size, align, SOCKET_ID_ANY);
5 }

rte_malloc()函数调用关系如下图:

 

rte_malloc_socket():指定从哪个socket上分配内存空间,默认是指定SOCKET_ID_ANY,即,程序在哪个socket上运行,就从哪个socket上分配内存。如果指定的socket上没有合适的内存空间,就再从其它socket上分配。

 

malloc_heap_alloc():从rte_config.mem_config->malloc_heaps[]数组中找到指定socket对应的堆(使用struct malloc_heap描述堆),即,从这个堆中分配空间。如果该堆是第一次使用,还没有被初始化过,则调用malloc_heap_init()初始化;首先,调用find_suitable_element()在堆中查找是否有合适内存可以分配,如果没有,则调用malloc_heap_add_memzone()在rte_config.mem_config->memzone[]中给堆分配一块内存。最后,调用malloc_elem_alloc()在堆中,将需要分配的内存划分出去。

复制代码
 1 void *
 2 malloc_heap_alloc(struct malloc_heap *heap,
 3         const char *type __attribute__((unused)), size_t size, unsigned align)
 4 {
 5     if (!heap->initialised)
 6         malloc_heap_init(heap);
 7 
 8     size = CACHE_LINE_ROUNDUP(size);
 9     align = CACHE_LINE_ROUNDUP(align);
10     rte_spinlock_lock(&heap->lock);
11     struct malloc_elem *prev, *elem = find_suitable_element(heap,
12             size, align, &prev);
13     if (elem == NULL){
14         if ((malloc_heap_add_memzone(heap, size, align)) == 0)
15             elem = find_suitable_element(heap, size, align, &prev);
16     }
17 
18     if (elem != NULL){
19         elem = malloc_elem_alloc(elem, size, align, prev);
20         /* increase heap's count of allocated elements */
21         heap->alloc_count++;
22     }
23     rte_spinlock_unlock(&heap->lock);
24     return elem == NULL ? NULL : (void *)(&elem[1]);
25 
26 }
复制代码

 

malloc_heap_init():主要是为struct malloc_heap数据结构的各个成员变量赋初始值,并将该堆的状态设置为INITIALISED。

 

malloc_heap_add_memzone():调用rte_memzone_reserve(),在rte_config.mem_config->memzone[]中分配合适大小的内存。分配的内存的大小是mz_size = MAX(min_size, 11M),其中,min_size = size + align + MALLOC_ELEM_OVERHEAD * 2; size是rte_malloc()指定的需要分配内存的大小。如果memzone[]中没有合适的内存块,将mz_size减半,再次查找。

1 do {
2     mz = rte_memzone_reserve(mz_name, mz_size, numa_socket,
3                  mz_flags);
4     if (mz == NULL)
5         mz_size /= 2;
6 } while (mz == NULL && mz_size > min_size);

 

find_suitable_element():在堆中找到一块合适大小的内存,分配的内存是从堆的底部开始查找的。如果堆剩余内存不够分配的,会再次调用malloc_heap_add_memzone()扩展堆的大小。

 

malloc_elem_alloc():查找到合适大小的内存块后,将这一块内存从堆中划分出去。

 

还是直接上图直接点。。。。

 

 

错误之处,欢迎指正。


  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值