Вопрос:

malloc () в newlib: тратит ли она память после одного большого сбоя выделения?

c embedded malloc newlib sbrk

791 просмотра

2 ответа

146 Репутация автора

Я пишу встроенное программное обеспечение для 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?

Автор: Piotr Jedyk Источник Размещён: 22.08.2016 08:55

Ответы (2)


5 плюса

146 Репутация автора

Newlib malloc()не работает должным образом при распределении всей памяти кучи из-за плохой malloc_extend_top()процедуры в 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;

он пытается вычислить поправку, чтобы соответствовать выравниванию страницы:

/* 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) & 4095), даст 4096, поэтому следующая пустая страница требуется, но для нее нет места.

Подпрограмма обрабатывает эту ситуацию неправильно и оставляет только выделенные данные (значение brk), что приводит к постоянному «неразборчивому» распределению всей кучи. Такая трата :-)

Автор: Piotr Jedyk Размещён: 22.08.2016 11:13

1 плюс

11 Репутация автора

Проверено, работает нормально.

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
Автор: Oleksandr Kapitanenko Размещён: 25.05.2018 04:34
Вопросы из категории :
32x32