Minimalny możliwy do uruchomienia przykład
Do czego służy wywołanie systemowe brk ()?
Prosi jądro, abyś mógł czytać i pisać w ciągłym fragmencie pamięci zwanym stertą.
Jeśli nie zapytasz, może cię to zepsuć.
Bez 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;
}
Z 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 w górę .
Powyższe może nie trafić na nową stronę i nie uszkodzić nawet bez brk
, więc tutaj jest bardziej agresywna wersja, która alokuje 16 MB i jest bardzo prawdopodobne, że ulegnie awarii bez 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;
}
Testowane na Ubuntu 18.04.
Wizualizacja wirtualnej przestrzeni adresowej
Przed brk
:
+------+ <-- Heap Start == Heap End
Po brk(p + 2)
:
+------+ <-- Heap Start + 2 * sizof(int) == Heap End
| |
| You can now write your ints
| in this memory area.
| |
+------+ <-- Heap Start
Po brk(b)
:
+------+ <-- Heap Start == Heap End
Aby lepiej zrozumieć przestrzenie adresowe, powinieneś zapoznać się ze stronicowaniem: Jak działa stronicowanie x86?.
Dlaczego potrzebujemy zarówno brk
i sbrk
?
brk
można oczywiście zaimplementować za pomocą sbrk
+ obliczeń przesunięcia, oba istnieją tylko dla wygody.
W backendu jądro Linux v5.0 ma pojedyncze wywołanie systemowe, brk
które służy do implementacji obu: https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/syscalls/syscall_64. tbl # L23
12 common brk __x64_sys_brk
Czy brk
POSIX?
brk
kiedyś był POSIX, ale został usunięty w POSIX 2001, dlatego potrzeba _GNU_SOURCE
dostępu do opakowania glibc.
Usunięcie jest prawdopodobnie spowodowane wprowadzeniem mmap
, które jest nadzbiorem, który umożliwia przydzielenie wielu zakresów i więcej opcji alokacji.
Myślę, że nie ma uzasadnionego przypadku, w którym powinieneś używać brk
zamiast malloc
lub mmap
obecnie.
brk
vs malloc
brk
to jedna stara możliwość wdrożenia malloc
.
mmap
jest nowszym, znacznie silniejszym mechanizmem, który prawdopodobnie wszystkie systemy POSIX obecnie używają do implementacji malloc
. Oto minimalne runnable mmap
przykład alokacji pamięci .
Czy mogę mieszać brk
i Malloc?
Jeśli twoja malloc
implementacja jest zaimplementowana brk
, nie mam pojęcia, jak to może nie wysadzić rzeczy w powietrze, ponieważ brk
zarządza tylko jednym zakresem pamięci.
Nie znalazłem jednak nic na ten temat w dokumentacji glibc, np .:
Pewnie coś tam po prostu zadziała, jak przypuszczam, ponieważ mmap
prawdopodobnie jest używane malloc
.
Zobacz też:
Więcej informacji
Jądro wewnętrznie decyduje, czy proces może mieć tyle pamięci, i wyznacza strony pamięci na takie użycie.
To wyjaśnia, w jaki sposób stos różni się od stosu: jaka jest funkcja instrukcji push / pop używanych w rejestrach w zestawie x86?