Wie kann ich Perl davon abhalten, tonnenweise Speicher zu verbrauchen, wenn Child-Forks eines großen Parent-Prozesses heruntergefahren werden?

8

Kontext:

Ich habe einen Multi-Forking-Perl-Prozess (5.16), der unter Linux läuft. Der Parent-Fork lädt eine sehr große Menge an Perl-Code (über use/require ) und weist viele Datenstrukturen (mehrere GB) zu. Es entstehen dann viele Kindergabeln, die alle parallel arbeiten. Dies geschieht, um den Speicherbedarf des Prozesses während der Ausführung zu reduzieren, da die Kopie-auf-Schreib-Eigenschaft von fork() bedeutet, dass die untergeordneten Elemente die Daten des übergeordneten Elements verwenden können, ohne dass sie jeweils ihr eigenes großes Speicherabbild beibehalten. p>

Problem:

Alles funktioniert gut, bis ich versuche, die Gruppe von Prozessen herunterzufahren. Wenn ich das Elternelement unterbreche (das Signal wird an alle untergeordneten Elemente weitergegeben), füllt sich der Speicher auf dem Server, auf dem der Code ausgeführt wird, sofort, es beginnt zu wechseln, und andere Prozesse auf dem Server werden zum Stillstand gebracht. Wenn ein Copy-on-Write-Fork heruntergefahren wird, scheint Perl versucht zu haben, den gesamten im Elternteil beanspruchten Speicher neu zuzuordnen, so dass er es für free oder etwas markieren kann.

Frage:

Wie verhindere ich, dass dieses Bloat-on-Shutdown passiert? Gibt es eine Möglichkeit, wie ich den Kindern sagen kann, dass sie nur versuchen sollen, Speicher zu durchqueren und wiederzugewinnen, den diese Gabeln zugewiesen haben?

    
Zac B 18.03.2015, 15:58
quelle

2 Antworten

10

Die Zuweisung von Speicherseiten ist auf die Freigabe von Variablen beim Beenden zurückzuführen. Dies ist notwendig, um Destruktoren zu haben.

Der Aufruf von POSIX::_exit() wird sofort beendet, wobei die Freigabe für einzelne Variablen übersprungen wird, aber auch Aufrufe von Destruktoren übersprungen werden.

    
ikegami 18.03.2015, 16:08
quelle
2

Ich akzeptierte @ Ikegamis Antwort, weil sie direkt die Frage beantwortet.

Ich poste dies, weil meine "Lösung" (wirklich eine Möglichkeit, einen Teil des Problems zu optimieren) für andere nützlich sein könnte.

Die endgültige Lösung in meinem Fall war ein Paradigmenwechsel: Ich erkannte, dass das Problem nicht darin lag, dass any Perl-Prozesse viel Speicher beim Herunterfahren von Forks sanken, sondern dass ich hatte so viele Perl verarbeitet gleichzeitig Speicher beim Herunterfahren .

Als mein Elternprozess die Anweisung "shutdown" erhielt, schickte er sofort eine "shutdown" Nachricht an alle seine Kinder, und alle beendeten ihre Arbeit und gingen ungefähr zur gleichen Zeit aus. Mit Dutzenden von Hunderten von untergeordneten Prozessen, die gleichzeitig heruntergefahren wurden, war der Speicheraufwand zu groß.

Die Fehlerbehebung bestand darin, das Herunterfahren zu einem zweiphasigen Prozess zu machen: Zuerst sendete der übergeordnete Prozess eine Nachricht "Stop what you do" an alle untergeordneten Elemente, sodass die Geschäftslogik zu einem vorhersehbaren Zeitpunkt nicht mehr ausgeführt wurde. Es schickte diese Nachricht an alle Kinder gleichzeitig / in einer sehr schnellen Schleife. Dann schaltete es die Kinder nacheinander aus . Es gab für jedes Kind einen Interrupt aus, der bis zum Ende waitpid genannt wurde und ging dann zum nächsten weiter.

Auf diese Weise wurde der Speicherabzug im Worst-Case-Fall (mit p für den Pre-Fork-Speicher und f für die Anzahl der untergeordneten Forks) 2p und nicht fp .

Diese Lösung funktioniert nicht in Fällen, in denen 2p Speicherverbrauch immer noch unannehmbar hohe Kosten verursacht.

Es wurden zwei Optimierungen hinzugefügt: timeout / forceful-kill-Prüfungen bei hartnäckigen / kaputten Kindern und bedingte sleep s zwischen untergeordneten Shutdowns, wenn das Herunterfahren des vorherigen Kindes das System zum Swapping zwang. Die sleep s gaben dem System Zeit, Seiten aus dem Swap zu atmen / zu bekommen.

Auch dies ist eine Optimierung des Problems, keine Antwort. Die wirkliche Antwort ist @ Ikegamis.

    
Zac B 25.03.2015 12:20
quelle