Как делают malloc () и свободный () работа?

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

единственная разница - то, что std::endl сбросы буфер вывода, и '\n' не делает. Если Вы не хотите буфер, сбрасываемый часто, используйте '\n'. Если Вы делаете (например, если Вы хотите получить весь вывод, и программа нестабильна), используйте std::endl.

262
задан SU3 12 April 2018 в 16:42
поделиться

11 ответов

Хорошо, некоторые ответы о malloc уже были опубликованы.

Более интересная часть как работает free (и в этом направлении malloc тоже можно понять лучше).

Во многих реализациях malloc / free команда free обычно не возвращает память операционной системе (или, по крайней мере, только в редких случаях). Причина в том, что у вас будут пробелы в куче, и, таким образом, может случиться так, что вы просто закончите свои 2 или 4 ГБ виртуальной памяти с пробелами. Этого следует избегать, так как как только виртуальная память закончится, у вас будут действительно большие проблемы. Другая причина заключается в том, что ОС может обрабатывать только фрагменты памяти определенного размера и выравнивания. Чтобы быть конкретным: обычно ОС может обрабатывать только те блоки, которые может обрабатывать диспетчер виртуальной памяти (чаще всего кратные 512 байтам, например 4 КБ).

Таким образом, возврат 40 байт в ОС просто не сработает. Так что же делает free?

Free помещает блок памяти в свой собственный список свободных блоков. Обычно он также пытается объединить смежные блоки в адресном пространстве. Список свободных блоков - это просто круговой список блоков памяти, в начале которых есть некоторые административные данные. Это также причина того, почему управление очень маленькими элементами памяти с помощью стандартного malloc / free неэффективно. Каждому фрагменту памяти требуются дополнительные данные, и при меньшем размере происходит большая фрагментация.

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

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

Почему происходит сбой вашего кода:

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

Это довольно изящное поведение. Я также встречал ситуации, когда неконтролируемый указатель где-то перезаписывал данные в списке свободной памяти, и система вылетала не сразу, а выполнялись некоторые подпрограммы позже. Даже в системе средней сложности такие проблемы очень, очень сложно отладить! В одном случае, в котором я участвовал, нам (большей группе разработчиков) потребовалось несколько дней, чтобы найти причину сбоя, поскольку он находился в совершенно другом месте, чем указано в дампе памяти. Это похоже на бомбу замедленного действия. Вы знаете, что следующий "бесплатный" или "malloc" выйдет из строя, но вы не знаете почему!

Это одни из наихудших проблем C / C ++ и одна из причин, почему указатели могут быть такими проблематичными.

372
ответ дан 23 November 2019 в 02:34
поделиться

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

Что касается реализации malloc / free - целые книги посвящены теме. По сути, распределитель будет получать большие куски памяти из ОС и управлять ими за вас. Некоторые из проблем, которые должен решать распределитель:

  • Как получить новую память
  • Как ее сохранить - (список или другая структура, несколько списков для блоков памяти разного размера и т. Д.)
  • делать, если пользователь запрашивает больше памяти, чем доступно в настоящее время (запросить больше памяти у ОС, присоединить некоторые из существующих блоков, как точно присоединиться к ним, ... ...
3
ответ дан 23 November 2019 в 02:34
поделиться

Трудно сказать, потому что фактическое поведение различается между разными компиляторами / средами выполнения. Даже сборки отладки / выпуска имеют другое поведение. Отладочные сборки VS2005 будут вставлять маркеры между выделениями для обнаружения повреждения памяти, поэтому вместо сбоя он будет утвержден в free ().

2
ответ дан 23 November 2019 в 02:34
поделиться

Ну, это зависит от реализации распределителя памяти и ОС.

Под окнами, например, процесс может запросить страницу или больше ОЗУ. Затем ОС назначает эти страницы процессу. Однако это не память, выделенная вашему приложению. Распределитель памяти CRT пометит память как непрерывный «доступный» блок. Затем распределитель памяти CRT просматривает список свободных блоков и находит наименьший возможный блок, который он может использовать. Затем он возьмет столько блока, сколько потребуется, и добавит его в «выделенный» список. К заголовку фактического выделения памяти будет прикреплен заголовок. Этот заголовок будет содержать различную информацию (например, он может содержать следующие и предыдущие выделенные блоки для формирования связного списка. Скорее всего, он будет содержать размер выделения).

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

Это непростая проблема. Распределитель ОС полностью находится вне вашего контроля. Я рекомендую вам прочитать что-нибудь вроде Malloc Дуга Ли (DLMalloc), чтобы понять, как будет работать довольно быстрый распределитель.

Редактировать: Ваш сбой будет вызван тем, что, написав больше, чем выделенное, вы перезаписали следующий заголовок памяти. Таким образом, когда он освобождается, он очень сбивается с толку относительно того, что именно он освобождает и как объединить в следующий блок. Это не всегда может сразу вызвать сбой в бесплатном приложении. Позже это может вызвать сбой. В общем, избегайте перезаписи памяти!

4
ответ дан 23 November 2019 в 02:34
поделиться

Это не имеет ничего общего с malloc и free. Ваша программа демонстрирует неопределенное поведение после копирования строки - она ​​может аварийно завершить работу в этот момент или в любой момент после этого. Это было бы так, даже если вы никогда не использовали malloc и free и размещали массив char в стеке или статически.

6
ответ дан 23 November 2019 в 02:34
поделиться

malloc и free зависят от реализации. Типичная реализация включает разделение доступной памяти на «свободный список» - связанный список доступных блоков памяти. Многие реализации искусственно делят его на маленькие и большие объекты. Свободные блоки начинаются с информации о том, насколько велик блок памяти, где находится следующий и т. Д.

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

5
ответ дан 23 November 2019 в 02:34
поделиться

Строка strcpy пытается сохранить 9 байтов, а не 8 из-за терминатора NUL. Он вызывает неопределенное поведение.

Вызов free может дать сбой, а может и нет. Память «после» 4 байтов вашего выделения может быть использована для чего-то еще вашей реализацией C или C ++. Если он используется для чего-то еще, то каракули повсюду приведут к тому, что это «что-то еще» пойдет не так, но если оно не используется ни для чего другого, то это может сойти вам с рук. «Сойти с рук» может звучать хорошо, но на самом деле это плохо, поскольку это означает, что ваш код будет работать нормально, но в будущем вам может не сойти с рук.

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

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

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

12
ответ дан 23 November 2019 в 02:34
поделиться

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

Когда вы выделяете 4 байта, например, malloc дает вам указатель на 4 байта. Вы можете не осознавать, что память 8-12 байтов до ваших 4 байтов используется malloc для создания цепочки всей выделенной вами памяти. Когда вы вызываете free, он берет ваш указатель, выполняет резервное копирование туда, где находятся данные, и работает с ними.

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: То, что я описал, является обычной реализацией malloc, но никоим образом не единственно возможной.

23
ответ дан 23 November 2019 в 02:34
поделиться

Одна реализация malloc / free делает следующее:

  1. Получает блок памяти из ОС через sbrk () (вызов Unix).
  2. Создает верхний и нижний колонтитулы вокруг этот блок памяти с некоторой информацией, такой как размер, разрешения и где находятся следующий и предыдущий блоки.
  3. Когда поступает вызов malloc, делается ссылка на список, который указывает на блоки соответствующего размера.
  4. Это затем возвращается блок, а заголовки и нижние колонтитулы обновляются соответствующим образом.
34
ответ дан 23 November 2019 в 02:34
поделиться

Как говорит aluser в этой ветке форума :

Ваш процесс имеет область памяти, от адреса x до адреса y, называется куча. Все ваши malloc'd данные находятся в этой области. malloc () хранит некоторую структуру данных, скажем, список всех свободных фрагментов место в куче. Когда вы вызываете malloc, он просматривает список на предмет кусок, который достаточно велик для вас, возвращает указатель на него и фиксирует тот факт, что он больше не бесплатный, а также его размер. Когда вы вызываете free () с тем же указателем, free () проверяет, насколько велик этот кусок есть и добавляет его обратно в список свободных фрагментов (). если ты вызовите malloc (), и он не может найти достаточно большой кусок в куче, он использует системный вызов brk () для увеличения кучи, то есть увеличения адреса y и сделать все адреса между старым y и новым y действительными объем памяти. brk () должен быть системным вызовом; нет возможности сделать то же самое полностью из пользовательского пространства.

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

malloc () и free () не работают одинаково на всех операционных системах.

54
ответ дан 23 November 2019 в 02:34
поделиться

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

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

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

12
ответ дан 23 November 2019 в 02:34
поделиться