写了个取剩余可用堆内存的函数
2017-06-29 by Stavros原理就是二分查找, 不断malloc, 找到一个malloc(n)成功, malloc(n+1)失败的位置
如果堆空间不连续, 返回的应该是最大连续空间.
程序如下, get_free_mem()中调用__get_free_mem()时指定上下界.
static size_t __get_free_mem(size_t start, size_t end)
{
unsigned char *p;
size_t size = (start + end) / 2;
if(start == end - 1)
return start - 1;
p = malloc(size);
if(p != NULL) { // malloc succeeded
free(p);
return __get_free_mem(size + 1, end);
}
else { // malloc failed
return __get_free_mem(start, size);
}
}
size_t get_free_mem(void)
{
return __get_free_mem(0, 65536UL);
}
主要问题有两个, 一是在64k范围时要递归十几次, 4G范围时得递归30多次. 如果调用位置比较深总担心要爆栈, 得改成非递归的;
二是stm32的syscall.c里的_sbrk()实现是判断heap_end加上incr是否超过asm("SP"), 其中heap_end是静态变量, 第一次调用时取_end的值. 这样有个问题就是把bss段和当前未用的栈空间都给算到堆空间了, 似乎不太合适. 是否应该跳过bss段, 以及把整个栈空间都去掉?
实测: 在PC上用32位gcc编译, 结果是1.8G左右的内存. 在512M内存的树莓派上是345M左右, 都还算合理.
在48k RAM的STM32F103RCT6上则比较有趣, make完size的结果是.data+.bss一共10256字节, 49152-10256=38896, 再去掉已用掉的栈空间, 应该是38k不到的样子. 但是get_free_mem的结果总是32768.
如果先malloc(16000), get_free_mem的结果就变成了16384. 继续malloc, 结果逐渐减小为8192, 4096. 估计是arm-none-eabi-gcc的malloc内部实现上做了限制, 只取可用堆空间里最大的2的整数次幂.
如果先这样:
p = malloc(12000);
q = malloc(12000);
r = malloc(11300);
s = malloc(11000);
这样可以成功分配到46300字节, 看来确实是把bss都算进来了. 然后, 果然hardfault了...
上面两点有空再改吧.
----------------更新--------------------
上面的说法有误,arm-none-eabi-size -A main_rom.elf的结果, .bss是1860, ._user_heap_stack是8192. 而arm-none-eabi-size -B main_rom.elf的结果, bss是10052,应该是把上面两者都算作bss了。
_user_heap_stack这部分作为堆空间是没有问题的,它应该只是在编译时起到检查静态空间是否越界的作用。
--------------------再更新---------------------
非递归版本的__get_free_mem函数如下... 脑子短路了,这个感觉比递归的还简单...
static size_t __get_free_mem2(size_t start, size_t end)
{
unsigned char *p;
while(start < end - 1) {
size_t size = (start + end) / 2;
if(size == 0)
return 0;
p = malloc(size);
if(p != NULL) { // malloc succeeded
free(p);
start = size;
}
else { // malloc failed
end = size - 1;
}
}
return start;
}