目录
1.2.1 rte_eal_malloc_heap_init
1.1 内存堆的作用
内存堆的主要作用就是将大页分段信息保存到堆中,用堆对这块内存进行管理。
1.2 分析函数
1.2.1 rte_eal_malloc_heap_init
int
rte_eal_malloc_heap_init(void)
{
struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
unsigned int i;
//参数没有设置
if (internal_config.match_allocations) {
RTE_LOG(DEBUG, EAL, "Hugepages will be freed exactly as allocated.\n");
}
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
/* assign min socket ID to external heaps */
mcfg->next_socket_id = EXTERNAL_HEAP_MIN_SOCKET_ID;
/* assign names to default DPDK heaps */
//设置默认堆的sock id,使堆的信息和head关联
for (i = 0; i < rte_socket_count(); i++) {
struct malloc_heap *heap = &mcfg->malloc_heaps[i];
char heap_name[RTE_HEAP_NAME_MAX_LEN];
int socket_id = rte_socket_id_by_idx(i);
snprintf(heap_name, sizeof(heap_name),
"socket_%i", socket_id);
strlcpy(heap->name, heap_name, RTE_HEAP_NAME_MAX_LEN);
heap->socket_id = socket_id;
}
}
//注册了一个mp请求
if (register_mp_requests()) {
RTE_LOG(ERR, EAL, "Couldn't register malloc multiprocess actions\n");
rte_mcfg_mem_read_unlock();
return -1;
}
/* unlock mem hotplug here. it's safe for primary as no requests can
* even come before primary itself is fully initialized, and secondaries
* do not need to initialize the heap.
*/
rte_mcfg_mem_read_unlock();
/* secondary process does not need to initialize anything */
if (rte_eal_process_type() != RTE_PROC_PRIMARY)
return 0;
/* add all IOVA-contiguous areas to the heap */
//将所有虚拟rte_msg segment添加到heap中
return rte_memseg_contig_walk(malloc_add_seg, NULL);
}
1.2.2 rte_memseg_contig_walk
//加锁调用,主要功能是在这个函数中rte_memseg_contig_walk_thread_unsafe
int
rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
{
int ret = 0;
/* do not allow allocations/frees/init while we iterate */
rte_mcfg_mem_read_lock();
ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
rte_mcfg_mem_read_unlock();
return ret;
}
int
rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
{
struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
int i, ms_idx, ret = 0;
//遍历所有mem_seg,在上一篇中有分析,根据当前系统,实际能够产生4个内存段
for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
struct rte_memseg_list *msl = &mcfg->memsegs[i];
const struct rte_memseg *ms;
struct rte_fbarray *arr;
if (msl->memseg_arr.count == 0)
continue;
//根据rte_meseg的页的个数,以及掩码申请的一块虚拟内存,将内存地址放到nenseg_arr
arr = &msl->memseg_arr;
ms_idx = rte_fbarray_find_next_used(arr, 0);
while (ms_idx >= 0) {
int n_segs;
size_t len;
//根据id找到rte_memseg
ms = rte_fbarray_get(arr, ms_idx);
/* find how many more segments there are, starting with
* this one.
*/
//查找有多少个页能用
n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
//算出整个内存的大小
len = n_segs * msl->page_sz;
//调用函数malloc_add_seg
ret = func(msl, ms, len, arg);
if (ret)
return ret;
ms_idx = rte_fbarray_find_next_used(arr,
ms_idx + n_segs);
}
}
return 0;
}
1.2.3 malloc_add_seg
static int
malloc_add_seg(const struct rte_memseg_list *msl,
const struct rte_memseg *ms, size_t len, void *arg __rte_unused)
{
struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
struct rte_memseg_list *found_msl;
struct malloc_heap *heap;
int msl_idx, heap_idx;
if (msl->external)
return 0;
heap_idx = malloc_socket_to_heap_id(msl->socket_id);
if (heap_idx < 0) {
RTE_LOG(ERR, EAL, "Memseg list has invalid socket id\n");
return -1;
}
heap = &mcfg->malloc_heaps[heap_idx];
/* msl is const, so find it */
msl_idx = msl - mcfg->memsegs;
if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
return -1;
found_msl = &mcfg->memsegs[msl_idx];
//ms->addr这块地址是每一个rte_segmem的首地址
//在hugepage_init中ms->addr是遍历每一段内存中每一页,根据页的偏移计算出ms->addr,
//在本函数中ms->addr根据每一段的页的个数和大小还有首页的地址算出来的
malloc_heap_add_memory(heap, found_msl, ms->addr, len);
heap->total_size += len;
RTE_LOG(DEBUG, EAL, "Added %zuM to heap on socket %i\n", len >> 20,
msl->socket_id);
return 0;
}
//初始化元素信息
void
malloc_elem_init(struct malloc_elem *elem, struct malloc_heap *heap,
struct rte_memseg_list *msl, size_t size,
struct malloc_elem *orig_elem, size_t orig_size)
{
elem->heap = heap;
elem->msl = msl;
elem->prev = NULL;
elem->next = NULL;
memset(&elem->free_list, 0, sizeof(elem->free_list));
elem->state = ELEM_FREE;
elem->size = size;
elem->pad = 0;
elem->orig_elem = orig_elem;
elem->orig_size = orig_size;
set_header(elem);
set_trailer(elem);
}
//把元素插入到free list的链表中
static struct malloc_elem *
malloc_heap_add_memory(struct malloc_heap *heap, struct rte_memseg_list *msl,
void *start, size_t len)
{
//elem的地址是rte_seg的首地址
struct malloc_elem *elem = start;
//初始化元素
malloc_elem_init(elem, heap, msl, len, elem, len);
//将elem这个元素插入到堆中
malloc_elem_insert(elem);
//如果元素是相邻的元素就会把钱一个元素后一个元素合并成一个元素,变成一块内存
elem = malloc_elem_join_adjacent_free(elem);
//把这一类地址空间加到free_list里边
malloc_elem_free_list_insert(elem);
return elem;
}
2.3 总结
1、rte_eal_memory_init是为rte_eal_malloc_heap_init做了一个铺垫,通过一系列的内存分配,分页,分段的初始化,最终的目的是把这些内存放到堆中,所有内存的调用,缓冲池,rte_alloc的调用最终申请的是堆的内存,而堆用到的内存是虚拟内存。
2、分析到目前还有以下疑问:
(1)大页的产生是因为TLB大小有限,但是大页在TLB的应用是怎么体现的,这个还未开始研究
(2)内存申请,缓存池内存申请,mbuf的内存申请这块还没有研究,接下来花一定的时间去探究这些问题