Комментарии 19
Так у вас в способе #2 MyHeap каждый раз выделяет блок только с бОльшим адресом?? А что если память кончится? А как возвращать ему память (free()) и переиспользовать её?
В общем виде этот компонент называется "аллокатор памяти" и реализовать его правильно - это грёбаная ядерная физика. В 90% случаев лучше всего взять сторонний а не делать самому.
Память должна распределяться при старте раз и навсегда. Динамическое выделение с потенциальной фрагментацией кучи может привести к отказу из-за неудачного распределения свободных блоков, которые когда-нибудь не соберутся воедино.
конкретно тут как я понял ембед, а в ОС ну типо десктоп, есть сискол(ну или не сискол а стандартизированный вызов на эту операцию точно есть!) стандартизированный, а в другой ОС свой интерфейс шлефанули поверх лоу-левела, тут смотря что обсуждаем я понял что на ембед нету ОС и там своё окружение, а в ос ябы тестил тот вызов и смотрел бы, ведь вроде память самому не взять должен быть интерфейс - он сопственно и предназначен для этого чтобы коммуницировать с чем там ядром наверно
прадедушка ОС это посикс стандарт наверно если по вики смотреть Version 1 Unix перед ним PDP если смотреть в вики на дерево соответственно стандарты там тоже ранжированы и прочее
template<class T, int Id = 0>
T& instance()
{
static char data[sizeof(T)];
static bool initialized;
if (!initialized)
{
initialized = true;
new(data)T();
}
return *reinterpret_cast<T*>(data);
}
instance<Timer>();
instance<Channel, 0>();
instance<Channel, 1>();
И память статически размещена, и накладных расходов минимум (потому что блокировки на многопоточную инициализацию нет), и отложенная инициализация до первого вызова.
С этой точки под необходимость можно протащить аргументы конструктора либо непосредственно через аргумент функции, либо через trait, например, либо ещё как-то.
Стоит добавить три важные вещи, о которых все часто забывают:
Если у вас есть кастомный аллокатор, унаследовав его от std::pmr::memory_resource можно его использовать со стандартными контейнерами типа std::pmr::string, std::pmr::vector, и т.д. - и более того, можно с ними использовать разные типы аллокаторов (в одном случае свою кучу, а в другом случае просто локальный буфер), при этом они будут интероперабельны
Если нужно non-monotonic поведение аллокатора (возможность деаллокации), то просто pool allocator (он же arena allocator) на базе списков с фиксированным размером блока в реализации не сильно сложнее чем то, что у автора, зато гораздо более эффективен для многих применений
Существуют реализации
malloc()
иfree()
, которые выполняются за постоянное время и с предсказуемой фрагментацией кучи, что позволяет их использовать даже в системах жесткого реального времени
Когда мы разрабатываем под embedded, нам приходится сталкиваться с такими флагами компиляции как
-nostdlib -fno-exceptions -fno-rtti
избыточность -fno-exceptions -fno-rtti понятна. А чем stdlib мешает?
Современный .NET nano Framework требует от 64кб ОЗУ и умеет динамическую аллокацию
В древности в кнопочных телефонах тоже бывало до 1мб ОЗУ (несколько сотен кб) - но там работала java me с динамической аллокацией
Объясните мне, а почему стандартный аллокатор отсутствует? Ведь выделять память динамически иногда всё же нужно. С этим ограничением каждый велосипедитчто-то своё, как сказано в данной статье. Небольшой объём памяти - не аргумент. Вполне возможно реализовать простенький аллокатор, работающий на объёмах от единиц до сотен килобайт.
Это из стандартов НАСО на код. Что бы пройти верификатор. От туда же и запрет на рекурсию. А как уж кто извращаться будет…
Контрпродуктивный стандарт какой-то. Мол давайте запретим использовать кучу (дабы чего не вышло), но в результате все используют свои велосипедные замены, что ведёт к возможным ошибкам, которых не было бы, если бы все использовали одну стандартную и посему хорошо протестированную реализацию аллокатора.
Не нужно. Нужно мышление перестроить
Представим, что у нас N независимых задач, каждая из которых может потребовать до M байт памяти. Представим, что физически в системе у нас (N-1)*M памяти.
Что будет, если N-1 задач разом запросят выделения по M байт памяти каждой, а затем N-я задача также запросит память? Аллокатор откажет ей и она должна дождаться, пока кто-нибудь эту память освободит.
Вот это "дождаться" и является недопустимым в задачах реального времени.
Если задачи не-независимы, можно организовать стек и гарантировать, что в момент, когда какой-либо функции потребуется много памяти, этот стек будет пуст.
Если задачи независимы, под каждую придётся зарезервировать тот объём памяти, который потребуется в самом худшем случае.
Динамическое же распределение памяти - это механизм абстрагирования от факта, что физически в системе памяти меньше, чем может потребоваться. В ряде случаев подобное абстрагирование допустимо, но "в embedded" зачастую - нет.
С выделением памяти без кучи стреляют часто себе в ногу, потребляя больше памяти, чем необходимо. Часто ведь под N задач выделяют по M байт памяти на каждую (с запасом), потребляя в результате M*N памяти суммарно. При этом по факту часто каждая задача потребляет меньше, чем M памяти, M - это предел. Так вот, при наличии кучи можно было бы выделять памяти для каждой задачи ровно столько, сколько необходимо, так, что в результате потребление будет меньше, чем M * N, что потенциально может дать возможность обрабатывать больше задач (увеличить N), или же поставить микросхему памяти поменьше.
Отсутствие динамической аллокации в embedded мире
Что вы подразумеваете под "embedded миром"?
Почему динамической аллокации часто нет?
Ограниченные ресурсыПамять (RAM/Flash) в контроллерах может исчисляться десятками или сотнями килобайт, поэтому каждый байт на счету.
...что сравнимо с машинами эпохи Windows 3.11. Там динамическая аллокация была. В общем - не аргумент.
Отсутствие стандартных библиотек
Учитывая, что ядро микроконтроллеров 8051 появилось в 1980 году, у отрасли было 45 лет, чтобы написать стандартные функции аллокации. Я вижу два конкурирующих объяснения, почему этих функций до сих пор нет. Либо в отрасли работают безмозглые идиоты. Либо динамическая аллокация "в embeddeb" не нужна.
Отсутствие библиотек - это как бы следствие, а не причина :)
Встраиваемые приложения часто требуют детерминированного поведения. Фрагментация кучи и непредсказуемые задержки неприемлемы.
Почти так. Только не исключительно фрагментация кучи, а динамическое выделение памяти в общем, по своей сути, является недетерминированным процессом. Поэтому в задачах реального времени, которых в embedded достаточно много, динамическую аллокацию памяти стараются не применять.
Не пробовали Embedded Template Library вместо STL?
Отсутствие динамической аллокации в embedded мире