2015-12-13 4 views
4

Я попытался в своей машине с помощью sbrk (1), а затем умышленно выписал из строя размер тестовой страницы, который составляет 4096 байт. Но когда я вызываю malloc (1), я получаю SEGV после доступа к 135152 байтам, что является более чем одним размером страницы. Я знаю, что malloc - это функция библиотеки, и она зависит от реализации, но, учитывая, что она вызывает sbrk в конце концов, почему она дает более одного размера страницы. Может ли кто-нибудь рассказать мне о своей внутренней работе?Почему malloc (1) дает более одного размера страницы?

Моя операционная система Ubuntu 14.04 и моя архитектура x86

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

+0

В какой операционной системе и архитектуре вы используете точно? Также вы уверены, что malloc() вызывает sbrk()? – Crashworks

+0

Я не уверен, но мой профессор и некоторые онлайн-источники говорят мне об этом. Я использую архитектуру x86 под ubuntu 14.04 –

+2

ITYM 4096, а не 4086. Что такое «(1)»? 'sbrk' - системный вызов (2), а' malloc' - это библиотечная функция (3). –

ответ

6

Старые malloc() Используемые системы UNIX sbrk()/brk() системные вызовы. Но в наши дни в реализациях используются mmap() и sbrk(). Реализация glibc malloc() (это, вероятно, тот, который вы используете на вашем Ubuntu 14.04) использует как sbrk(), так и mmap(), и выбор того, какой из них выделять при запросе, обычно зависит от размера запроса на распределение, который glibc выполняет динамически.

Для небольших распределений glibc использует sbrk(), а для больших распределений используется mmap(). Для решения этого вопроса используется макрос M_MMAP_THRESHOLD. В настоящее время значением по умолчанию является set to 128K. Это объясняет, почему вашему коду удалось выделить 135152 байта, поскольку это примерно ~ 128K. Несмотря на то, что вы запросили только 1 байт, ваша реализация выделяет 128 КБ для эффективного распределения памяти. Таким образом, segfault не произошел, пока вы не преодолеете этот предел.

Вы можете играть с M_MAP_THRESHOLD с помощью mallopt(), изменив параметры по умолчанию.

M_MMAP_THRESHOLD

Для распределения больше или равен пределу указанных (в байтах) по M_MMAP_THRESHOLD, которые не могут быть удовлетворены из свободного списка, функция памяти распределения использует ММАП (2) вместо этого увеличения разрыва программы с помощью sbrk (2).

Выделение памяти с помощью mmap (2) имеет существенное преимущество в том, что выделенные блоки памяти всегда могут независимо отбрасываться обратно в систему. (В отличие от этого куча может быть обрезана только в том случае, если память освобождена на верхнем конце.) С другой стороны, есть некоторые недостатки в использовании mmap (2): освобожденное пространство не размещено в свободном списке для повторного использования путем более поздних распределений; память может быть потрачена впустую , потому что mmap (2) распределения должны быть выровнены по страницам; и ядро ​​должно выполнить дорогостоящую задачу обнуления памяти, выделенной через mmap (2). Балансировка этих факторов приводит к установке по умолчанию 128 * 1024 для параметра M_MMAP_THRESHOLD.

Нижний предел для этого параметра равно 0. Верхний предел DEFAULT_MMAP_THRESHOLD_MAX: 512 * 1024 на 32-разрядных системах или 4 * 1024 * 1024 * SizeOf (длинный) на 64-битных системах.

Примечание. В настоящее время glibc использует динамический порог mmap по умолчанию. Начальное значение порога составляет 128 * 1024, но когда блоки , превышающие текущий порог и меньше или равные , DEFAULT_MMAP_THRESHOLD_MAX освобождаются, пороговое значение корректируется вверх до размера освобожденного блока. Когда действует динамическое mmap , пороговое значение для обрезки кучи также равно , динамически скорректированное с удвоенным динамическим порогом mmap. Dynamic настройка порога mmap отключена, если задан какой-либо из параметров M_TRIM_THRESHOLD, M_TOP_PAD, M_MMAP_THRESHOLD или M_MMAP_MAX .

Например, если вы:

#include<malloc.h> 

mallopt(M_MMAP_THRESHOLD, 0); 

перед вызовом malloc(), вы, вероятно, увидите другой предел. Большинство из них - детали реализации, а в стандарте C указано, что undefined behaviour записывает в память, что ваш процесс не принадлежит. Так что сделайте это на свой риск - в противном случае, demons may fly out of your nose ;-)

+0

Спасибо, ваш ответ помогает –

3

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

this article От:

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

Обратите внимание, что некоторые современные реализации malloc использования mmap вместо brk/sbrk выделить память, но в остальном выше по-прежнему верно.

 Смежные вопросы

  • Нет связанных вопросов^_^