>

写了个取剩余可用堆内存的函数

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;
}