Насколько плохо использовать динамические массивы данных на встроенной системе?
Таким образом, во встроенном системном блоке, который я возьму в следующем году, мы узнаем, что динамические структуры данных - это плохо, что есть во встроенной системной программе.
но в лекционных заметках не говорится, почему.
Теперь я работаю над небольшим масштабом, встроенный контроллер "LURC", в основном, просто использует преимущества периферии демонстрационной платы "Butterfly" для AVR169MEGA.
выдает 4 сигнала PWM для сервоуправления и ESC. а также предоставить 9-сегментный ЖК-экран.
Теперь я не могу придумать какой-либо способ хранения инструкций, так как они получают серийный номер USART, чем очередь.
esp для вещей, где мне нужно подождать, пока не будет получено неизвестное количество данных: например, строка, отображаемая на ЖК-экране.
так почему бы вам не использовать динамические структуры данных на микроконтроллере во встроенных системах?
Это просто, что вы в значительной степени ограничены окружающей средой, и должны быть уверены, что ваши mallocs преуспевают?
Ответы
Ответ 1
Существует несколько причин не использовать malloc (или эквивалент) во встроенной системе.
- Как вы упомянули, важно, чтобы вы не внезапно оказались вне памяти.
- Фрагментация - встроенные системы могут работать в течение многих лет, что может привести к серьезной трате памяти из-за фрагментации.
- Не требуется. Динамическое распределение памяти позволяет повторно использовать одну и ту же память, чтобы делать разные вещи в разное время. Встраиваемые системы имеют тенденцию делать то же самое все время (кроме при запуске).
- Скорость. Динамическое распределение памяти является относительно медленным (и становится медленнее по мере фрагментации памяти) или довольно расточительно (например, приятельская система).
- Если вы собираетесь использовать одну и ту же динамическую память для разных потоков и прерываний, тогда подпрограммы распределения/освобождения должны выполнять блокировку, что может вызвать проблемы с обслуживанием прерываний достаточно быстро.
- Распределение динамической памяти очень затрудняет отладку, особенно с некоторыми из инструментов ограниченного/примитивного отладки, доступных во встроенной системе. Если вы статически выделяете материал, тогда вы знаете, где материал все время, что означает, что гораздо проще проверить состояние чего-то.
Лучше всего - если вы не динамически выделяете память, вы не можете получить утечки памяти.
Ответ 2
Ну, у многих небольших микроконтроллеров нет ничего похожего на MMU или на ОС с красивой кучей, с которой вы можете работать.
Для тех, кто это делает, до тех пор, пока вы держите разумную привязку к объему памяти, о которой вы просите, я действительно не вижу в ней огромной проблемы.
Однако многие встроенные системы также являются системами реального времени. Если ваше приложение имеет жесткие сроки для того, сколько времени может потребоваться для запуска, у вас возникнут проблемы с динамическими распределениями. Большинство реализаций кучи используют алгоритмы, которые не имеют очень хорошо ограниченного времени выполнения. В некоторых (возможно, редких) случаях они будут считать waaaay дольше, чем обычно. Есть несколько реализаций кучи в реальном времени, но они не очень широко используются. Общее правило заключается в том, чтобы избежать динамического выделения или освобождения в жесткой системе реального времени после инициализации.
Ответ 3
Мое впечатление, что во встроенной системе я точно знаю, сколько памяти доступно, и мне разрешено использовать ровно 100%; нет необходимости оставлять бит для других (одновременно работающих) программ, но также нет виртуальной памяти, чтобы дать мне 101-й процент. Поэтому для очереди я могу легко вычислить, что у меня есть место для (скажем) 981 записей; поэтому я создаю массив для этих записей, и если мне когда-либо понадобится 982-я запись, я буду fscked вверх и должен найти способ потерпеть неудачу изящно.
Ответ 4
Это зависит от того, как значение "встроенный" на мой взгляд расширилось за последние 4 года.
Традиционно встроенные устройства имели на них микроконтроллеры и вообще не имеют операционной системы. Нет защищенной памяти, и они были однопоточными. Вы должны быть предельно осторожны с malloced памятью, потому что так легко выбежать из нее, когда у вас есть только 32 КБ ее, например, доступной. В общем, мы будем писать наш код с буферами фиксированного размера и никогда не будем использовать malloc или, если он будет использоваться каждый раз - очень экономно.
В последние несколько лет мы видим, что по существу одночиповые ПК или микро-платы, которые так же мощны, как и наши старые ПК Pentium. Цены на RAM сейчас настолько дешевы и настолько малы, что ограничения памяти ничем не отличаются. Они также часто запускают встроенный linux или вздрагивают, так что теперь у нас есть возможность использовать динамическую память более либерально.
С помощью этой возможности можно использовать гораздо более широкий диапазон языков, включая Java, С++, многие языки сценариев и другие языки, которые обеспечивают защиту от переполнения буфера и обработку исключений и другие языки более высокого уровня. Так что действительно, эти старые проблемы не такие, как раньше.
Я подозреваю, что все это новое доступное оборудование выходит на новый уровень проблем.
Ответ 5
Нет ничего плохого в динамической памяти во встроенной среде как таковой, хотя обычно она не покупает вас во встроенной среде.
По-моему, это очень хорошая идея использовать кольцевые буферы (это очень простая структура данных для драйверов ввода-вывода и т.д.). Таким образом, если по какой-то причине вы не можете обслуживать свою очередь, использование памяти по-прежнему детерминировано.
Используя некоторые макросы, можно выделить структуры переменных размера во время компиляции.
Например -
//we exploit the fact that C doesn't check array indices to allow dynamic alloc of this struct
typedef struct ring_buf_t {
int element_sz,
buffer_sz,
head,
tail;
char data[0];
} ring_buf_t;
#define RING_BUF_ALLOC_SZ(element_sz,n_elements) (sizeof (ring_buf_t) + \
(element_sz) * (n_elements))
char backing_buf[RING_BUF_ALLOC_SZ (sizeof(type_to_buffer), 16)];
//ring_buf_init() casts backing buf ring_buf_t and initialises members...
ring_buf_t *ring_buffer = ring_buf_init (element_sz, n_elemements, backing_buf);
;
Этот шаблон представляет собой динамически значимый буфер с использованием памяти guarenteed. Конечно, структуры данных других типов (списки, очереди и т.д.) Могут быть реализованы таким же образом.
Ответ 6
Я бы сказал, что и нехватка памяти, и проблема неудачного malloc. Последнее представляет большую проблему, поскольку у вас нет ОС/интерфейса для спасения системы от такого сбоя. Очень опасно использовать функцию, которая может помещать вашу полную систему, которая может работать без головного убора, в визг остановки (или, возможно, вызывать reset, все еще плохо).
Ответ 7
Динамические структуры данных на встроенных системах немного напоминают указатели на С++. Указатели (на С++) являются злыми. Но иногда это единственный вариант; иногда они являются меньшим злом; и иногда это нормально, чтобы полностью их избежать. В тех случаях, когда есть веские основания для их использования, могут быть "хорошие" способы и "плохие" способы сделать это.
Статически распределенные переменные и массивы намного быстрее выделяют и освобождают и могут быстрее обращаться к динамически распределенным данным. См. этот ответ.
Динамически выделяемые (под которыми я имею в виду malloc()
ed или аналогичные) данные также требуют пространственных накладных расходов, чтобы отслеживать распределения. По меньшей мере несколько байтов на рассылку - это пространство может быть очень ценным для встроенных систем!
Утечки памяти являются серьезной проблемой для встроенных систем, которые иногда можно ожидать в течение многих лет. С этой точки зрения разумно избегать динамического распределения.
Встроенные устройства обычно имеют достаточно надежные спецификации. Вы знаете, что такое скорость передачи, вы знаете, как быстро вы можете справляться с информацией, и так далее. В вашем примере решение должно использовать буфер фиксированного размера в виде круговой очереди. Сделайте буфер достаточно большим, чтобы обрабатывать то, что ваше устройство должно обрабатывать (и, возможно, немного больше). Если поступает слишком много данных, возможно, из-за ошибки или помех где-то в другом месте, поэтому нет большого количества точек и попытки использовать все эти данные.
Ответ 8
Я не знаю об Atmel MEGA169, но MEGA168, который, я полагаю, связан с 169, имеет только 1024 байта SRAM. Он также имеет только 16 тыс. Программных ПЗУ и относительно медленный по сравнению с современными компьютерами. Таким образом, он ограничен в памяти, размере и скорости программы.
В моем опыте программирования AVR-ассемблера требуется много усилий, чтобы максимально использовать функциональность в ПОС. Объем накладных расходов, необходимых для использования динамических структур данных (дополнительное использование памяти, дополнительные инструкции, необходимые для вытягивания и выталкивания данных из SRAM, отслеживание динамической переменной, где происходит перемещение блоков памяти, когда переменные "между" удаляются...) просто не оправдывает заслуги.
Поэтому, даже если компилятор реализует это, я бы придерживался статических структур данных для производительности.