首先看一下重定位前,在重定位前也就是第一阶段,u-boot会保留一片内存用于堆,也就是gd->malloc_base这篇区域,其大小为0x400,具体见下面的内存图:
图1在源码中搜索malloc可以在include/malloc.h中找到下面的宏定义:
/*你可能发现很多重复的宏定义,但是经过分析,最终生效的是下面这些*/ # define cALLOc calloc # define fREe free # define mALLOc malloc # define mEMALIGn memalign # define rEALLOc realloc # define vALLOc valloc # define pvALLOc pvalloc # define mALLINFo mallinfo # define mALLOPt mallopt直接搜malloc的实现是搜不到的,因为它的实现是由mALLOc函数完成的(看上面的宏定义mALLOc会被替换成malloc),其函数体在common/dlmalloc.c文件中:
#if __STD_C Void_t* mALLOc(size_t bytes) #else Void_t* mALLOc(bytes) size_t bytes; #endif { /*定义了一堆变量*/ /* 省略 */ /*这个宏定义了,才能在重定位前使用malloc来申请内存*/ #if CONFIG_VAL(SYS_MALLOC_F_LEN) /*这个标志是在board_init_r里面设置的,也就是重定位 完成,所以重定位前使用的就是这种分配方式*/ if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT)) return malloc_simple(bytes); #endif /*下面就是重定位后的分配方式,太长了,有200多行,直接省 略,感兴趣的直接看源码吧*/ /* 省略 */ }本文不讨论其内存的分配策略,仅为找到malloc的源头。 从下面的代码可以看出重定位前使用的堆内存池是gd->malloc_base这片区域,重定位前使用的策略比较简单,就是分块,一块一块的对外分配就完了:
static void *alloc_simple(size_t bytes, int align) { ulong addr, new_ptr; void *ptr; addr = ALIGN(gd->malloc_base + gd->malloc_ptr, align); new_ptr = addr + bytes - gd->malloc_base; log_debug("size=%zx, ptr=%lx, limit=%lx: ", bytes, new_ptr, gd->malloc_limit); if (new_ptr > gd->malloc_limit) { log_err("alloc space exhausted\n"); return NULL; } ptr = map_sysmem(addr, bytes); gd->malloc_ptr = ALIGN(new_ptr, sizeof(new_ptr)); return ptr; }还是先看下重定位后的内存图:
图2 从图上可以看出,重定位后的堆内存空间比重定位前的内存空间要大得多,这篇区域在重定位前仅是保留的,没有任何指针指向这里,在board_init_r阶段,这片区域的起始地址才和一个全局变量mem_malloc_start绑定,用于重定位后的堆内存,代码如下: /*common/board_r.c*/ static int initr_malloc(void) { ulong malloc_start; #if CONFIG_VAL(SYS_MALLOC_F_LEN) debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr, gd->malloc_ptr / 1024); #endif /* The malloc area is immediately below the monitor copy in DRAM */ /* * This value MUST match the value of gd->start_addr_sp in board_f.c: * reserve_noncached(). */ malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN), TOTAL_MALLOC_LEN); return 0; } --> /*common/dlmalloc.c*/ void mem_malloc_init(ulong start, ulong size) { mem_malloc_start = start; mem_malloc_end = start + size; mem_malloc_brk = start; debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start, mem_malloc_end); #ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT memset((void *)mem_malloc_start, 0x0, size); #endif malloc_bin_reloc(); }由于重定位后的内存分配代码太长了,这里仅仅看下重定位后的malloc使用的哪片内存区域,首先有一个很重要的宏:
#ifndef MORECORE #define MORECORE sbrk #endifsbrk是一个函数,主要操作mem_malloc_brk全局变量,检查新申请内存时,是否落在有效的堆内存空间内,代码如下:
void *sbrk(ptrdiff_t increment) { ulong old = mem_malloc_brk; ulong new = old + increment; /* * if we are giving memory back make sure we clear it out since * we set MORECORE_CLEARS to 1 */ if (increment < 0) memset((void *)new, 0, -increment); if ((new < mem_malloc_start) || (new > mem_malloc_end)) return (void *)MORECORE_FAILURE; mem_malloc_brk = new; return (void *)old; }和前面的malloc一样,直接搜sbrk是搜不到调用它的函数的,我们要搜索MORECORE,从搜索结果来看,其被mALLOc调用,可以知道重定位后就是使用的内存图中重定位后的那片堆内存空间。
从上面的分析来看,重定位后使用的新的堆内存空间,之前的那片内存相当于就废弃了没用了,也就解释了当时看完board_init_r函数时,发现有两片堆空间的疑惑了。
欢迎扫码关注我的微信公众号