2009-11-12 2 views
13

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

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

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

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

+1

это: http://www.nongnu.org/avr-libc/user-manual/malloc.html очень хорошо –

ответ

18

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

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

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

+0

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

+1

«Динамически выделенная память может очень легко перезаписать стек процессора». - Это не так. Фактически, это * стек *, который является динамическим по своей природе и может расти, чтобы перезаписать иначе выделенную память. В памяти, отличной от стека, будет установлен верхний предел для динамического и статического распределения, но когда стек будет превосходить ожидания, он просто перезаписывает данные, которые он не должен, независимо от статического распределения или иным образом. При использовании динамического распределения стек больше не угрожает. – JimmyB

0

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

4

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

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

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

3

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

+0

Большая часть моей встроенной работы была на правительственных работах, где есть обычное требование оставить 50% запасной для будущих обновлений. Разве коммерческий мир не делает этого? –

+0

T.E.D. это мудрый совет, который я узнал от вас. – bazz

0

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

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

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

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

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

4

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

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

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

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

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

+0

32 KB RAM! ты был счастливчиком. У меня 1 Кбайт оперативной памяти (все 26 из нас), с которой мне приходится делиться с ним ядром CPU –

0

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

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

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

+0

PIC? OP спрашивает о AVR. – iter

+0

Я также говорил о AVR. Виноват. – thomaspaulb

2

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

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

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

Например -

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

;

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

Смежные вопросы