Маллок из Newlib "игнорирует" целую кучу свободной памяти

Поэтому я использую голый металл malloc в сочетании с самописным _sbrk. Я запускаю все на панели запуска Stellaris. Эта доска содержит кору M4. Он также содержит 32 КБ ОЗУ, начиная с memaddr 0x20000000 и до 0x20007FFF. В самом начале программы ситуация выглядит следующим образом:

  • _ebss - это начало назначаемой памяти, все с более низким адресом, чем _ebss, является consts, программными инструкциями и т. д. Это определяется в сценарии компоновщика. Значение в начале 0x200008b0
  • На этапе загрузки программного обеспечения указатель основного стека устанавливается в 0x20007FFF, и стек увеличивается. Это означает, что на начальном этапе для назначения выделено около 30,5 КБ ОЗУ.

_sbrk хочет раздать эту оперативную память и подтверждает правильное количество свободной оперативной памяти: после фазы инициализации (что означает, что стек немного увеличился, поэтому объем свободной оперативной памяти меньше), существуют следующие распечатки памяти:

(S) MSP: 0x20007f4c, heap_end 0x200008b0, diff 30364
(F) MSP: 0x20007f4c, heap_end 0x200008e8, diff 30308
(S) MSP: 0x20007f4c, heap_end 0x200008e8, diff 30308
(F) MSP: 0x20007f4c, heap_end 0x20000fc8, diff 28548
arena: 1816
ordblks: 1
smblks: 0
hblks: 0
hblkhd 0
usmblks: 0
fsmblks: 0
uordblks: -1056
fordblks: 2872
keepcost: 2872

Первые четыре строки взяты из _sbrk, последние две - из mallinfo(). (S) означает в начале _sbrk (до перемещения указателя кучи) и (F) означает после перемещения указателя кучи. Если я могу правильно прочитать документацию mallinfo (боюсь, это сложная часть), то арена означает количество кучи, запрошенное у _sbrk. Это имеет смысл, потому что разница между самым первым напечатанным heap_end и самым последним напечатанным heap_end действительно равна 1816. И fordblks должен означать общее количество свободной кучи, поэтому максимальный объем кучи, который все еще можно запросить у _sbrk. И это неверно. Как вы можете видеть из последнего различия, разница между heap_end и MSP составляет 28K. Конечно, мы хотим сохранить буфер между ними, чтобы MSP мог расти, не повреждая все, но мы хотим выделить более 2,8 КБ оперативной памяти.

Это становится проблемой, когда я позволяю программе работать немного дольше: в конце концов fordblks приблизится к нулю, и malloc начнет возвращать NULL, в то время как heap_end не достиг MSP длинным выстрелом. Таким образом, malloc отказывается выдавать больше памяти, прежде чем это необходимо. Как я могу исправить это behviour? На чем основано значение fordblks?

(S) MSP: 0x20007f34, heap_end 0x20000fc8, diff 28524
(F) MSP: 0x20007f34, heap_end 0x20001fc8, diff 24428
(S) MSP: 0x20007f34, heap_end 0x20001fc8, diff 24428
(F) MSP: 0x20007f34, heap_end 0x20002000, diff 24372
Could not create process timeoutProc30s
Test prepared
arena: 8304
ordblks: 2
smblks: 0
hblks: 0
hblkhd 0
usmblks: 0
fsmblks: 0
uordblks: 7648
fordblks: 656
keepcost: 112

Изменить: Подробнее! Выше вы видите фактический момент, когда Маллок говорит нет: его на Could not create process.. линия. Как вы можете ясно видеть, чуть выше Malloc сделал последнюю попытку получения кучи от _sbrk, и _sbrk обязался (указатель кучи изменен). Также осталось 24 КБ ОЗУ. Я попытался распределить 1024 байта оперативной памяти, когда malloc сказал мне NULL,

Изменить: файл linker_script.ld:

_stack_buffer = 128; /*128 byte buffer between HEAP and STACK*/

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}

SECTIONS
{
    .text :
    {
        _text = .;
        KEEP(*(.isr_vector))
        *(.text*)
        *(.rodata*)
        _etext = .;
    } > FLASH

    .data : /*AT(ADDR(.text) + SIZEOF(.text))*/
    {
        _data = .;
        *(vtable)
        *(.data*)
        _edata = .;
    } > SRAM AT > FLASH

    .bss : AT (ADDR(.data) + SIZEOF(.data))
    {
        _bss = .;
        *(.bss*)
        *(COMMON)
        _ebss = .;
        _end = .;
    } > SRAM

    _stack_top = ORIGIN(SRAM) + LENGTH(SRAM) - 1; /*The starting point of the stack, at the very bottom of the RAM*/
}

Редактировать: я провел еще несколько исследований по этому вопросу, и я нашел некоторые интересные вещи. Прежде всего: fordblks не связан с реальной пустой кучей, и я не знаю, на чем она основана. Потому что, если вы используете malloc много 100 байтов в цикле true, то неправильная блокировка будет продолжаться до тех пор, пока _sbrk вернет -1, что является ожидаемым поведением. Существуют определенные обстоятельства, когда malloc возвращает NULL без заполнения кучи. Один пример - это когда malloc(1024) возвращает NULL, но допустимо пять malloc(555). Так что, похоже, это как-то связано с внутренностями Маллока.

Отказ от ответственности: в прошлый раз, когда я задал голый металлический вопрос, я был осмеян за тусклость: я не говорю, что newlib Malloc делает что-то не так, и я подозреваю, что мне нужно определить что-то в сценарии компоновщика или что-то, чтобы это исправить. Я осознаю тот факт, что все это моя вина, и я здесь, чтобы спросить, почему эта проблема здесь и как мне нужно исправить свой код, чтобы исправить это поведение. Нельзя сказать, что ребята из Newlib понятия не имеют, что они делают.

1 ответ

Хирон,

  1. Обычно, если система имеет небольшой объем памяти или высокую нагрузку, любое динамическое распределение максимально избегается. Я бы порекомендовал посмотреть на методы, такие как пулы памяти.
  2. Менеджеры памяти всегда имеют проблему фрагментации в долго работающей системе. Блоки имеют выравнивания. Например, я видел диспетчеры памяти, которые выделяют как минимум 48 байтов для чего-либо. И ни один менеджер памяти не может честно учитывать вашу предметную область и, в свою очередь, схему использования памяти.

Поэтому я рекомендую избегать динамического выделения памяти и использовать пулы объектов, соответствующих вашим требованиям. Это почти 100% случай для встраиваемых систем, где почти у всех есть "пулы", "киркулярные буферы" и так далее. Надеюсь, что этот совет поможет вам в некотором роде.

Другие вопросы по тегам