Когда malloc возвращает NULL в среде с «голым железом»?

Существует следующая модель памяти c:

      +--------+   Last Address of RAM
      | Stack  |
      |   |    |
      |   v    |
      +--------+
RAM   |        |
      |        |
      +--------+
      |   ^    |
      |   |    |
      | Heap   |
      +--------+
      |   ZI   |
      +--------+
      |   RW   |  
      +========+  First Address of RAM

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

  • Когда в среде «голого железа» malloc возвращает NULL?
  • Как предотвратить перекрытие стека кучей в среде с голым металлом?

person lrouter    schedule 24.08.2016    source источник
comment
Что такое никакая ОС?   -  person Ed Heal    schedule 24.08.2016
comment
Вы имеете в виду не-ОС, как на голом железе?   -  person dbush    schedule 24.08.2016
comment
Да, извините за путаницу.   -  person lrouter    schedule 24.08.2016
comment
В среде с «голым железом» обычно лучше вообще не использовать malloc. Вместо этого используйте пулы памяти, которые управляют фиксированным количеством блоков памяти фиксированного размера.   -  person user3386109    schedule 24.08.2016
comment
В среде «голого железа» malloc часто не существует. Если он существует, проверьте документацию, откуда он взялся.   -  person user253751    schedule 24.08.2016
comment
Кроме того, эта модель памяти C неверна уже более двух десятилетий.   -  person user253751    schedule 24.08.2016
comment
когда malloc возвращает NULL? --› Обратите внимание, что malloc(0) может возвращать NULL и не подразумевает нехватку памяти. YMMV.   -  person chux - Reinstate Monica    schedule 24.08.2016
comment
@user3386109 user3386109 ·malloc· — это библиотечная функция c. Вы имеете в виду объявить пул памяти глобально? Затем вы можете реализовать свою собственную функцию malloc для управления этим пулом памяти.   -  person lrouter    schedule 24.08.2016
comment
Возможно, вам поможет эта статья. Пул памяти — это другой способ управления динамической памятью. Он используется вместо malloc в системах, где malloc либо недоступен, либо не подходит для использования. Это была бы почти любая система, в которой нет ОС.   -  person user3386109    schedule 24.08.2016
comment
файл link.cmd (для TI аналогичный файл для других сред) сообщает компоновщику, где именно разместить все в памяти. Одним из размещенных предметов является heap. в файле link.cmd (обычно) не указано, где именно разместить heap, но часто указано, какого размера сделать heap.   -  person user3629249    schedule 25.08.2016


Ответы (5)


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

Если нет, и если вы имеете в виду «голое железо», это зависит от реализации библиотеки C в вашем пакете поддержки платы. Библиотека должна обеспечивать некоторую реализацию brk(2) или функции, имеющей аналогичный эффект. malloc работает в пределах области памяти, заданной brk или sbrk. Например, см. источник malloc, вызывающий макрос MORECORE, который по умолчанию sbrk. Ваша библиотека C должна будет использовать для этого что-то другое, кроме вызова ядра :).

person cxw    schedule 24.08.2016

Размер стека определяется во время компиляции.

      +--------+   Last Address of RAM
      | Stack  |
      |   |    |
      |   v    |
      +--------+
RAM   |        |
      +--------+   Stack Limit
      |        |
      +--------+
      |   ^    |
      |   |    |
      | Heap   |
      +--------+
      |   ZI   |
      +--------+
      |   RW   |  
      +========+  First Address of RAM

malloc возвращает null, если для запрошенной кучи недостаточно места.

И ваша обязанность — предотвратить переполнение стека!

person Wiki Wang    schedule 24.08.2016
comment
Основываясь на вашей фигуре, я обнаружил метку «heap_limit» и stack_limit в своем файле карты. - person lrouter; 24.08.2016

когда malloc возвращает NULL?

Это может зависеть от компилятора C и реализации библиотеки. Например, моя реализация malloc вызывает sbrk, что является системным вызовом в среде Linux. Поскольку у нас нет Linux на MCU, я предоставил свою собственную реализацию sbrk, например:

// Global variables.
extern unsigned int _heap;
extern unsigned int _eheap;
static caddr_t heap = NULL;

caddr_t _sbrk(int incr)
{
  caddr_t prevHeap;
  caddr_t nextHeap;

  if (heap == NULL) { // first allocation
    heap = (caddr_t) & _heap;
  }

  prevHeap = heap;

  // Always return data aligned on a 8 byte boundary
  nextHeap = (caddr_t) (((unsigned int) (heap + incr) + 7) & ~7);
  if (nextHeap >= (caddr_t) & _eheap) {
    errno = ENOMEM;
    return ((void*)-1); // error - no more memory
  } else {
    heap = nextHeap;
    return (caddr_t) prevHeap;
  }
}

и _eheap определяется в скрипте компоновщика:

PROVIDE ( _eheap = ALIGN(ORIGIN(ram) + LENGTH(ram) - 8 ,8) );

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

как предотвратить перекрытие стека кучей?

Вы можете определить свой собственный предел для sbrk для возврата ошибки, что предотвратит вторжение malloc в память стека.

person Sergey    schedule 24.08.2016
comment
Я проверил свой файл карты. Да, есть предел кучи. - person lrouter; 24.08.2016

1) голый металл, который вы, вероятно, не хотите использовать malloc в первую очередь

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

3) даже на не голом железе (windows, linux и т. д.) стек, попадающий в кучу или попадающий в пространство кода, не является чем-то, от чего вы обычно получаете защиту, вам нужно либо сообщить компилятору, если он поддерживает это, чтобы добавить еще тонну кода, или вы просто проектируете свое программное обеспечение правильно, чтобы не конфликтовать.

Какова ваша схема распределения памяти, что делает ваш код, как, если вы вообще сказали этому коду, из какого пространства он может выделять. Если компилятор вообще поддерживает это, вам может потребоваться сообщить компилятору, что пространство или среда выполнения указывают, где в настоящее время находится вершина кучи. Или, может быть, компилятор полагается на то, что это пространство заполнено шаблоном, и он проверяет шаблон перед выделением, что означает, что в таком случае на «голом железе» вам нужно будет заполнить эту память этим шаблоном. Прежде всего вам нужно выяснить, работают ли выходные данные компилятора в отношении предотвращения столкновения стека с данными или программным пространством, и если да, то каким образом.

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

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

person old_timer    schedule 24.08.2016
comment
Спасибо. Как проверить максимальную глубину стека? - person lrouter; 24.08.2016
comment
один - путем анализа вашего кода. Другой подход - это подход типа valgrind, заполните оперативную память каким-либо шаблоном, запустите свою программу, остановитесь и проверьте оперативную память, где заканчивается шаблон заполнения, насколько глубоко ушел стек. С помощью анализа вам нужно подтвердить, что вы выбрали самый глубокий путь с точки зрения потребления стека. Не выполняя анализ правильно и полагаясь только на эксперименты, вы можете пропустить путь и немного отклониться от нормы. - person old_timer; 24.08.2016
comment
Есть ли какой-нибудь инструмент, который мог бы закончить работу? - person lrouter; 26.08.2016

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

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

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

При этом на «голом железе» вы проектируете структуру памяти, и вы можете настроить ее так, как хотите, в зависимости от ваших потребностей. malloc обычно не одобряют, потому что это недетерминировано. Кроме того, вы должны профилировать использование стека и знать максимальную глубину стека, чтобы правильно определить размер стека.

person Andrea Biondo    schedule 24.08.2016
comment
Так же и malloc в библиотеке c проверяет стек и ограничения кучи - person lrouter; 26.08.2016
comment
@D.E.T Это зависит от того, как это реализует ваша libc. В наиболее распространенном случае он не будет делать это напрямую, но будет выделять память, вызывая sbrk, который вы определяете, так что вы можете проверить это там. - person Andrea Biondo; 26.08.2016
comment
Спасибо за объяснение. - person lrouter; 27.08.2016