Минимальный исполняемый пример
Что делает системный вызов brk ()?
Просит ядро разрешить вам читать и записывать в непрерывный кусок памяти, называемый кучей.
Если вы не спросите, это может вас обидеть.
Без brk
:
#define _GNU_SOURCE
#include <unistd.h>
int main(void) {
/* Get the first address beyond the end of the heap. */
void *b = sbrk(0);
int *p = (int *)b;
/* May segfault because it is outside of the heap. */
*p = 1;
return 0;
}
С brk
:
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
int main(void) {
void *b = sbrk(0);
int *p = (int *)b;
/* Move it 2 ints forward */
brk(p + 2);
/* Use the ints. */
*p = 1;
*(p + 1) = 2;
assert(*p == 1);
assert(*(p + 1) == 2);
/* Deallocate back. */
brk(b);
return 0;
}
GitHub вверх по течению .
Вышеприведенное может не попасть на новую страницу и не использовать segfault даже без brk
, так что вот более агрессивная версия, которая выделяет 16MiB и очень вероятно, что segfault без brk
:
#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>
int main(void) {
void *b;
char *p, *end;
b = sbrk(0);
p = (char *)b;
end = p + 0x1000000;
brk(end);
while (p < end) {
*(p++) = 1;
}
brk(b);
return 0;
}
Проверено на Ubuntu 18.04.
Визуализация виртуального адресного пространства
До brk
:
+------+ <-- Heap Start == Heap End
После brk(p + 2)
:
+------+ <-- Heap Start + 2 * sizof(int) == Heap End
| |
| You can now write your ints
| in this memory area.
| |
+------+ <-- Heap Start
После brk(b)
:
+------+ <-- Heap Start == Heap End
Чтобы лучше понять адресные пространства, вы должны ознакомиться с подкачкой: как работает подкачка 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
12 common brk __x64_sys_brk
Такое brk
POSIX?
brk
Раньше это был POSIX, но он был удален в POSIX 2001, поэтому необходимо _GNU_SOURCE
было получить доступ к оболочке glibc.
Удаление, вероятно, связано с введением mmap
, которое является надмножеством, позволяющим распределять несколько диапазонов и иметь больше возможностей выделения.
Я думаю, что нет действительного случая, когда вы должны использовать brk
вместо malloc
или в mmap
настоящее время.
brk
против malloc
brk
это одна старая возможность реализации malloc
.
mmap
это новый, более мощный механизм, который в настоящее время используют для реализации все системы POSIX malloc
. Вот пример минимального выделения mmap
памяти для запуска .
Могу ли я смешать brk
и Malloc?
Если ваш malloc
реализован с помощью brk
, я понятия не имею, как это может не взорвать вещи, так как brk
управляет только один диапазон памяти.
Однако я не смог найти что-либо об этом в документации по glibc, например:
Вещи, скорее всего, просто будут работать там, я думаю, так mmap
как, вероятно, используется дляmalloc
.
Смотрите также:
Больше информации
Внутренне ядро решает, может ли процесс иметь столько памяти, и выделяет страницы памяти для этого использования.
Это объясняет, как стек сравнивается с кучей: Какова функция инструкций push / pop, используемых для регистров в сборке x86?