Согласно руководству для программистов Linux:
brk () и sbrk () изменяют место остановки программы, которое определяет конец сегмента данных процесса.
Что означает сегмент данных здесь? Это просто сегмент данных или данные, BSS и куча вместе взятые?
Согласно вики:
Иногда данные, BSS и области кучи вместе называются «сегментом данных».
Я не вижу причин для изменения размера только сегмента данных. Если это данные, BSS и куча вместе, то это имеет смысл, поскольку куча получит больше места.
Что подводит меня ко второму вопросу. Во всех статьях, которые я до сих пор читал, автор говорит, что куча растет вверх, а стек - вниз. Но они не объясняют, что происходит, когда куча занимает все пространство между кучей и стеком?
brk()
системный вызов более полезен на ассемблере, чем на языке C. В C егоmalloc()
следует использовать вместоbrk()
каких-либо целей выделения данных, но это никоим образом не лишает законной силы предложенный вопрос.brk()
иsbrk()
? Стеки управляются распределителем страниц на гораздо более низком уровне.Ответы:
На диаграмме, которую вы разместили, «разрыв» - адрес, которым манипулирует
brk
иsbrk
- это пунктирная линия в верхней части кучи.В прочитанной вами документации это описывается как конец «сегмента данных», потому что в традиционном (pre-shared-library, pre-
mmap
) Unix сегмент данных был непрерывным с кучей; перед запуском программы ядро загружало бы блоки «текст» и «данные» в ОЗУ, начиная с нулевого адреса (фактически немного выше нулевого адреса, чтобы указатель NULL действительно ни на что не указывал), и устанавливал адрес прерывания равным конец сегмента данных. Первый вызовmalloc
затем будет использоватьсяsbrk
для перемещения разбиения и создания кучи между вершиной сегмента данных и новым, более высоким адресом разбиения, как показано на схеме, а последующее использованиеmalloc
будет использовать его для увеличения кучи. как надо.Тем временем, стек начинается с верхней части памяти и уменьшается. Стек не нуждается в явных системных вызовах, чтобы увеличить его; либо он запускается с выделением для него столько оперативной памяти, сколько он может иметь (это был традиционный подход), либо область зарезервированных адресов под стеком, для которой ядро автоматически выделяет ОЗУ, когда замечает попытку записи туда (это современный подход). В любом случае, в нижней части адресного пространства может быть или не быть «защитная» область, которую можно использовать для стека. Если этот регион существует (все современные системы делают это), он постоянно не отображается; если либостек или куча пытаются врасти в него, вы получаете ошибку сегментации. Традиционно, однако, ядро не предпринимало попыток установить границы; стек может вырасти в кучу, или куча может вырасти в стек, и в любом случае они будут перебирать данные друг друга, и программа будет аварийно завершать работу. Если вам очень повезет, он сразу же потерпит крах.
Я не уверен, откуда исходит число 512 ГБ в этой диаграмме. Это подразумевает 64-битное виртуальное адресное пространство, которое несовместимо с очень простой картой памяти, которая у вас есть. Настоящее 64-битное адресное пространство выглядит примерно так:
Это не для удаленного масштабирования, и его не следует интерпретировать как то, что делает любая конкретная ОС (после того, как я нарисовал ее, я обнаружил, что Linux фактически помещает исполняемый файл гораздо ближе к нулевому адресу, чем я думал, и разделяемые библиотеки). по удивительно высоким адресам). Черные области на этой диаграмме не отображены - любой доступ вызывает немедленный сбой - и они гигантские по отношению к серым областям. Светло-серые области - это программа и ее общие библиотеки (могут быть десятки общих библиотек); у каждого есть независимыйсегмент текста и данных (и сегмент «bss», который также содержит глобальные данные, но инициализируется нулевыми битами, а не занимает место в исполняемом файле или библиотеке на диске). Куча больше не обязательно непрерывна с сегментом данных исполняемого файла - я нарисовал его таким образом, но, похоже, Linux, по крайней мере, этого не делает. Стек больше не привязан к вершине виртуального адресного пространства, а расстояние между кучей и стеком настолько велико, что вам не нужно беспокоиться о его пересечении.
Разрыв по-прежнему является верхним пределом кучи. Однако то, что я не показал, это то, что где-то в черном может быть множество независимых распределений памяти, сделанных
mmap
вместоbrk
. (ОС будет стараться держать их подальше отbrk
области, чтобы они не сталкивались.)источник
malloc
все еще полагаетсяbrk
или использует его,mmap
чтобы иметь возможность «отдавать» отдельные блоки памяти?malloc
используютbrk
область для небольших распределений и отдельныеmmap
для больших (скажем,> 128 КБ). Смотрите, например, обсуждение MMAP_THRESHOLD на странице руководства Linuxmalloc(3)
.mmap
; это очень зависит от ОС.Минимальный исполняемый пример
Просит ядро разрешить вам читать и записывать в непрерывный кусок памяти, называемый кучей.
Если вы не спросите, это может вас обидеть.
Без
brk
:С
brk
:GitHub вверх по течению .
Вышеприведенное может не попасть на новую страницу и не использовать segfault даже без
brk
, так что вот более агрессивная версия, которая выделяет 16MiB и очень вероятно, что segfault безbrk
:Проверено на Ubuntu 18.04.
Визуализация виртуального адресного пространства
До
brk
:После
brk(p + 2)
:После
brk(b)
:Чтобы лучше понять адресные пространства, вы должны ознакомиться с подкачкой: как работает подкачка x86? ,
Зачем нам и то
brk
и другоеsbrk
?brk
конечно, может быть реализовано с помощьюsbrk
расчетов + смещения, оба существуют просто для удобства.В бэкэнде ядро Linux v5.0 имеет единственный системный вызов,
brk
который используется для реализации обоих: https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/syscalls/syscall_64. TBL # L23Такое
brk
POSIX?brk
Раньше это был POSIX, но он был удален в POSIX 2001, поэтому необходимо_GNU_SOURCE
было получить доступ к оболочке glibc.Удаление, вероятно, связано с введением
mmap
, которое является надмножеством, позволяющим распределять несколько диапазонов и иметь больше возможностей выделения.Я думаю, что нет действительного случая, когда вы должны использовать
brk
вместоmalloc
или вmmap
настоящее время.brk
противmalloc
brk
это одна старая возможность реализацииmalloc
.mmap
это новый, более мощный механизм, который в настоящее время используют для реализации все системы POSIXmalloc
. Вот пример минимального выделенияmmap
памяти для запуска .Могу ли я смешать
brk
и Malloc?Если ваш
malloc
реализован с помощьюbrk
, я понятия не имею, как это может не взорвать вещи, так какbrk
управляет только один диапазон памяти.Однако я не смог найти что-либо об этом в документации по glibc, например:
Вещи, скорее всего, просто будут работать там, я думаю, так
mmap
как, вероятно, используется дляmalloc
.Смотрите также:
Больше информации
Внутренне ядро решает, может ли процесс иметь столько памяти, и выделяет страницы памяти для этого использования.
Это объясняет, как стек сравнивается с кучей: Какова функция инструкций push / pop, используемых для регистров в сборке x86?
источник
p
как указатель на типint
, не должно ли это бытьbrk(p + 2);
?*(p + i) = 1;
brk(p + 2)
вместо того, чтобы просто увеличить его наsbrk(2)
? BRK действительно необходим?brk
syscall).brk
Немного удобнее восстановить ранее выделенный стек.Вы можете использовать
brk
иsbrk
себя, чтобы избежать "накладных расходов на malloc", на которые все всегда жалуются. Но вы не можете легко использовать этот метод в сочетании с,malloc
так что он подходит только тогда, когда вам ничего не нужноfree
. Потому что ты не можешь. Кроме того, вы должны избегать любых библиотечных вызовов, которые могут использоватьсяmalloc
внутри. То есть.strlen
вероятно безопасно, ноfopen
вероятно нет.Звоните так
sbrk
же, как вы звонитеmalloc
. Он возвращает указатель на текущий разрыв и увеличивает его на эту величину.Несмотря на то, что вы не можете освободить отдельные выделения (потому что нет служебных данных malloc , помните), вы можете освободить все пространство , вызвав
brk
значение, возвращаемое первым вызовомsbrk
, таким образом, перематывая brk .Вы могли бы даже сложить эти регионы, отбросив самый последний регион, перемотав разрыв в начало региона.
Еще кое-что ...
sbrk
также полезно в коде гольф, потому что это на 2 символа короче, чемmalloc
.источник
malloc
/free
наверняка может (и делает) вернуть память операционной системе. Возможно, они не всегда делают это, когда вы этого хотите, но дело в том, что эвристика была неправильно настроена для вашего варианта использования. Что еще более важно, небезопасно вызыватьsbrk
с ненулевым аргументом в любой программе, которая может когда-либо вызыватьсяmalloc
- и почти все функции библиотеки C могут вызыватьсяmalloc
внутри. Единственные, которые определенно не будут - это функции, защищенные от асинхронных сигналов .malloc
.sbrk
для этого полезно только для code-golf, потому что ручное использованиеmmap(MAP_ANONYMOUS)
лучше во всех отношениях, кроме размера исходного кода.Существует специальное обозначенное анонимное сопоставление частной памяти (традиционно расположенное сразу за data / bss, но современный Linux фактически отрегулирует местоположение с помощью ASLR). В принципе, это не лучше, чем любое другое сопоставление, которое вы могли бы создать
mmap
, но в Linux есть некоторые оптимизации, которые позволяют расширить конец этого сопоставления (с помощьюbrk
системного вызова) вверх с уменьшенной стоимостью блокировки относительно того, чтоmmap
илиmremap
может возникнуть. Это делает его привлекательным дляmalloc
реализации при реализации основной кучи.источник
Я могу ответить на ваш второй вопрос. Malloc потерпит неудачу и возвратит нулевой указатель. Вот почему вы всегда проверяете нулевой указатель при динамическом выделении памяти.
источник
malloc()
будет использоватьbrk()
и / илиsbrk()
под капотом - и вы тоже можете, если вы хотите реализовать свою собственную настроенную версиюmalloc()
.Куча помещается последней в сегменте данных программы.
brk()
используется для изменения (расширения) размера кучи. Когда куча больше не может расти, любойmalloc
вызов потерпит неудачу.источник
Сегмент данных - это часть памяти, которая содержит все ваши статические данные, считанные из исполняемого файла при запуске и обычно заполненные нулями.
источник
.bss
) инициализируются операционной системой до нуля со всеми битами до запуска программы; это фактически гарантировано стандартом C. Некоторые встроенные системы могут не беспокоить, я полагаю (я никогда не видел ни одной, но я не работаю со всемиmmap
, но я бы предположил, что.bss
все равно будет обнулено. Пространство BSS, вероятно, является наиболее компактным способом выразить тот факт, что программе нужны массивы нулей..bss
нулевое значение.bss
и поэтому не будет соответствовать. Но ничто не заставляет реализацию C использовать.bss
вообще или даже иметь такую вещь.main
; этот код может обнулять.bss
область, а не заставлять ядро делать это, и это все равно будет соответствовать.malloc использует системный вызов brk для выделения памяти.
включают
запустите эту простую программу с помощью strace, она вызовет систему brk.
источник