Использование malloc в PIC

10

Как я могу использовать malloc()и free()функции в PIC? Я проверил stdlib.hзаголовок, и нет никаких упоминаний о них. Я использую MCC18.

Кто-нибудь должен был их использовать?

Они мне нужны, потому что я портирую библиотеку с Windows XP на PIC. Руководство по портированию говорит

адаптировать конкретные функции операционной системы к моим PIC

Но я не знаю, как "перевести" malloc()и free()функции.

Stef
источник
4
Попробуйте использовать статическое распределение, если это возможно.
Ник Т
1
Почему? Проблема на самом деле в том, что я пишу нижний слой (платформо-зависимый) довольно огромной библиотеки и множество функций, я понятия не имею, для чего они используются, и я не знаю, как перейти с от динамического к статическому ..
stef
11
Похоже, микроконтроллер PIC с <4KB RAM может быть неправильным для вашего приложения. На ПК измерьте использование памяти вашей компьютерной библиотекой перед началом порта. Вам может быть лучше с чем-то более мощным, как ARM Cortex-M3. Практическое правило. Если кодовая база, которую вы переносите, слишком велика, чтобы ее понимать, она не подходит для PIC.
Тоби Джаффей
Драйверы Windows (и приложения в целом) по сути написаны с парадигмой «неограниченное ОЗУ», поскольку, если физическая ОЗУ исчерпана, виртуальная память может быть заменена. В зависимости от того, что делает библиотека, она вполне может потреблять больше, чем 4 КБ доступны в вашем PIC18F87J11. Я подозреваю, что вы не сможете определить, сколько памяти будет использовать драйвер.
Адам Лоуренс
Еще одна потенциальная проблема: Win32 int имеет 32 бита, а с компилятором MCC18 - только 16 бит. Вы можете получить странные проблемы переполнения, если не будете осторожны.
Адам Лоуренс

Ответы:

8

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

char * next_alloc;
void * malloc (int size)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <размер)
      возврат -1;
    next_alloc + = размер;
    вернуть this_alloc;
}
void free (void * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}

Легко и просто, и всего два байта общих накладных расходов для любого количества распределений. Вызов free () для блока освободит этот блок и все после него.

Чуть более сложные шаблоны распределения могут быть обработаны с помощью двух указателей - один из которых распределяет содержимое из нижней части памяти, перемещаясь вверх, а другой - из верхней части памяти вниз. Также возможно использовать компактируемый сборщик мусора, если данные в куче однородны и известно, где находятся все внешние ссылки на них.

Supercat
источник
Чтобы быть complety информации о максимальных возможностях, прочитать ответ на electronics.stackexchange.com/questions/7850/...
gavioto20
14

malloc()В микроконтроллерах это вообще считается «плохой вещью». Но, если вам это абсолютно необходимо, вы захотите найти стороннюю версию.

Если вам повезет, код, который вы переносите, может не зависеть от повторного использования блоков памяти. Если это так, вы можете написать простой распределитель, который возвращает указатель в буфер ОЗУ, а затем увеличивает указатель на запрошенный размер блока.

Ранее я успешно использовал этот подход при переносе библиотек ПК на микроконтроллеры.

Ниже вы можете настроить распределитель с помощью my_malloc_init()и выделить память с помощью my_malloc(). my_free()есть, чтобы удовлетворить зависимость, но на самом деле не будет ничего делать. В конце концов, вам не хватит места, конечно.

Чтобы это работало, вам нужно измерить наихудшую потребность в памяти для вашего кода (сделайте это на ПК, если это возможно), а затем настроить ее HEAP_SIZEсоответствующим образом. Прежде чем войти в ту часть вашей библиотеки, которая требует динамической памяти, позвоните my_malloc_init(). Перед повторным использованием убедитесь, что ничего не указывает на heap.

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(примечание: в реальном мире может потребоваться выравнивание указателя, т. е. округление heap_ptrна 2 или 4 байта)

Другой вариант - использовать более простую структуру размещения, чем malloc()обычно предоставляет, например FreeList , хотя это может не позволить вам выделять блоки переменного размера.

Тоби джеффи
источник
3
Интересно, когда Malloc IS считается хорошей вещью во встроенном.
Kellenjb
1
Я все еще согласен, что вы не хотите динамического распределения в программах, как уже говорили другие, но это отличный способ сделать это. Сторонний malloc, разработанный для встраиваемых систем, безусловно, лучший выбор. Избегать сегментации является обязательным. @jobyTaffey Хорошо написано.
Кортук
1
@Kellenjb ну, это совершенно новый вопрос :-)
Тоби Джеффи
1
Я хотел бы предложить, чтобы my_free установил в heap_ptr переданное значение, эффективно освобождая указанный элемент и все, что было выделено после него. Очевидно, что нужно распределять вещи в последовательности, которая разрешает такое использование, но такие шаблоны не редкость. Другой полезный вариант состоит в том, чтобы иметь две пары функций выделения / освобождения, одна из которых выделяет сверху вниз, а другая - снизу вверх.
суперкат
13

Это вряд ли является ответом на ваш вопрос, но динамическое распределение памяти обычно не одобряется в небольших средах оперативной памяти и в отсутствие операционной системы (например, в мире микроконтроллеров) ... пространство кучи, доступное во встроенной среде, обычно измеряется сотнями байтов ...

Реализация malloc и free - это, по сути, поддержка связанного списка структур «свободного сегмента», и, как вы можете себе представить, метаданные, связанные со свободными сегментами, не являются существенными по сравнению с объемом обычно доступной памяти, то есть «накладными расходами». «Управление динамическим пулом памяти потребляет значительное количество доступных ресурсов.

vicatcu
источник
Есть реализации, где издержки метаданных очень малы. Для выделенного блока вам нужен только его размер. Для неиспользуемых блоков указатель (-ы) связанного списка обычно могут соответствовать бесплатно, даже при вполне разумных минимальных размерах блоков.
Восстановить Монику
Проблема с маленькими, долго работающими системами, которые используют микроконтроллеры, обычно не в метаданных, а в фрагментации памяти. Что еще хуже, небольшие изменения в вашем коде могут привести к фрагментации памяти там, где ее раньше не было, так что вы можете внести невинные изменения, которые внезапно заставят вашу программу перестать работать «слишком рано».
Восстановите Монику
11

Я не знаю, поддерживает ли стандартная библиотека C18 mallocи free, но в Microchip App Note AN914 показано, как вы можете реализовать свою собственную.

В любом случае, Томас и другие авторы предложили, что использование динамической памяти на PIC с очень малым объемом ОЗУ чревато опасностью. Вы можете быстро исчерпать непрерывное пространство из-за отсутствия более продвинутых менеджеров виртуальной памяти, которые дают вам полноценные ОС, что приводит к сбоям при выделении ресурсов и сбоям. Хуже того, он не может быть детерминированным и, скорее всего, будет затруднительным для отладки.

Если то, что вы делаете, действительно динамически определяется во время выполнения (редко для большинства встроенных вещей), и вам нужно только выделить место в паре очень особых случаев, я мог бы видеть это mallocи freeбыть приемлемым.

Ник Т
источник
Недостаточно свободного пространства, то есть фрагментации кучи, - это проблема, которая полностью не зависит от размера вашего адресного пространства и наличия виртуальной памяти. Возможно, вы захотите обменять часть неиспользуемой оперативной памяти на более низкую фрагментацию кучи, но в конечном итоге в долго работающей системе у вас нет никаких гарантий не исчерпать пространство кучи. Единственная разница между малыми и большими системами заключается в том, сколько времени требуется, чтобы диск начал перебрасываться (в системах с виртуальной страницей с дисковой страницей), или распределитель возвращает NULL (во встроенных компонентах).
Восстановить Монику
@KubaOber: Как правило, можно гарантировать, что ОЗУ определенного размера сможет обрабатывать любую последовательность операций выделения и освобождения, для которой никогда не требуется больше определенного (меньшего) объема ОЗУ для одновременного распределения. Проблема со встроенными системами заключается в том, что для гарантированного успеха даже в худшем случае потребуется гораздо больше оперативной памяти, чем без фрагментации.
суперкат
@supercat Ты прав. Я был действительно чрезмерно драматичен. Есть формальные доказательства этих гарантий.
Восстановить Монику
2

Ну, а насколько велик ваш PIC с точки зрения памяти?

malloc - очень неэффективный способ выделения памяти. Проблема заключается в том, что память может быть фрагментирована с частыми освобождениями и mallocs, и только с несколькими килобайтами памяти, ошибки выделения слишком распространены. Вполне вероятно, что если вы используете меньшую микросхему или более раннюю PIC18, то нет поддержки malloc, поскольку Microchip либо посчитал ее очень сложной для реализации (или, возможно, даже невозможной в некоторых случаях), либо недостаточно используемой для ее использования. стоило того. Не говоря уже об этом, но он также довольно медленный, вы смотрите на 1 цикл, чтобы использовать статический буфер, который уже доступен, и от 100 до 1000 циклов, чтобы сделать malloc.

Если вы хотите выделить статически, создайте такие вещи, как буфер для ваших функций sprintf (если есть, около 128 байт), буфер для вашей SD-карты (если есть) и так далее, до тех пор, пока вы не удалите необходимость в malloc. В идеале вы используете его только там, где это абсолютно необходимо, и не можете избежать статического распределения, но такие ситуации обычно редки и, возможно, являются признаком того, что вы должны смотреть на более крупные / более мощные микроконтроллеры.

И если вы разрабатываете / переносите «операционную систему» ​​на PIC18 и если она поддерживает микроконтроллеры, она, вероятно, поддерживает статическое распределение. Например, SQLite3 поддерживает статическое распределение - вы выделяете ему большой буферный массив, и он использует его там, где это возможно, даже если это не для микроконтроллеров. Если это не так, то вы уверены, что он предназначен для небольшого PIC18?

Томас О
источник
Я понимаю, что вы имеете в виду .. Я использую PIC18F87J11, который имеет 128 КБ оперативной памяти, это может быть достаточно?
stef
Стефано, этот чип имеет 3,904 байта оперативной памяти. Он имеет 128 Кб программной флэш-памяти.
W5VO
@Stefao Salati - 3.8KB крошечный.
Томас О,
Прямо извините .. как вы думаете, этого может быть достаточно в любом случае?
stef
@ Стефано Салати, не нужно извиняться. Я думаю, что ты действительно будешь настаивать. Это может сработать, но это приведет к снижению производительности и вашей свободной памяти.
Томас О,
2

Если вы рассматриваете malloc()и free()для встроенного программного обеспечения , я предлагаю вам взглянуть на УНЦ / OS-II и OSMemGet()и OSMemPut(). Хотя malloc()позволяете выделить произвольный блок памяти, OSMem*()дать вам фиксированный размер блок из предварительно выделенного бассейна. Я считаю этот подход хорошим балансом между гибкостью malloc()и надежностью статического распределения.

trondd
источник
0

AFAIK, чтобы сделать это правильно, вам действительно нужно смотреть на устройство с каким-то блоком управления памятью (MMU). Хотя механизмы динамического распределения для серии PIC18 существуют, на самом деле они не будут такими надежными - и, говоря о том, кто работает над прошивкой, которая раздвигает пределы серии PIC18, я могу сказать, что вы не получите большое приложение, если вы потратите все накладные расходы на менеджер памяти.

Лучшее решение: попытайтесь понять, что он делает и почему он требует динамического распределения. Посмотрите, не можете ли вы перефакторировать его для работы со статическим распределением. (Это может быть случай, когда это просто невозможно - если библиотека / приложение спроектировано так, чтобы делать что-то, что масштабируется свободно, или не имеет границ объема ввода, который может принять.) Но иногда, если вы действительно думаете, о том, что вы пытаетесь сделать, вы можете обнаружить, что возможно (и, возможно, даже довольно просто) использовать вместо этого статическое распределение.

andersop
источник
1
Вы не правы. MMU позволяет вам взаимодействовать с внешней памятью (вероятно, больше, чем 4 КБ на PIC). Существует очень небольшая разница в динамическом и статическом распределении с MMU и без него. Как только вы начинаете проникать в виртуальную память, есть разница, но это только косвенно связано с malloc.
Кевин Вермеер
1
Ранние программисты Macintosh довольно часто использовали malloc () и free () (или их эквиваленты на Pascal), несмотря на то, что на ранних компьютерах Macintosh не было MMU. Идея, что для «правильного» использования malloc () требуется MMU, мне кажется неверной.
Дэвидкари