Minimal lauffähiges Beispiel
Was macht der Systemaufruf brk ()?
Bittet den Kernel, Sie lesen und in einen zusammenhängenden Speicherblock namens Heap schreiben zu lassen.
Wenn Sie nicht fragen, kann dies zu einem Segfault führen.
Ohne 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;
}
Mit 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 stromaufwärts .
Das Obige trifft möglicherweise nicht auf eine neue Seite und auch ohne das nicht auf Segfault. brk
Hier ist also eine aggressivere Version, die 16 MB zuweist und sehr wahrscheinlich ohne das brk
folgende Fehler auftritt :
#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;
}
Getestet unter Ubuntu 18.04.
Visualisierung des virtuellen Adressraums
Vorher brk
:
+------+ <-- Heap Start == Heap End
Nachher brk(p + 2)
:
+------+ <-- Heap Start + 2 * sizof(int) == Heap End
| |
| You can now write your ints
| in this memory area.
| |
+------+ <-- Heap Start
Nachher brk(b)
:
+------+ <-- Heap Start == Heap End
Um Adressräume besser zu verstehen, sollten Sie sich mit Paging vertraut machen: Wie funktioniert x86-Paging? .
Warum brauchen wir beide brk
und sbrk
?
brk
könnte natürlich mit sbrk
+ Offset-Berechnungen implementiert werden, beide existieren nur der Einfachheit halber.
Im Backend verfügt der Linux-Kernel v5.0 über einen einzigen Systemaufruf brk
, mit dem beide implementiert werden: https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/syscalls/syscall_64. tbl # L23
12 common brk __x64_sys_brk
Ist brk
POSIX?
brk
Früher war es POSIX, aber es wurde in POSIX 2001 entfernt, sodass _GNU_SOURCE
auf den Glibc-Wrapper zugegriffen werden muss.
Das Entfernen ist wahrscheinlich auf die Einführung zurückzuführen mmap
, bei der es sich um eine Obermenge handelt, mit der mehrere Bereiche und mehr Zuordnungsoptionen zugewiesen werden können.
Ich denke, es gibt keinen gültigen Fall, in dem Sie brk
anstelle von malloc
oder mmap
heutzutage verwenden sollten.
brk
vs. malloc
brk
ist eine alte Möglichkeit der Implementierung malloc
.
mmap
ist der neuere, streng leistungsfähigere Mechanismus, den wahrscheinlich alle POSIX-Systeme derzeit zur Implementierung verwenden malloc
. Hier ist ein Beispiel für eine minimale lauffähige mmap
Speicherzuweisung .
Kann ich mischen brk
und malloc?
Wenn Ihr malloc
mit implementiert ist brk
, habe ich keine Ahnung, wie das möglicherweise nicht explodieren kann, da brk
nur ein einziger Speicherbereich verwaltet wird.
Ich konnte jedoch nichts darüber in den glibc-Dokumenten finden, z.
Die Dinge werden wahrscheinlich nur dort funktionieren, da sie mmap
wahrscheinlich für verwendet werden malloc
.
Siehe auch:
Mehr Info
Intern entscheidet der Kernel, ob der Prozess so viel Speicher haben kann, und reserviert Speicherseiten für diese Verwendung.
Dies erklärt, wie der Stapel mit dem Heap verglichen wird: Welche Funktion haben die Push / Pop-Anweisungen, die für Register in der x86-Assembly verwendet werden?