Приклад мінімальної експлуатації
Що робить системний дзвінок 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 вище за течією .
Вищезгадане може не потрапляти на нову сторінку і не сегментуватись навіть без brk
, тому тут є більш агресивна версія, яка виділяє 16MiB і, швидше за все, може бути сегментацією без 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
реалізована brk
, я не маю уявлення, як це може не підірвати речі, оскільки brk
керує лише одним діапазоном пам'яті.
Однак я нічого не можу знайти про це у документах glibc, наприклад:
Тут, ймовірно, просто працюватимуть речі, оскільки mmap
, ймовірно, вони використовуютьсяmalloc
.
Дивитися також:
Більше інформації
Внутрішньо ядро вирішує, чи може процес мати стільки пам'яті, і виділяє сторінки пам'яті для цього використання.
Це пояснює, як стек порівнюється з купою: Яка функція інструкцій push / pop, що використовуються в регістрах у складі x86?