Вопрос по free, c++, memory-management, c, malloc – Как работают malloc () и free ()?

256

Я хочу знать какmalloc а такжеfree Работа.

int main() {
    unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
    memset(p,0,4);
    strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
    cout << p;
    free(p); // Obvious Crash, but I need how it works and why crash.
    cout << p;
    return 0;
}

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

@ L & # x1B0; uV & # x129; nhPh & # xFA; c, который 's C ++. Обратите вниманиеcout << Braden Best
что strcpy записывает 9 байтов, а не 8. Не забывайте терминатор NULL ;-). Evan Teran
это будет зависеть от реализации CRT. Таким образом, вы не можете обобщить это. Naveen
Разве это не должно зависеть от компилятора и используемой библиотеки времени выполнения? Vilx-

Ваш Ответ

13   ответов
34

Get a block of memory from the OS through sbrk() (Unix call). Create a header and a footer around that block of memory with some information such as size, permissions, and where the next and previous block are. When a call to malloc comes in, a list is referenced which points to blocks of the appropriate size. This block is then returned and headers and footers are updated accordingly.
25
Memory protection has page-granularity and would require kernel interaction

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

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

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

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

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

Sometimes allocators maintain separate blocks of memory, sometimes they use a header they allocate just before or after (a "footer", I guess) your block, but they just might want to use memory within the block for the purpose of keeping the free list linked together. If so, your reading the block is OK, but its contents may change, and writing to the block would be likely to cause the allocator to misbehave or crash. Naturally, your block may be allocated in the future, and then it is likely to be overwritten by your code or a library routine, or with zeroes by calloc(). If the block is reallocated, it may also have its size changed, in which case yet more links or initialization will be written in various places. Obviously you may reference so far out of range that you cross a boundary of one of your program's kernel-known segments, and in this one case you will trap. Theory of Operation

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

1. The fact that very few implementations of free() attempt to return memory to the system is not necessarily due to the implementors slacking off. Interacting with the kernel is much slower than simply executing library code, and the benefit would be small. Most programs have a steady-state or increasing memory footprint, so the time spent analyzing the heap looking for returnable memory would be completely wasted. Other reasons include the fact that internal fragmentation makes page-aligned blocks unlikely to exist, and it's likely that returning a block would fragment blocks to either side. Finally, the few programs that do return large amounts of memory are likely to bypass malloc() and simply allocate and free pages anyway.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
4

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

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

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

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

23

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

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

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

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

1

что простое перемещение указателя разрыва программы с помощьюbrk а такжеsbrk на самом деле неallocate память, он просто устанавливает адресное пространство. Например, в Linux память будет «зарезервирована». фактическими физическими страницами при обращении к этому диапазону адресов, что приведет к ошибке страницы и в конечном итоге приведет к тому, что ядро вызовет распределитель страниц, чтобы получить резервную страницу.

3

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

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

How to get new memory How to store it - ( list or other structure, multiple lists for memory chunks of different size, and so on ) What to do if the user requests more memory than currently available ( request more memory from OS, join some of the existing blocks, how to join them exactly, ... ) What to do when the user frees memory Debug allocators may give you bigger chunk that you requested and fill it some byte pattern, when you free the memory the allocator can check if wrote outside of the block ( which is probably happening in your case) ...
2

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

51

эта ветка форума:

Your process has a region of memory, from address x to address y, called the heap. All your malloc'd data lives in this area. malloc() keeps some data structure, let's say a list, of all the free chunks of space in the heap. When you call malloc, it looks through the list for a chunk that's big enough for you, returns a pointer to it, and records the fact that it's not free any more as well as how big it is. When you call free() with the same pointer, free() looks up how big that chunk is and adds it back into the list of free chunks(). If you call malloc() and it can't find any large enough chunk in the heap, it uses the brk() syscall to grow the heap, i.e. increase address y and cause all the addresses between the old y and the new y to be valid memory. brk() must be a syscall; there is no way to do the same thing entirely from userspace.

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

malloc() and free() don't work the same way on every O/S.

Error: User Rate Limit Exceeded
12

а не 8, из-за ограничителя NUL. Это вызывает неопределенное поведение.

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

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

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

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

5

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

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

6

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

12

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

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

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

357

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

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

Поэтому возврат 40 байт в ОС просто не будет работать. Так что же делать бесплатно?

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

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

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

Why does your code crash:

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

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

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededgoo.gl/3O2Kyu

Похожие вопросы