【问题标题】:malloc() in newlib : does it waste memory after one big failure allocation?newlib 中的 malloc() :在一次大失败分配后是否会浪费内存?
【发布时间】:2023-04-06 04:45:01
【问题描述】:

我正在为 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;
}

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

/* 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() 之后都能正常工作)。

我是否遗漏了什么,或者这是 newlib 中的错误或预期行为?

【问题讨论】:

  • 调试器说什么?您是否单步执行了代码?注意:在嵌入式系统中使用像malloc 这样的基于堆的动态内存分配通常是一个坏主意,许多编码标准出于充分的理由不允许这样做。尤其是确定性行为和保证分配。在考虑 malloc 等之前评估池或其他措施(如静态变量)的使用!
  • 哦,使用带有指针的NULL 宏。 0 作为空指针常量是有效的,但是 C++ 程序员的一个坏习惯。 C++11 引入 nullptr 是有充分理由的。 (希望 C11 跟着他们)
  • 我单步执行了我的代码(我检查了mem 的值;__BKPT() 也是一个断点)。为了用 gdb 进入 newlib 的代码,我现在用-g3 -O0 重新编译它。我想要工作snprintf,它依赖于malloc。我应该开始搜索snprintf 替代品吗?我不需要malloc 做其他任何事情。
  • 也许您应该首先了解嵌入式软件开发。流 IO 函数也是主要的资源消耗者。虽然我想知道为什么 snprintf需要 malloc
  • @Olaf 0 是 C 中的空指针常量。这不是“C++ 的坏习惯”,早在 C++ 发明之前就一直使用它

标签: c embedded malloc newlib sbrk


【解决方案1】:

由于newlib/libc/stdlib/mallocr.c:2137 中的错误malloc_extend_top() 例程,newlib 的malloc() 在分配整个堆内存时无法正常工作。成功调用_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;

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

/* 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));

修正总是积极的,因为即使分配完全合适,它也会尝试分配下一页。例如,如果页面大小为4096 并且brk + sbrk_size = 4096*n,则表达式4096 - ((brk + sbrk_size) &amp; 4095) 将给出4096,因此需要下一个空白页面,但没有空间。

例程没有正确处理这种情况,只留下分配的数据(brk 值),导致永久“无法释放”的整个堆分配。这样的浪费:-)

【讨论】:

  • 也许你应该为此打开一个错误报告?
  • +1 因为我经常使用 newlib malloc。在这种情况下,front_misalign 将始终为零(因为块地址和标头大小都是对齐的)。 correction 确实是 4096,但在代码的更下方是 sbrked,这将失败,因此 correction 将重置为零,sbrked_mem(实际全局)将保持不变。在我看来,真正的问题是,当校正失败时,它会设置new_brk = brk,所以再往下你会得到一个零top_size,它会被传递给set_head,导致一个零大小的头块进一步失败分配(不能扩展)。
【解决方案2】:

经过测试,工作正常。

From 1473e08d2a16ad448afedb7036a476231a785643 Mon Sep 17 00:00:00 2001
From: Jeff Johnston <jjohnstn@redhat.com>
Date: Thu, 24 May 2018 23:53:15 -0400
Subject: [PATCH] Fix issue with malloc_extend_top

- when calculating a correction to align next brk to page boundary,
  ensure that the correction is less than a page size
- if allocating the correction fails, ensure that the top size is
  set to brk + sbrk_size (minus any front alignment made)

Signed-off-by: Jeff Johnston <jjohnstn@redhat.com>
---
 newlib/libc/stdlib/mallocr.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/newlib/libc/stdlib/mallocr.c b/newlib/libc/stdlib/mallocr.c
index ecc445f..26d1c89 100644
--- a/newlib/libc/stdlib/mallocr.c
+++ b/newlib/libc/stdlib/mallocr.c
@@ -2198,13 +2198,18 @@ static void malloc_extend_top(RARG nb) RDECL INTERNAL_SIZE_T nb;
     /* Guarantee the next brk will be at a page boundary */
     correction += pagesz - ((POINTER_UINT)(brk + sbrk_size) & (pagesz - 1));

+    /* To guarantee page boundary, correction should be less than pagesz */
+    correction &= (pagesz - 1);
+
     /* Allocate correction */
     new_brk = (char*)(MORECORE (correction));
     if (new_brk == (char*)(MORECORE_FAILURE))
       {
    correction = 0;
    correction_failed = 1;
-   new_brk = brk;
+   new_brk = brk + sbrk_size;
+   if (front_misalign > 0)
+     new_brk -= (MALLOC_ALIGNMENT) - front_misalign;
       }

     sbrked_mem += correction;
-- 
1.8.3.1

【讨论】:

    猜你喜欢
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-12
    相关资源
    最近更新 更多