newlib中的malloc():分配大量故障后是否会浪费内存?

Pio*_*dyk 4 c embedded malloc newlib sbrk

我正在为STM32F7编写嵌入式软件,而我的libc是newlib-2.4.0.20160527。

我已经实现_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;
}
Run Code Online (Sandbox Code Playgroud)

然后,当我执行以下操作时:

/* total capacity of my heap is 0x40000 */
void * mem = malloc(0x40000);
free(mem); mem = 0;
mem = malloc(0x40000);
Run Code Online (Sandbox Code Playgroud)

一切正常(即,malloc两次返回非零值)。

但是当我执行以下操作(出于测试目的)时:

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

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

        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

每次malloc()失败了,甚至malloc(0)(即,__BKPT()从来没有达到)。因此,实际上堆上没有分配的内存(我什么也没得到,mem != 0所以我什至free()什么也没有),也没有可用的内存。

我期望malloc()每个人都会失败,每个人都会sz > 0x40000成功sz <= 0x40000(假设每次都free()可以正常工作malloc())。

我是否错过了某些东西,或者这是newlib中的错误还是预期的行为?

Pio*_*dyk 5

malloc()由于中存在错误的malloc_extend_top()例程,因此在分配整个堆内存时,newlib 不能正常工作newlib/libc/stdlib/mallocr.c:2137。成功致电后_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;
Run Code Online (Sandbox Code Playgroud)

它尝试计算校正以适合页面对齐方式:

/* 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));
Run Code Online (Sandbox Code Playgroud)

该校正始终为正,因为即使分配完全合适,它也会尝试分配下一个整个页面。例如,如果页面大小是4096brk + sbrk_size = 4096*n,表达4096 - ((brk + sbrk_size) & 4095)会给4096,因此需要下一个空白页,但它没有空间。

该例程无法正确处理这种情况,并且仅留下分配的数据(brk值),从而导致永久的“不可释放”的整个堆分配。太浪费了 :-)

  • 也许您应该为此打开错误报告? (2认同)