3

Я слышал во встроенной системе, мы должны использовать некоторые предварительно выделенные блоки памяти фиксированного размера (например, систему памяти приятеля?). Может ли кто-нибудь дать мне подробное объяснение, почему? Спасибо,Почему мы не должны иметь динамически распределенную память разного размера во встроенной системе

ответ

11

В встроенных системах у вас очень ограниченная память. Поэтому, если вы иногда теряете только один байт памяти (потому что вы его выделяете, но вы его не освобождаете), это довольно быстро поедет в системную память (1 ГБ ОЗУ со скоростью утечки 1/час займет Если у вас 4 КБ ОЗУ, не так долго)

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

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

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

Как указано Hao Shen, фрагментация также представляет опасность. Может ли это произойти, зависит от вашего точного usecase, но во встроенных системах довольно легко потерять 50% вашей памяти из-за фрагментации. Вы можете избежать фрагментации, если вы выделяете куски, которые всегда имеют одинаковый размер.

Производительность также играет определенную роль (зависит от usecase - спасибо Hao Shen). Статически выделенная память выделяется компилятором, тогда как malloc() и аналогичные должны выполняться на устройстве и, следовательно, потреблять процессорное время (и мощность).

Многие встроенные ОС (например, ChibiOS) поддерживают какой-то динамический распределитель памяти. Но использование этого только увеличивает вероятность возникновения неожиданных проблем.

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

Как указывается Stephano Sanfilippo, в какой-то системе даже не хватает ресурсов для поддержки распределения динамической памяти.

Примечание: Большинство стандарта кодирования, в том числе и the JPL coding standardDO-178B (для критического кода авионики - спасибо [Stephano Sanfilippo]) (https://stackoverflow.com/users/2344584/stefano-sanfilippo) запретить использование таНоса.

Я также принимаю MISRA C standard запрещает malloc() из-за this forum post - однако у меня нет доступа к самому стандарту.

+0

Как раз для того, чтобы привести практический пример, американский военный стандарт DO-178B запрещает использование «malloc» в критическом для безопасности авионике. –

+0

Привет Ули Спасибо за вашу информацию. Я считаю, что фрагментация потеряет ценную память во встроенной системе. Но вы считаете, что скорость также вызывает беспокойство? Может быть, использование более мелкой статически распределенной памяти происходит быстрее? –

+0

@HaoShen Да, я согласен! Если фрагментация происходит, зависит от вашего usecase, но OP специально запрашивает * память с разным размером *. Я отредактирую это в свой ответ! –

5

Основные причины не использовать динамическое распределение памяти кучи здесь в основном:

а) Детерминизм и, коррелируют, б) фрагментации памяти.

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

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

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

+0

+1 для детерминизма, но в объяснении отсутствует более важное соображение: в системе реального времени недетерминированное поведение связано с операциями, которые принимают переменную и неограниченную длительность - независимо от того, сбой или успех.Типичное распределение памяти с «первым набором» не может найти блок в фиксированное время, поэтому в задачах реального времени могут быть пропущены крайние сроки. Это не так, что динамическая память не должна использоваться во встроенных системах, а не должна использоваться в режиме реального времени. – Clifford

+1

@Clifford Спасибо за предложение относительно неопределенного времени. Я отредактировал это в своем ответе. Даже для не-RT-систем я бы не использовал динамический memalloc (если это возможно) из-за отсутствия детерминизма и риска утечки памяти. –

+0

@Clifford Это не просто вопрос детерминированных сроков. Когда память становится фрагментированной, а при отсутствии MMU конкретный вызов «malloc» может преуспеть или сбой только в зависимости от истории событий, с которыми ранее сталкивалось приложение, хотя в сумме достаточно доступной памяти. Это затрудняет прогнозирование, если память может быть выделена при необходимости в живой системе. – JimmyB

3

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

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

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

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

+0

Спасибо за ваш ответ. Вы говорите: «Выделение из пула фиксированных размеров не вызывает фрагментации». Хотя я знаю, что это правда, на самом деле я не совсем понял. Если я правильно пойму, со временем все еще будут фрагментированные небольшие фиксированные куски, верно? Большие запросы памяти по-прежнему не могут их использовать, не так ли? –

+2

@ HaoShen. Когда вы используете пул фиксированных размеров, вам нужно разработать приложение для выделения кусков только определенного размера. Ваше приложение никогда не должно запрашивать более крупный (или меньший) кусок. Поэтому, если какие-либо куски доступны, тогда они всегда имеют нужный размер. Это предотвращает фрагментацию при правильном выполнении. – kkrambo