Ich habe mehrere Posts im Internet gesehen, in denen sich Leute anscheinend über einen gehosteten VPS beschwert haben, der Prozesse unerwartet beendet hat, weil sie zu viel RAM verwendet haben.
Wie ist das möglich? Ich dachte, alle modernen Betriebssysteme bieten "unendlichen RAM", indem sie nur den Festplattentausch für alles verwenden, was über den physischen RAM geht. Ist das richtig?
Was kann passieren, wenn ein Prozess "wegen zu wenig RAM abgebrochen" wird?
Antworten:
Es wird manchmal gesagt, dass Linux standardmäßig niemals Anfragen nach mehr Speicher aus dem Anwendungscode ablehnt - z
malloc()
. 1 Dies ist in der Tat nicht wahr; Standardmäßig wird eine Heuristik verwendetVon
[linux_src]/Documentation/vm/overcommit-accounting
(alle Anführungszeichen stammen aus dem 3.11-Baum). Was genau als "ernsthafte wilde Zuordnung" gilt, wird nicht explizit angegeben, daher müssten wir die Quelle durchgehen, um die Details zu bestimmen. Wir könnten auch die experimentelle Methode in Fußnote 2 (unten) verwenden, um zu versuchen, eine Reflexion der Heuristik zu erhalten. Meine erste empirische Beobachtung ist, dass unter idealen Umständen (== das System ist im Leerlauf), wenn Sie nicht " Wenn Sie keinen Swap haben, können Sie ungefähr die Hälfte Ihres Arbeitsspeichers zuweisen. Wenn Sie Swap haben, erhalten Sie ungefähr die Hälfte Ihres Arbeitsspeichers plus den gesamten Swap. Das ist mehr oder weniger pro Prozess (Beachten Sie jedoch , diese Grenze ist dynamisch und Änderungen vorbehalten wegen der staatlichen finden einige Beobachtungen in Fußnote 5).Die Hälfte Ihres Arbeitsspeichers plus Swap ist explizit die Standardeinstellung für das Feld "CommitLimit" in
/proc/meminfo
. Das bedeutet Folgendes - und beachten Sie, dass dies eigentlich nichts mit dem soeben diskutierten Grenzwert (von[src]/Documentation/filesystems/proc.txt
) zu tun hat :Das zuvor zitierte Dokument zur Überlastungsabrechnung gibt an, dass der Standardwert
vm.overcommit_ratio
50 ist. Wenn Sie dies alsosysctl vm.overcommit_memory=2
tun, können Sie vm.covercommit_ratio (mitsysctl
) anpassen und die Konsequenzen anzeigen . 3 Der Standardmodus ist, wenn erCommitLimit
nicht erzwungen wird und nur "offensichtliche Überschreibungen des Adressraums abgelehnt werden", wennvm.overcommit_memory=0
.Während die Standardstrategie ein heuristisches Pro-Prozess-Limit hat, das die "ernsthafte wilde Allokation" verhindert, kann das System als Ganzes ernsthaft wild und allokationsweise werden. 4 Dies bedeutet, dass irgendwann nicht mehr genügend Arbeitsspeicher zur Verfügung steht und über den OOM-Killer Konkurs für einen oder mehrere Prozesse angemeldet werden muss .
Was tötet der OOM-Killer? Nicht unbedingt der Prozess, der nach Speicher gefragt hat, als es keinen gab, da dies nicht unbedingt der wirklich schuldige Prozess ist, und was noch wichtiger ist, nicht unbedingt der Prozess, der das System am schnellsten von dem Problem befreit, in dem es sich befindet.
Dies wird hier zitiert , wo wahrscheinlich eine 2.6.x-Quelle zitiert wird:
Das scheint eine anständige Begründung zu sein. Ohne forensisch zu werden, scheint jedoch # 5 (die redundant zu # 1 ist) eine schwierige Verkaufsimplementierung zu sein, und # 3 ist redundant zu # 2. Es könnte also sinnvoll sein, dies auf # 2/3 und # 4 zu reduzieren.
Ich habe mich in einer kürzlich erschienenen Quelle (3.11) umgesehen und festgestellt, dass sich dieser Kommentar in der Zwischenzeit geändert hat:
Dies ist ein wenig mehr explizit über # 2: „Das Ziel ist es , [töten] die Aufgabe , die meisten Speicherraubend nachfolgende oom Ausfälle zu vermeiden“ , und durch Implikation # 4 ( „wir wollen die minimale Menge an Prozesse töten ( eine ) ) .
Wenn Sie den OOM-Killer in Aktion sehen möchten, lesen Sie Fußnote 5.
1 Eine Täuschung, die Gilles mich dankbar loswurde, siehe Kommentare.
2 Hier ist ein einfaches Bit von C, das nach immer größeren Speicherblöcken fragt, um festzustellen, wann eine Anforderung nach mehr fehlschlägt:
Wenn Sie C nicht kennen, können Sie es kompilieren
gcc virtlimitcheck.c -o virtlimitcheck
und dann ausführen./virtlimitcheck
. Es ist völlig harmlos, da der Prozess keinen der benötigten Speicherplätze belegt - dh, er verwendet nie wirklich RAM.Auf einem 3,11 x 86_64-System mit 4 GB System und 6 GB Swap schlug ich bei ~ 7400000 kB fehl. Die Anzahl schwankt, also ist der Zustand vielleicht ein Faktor. Dies ist zufällig nahe der
CommitLimit
in/proc/meminfo
, aber diese durch Modifikationvm.overcommit_ratio
keinen Unterschied machen. Auf einem 3.6.11 32-Bit ARM 448 MB System mit 64 MB Swap scheitere ich jedoch bei ~ 230 MB. Dies ist interessant, da im ersten Fall die Menge fast doppelt so groß ist wie der RAM-Speicher, während im zweiten Fall etwa 1/4 der Swap-Größe eine Rolle spielt. Dies wurde durch Ausschalten von Swap auf dem ersten System bestätigt, als die Ausfallschwelle auf ~ 1,95 GB sank, ein sehr ähnliches Verhältnis wie bei der kleinen ARM-Box.Aber ist das wirklich pro Prozess? Es scheint zu sein. Das kurze Programm unten fragt nach einem benutzerdefinierten Speicherblock und wartet bei Erfolg darauf, dass Sie die Eingabetaste drücken. Auf diese Weise können Sie mehrere Instanzen gleichzeitig ausführen:
Beachten Sie jedoch, dass es unabhängig von der Verwendung nicht ausschließlich um die Größe des Arbeitsspeichers und des Auslagerungsspeichers geht. In Fußnote 5 finden Sie Hinweise zu den Auswirkungen des Systemzustands.
3
CommitLimit
bezieht sich auf die Menge an Adressraum, die für das System zulässig ist , wenn vm.overcommit_memory = 2. Vermutlich sollte die Menge, die Sie zuweisen können, minus der bereits festgeschriebenen Menge sein, was anscheinend dasCommitted_AS
Feld ist.Ein potenziell interessantes Experiment, das dies demonstriert, ist das Hinzufügen
#include <unistd.h>
von oben in virtlimitcheck.c (siehe Fußnote 2) undfork()
rechts vor derwhile()
Schleife. Es ist nicht garantiert, dass dies ohne langwierige Synchronisierung wie hier beschrieben funktioniert, aber es besteht eine gute Chance, dass dies der Fall ist, YMMV:Dies ist sinnvoll. Wenn Sie sich die Datei tmp.txt im Detail ansehen, können Sie sehen, dass die Prozesse ihre immer größeren Zuordnungen abwechseln (dies ist einfacher, wenn Sie die PID in die Ausgabe werfen), bis einer offensichtlich genug behauptet hat, dass der andere fehlschlägt. Dem Gewinner steht es dann frei, alles bis zum
CommitLimit
Minus zu greifenCommitted_AS
.4 An dieser Stelle ist zu erwähnen, dass, wenn Sie die virtuelle Adressierung und das Anfordern von Paging noch nicht verstehen, die Möglichkeit einer Überbindung in erster Linie darin besteht, dass der Kernel Userland-Prozessen überhaupt keinen physischen Speicher zuweist, sondern diesen virtueller Adressraum . Wenn ein Prozess beispielsweise 10 MB für etwas reserviert, ist dies eine Folge von (virtuellen) Adressen, aber diese Adressen entsprechen noch nicht dem physischen Speicher. Wenn auf eine solche Adresse zugegriffen wird, führt dies zu einem Seitenfehlerund dann versucht der Kernel, es auf den realen Speicher abzubilden, so dass es einen realen Wert speichern kann. Prozesse reservieren in der Regel viel mehr virtuellen Speicherplatz als sie tatsächlich in Anspruch nehmen, wodurch der Kernel den Arbeitsspeicher am effizientesten nutzen kann. Der physische Speicher ist jedoch immer noch eine begrenzte Ressource, und wenn er vollständig dem virtuellen Adressraum zugeordnet wurde, muss ein Teil des virtuellen Adressraums entfernt werden, um RAM freizugeben.
5 Zuerst eine Warnung : Wenn Sie dies mit versuchen
vm.overcommit_memory=0
, stellen Sie sicher, dass Sie Ihre Arbeit zuerst speichern und alle kritischen Anwendungen schließen, da das System für ~ 90 Sekunden eingefroren wird und ein Prozess abstürzt!Die Idee ist, eine Gabelbombe zu starten , deren Zeit nach 90 Sekunden abläuft, wobei die Gabeln Speicherplatz zuweisen und einige von ihnen große Datenmengen in den RAM schreiben, während sie an stderr berichten.
Kompilieren Sie dies
gcc forkbomb.c -o forkbomb
. Versuchen Sie es zuerst mitsysctl vm.overcommit_memory=2
- Sie werden wahrscheinlich etwas bekommen wie:In dieser Umgebung kommt diese Art von Gabelbombe nicht sehr weit. Beachten Sie, dass die Anzahl in "sagt N Gabeln" nicht die Gesamtzahl der Prozesse ist, sondern die Anzahl der Prozesse in der Kette / dem Zweig, die zu diesem Prozess führen.
Probieren Sie es jetzt mit
vm.overcommit_memory=0
. Wenn Sie stderr in eine Datei umleiten, können Sie anschließend eine grobe Analyse durchführen, z.Nur 15 verarbeitet fehlgeschlagen 1 GB zuzuteilen - zeigt , daß die Heuristik für overcommit_memory = 0 wird von Zustand betroffen. Wie viele Prozesse gab es? Mit Blick auf das Ende von tmp.txt, wahrscheinlich> 100.000. Wie darf man nun eigentlich die 1 GB nutzen?
Acht - was wieder Sinn macht, da ich zu der Zeit ~ 3 GB RAM frei und 6 GB Swap hatte.
Sehen Sie sich danach Ihre Systemprotokolle an. Sie sollten sehen, dass der OOM-Killer (unter anderem) Punkte meldet. vermutlich bezieht sich dies auf
oom_badness
.quelle
Dies passiert Ihnen nicht, wenn Sie nur 1 GB Daten in den Speicher laden. Was ist, wenn Sie viel, viel mehr laden? Zum Beispiel arbeite ich oft mit riesigen Dateien, die Millionen von Wahrscheinlichkeiten enthalten, die in R geladen werden müssen. Dies benötigt ungefähr 16 GB RAM.
Das Ausführen des obigen Vorgangs auf meinem Laptop führt dazu, dass es wie verrückt wechselt, sobald meine 8 GB RAM voll sind. Dies wiederum verlangsamt alles, da das Lesen von der Festplatte viel langsamer ist als das Lesen aus dem RAM. Was ist, wenn ich einen Laptop mit 2 GB RAM und nur 10 GB freiem Speicherplatz habe? Sobald der Prozess den gesamten Arbeitsspeicher belegt hat, füllt er auch den Datenträger, da es sich um einen Schreibvorgang zum Auslagern handelt und mir kein Arbeitsspeicher und kein Speicherplatz mehr zum Auslagern verbleibt (Benutzer beschränken den Auslagerungsvorgang in der Regel eher auf eine dedizierte Partition als auf eine Swap-Datei aus genau diesem Grund). Hier kommt der OOM-Killer ins Spiel und beginnt Prozesse zu töten.
Dem System kann also tatsächlich der Speicher ausgehen. Darüber hinaus können stark vertauschte Systeme unbrauchbar werden, lange bevor dies einfach aufgrund der langsamen E / A-Operationen aufgrund des Austauschs geschieht. Man möchte generell vermeiden, so viel wie möglich zu tauschen. Selbst auf High-End-Servern mit schnellen SSDs ist ein deutlicher Leistungsabfall zu verzeichnen. Auf meinem Laptop, der über ein klassisches Laufwerk mit 7200 U / min verfügt, wird das System durch signifikantes Austauschen im Wesentlichen unbrauchbar. Je mehr es tauscht, desto langsamer wird es. Wenn ich den Prozess nicht schnell abbringe, bleibt alles hängen, bis der OOM-Killer einspringt.
quelle
Prozesse werden nicht abgebrochen, wenn kein RAM mehr vorhanden ist. Sie werden abgebrochen, wenn sie auf folgende Weise betrogen wurden:
Dies kann auch passieren, wenn das System nicht aktiv austauscht, z. B. wenn der Auslagerungsbereich mit Speicherseiten für Sleeping Daemons gefüllt ist.
Dies geschieht niemals bei Betriebssystemen, bei denen der Speicher nicht überlastet wird. Bei ihnen wird kein zufälliger Prozess beendet, aber der erste Prozess, der nach virtuellem Speicher fragt, während er erschöpft ist, führt zu einer fehlerhaften Rückkehr von malloc (oder ähnlichem). Es wird somit eine Chance gegeben, die Situation richtig zu handhaben. Unter diesen Betriebssystemen kann es jedoch auch vorkommen, dass dem System der virtuelle Speicher ausgeht, solange noch freier Arbeitsspeicher vorhanden ist. Dies ist ziemlich verwirrend und wird im Allgemeinen missverstanden.
quelle
Wenn der verfügbare RAM erschöpft ist, beginnt der Kernel, Verarbeitungsbits auf die Festplatte auszutauschen. Tatsächlich beginnt der Kernel mit dem Auslagern eines Arbeitsspeichers, wenn der Arbeitsspeicher nahezu erschöpft ist: Er beginnt mit dem proaktiven Auslagern, wenn ein Leerlauf vorliegt, um reaktionsschneller zu sein, wenn eine Anwendung plötzlich mehr Arbeitsspeicher benötigt.
Beachten Sie, dass der RAM nicht nur zum Speichern des Speichers von Prozessen verwendet wird. Auf einem typischen fehlerfreien System wird nur etwa die Hälfte des Arbeitsspeichers von Prozessen und die andere Hälfte für Festplatten-Cache und Puffer verwendet. Dies bietet ein ausgewogenes Verhältnis zwischen laufenden Prozessen und Dateieingabe / -ausgabe.
Der Swap Space ist nicht unendlich. Wenn Prozesse immer mehr Speicher zuweisen, füllen die Überlaufdaten aus dem RAM den Swap. In diesem Fall wird den Prozessen, die versuchen, mehr Speicher anzufordern, die Anforderung verweigert.
Standardmäßig überlastet Linux den Speicher. Dies bedeutet, dass ein Prozess manchmal mit reserviertem, aber nicht verwendetem Speicher ausgeführt werden kann. Der Hauptgrund für die Überbindung ist die Art und Weise, wie das Gabeln funktioniert. Wenn ein Prozess einen Unterprozess startet, wird der untergeordnete Prozess konzeptionell in einer Replik des Speichers des übergeordneten Prozesses ausgeführt. Die beiden Prozesse verfügen anfangs über Speicher mit demselben Inhalt, dieser Inhalt wird jedoch divergieren, da die Prozesse jeweils Änderungen in ihrem eigenen Bereich vornehmen. Um dies vollständig zu implementieren, müsste der Kernel den gesamten Speicher des übergeordneten Elements kopieren. Dies würde das Gabeln langsam machen, so dass der Kernel das Kopieren beim Schreiben übt: anfangs teilt das Kind sein gesamtes Gedächtnis mit dem Elternteil; Wenn ein Prozess auf eine freigegebene Seite schreibt, erstellt der Kernel eine Kopie dieser Seite, um die Freigabe zu unterbrechen.
Oft lässt ein Kind viele Seiten unberührt. Wenn der Kernel genügend Speicherplatz zuweist, um den Speicherplatz des übergeordneten Elements auf jedem Zweig zu replizieren, würde viel Speicher für Reservierungen verschwendet, die untergeordnete Prozesse niemals verwenden werden. Überlastung: Der Kernel reserviert nur einen Teil dieses Speichers, basierend auf einer Schätzung, wie viele Seiten das Kind benötigen wird.
Wenn ein Prozess versucht, Speicher zuzuweisen, und nicht genügend Speicher verfügbar ist, erhält der Prozess eine Fehlerantwort und verarbeitet sie nach eigenem Ermessen. Wenn ein Prozess indirekt Speicher anfordert, indem er auf eine freigegebene Seite schreibt, deren Freigabe aufgehoben werden muss, ist dies eine andere Geschichte. Es gibt keine Möglichkeit, diese Situation der Anwendung mitzuteilen: Sie geht davon aus, dass sie dort beschreibbare Daten hat und diese sogar lesen kann. Das Schreiben erfordert lediglich etwas aufwendigere Operationen unter der Haube. Wenn der Kernel keine neue Speicherseite bereitstellen kann, kann er nur den anfordernden Prozess oder einen anderen Prozess beenden, um den Speicher zu füllen.
Sie könnten an dieser Stelle denken, dass das Beenden des Anforderungsprozesses die naheliegende Lösung ist. In der Praxis ist dies jedoch nicht so gut. Der Prozess kann ein wichtiger sein, der nur auf eine seiner Seiten zugreifen muss, während möglicherweise andere, weniger wichtige Prozesse ausgeführt werden. Daher enthält der Kernel komplexe Heuristiken, um zu entscheiden, welche Prozesse beendet werden sollen - den (in) berühmten OOM-Killer .
quelle
Um einen anderen Gesichtspunkt aus den anderen Antworten hinzuzufügen, hosten viele VPS mehrere virtuelle Maschinen auf einem bestimmten Server. Jede einzelne VM verfügt über eine bestimmte Menge an RAM für den eigenen Gebrauch. Viele Anbieter bieten "Burst-RAM" an, bei dem sie RAM über die von ihnen festgelegte Menge hinaus verwenden können. Dies ist nur für den kurzfristigen Gebrauch gedacht, und diejenigen, die über diesen längeren Zeitraum hinausgehen, werden möglicherweise vom Host bestraft, indem er Prozesse abbricht, um die Menge des verwendeten Arbeitsspeichers zu verringern, damit andere nicht darunter leiden Die Host-Maschine ist überlastet.
quelle
Einige Zeit benötigt Linux externen virtuellen Raum. Das ist die Swap-Partition. Wenn Ram gefüllt ist, verwendet Linux diesen Swap-Bereich, um den Prozess mit niedriger Priorität auszuführen.
quelle