Was könnte die Ursache dafür sein, dass make beim Kompilieren auf mehreren Kernen hängen bleibt?

Was könnte die Ursache dafür sein, dass make beim Kompilieren auf mehreren Kernen hängen bleibt?

Gestern habe ich versucht, dieWURZELPaket aus der Quelle. Da ich es auf einer 6-Core-Monstermaschine kompilierte, entschied ich mich, es mit mehreren Kernen zu kompilieren make -j 6. Das Kompilieren lief zunächst reibungslos und sehr schnell, blieb aber irgendwann makehängen, da nur ein Kern 100 % CPU-Auslastung hatte.

Ich habe gegoogelt und gefundenDasPoste in den ROOT-Foren. Da ich diesen Computer selbst gebaut habe, hatte ich Angst, dass ich den Kühlkörper nicht richtig angebracht hatte und die CPU überhitzte oder so. Leider habe ich hier bei der Arbeit keinen Kühlschrank, in den ich ihn stellen könnte. ;-)

Ich habe das lm-sensorsPaket installiert und make -j 6erneut ausgeführt, wobei ich diesmal die CPU-Temperatur überwacht habe. Obwohl sie hoch war (fast 60 °C), wurde sie nie über die hohe oder kritische Temperatur hinaus überschritten.

Ich habe es versucht, es auszuführen make -j 4, aber makees blieb irgendwann während der Kompilierung wieder hängen, diesmal an einer anderen Stelle.

Am Ende habe ich einfach kompiliert makeund es hat gut funktioniert. Meine Frage ist: Warum blieb es hängen? Da es an zwei verschiedenen Stellen stehen blieb, würde ich vermuten, dass es an einer Art Race Condition lag, aber ich denke, es sollte makeclever genug sein, alles in die richtige Reihenfolge zu bringen, da es diese -jOption bietet.

Antwort1

Ich habe keine Antwort auf dieses konkrete Problem, aber ich kann versuchen, Ihnen einen Hinweis darauf zu geben, was passieren könnte: Fehlende Abhängigkeiten in Makefiles.

Beispiel:

target: a.bytecode b.bytecode
    link a.bytecode b.bytecode -o target

a.bytecode: a.source
    compile a.source -o a.bytecode

b.bytecode: b.source
    compile b.source a.bytecode -o a.bytecode

Wenn Sie aufrufen, make targetwird alles korrekt kompiliert. Die Kompilierung von a.sourcewird zuerst (willkürlich, aber deterministisch) durchgeführt. Dann b.sourcewird die Kompilierung von durchgeführt.

Aber wenn Sie make -j2 targetbeide compileBefehle parallel ausführen, werden Sie feststellen, dass die Abhängigkeiten Ihres Makefiles beschädigt sind. Die zweite Kompilierung geht davon aus, a.bytecodedass es bereits kompiliert ist, aber es erscheint nicht in den Abhängigkeiten. Daher tritt wahrscheinlich ein Fehler auf. Die richtige Abhängigkeitszeile für b.bytecodesollte lauten:

b.bytecode: b.source a.bytecode

Um auf Ihr Problem zurückzukommen: Wenn Sie Pech haben, kann es sein, dass ein Befehl aufgrund einer fehlenden Abhängigkeit in einer 100% CPU-Schleife hängen bleibt. Das ist hier wahrscheinlich der Fall. Die fehlende Abhängigkeit konnte durch einen sequentiellen Build nicht erkannt werden, wurde aber durch Ihren parallelen Build erkannt.

Antwort2

Mir ist klar, dass das eine sehr alte Frage ist, aber sie erscheint immer noch ganz oben in den Suchergebnissen. Hier also meine Lösung:

GNU make verfügt über einen Jobserver-Mechanismus, um sicherzustellen, dass make und seine rekursiven untergeordneten Elemente nicht mehr als die angegebene Anzahl von Kernen verbrauchen: http://make.mad-scientist.net/papers/jobserver-implementation/

Es basiert auf einer Pipe, die von allen Prozessen gemeinsam genutzt wird. Jeder Prozess, der zusätzliche untergeordnete Prozesse abzweigen möchte, muss zuerst Token aus der Pipe verbrauchen und sie dann freigeben, wenn er fertig ist. Wenn ein untergeordneter Prozess die verbrauchten Token nicht zurückgibt, bleibt das Make While der obersten Ebene ewig hängen und wartet auf deren Rückgabe.

https://bugzilla.redhat.com/show_bug.cgi?id=654822

Ich habe diesen Fehler beim Erstellen von Binutils mit GNU make auf meiner Solaris-Box festgestellt, wo „sed“ nicht GNU sed ist. Das Herumspielen mit PATH, um sed==gsed Vorrang vor dem System-sed zu geben, hat das Problem behoben. Ich weiß allerdings nicht, warum sed Token aus der Pipe verbraucht hat.

Antwort3

makescheint einen Deadlock zu erzeugen. Bei ps -efscheinen diese Prozesse die Ursache zu sein:

root 695 615 1 22:18 ? 00:00:00 make PREBUILD -j32
root 2127 695 20 22:18 ? 00:00:04 make -f Makefile.prenobuild

Wenn Sie überprüfen, was jeder tut, schreiben die untergeordneten Prozesse in den Dateideskriptor 4 und die übergeordneten Prozesse warten darauf, dass alle untergeordneten Prozesse beendet werden:

root@ltzj2-6hl3t-b98zz:/# strace -p 2127
strace: Prozess 2127 angehängt
schreiben(4, "+", 1
root@ltzj2-6hl3t-b98zz:/# strace -p 695
strace: Prozess 695 angehängt
{{wait4(-1, }}

Dateideskriptor 4 ist zufällig eine Pipe:

root@ltzj2-6hl3t-b98zz:/# ls -la /proc/2127/fd/4
l-wx------ 1 root root 64 3. September 22:22 /proc/2127/fd/4 -> 'pipe:[1393418985]'

und diese Pipe besteht nur zwischen den übergeordneten und untergeordneten Prozessen:

root@ltzj2-6hl3t-b98zz:/# lsof | grep 1393418985
mache 695 Wurzel 3r FIFO 0,12 0t0 1393418985 Rohr
mache 695 Wurzel 4w FIFO 0,12 0t0 1393418985 Rohr
mache 2127 Wurzel 3r FIFO 0,12 0t0 1393418985 Rohr
mache 2127 Wurzel 4w FIFO 0,12 0t0 1393418985 Rohr

es scheint also, dass 2127 beim Versuch, die Ausgabe in die Pipe zurück zu 695 einzufügen, hängen bleibt, aber 695 ist anhängig wait4(), sodass diese Pipe nie geleert wird.

Wenn ich die Pipe mithilfe von „cat“ aus der Shell leere, wird der Build fortgesetzt und wie erwartet abgeschlossen …

root@ltzj2-6hl3t-b98zz:/# Katze /proc/695/fd/3
+++++++++++++++++++++++++++++++

Der Build wird entsperrt und läuft weiter …


Ich hatte es zunächst falsch verstanden, aber nach weiteren Untersuchungen bin ich schließlich auf diesen Linux-Kernel-Defekt gestoßen:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=46c4c9d1beb7f5b4cec4dd90e7728720583ee348

Eine genaue Erklärung wie das funktioniert findet ihr hier:https://lore.kernel.org/lkml/1628086770.5rn8p04n6j.none@localhost/.

Sie können dies umgehen, solange kein Kernel-Patch verfügbar ist. Wenden Sie dazu den folgenden Workaround auf den Quellcode von GNU Make an:

--- a/src/posixos.c 2020-01-02 23:11:27.000000000 -0800
+++ b/src/posixos.c 2021-09-18 09:12:02.786563319 -0700
@@ -179,8 +179,52 @@
 jobserver_release (int ist_schwerwiegend)
 {
   int r;
- EINTRLOOP (r, schreibe (job_fds[1], &token, 1));
- wenn (r != 1)
+ Ganzzahl n;
+ Zeichen b[32];
+
+ /* Nicht blockierendes Schreiben verwenden, um Deadlocks durch mehrere Make-Childs zu vermeiden
+ * gleichzeitige Freigabe von Jobs. */
+ set_blocking (job_fds[1], 0);
+ memset(b,Token,Größe von(b));
+ n = 1;
+ solange ( n > 0 )
+ {
+ r = schreiben (job_fds[1], b, n);
+ /* Unterbrochener Systemaufruf, versuchen Sie es erneut */
+ wenn (r == -1)
+ {
+ wenn (errno == EINTR)
+ weiter;
+
+ /* Wir kommen hierher, weil dieser Prozess und ein anderer beide versucht haben, in die Pipe zu schreiben.
+ * genau zur gleichen Zeit, und die Pipe enthält nur 1 Seite. Wir haben verloren, die andere
+ * Prozess gewonnen (in die Pipe geschrieben). Wir können diesen Zustand nur zurücksetzen, indem wir zuerst
+ * Lesen aus der Pipe. Das bedeutet natürlich, dass wir dann eine zusätzliche
+ * Zeichen. */
+ wenn (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ wenn ( jobserver_acquire(0) )
+ {
+ n++;
+ /* Wahrscheinlich fast unmöglich... */
+ wenn ( n > 32 )
+ Pause;
+ weiter;
+ }
+ }
+ }
+ if ( r == 0 ) /* 0 Bytes geschrieben, aber kein Fehler, versuche es erneut */
+ weiter;
+ wenn (r > 0)
+ {
+ n - = r;
+ weiter;
+ }
+ break; /* Bei allen anderen Fehlern abbrechen. */
+ }
+ set_blocking (job_fds[1], 1);
+
+ wenn (n != 0)
     {
       wenn (ist_schwerwiegend)
         pfatal_with_name (_("Jobserver schreiben"));

Antwort4

Ihr System ist möglicherweise in Ordnung, aber es könnte ein Race Condition auftreten, makewenn Builds parallel ausgeführt werden.

Wenn mit Ihrem System etwas nicht stimmt, würde es auch in anderen Szenarien hängen bleiben/abstürzen, nicht nur bei parallelen Builds.

verwandte Informationen