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 brki 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, brkktó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 brkPOSIX?
brkkiedyś był POSIX, ale został usunięty w POSIX 2001, dlatego potrzeba _GNU_SOURCEdostę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ć brkzamiast malloclub mmapobecnie.
brk vs malloc
brkto jedna stara możliwość wdrożenia malloc.
mmapjest nowszym, znacznie silniejszym mechanizmem, który prawdopodobnie wszystkie systemy POSIX obecnie używają do implementacji malloc. Oto minimalne runnable mmapprzykład alokacji pamięci .
Czy mogę mieszać brki Malloc?
Jeśli twoja mallocimplementacja jest zaimplementowana brk, nie mam pojęcia, jak to może nie wysadzić rzeczy w powietrze, ponieważ brkzarzą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ż mmapprawdopodobnie 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?