虫虫首页|资源下载|资源专辑|精品软件
登录|注册

您现在的位置是:首页 > 技术阅读 >  u-boot2020.04移植(小记:重定位前后保留的堆内存是如何被malloc的)

u-boot2020.04移植(小记:重定位前后保留的堆内存是如何被malloc的)

时间:2024-06-01

文章目录

  • 重定位前的堆内存池

  • 那当我们使用malloc时,它是如何给我们分配内存的呢?

  • 以malloc为例

  • 再来看下重定位后

  • 结论

点击下方阅读原文可访问文中超链接

重定位前的堆内存池

首先看一下重定位前,在重定位前也就是第一阶段,u-boot会保留一片内存用于堆,也就是gd->malloc_base这篇区域,其大小为0x400,具体见下面的内存图:

图1

那当我们使用malloc时,它是如何给我们分配内存的呢?

在源码中搜索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会被替换成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
#endif

sbrk是一个函数,主要操作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函数时,发现有两片堆空间的疑惑了。

欢迎扫码关注我的微信公众号