Насколько плохо использовать динамические данные во встроенной системе? - PullRequest
14 голосов
/ 13 ноября 2009

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

Сейчас я работаю в контроллере LURC со встроенными системами среднего масштаба, в основном просто использующим преимущества периферийного устройства демонстрационной платы «Butterfly» для AVR169MEGA. произвел 4 сигнала ШИМ для управления сервоприводом и ESC. а также обеспечить 9-сегментный ЖК-экран.

Теперь я не могу придумать более удобный способ хранения инструкций по мере их поступления через серийный номер USART, чем очередь. Особенно в тех случаях, когда мне нужно ждать, пока не будет получено неизвестное количество данных: например, строка для отображения на экране ЖКД.

так почему бы вам не использовать динамические структуры данных на микроконтроллере во встроенных системах? Это просто то, что вы находитесь в среде с ограниченным объемом памяти и должны быть уверены, что ваши mallocs преуспевают?

Ответы [ 8 ]

21 голосов
/ 13 ноября 2009

Существует ряд причин, по которым не следует использовать malloc (или его эквивалент) во встроенной системе.

  • Как вы упомянули, важно, чтобы вы внезапно не обнаружили себя нехваткой памяти.
  • Фрагментация - встроенные системы могут работать годами, что может привести к серьезным потерям памяти из-за фрагментации.
  • На самом деле не требуется. Динамическое распределение памяти позволяет вам повторно использовать одну и ту же память, чтобы делать разные вещи в разное время. Встраиваемые системы имеют тенденцию все время выполнять одно и то же (кроме запуска).
  • Speed. Динамическое распределение памяти либо относительно медленное (и становится медленнее, когда память фрагментируется), либо является довольно расточительным (например, система друзей).
  • Если вы собираетесь использовать одну и ту же динамическую память для разных потоков и прерываний, то процедуры выделения / освобождения должны выполнять блокировку, которая может вызвать проблемы с обработкой прерываний достаточно быстро.
  • Динамическое выделение памяти очень затрудняет отладку, особенно с некоторыми из ограниченных / примитивных инструментов отладки, доступных во встроенной системе. Если вы статически распределяете вещи, то вы знаете, где они находятся постоянно, а это значит, что намного проще проверять состояние чего-либо.

Лучше всего - если вы не распределяете память динамически, вы не можете получить утечки памяти.

5 голосов
/ 13 ноября 2009

Ну, на многих небольших микроконтроллерах нет ничего похожего на MMU или ОС с приятной кучей для работы.

Для тех, кто это делает, до тех пор, пока вы сохраняете разумный объем памяти, который вы запрашиваете, я не вижу в этом огромной проблемы.

Однако многие встроенные системы также являются системами реального времени . Если у вашего приложения есть жесткие сроки для запуска, у вас будут проблемы с динамическим распределением. Большинство реализаций кучи используют алгоритмы, которые не имеют очень ограниченного времени выполнения. В некоторых (возможно, редких) случаях они будут работать дольше, чем обычно. Существует несколько реализаций кучи в реальном времени, но они не очень широко используются. Общее правило заключается в том, чтобы избегать динамического выделения или освобождения в жесткой системе реального времени после инициализации.

4 голосов
/ 13 ноября 2009

Нет ничего плохого в динамической памяти во встроенной среде как таковой, хотя, как правило, во встроенной среде она мало что дает.

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

Используя некоторые макросы, можно выделить структуры переменного размера во время компиляции.

Например -

    //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);

;

Это шаблон динамически расширяемого буфера с гарантированным использованием памяти. Конечно, другие типы структур данных (списки, очереди и т. Д.) Могут быть реализованы таким же образом.

4 голосов
/ 13 ноября 2009

Это зависит от того, как расширилось значение «встроенного», на мой взгляд, за последние 4 года.

Традиционно на встроенных устройствах были установлены микроконтроллеры и, как правило, отсутствует операционная система. Нет защищенной памяти, и были однопоточными. Вы должны быть чрезвычайно осторожны с недопустимой памятью, потому что ее так легко исчерпать, например, если у вас есть только 32 КБ. В общем, мы пишем наш код с буферами фиксированного размера и никогда не используем malloc или at, если он использовался каждый - очень экономно.

В последние несколько лет мы видим, что представляют собой однопроцессорные ПК или микроплаты, которые так же мощны, как и наши старые ПК Pentium. Цены на оперативную память сейчас настолько низки и настолько малы, что ограничения памяти совсем не такие, как раньше. Они также часто используют встроенный Linux или Wince, так что теперь у нас есть возможность более свободно использовать динамическую память.

Это дает возможность использовать гораздо более широкий диапазон языков, включая Java, C ++, многие языки сценариев и другие языки, которые обеспечивают защиту от переполнения буфера и обработку исключений, а также другие языки более высокого уровня. Так что, на самом деле, эти старые проблемы не такие, как раньше.

Я подозреваю, что все это новое доступное оборудование порождает новые проблемы.

3 голосов
/ 13 ноября 2009

У меня сложилось впечатление, что во встроенной системе я точно знаю, сколько памяти доступно, и мне разрешено использовать ровно 100%; нет необходимости оставлять немного для других (одновременно работающих) программ, но также нет доступной виртуальной памяти, чтобы дать мне 101-й процент. Поэтому для очереди я могу легко рассчитать, что у меня есть место для (скажем) 981 записи; поэтому я создаю массив для этих записей, и если мне когда-нибудь понадобится 982-я запись, я облажался и должен найти способ изящно провалиться.

0 голосов
/ 13 ноября 2009

Я не знаю об Atmel MEGA169, но MEGA168, который, я полагаю, связан с 169, имеет только 1024 байта SRAM. Он также имеет только 16 КБ ПЗУ и является относительно медленным по сравнению с современными компьютерами. Так что он ограничен в памяти, размере программы и скорости.

По моему опыту программирования на ассемблере AVR, нужно приложить как можно больше функциональности к PIC. Объем накладных расходов, необходимый для использования динамических структур данных (дополнительное использование памяти, дополнительные инструкции, необходимые для извлечения и извлечения данных из SRAM, отслеживания, где и где находится динамическая переменная, перемещение блоков памяти вокруг, когда переменные «между ними» удаляются .. ..) просто не оправдывает заслуги.

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

0 голосов
/ 13 ноября 2009

Динамические структуры данных во встроенных системах немного похожи на указатели в C ++. Указатели (в C ++) являются злом. Но иногда они являются единственным вариантом; иногда они меньшее зло; и иногда все в порядке, чтобы избежать их полностью. В тех случаях, когда является веской причиной для их использования, могут быть «хорошие» и «плохие» способы сделать это.

Статически распределенные переменные и массивы намного быстрее распределяются и освобождаются, а доступ к ним может быть быстрее, чем динамически распределяемые данные. См. этот ответ .

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

Утечки памяти - это массивная проблема во встроенных системах, которая иногда может длиться годами. С этой точки зрения разумно избегать динамического распределения.

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

0 голосов
/ 13 ноября 2009

Я бы сказал, что нехватка памяти и проблема сбоя malloc. Последнее является более серьезной проблемой, поскольку у вас нет ОС / интерфейса для спасения системы от такого сбоя. Очень опасно использовать функцию, которая может привести к полной остановке вашей системы, которая может работать без головы (или, возможно, вызвать сброс, все еще плохо).

...