Что лучше для выделения памяти: brk или mmap?
Необходимо реализовать динамическое распределение памяти, то бишь собственные malloc()
и realloc()
. Разобрался с логикой и теперь никак не могу решить, какой системный вызов лучше использовать: brk или mmap.
Brk
изменяет размер кучи. Крайне прост в использовании. Но вот для realloc()
придётся иногда ещё собственноручно копировать память.
Mmap
отображает файл (или, с дескриптором -1, можно просто блок данных) на память и возвращает указатель на эту самую память. Получаем удобства в виде системных вызовов munmap
(отвязать) и mremap
(изменить размер существующего; система также выполнит копирование при необходимости).
Резонный вопрос: кого использовать? Основными факторами выступают быстродействие и эффективность (выделить как можно больше из доступной памяти).
Думается мне, (предположение) реализация brk
проще, и он оттого шустрее, но ведь запрашивает непрерывную область памяти — загвоздка: ежели на пути встретится какая-нибудь память, используемая другими процессами, надо будет либо её сдвинуть, либо попросту отказать в предоставлении памяти. И тут кажется, что mmap
побеждает, так как выполняет именно то, что нужно: ищет куски нужного размера и даёт на них указатель. Пишут также, что brk
устарел.
Может, кто сталкивался. Есть кардинальное отличие? Предпочтительнее ли использовать одно взамен другого? Почему?
Ответы (1 шт):
Предупреждаю сразу: это всего лишь теоретические измышления. Сам я писал аллокатор только когда проходил курс по операционным системам, и тот аллокатор был наипростейшим - односвязный список занятых и свободных блоков с аллокацией первого подходящего. Для такого тривиального аллокатора sbrk
за глаза. Он хорош как учебное задание, и плох для реальной жизни: есть риск фрагментации памяти, и практически 100% гарантия, что процесс не будет возвращать память операционной системе.
В реальной жизни все используют mmap
, даже самые маленькие:
Разумеется, glibc использует mmap
Но ИМХО, выбор между mmap
и sbrk
для аллокатора сугубо вторичен. Можно и с mmap такого наворотить, что память быстро закончится.
В своей уже классической статье Дуг Ли (Doug Lea) выделяет несколько требований к аллокаторам:
Minimizing Space: The allocator should not waste space: It should obtain as little memory from the system as possible, and should maintain memory in ways that minimize fragmentation -- ``holes''in contiguous chunks of memory that are not used by the program.
Minimizing Time: The malloc(), free() and realloc routines should be as fast as possible in the average case.
Maximizing Tunability: Optional features and behavior should be controllable by users either statically (via #define and the like) or dynamically (via control commands such as mallopt).
Maximizing Locality: Allocating chunks of memory that are typically used together near each other. This helps minimize page and cache misses during program execution.
Maximizing Error Detection: It does not seem possible for a general-purpose allocator to also serve as general-purpose memory error testing tool such as Purify. However, allocators should provide some means for detecting corruption due to overwriting memory, multiple frees, and so on.
Minimizing Anomalies: An allocator configured using default settings should perform well across a wide range of real loads that depend heavily on dynamic allocation -- windowing toolkits, GUI applications, compilers, interpretors, development tools, network (packet)-intensive programs, graphics-intensive packages, web browsers, string-processing applications, and so on.
Первичен алгоритм аллокации блоков и структура данных для индексирования блоков. Поэтому я бы начинал с этого. И в первую очередь ознакомился с dlmalloc
, который до сих пор используют в libc
для встроенных систем.
Что же касается собственно вашего вопроса, то mmap
даёт всё, что можно получить от sbrk
, и ещё немного сверху. Чем хорош mmap:
- можно сделать несколько независимых пулов. Например, пул для маленьких объектов очень удобен для борьбы с фрагментацией, чтобы не смешивать мелкие объекты с большими. Плюс возможны независимые аллокации для разных потоков (threads) - как минимум, можно автоматически подобрать всю висящую память после завершения потока
- для больших объектов можно сразу аллоцировать память из операционной системы, и вернуть её обратно после освобождения объекта