Должен ли я сделать сегмент стека большим или большим? - PullRequest
1 голос
/ 14 марта 2012

Я программирую дизайн для микропроцессора с очень ограниченной памятью, и я должен использовать «много» памяти для различных функций. У меня не может быть большого сегмента стека, сегмента кучи, сегмента данных, я должен выбрать, какой из них сделать большим, а какой - маленьким. У меня всего около 32КБ,

Я использую около 20 КБ для текстового сегмента, что дает мне 12 КБ для остальных. И мне нужен буфер 4KB для перехода к различным функциям (размер сектора SPI Flash). Где следует инициализировать этот большой буфер?

Итак, мой выбор:

1) Если я объявлю буфер в начале функции, стек нужно будет сделать большим

spiflash_read(...)
{
  u8 buffer[4096]; // allocated on stack 
  syscall_read_spi(buffer,...)
}

2) Выделите динамически, куча должна быть увеличена

spiflash_read(...)
{
  u8 *buffer = (u8*) malloc(4096); // allocated in heap
  syscall_read_spi(buffer,...)
}

3) Распределение статически, огромная обратная сторона, его нельзя использовать за пределами «Библиотеки SPI».

static u8 buffer[4096]; // allocated in data section.

spiflash_read(...)
{
  syscall_read_spi(buffer,...)
}

Мой вопрос: каков наилучший способ реализовать этот дизайн? Может кто-нибудь объяснить, пожалуйста, рассуждения?

Ответы [ 4 ]

9 голосов
/ 15 марта 2012

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

Динамическое выделение памяти можно проверить во время выполнения- если у вас заканчивается куча, malloc () возвращает нулевой указатель.Однако вам необходимо проверить возвращаемое значение и освободить память при необходимости.Блоки динамической памяти обычно выровнены на 4 или 8 байтов и несут служебные данные управления кучей, что делает их неэффективными для очень небольших выделений.Кроме того, частое выделение и освобождение блоков с очень разными размерами может привести к фрагментации кучи и потере памяти - это может иметь катастрофические последствия для постоянно работающих приложений.Если вы никогда не намереваетесь освободить память, и она всегда будет распределена, и вы знаете, apriori , сколько вам нужно, тогда вам может быть лучше статическое распределение.Если у вас есть источник библиотеки, вы можете изменить malloc для немедленной остановки при сбое выделения памяти, чтобы избежать необходимости проверять каждое выделение.Если размеры распределений обычно имеют несколько общих размеров, предпочтительнее использовать распределитель с фиксированными блоками, а не стандартный malloc ().Это было бы более детерминировано, и вы могли бы реализовать мониторинг использования, чтобы помочь оптимизации размеров блоков и чисел каждого размера.

Распределение стека является наиболее эффективным, поскольку оно автоматически получает и возвращает память по мере необходимости.Однако он также имеет небольшую поддержку или вообще не поддерживает проверку во время выполнения.Как правило, когда происходит переполнение стека, код не будет детерминированно - и не обязательно где-то рядом с основной причиной.Некоторые компоновщики могут генерировать вывод анализа стека, который будет вычислять использование стека наихудшего случая через дерево вызовов;Вы должны использовать это, если у вас есть такая возможность, но помните, что если у вас многопоточная система, будет несколько стеков, и вам не нужно проверять наихудший случай для точки входа в каждую.Кроме того, lonker не будет анализировать использование стека прерываний, и ваша система может иметь отдельный стек прерываний или совместно использовать системный стек.

Способ, которым я бы занялся, безусловно, заключается в том, чтобы не размещать большие массивы или объекты в стеке.но выполните следующий процесс:

  1. Используйте анализ стека компоновщика для расчета использования стека наихудшего случая, при необходимости добавьте дополнительный стек для ISR.Выделите столько стека.

  2. Статически выделите все объекты, необходимые для выполнения.

  3. Используйте карту ссылок, чтобы определить, сколько памяти осталось, выделить почтивсе это в кучу (ваш компоновщик или скрипт компоновщика может делать это автоматически, но если вам нужно явно установить размер кучи, оставьте немного неиспользованным, в противном случае каждый раз, когда вы добавляете новый статический объект или расширяете стек, у вас будетизменить размер кучи).Выделите все большие временные объекты из кучи и будьте бдительны в отношении освобождения выделенной памяти.

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

Анализ линкера "наихудший случай", скорее всего, будет больше, чем вы видите на практике - наихудшие пути никогда не будут выполнены.Вы можете предварительно заполнить стек определенным байтом (скажем, 0xEE) или шаблоном, а затем после тщательного тестирования и работы проверить наличие отметки «high-tide» и оптимизировать стек таким образом.Используйте эту технику с осторожностью;Ваше тестирование может не охватывать все возможные обстоятельства.

3 голосов
/ 15 марта 2012

это зависит от того, нужно ли вам постоянно буферизовать.Если 90% вашей работы тратится на работу с этим буфером, то я бы поместил его в сегмент данных

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

. В противном случае поместите его в кучу.

Действительно, если вы ограничены в памяти, вы должны сделать подробный анализ того, чтоваше потребление памяти.Как только вы становитесь настолько маленькими, вы не можете воспринимать это как «нормальный», бросьте его во время разработки ОС / среды выполнения.Я видел встроенные магазины разработчиков, которым не разрешается делать динамическое распределение памяти;каждая вещь рассчитывается заранее и распределяется статически.Хотя они могут иметь многоцелевые области памяти (например, общий буфер ввода-вывода).Назад в мои дни COBOL это был единственный способ, которым вы могли работать (молодежь сегодня ..., ворчать, ворчать ....)

0 голосов
/ 15 марта 2012

Вопрос в том, действительно ли вам нужно читать 4096 байт одновременно?

Если ваши объекты данных меньше, вы можете прочитать только необходимый размер.

И даже если вы можете толькоСтереть 4 КБ страницы, нет необходимости кэшировать весь блок в оперативной памяти, так как это плохая идея, чтобы кэшировать его, стереть, а затем переписать его.

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

Это такжебезопасное отключение питания во время выполнения одного из действий.

0 голосов
/ 14 марта 2012

Традиционный ответ заключается в том, что вы должны настроить время выполнения так, чтобы ваш стек и куча росли друг к другу.Это позволяет вам игнорировать, какой из них должен быть «больше», и просто беспокоиться о том, что произойдет, если вы не выделите достаточно места ВСЕГО.

...