u-boot2020.04移植(小记:重定位前后保留的堆内存是如何被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函数时,发现有两片堆空间的疑惑了。

欢迎扫码关注我的微信公众号
漫长当下

猜你喜欢

转载自blog.csdn.net/a1598025967/article/details/107108282