2016-08-22 140 views
3

我正在为STM32F7编写一个嵌入式软件,我的libc是newlib-2.4.0.20160527。newlib中的malloc():一次大的故障分配后会浪费内存吗?

我已经实现_sbrk()如下:

extern intptr_t g_bss_end; /* value after the last byte in .bss */ 
extern intptr_t g_msp_lim; /* stack buffer starts at this address */ 

intptr_t _sbrk(ptrdiff_t heap_incr) 
{ 
    static intptr_t heap_end = 0; 

    intptr_t prev_heap_end; 
    intptr_t new_heap_end; 

    if(heap_end == 0) { 
     heap_end = (intptr_t)&g_bss_end; 
    } 

    prev_heap_end = heap_end; 
    new_heap_end = prev_heap_end + heap_incr; 

    if(new_heap_end >= g_msp_lim) { 
     errno = ENOMEM; 

     return -1; 
    } 

    heap_end = new_heap_end; 

    return prev_heap_end; 
} 

然后,当我做到以下几点:

/* total capacity of my heap is 0x40000 */ 
void * mem = malloc(0x40000); 
free(mem); mem = 0; 
mem = malloc(0x40000); 

一切正常(即malloc的返回非零的两倍)。

但是,当我这样做(用于测试目的):

for(int32_t sz = 0x50000; sz >= 0; sz--) { 
    void * mem = malloc(sz); 

    if(mem != 0) { 
     __BKPT(); 
     free(mem); 

     break; 
    } 
} 

malloc()失败,甚至malloc(0)(即__BKPT()从未达到)。所以,实际上并没有在堆上分配内存(我没有得到任何mem != 0,所以我什至不能free()东西),也没有可用的内存。

我预计malloc()失败,每sz > 0x40000并为每sz <= 0x40000(假设每个free()malloc()正常工作)取得成功。

我错过了什么,或者这是bug或在newlib中的预期行为?

+0

调试器说什么?你是否已经完成了代码?注意:在嵌入式系统中使用基于堆的动态内存分配像'malloc'通常是一个坏主意,许多编码标准出于正当理由不允许使用这种分配。值得注意的是确定性行为和保证分配。在甚至考虑'malloc'等之前,先评估池或其他度量方法(如静态变量)的使用! – Olaf

+0

哦,并用指针的'NULL'宏。 '0'作为空指针常量是有效的,但是C++程序员的坏习惯。出于好的理由,C++ 11引入了'nullptr'。 (希望C11跟着他们) – Olaf

+0

我通过我的代码(我检查了'mem'的值;'__BKPT()'也是一个断点)。为了使用gdb进入newlib的代码,我现在用'-g3 -O0'重新编译它。我想要工作'snprintf',它依赖'malloc'。我应该开始搜索'snprintf'选项吗?我不需要'malloc'来做其他事情。 –

回答

4

由于在newlib/libc/stdlib/mallocr.c:2137中的例行程序出错,分配整个堆内存时,newlib的malloc()无法正常工作。

/* Guarantee alignment of first new chunk made from this space */ 
front_misalign = (POINTER_UINT)chunk2mem(brk) & MALLOC_ALIGN_MASK; 
if (front_misalign > 0) 
{ 
    correction = (MALLOC_ALIGNMENT) - front_misalign; 
    brk += correction; 
} 
else 
    correction = 0; 

/* Guarantee the next brk will be at a page boundary */ 
correction += pagesz - ((POINTER_UINT)(brk + sbrk_size) & (pagesz - 1)); 

校正始终是正的,因为即使分配完全符合它尝试分配:成功调用_sbrk()

brk = (char*)(MORECORE (sbrk_size)); /* MORECORE = _sbrk */ 

    /* Fail if sbrk failed or if a foreign sbrk call killed our space */ 
    if (brk == (char*)(MORECORE_FAILURE) || 
     (brk < old_end && old_top != initial_top)) 
    return; 

它试图计算修正以适应页面对齐后下一整页。例如,如果页面大小为4096brk + sbrk_size = 4096*n,则表达式4096 - ((brk + sbrk_size) & 4095)将给出4096,因此下一个空白页是必需的,但是没有空间。

例行程序不正确地处理这种情况,并留下刚刚分配的数据(brk值),导致永久的“不可用”整个堆分配。这样的浪费:-)

+2

也许你应该为此打开一个错误报告? –

+0

+1因为我使用newlib malloc很多。在这种情况下,'front_misalign'将始终为零(因为块地址和标头大小都是对齐的)。 'correction'确实是4096,但是在代码中进一步下降,这将会失败,所以'correction'将被重置为零,'sbrked_mem'(实际的全局)将保持不变。在我看来,真正的问题是,当校正失败时,它会设置'new_brk = brk',所以进一步下来,你会得到一个零'top_size',它被传递给'set_head',从而产生一个零大小的头部块未能进一步分配(无法扩展)。 –