Erstellt malloc träge die Backseiten für eine Zuordnung unter Linux (und anderen Plattformen)?

75

malloc(1024 * 1024 * 1024)Was macht malloc unter Linux eigentlich?

Ich bin sicher, dass der Zuweisung eine virtuelle Adresse zugewiesen wird (indem Sie die kostenlose Liste durchgehen und bei Bedarf eine neue Zuordnung erstellen), aber werden tatsächlich Swap-Seiten im Wert von 1 GiB erstellt? Oder ist es mprotectder Adressbereich und erstellt die Seiten, wenn Sie sie tatsächlich berühren, wie es mmaptut?

(Ich spezifiziere Linux, weil der Standard diese Art von Details nicht enthält, aber ich würde gerne wissen, was andere Plattformen auch tun.)

Aaron Maenpaa
quelle
2
Interessante Frage; Ich wäre neugierig auf das Verhalten auch auf anderen Plattformen, aber ein großes Lob dafür, dass ich diese Frage auf Linux beschränkt habe.
Paul Sonier
Es gab eine Zeit, in der dies wie viel Erinnerung schien ...
Schlitten

Antworten:

43

Linux verschiebt die Seitenzuweisung, auch bekannt als. 'Optimistische Speicherzuordnung'. Der Speicher, den Sie von malloc zurückerhalten, wird durch nichts gesichert. Wenn Sie ihn berühren, wird möglicherweise tatsächlich eine OOM-Bedingung angezeigt (wenn für die von Ihnen angeforderte Seite kein Swap Space vorhanden ist). In diesem Fall wird ein Prozess kurzerhand beendet .

Siehe zum Beispiel http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html

Remus Rusanu
quelle
5
Es ist interessant zu sehen, wie der Kernel die "Schlechtigkeit" eines Prozesses berechnet, um herauszufinden, welche Prozesse beendet werden müssen, wenn der Speicher knapp wird.
JesperE
IIRC hat Ebenen: Höchste bis niedrigste - Root-Prozesse, Prozesse, die E / A ausführen, Schlafprozesse ... niedrigste erhalten die Kugel.
Aiden Bell
@Aiden Die Funktion "Badness", mit der bestimmt wird, welcher Prozess beendet werden soll, wird im Link beschrieben.
Aaron Maenpaa
1
Das späte OOM-Verhalten ist nicht immer wahr; Dies hängt von der Overcommit-Einstellung ab. Die drei Modi finden Sie unter kernel.org/doc/Documentation/vm/overcommit-accounting .
ZachB
16

9. Speicher (Teil des Linux-Kernels , einige Anmerkungen zum Linux-Kernel von Andries Brouwer) ist ein gutes Dokument.

Es enthält die folgenden Programme, die den Umgang von Linux mit physischem Speicher im Vergleich zum tatsächlichen Speicher demonstrieren und die Interna des Kernels erläutern.

Normalerweise erhält das erste Demo-Programm sehr viel Speicher, bevor malloc () NULL zurückgibt. Das zweite Demo-Programm erhält viel weniger Speicher, da der zuvor erhaltene Speicher tatsächlich verwendet wird. Das dritte Programm erhält die gleiche große Menge wie das erste Programm und wird dann beendet, wenn es seinen Speicher nutzen möchte.

Demo-Programm 1: Speicher zuweisen, ohne ihn zu verwenden.

#include <stdio.h>
#include <stdlib.h>

int main (void) {
    int n = 0;

    while (1) {
        if (malloc(1<<20) == NULL) {
                printf("malloc failure after %d MiB\n", n);
                return 0;
        }
        printf ("got %d MiB\n", ++n);
    }
}

Demo-Programm 2: Speicher zuweisen und alles anfassen.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (void) {
    int n = 0;
    char *p;

    while (1) {
        if ((p = malloc(1<<20)) == NULL) {
                printf("malloc failure after %d MiB\n", n);
                return 0;
        }
        memset (p, 0, (1<<20));
        printf ("got %d MiB\n", ++n);
    }
}

Demo-Programm 3: zuerst zuordnen und später verwenden.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N       10000

int main (void) {
    int i, n = 0;
    char *pp[N];

    for (n = 0; n < N; n++) {
        pp[n] = malloc(1<<20);
        if (pp[n] == NULL)
            break;
    }
    printf("malloc failure after %d MiB\n", n);

    for (i = 0; i < n; i++) {
        memset (pp[i], 0, (1<<20));
        printf("%d\n", i+1);
    }

    return 0;
}

(Auf einem gut funktionierenden System wie Solaris erhalten die drei Demoprogramme dieselbe Speichermenge und stürzen nicht ab, siehe jedoch malloc (), das NULL zurückgibt.)

Aiden Bell
quelle
6
"gut funktionierend" ist Ansichtssache. Tatsächlich verfügt Linux in / proc / sys / vm über Optionen zur Steuerung des Overcommit-Verhaltens. Sie können es haben, wie Solaris es hat, wenn Sie möchten.
Zan Lynx
2
Seien Sie gewarnt, / proc / sys / vm ist die meiste Zeit GEBROCHEN !! groups.google.com/group/comp.os.linux.development.system/… Hier ist ein guter Tipp für die Leistung von LINUX und Festplatten. Wenn Sie jemals eine große Kopie erstellt haben und viel Cache aufgebraucht ist und Ihr E / A-System ins Stocken gerät ... Echo 1> / proc / sys / vm / drop_caches, ändern Sie Ihre Sicherung bis zu Hochdurchsatz :) go figure !!
RandomNickName42
11

Ich habe diese Antwort auf einen ähnlichen Beitrag zum gleichen Thema gegeben:

Sind einige Allokatoren faul?

Dies beginnt ein wenig abseits des Themas (und dann werde ich es mit Ihrer Frage verknüpfen), aber was passiert, ähnelt dem, was passiert, wenn Sie einen Prozess unter Linux abspalten. Beim Gabeln gibt es einen Mechanismus namens Kopieren beim Schreiben, der den Speicherplatz für den neuen Prozess nur kopiert, wenn auch der Speicher geschrieben wird. Auf diese Weise haben Sie den Aufwand für das Kopieren des ursprünglichen Programmspeichers gespart, wenn der gegabelte Prozess sofort ein neues Programm ausführt.

Zurück zu Ihrer Frage: Die Idee ist ähnlich. Wie andere bereits betont haben, erhalten Sie durch das Anfordern des Speichers sofort den virtuellen Speicherplatz, aber die tatsächlichen Seiten werden nur beim Schreiben zugewiesen.

Was ist der Zweck davon? Grundsätzlich wird das Mallocing von Speicher zu einer mehr oder weniger konstanten Zeitoperation Big O (1) anstelle einer Big O (n) -Operation (ähnlich wie der Linux-Scheduler seine Arbeit verteilt, anstatt sie in einem großen Block auszuführen).

Um zu demonstrieren, was ich meine, habe ich folgendes Experiment durchgeführt:

rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc

real    0m0.005s
user    0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef

real    0m0.558s
user    0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites

real    0m0.006s
user    0m0.000s
sys 0m0.008s

Das BigMalloc-Programm weist 20 Millionen Ints zu, macht aber nichts damit. deadbeef schreibt ein int auf jede Seite, was zu 19531-Schreibvorgängen führt, und justwrites weist 19531 ints zu und setzt sie auf Null. Wie Sie sehen, dauert die Ausführung von Deadbeef etwa 100-mal länger als bei Bigmalloc und etwa 50-mal länger als bei Justwrites.

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    return 0;
}

.

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    // Immediately write to each page to simulate an all-at-once allocation
    // assuming 4k page size on a 32-bit machine.

    for (int* end = big + 20000000; big < end; big += 1024)
        *big = 0xDEADBEEF;

    return 0;
}

.

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = calloc(sizeof(int), 19531); // Number of writes

    return 0;
}
Robert S. Barnes
quelle
6

Malloc reserviert Speicher aus Blöcken, die von libc verwaltet werden. Wenn zusätzlicher Speicher benötigt wird, geht die Bibliothek mithilfe des brk-Systemaufrufs zum Kernel.

Der Kernel weist dem aufrufenden Prozess Seiten des virtuellen Speichers zu. Die Seiten werden als Teil der Ressourcen verwaltet, die dem Prozess gehören. Physische Seiten werden nicht zugewiesen, wenn Speicherplatz belegt ist. Wenn der Prozess auf einen Speicherort auf einer der Seiten zugreift, tritt ein Seitenfehler auf. Der Kernel überprüft, ob der virtuelle Speicher zugewiesen wurde, und ordnet der virtuellen Seite eine physische Seite zu.

Die Seitenzuweisung ist nicht auf Schreibvorgänge beschränkt und unterscheidet sich deutlich von der Kopie beim Schreiben. Jeder Zugriff, Lesen oder Schreiben, führt zu einem Seitenfehler und einer Zuordnung einer physischen Seite.

Beachten Sie, dass der Stapelspeicher automatisch zugeordnet wird. Das heißt, ein explizites brk ist nicht erforderlich, um Seiten dem virtuellen Speicher zuzuordnen, der vom Stapel verwendet wird.


quelle
Beachten Sie, dass glibc große Zuordnungen erfüllt, indem anonyme Seiten mmap zugeordnet werden, anstatt brk zu verwenden. Siehe gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html .
ZachB
5

Unter Windows werden die Seiten festgeschrieben (dh der verfügbare freie Speicher geht aus), sie werden jedoch erst zugewiesen, wenn Sie die Seiten berühren (entweder lesen oder schreiben).

Ana Betts
quelle
2

Auf den meisten Unix-ähnlichen Systemen wird die brk- Grenze verwaltet. Die VM fügt Seiten hinzu, wenn sie vom Prozessor getroffen wird. Zumindest Linux und BSDs tun dies.

Javier
quelle