2016-08-22 8 views
3

Я пишу встроенное программное обеспечение для STM32F7, а мой libc - это newlib-2.4.0.20160527.malloc() в newlib: это потеря памяти после одного большого отказа?

Я реализовал _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); 

все работает отлично (т.е. таНос возвращает ненулевое значение в два раза).

Но когда я делаю следующее (для целей тестирования):

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?

+0

Что говорит отладчик? Прошли ли вы через код? Примечание: использование распределенной динамической памяти на основе кучи, такой как 'malloc' во встроенных системах, чаще всего является плохим идеей и запрещено многими стандартами кодирования по уважительным причинам. Особенно детерминированное поведение и гарантированное распределение. Оцените использование пулов или других мер, таких как статические переменные, прежде чем даже подумать о 'malloc' и т. Д.! – Olaf

+0

О, и используйте макрос 'NULL' с указателями. '0', поскольку константа нулевого указателя действительна, но плохая привычка от программистов на С ++. В C++ 11 был введен «nullptr» по уважительным причинам. (Желание C11 последовало за ними) – Olaf

+0

Я прошел через свой код (я проверил значения 'mem';' __BKPT() 'также была точкой останова). Чтобы войти в код newlib с помощью gdb, я перекомпилирую его с помощью '-g3 -O0'. Я хочу иметь работу 'snprintf', которая полагается на' malloc'. Должен ли я начинать поиск альтернатив snprintf? Мне не нужно «malloc» ни для чего другого. –

ответ

4

Newlib's 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»), что приводит к постоянному распределению целых кучи «несвоевременное». Такие отходы :-)

+2

Возможно, вам стоит открыть отчет об ошибке? –

+0

+1 потому что я использую newlib malloc много. В этом случае 'front_misalign' всегда будет равен нулю (поскольку адрес и размер заголовка совпадают). 'исправление' действительно будет 4096, но дальше в коде это' sbrk'ed, что не удастся, поэтому 'коррекция' будет сброшена до нуля, а' sbrked_mem' (фактический глобальный) останется неизменным. Мне кажется, что реальная проблема заключается в том, что при исправлении ошибок она устанавливает 'new_brk = brk', поэтому в дальнейшем вы получите нуль' top_size', который передается 'set_head', в результате чего образуется нулевой размер головного блока, не удается выполнить дальнейшие распределения (не может быть расширен). –