文章目錄
重定位前的堆內(nèi)存池
那當(dāng)我們使用malloc時,它是如何給我們分配內(nèi)存的呢?
以malloc為例
再來看下重定位后
結(jié)論
點擊下方閱讀原文可訪問文中超鏈接
重定位前的堆內(nèi)存池
首先看一下重定位前,在重定位前也就是第一階段,u-boot會保留一片內(nèi)存用于堆,也就是gd->malloc_base這篇區(qū)域,其大小為0x400,具體見下面的內(nèi)存圖:
那當(dāng)我們使用malloc時,它是如何給我們分配內(nèi)存的呢?
在源碼中搜索malloc可以在include/malloc.h中找到下面的宏定義:
/*你可能發(fā)現(xiàn)很多重復(fù)的宏定義,但是經(jīng)過分析,最終生效的是下面這些*/
# 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的實現(xiàn)是搜不到的,因為它的實現(xiàn)是由mALLOc函數(shù)完成的(看上面的宏定義mALLOc會被替換成malloc),其函數(shù)體在common/dlmalloc.c文件中:
#if __STD_C
Void_t* mALLOc(size_t bytes)
#else
Void_t* mALLOc(bytes) size_t bytes;
#endif
{
/*定義了一堆變量*/
/*
省略
*/
/*這個宏定義了,才能在重定位前使用malloc來申請內(nèi)存*/
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
/*這個標(biāo)志是在board_init_r里面設(shè)置的,也就是重定位
完成,所以重定位前使用的就是這種分配方式*/
if (!(gd->flags & GD_FLG_FULL_MALLOC_INIT))
return malloc_simple(bytes);
#endif
/*下面就是重定位后的分配方式,太長了,有200多行,直接省
略,感興趣的直接看源碼吧*/
/*
省略
*/
}
本文不討論其內(nèi)存的分配策略,僅為找到malloc的源頭。
從下面的代碼可以看出重定位前使用的堆內(nèi)存池是gd->malloc_base這片區(qū)域,重定位前使用的策略比較簡單,就是分塊,一塊一塊的對外分配就完了:
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;
}
再來看下重定位后
還是先看下重定位后的內(nèi)存圖:
從圖上可以看出,重定位后的堆內(nèi)存空間比重定位前的內(nèi)存空間要大得多,這篇區(qū)域在重定位前僅是保留的,沒有任何指針指向這里,在board_init_r階段,這片區(qū)域的起始地址才和一個全局變量mem_malloc_start綁定,用于重定位后的堆內(nèi)存,代碼如下:
/*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();
}
由于重定位后的內(nèi)存分配代碼太長了,這里僅僅看下重定位后的malloc使用的哪片內(nèi)存區(qū)域,首先有一個很重要的宏:
#ifndef MORECORE
#define MORECORE sbrk
#endif
sbrk是一個函數(shù),主要操作mem_malloc_brk全局變量,檢查新申請內(nèi)存時,是否落在有效的堆內(nèi)存空間內(nèi),代碼如下:
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是搜不到調(diào)用它的函數(shù)的,我們要搜索MORECORE,從搜索結(jié)果來看,其被mALLOc調(diào)用,可以知道重定位后就是使用的內(nèi)存圖中重定位后的那片堆內(nèi)存空間。
結(jié)論
從上面的分析來看,重定位后使用的新的堆內(nèi)存空間,之前的那片內(nèi)存相當(dāng)于就廢棄了沒用了,也就解釋了當(dāng)時看完board_init_r函數(shù)時,發(fā)現(xiàn)有兩片堆空間的疑惑了。
歡迎掃碼關(guān)注我的微信公眾號