Notieren Sie, dass ein Programm das Problem nicht starten kann

Notieren Sie, dass ein Programm das Problem nicht starten kann

27.06.2023

Hintergrundeinführung

Ein unabhängiges Programm, im Folgenden test-bin genannt, wird im Skript start.sh aufgerufen. Der allgemeine Code in start.sh lautet wie folgt:

#!/bin/bash
killall test-api
kiallall test-bin

test-bin 1>/dev/null
test-api 1>/dev/null

Der Grund, warum test-bin mit dem Skript start.sh gestartet wird, liegt darin, dass das gesamte System mehrere Dienste und einige andere Vorgänge umfasst und daher im Skript start.sh platziert wird.

Ein weiteres unabhängiges, in go geschriebenes Programm stellt eine externe Schnittstelle bereit und kann den Dienst neu starten. Es wird im Folgenden als test-api bezeichnet. Wenn test-api eine Anforderung zum Neustart erhält, ruft es das Skript start.sh auf, um alle Programme neu zu starten. Der ungefähre Code lautet wie folgt:

func Restart(c *gin.Context) {
    go func() {
        out, err := exec.Command(utils.ShellToUse, "-c", "start.sh").Output()
        if err != nil {
            model.Loger("Error", fmt.Sprintf("restart error: %s, out: %s", err.Error(), out))
            return
        }
    }()

    response.Ok(c)
}

Problemphänomen

  1. Führen Sie das Skript start.sh manuell im Terminal aus. Alle Dienste können normal gestartet werden und das System kann normal ausgeführt werden.

  2. Wenn test-api den Dienst nach Erhalt der Anfrage neu startet, kann test-bin nicht normal starten. Normalerweise sollten zwei Test-bin-Prozesse vorhanden sein, aber nur einer wird gefunden. Ein anderer Prozess startet nicht ohne Grund und es sind keine Kerndateiinformationen vorhanden.

Problemlokalisierung und -analyse

Denn unter normalen Umständen sollten zwei Test-Bin-Prozesse gestartet werden, und derzeit wird nur einer gestartet. Überprüfen Sie den Dateideskriptor des aktuell gestarteten Test-Bin-Prozesses und finden Sie Folgendes:

bash-5.0# ls -l /proc/29764/fd
total 0
lr-x------ 1 root root 64 Jun 16 10:00 0 -> /dev/null
l-wx------ 1 root root 64 Jun 16 10:00 1 -> /dev/null
l-wx------ 1 root root 64 Jun 16 10:00 2 -> 'pipe:[100456522]'
lrwx------ 1 root root 64 Jun 16 10:00 3 -> /dev/zero

Wenn Sie das Skript start.sh über das Terminal ausführen möchten und die beiden Test-Bin-Prozesse normal gestartet werden können, überprüfen Sie den Dateideskriptor des Test-Bin-Prozesses, der wie folgt lautet:

bash-5.0# ls -l /proc/34480/fd
total 0
lr-x------ 1 root root 64 Jun 16 10:03 0 -> /dev/null
l-wx------ 1 root root 64 Jun 16 10:03 1 -> /dev/null
lrwx------ 1 root root 64 Jun 16 10:03 10 -> 'anon_inode:[eventpoll]'

Es wurde festgestellt, dass der stderr (fd ist 2) des untergeordneten Prozesses geändert wurde. Es handelt sich um eine Pipe (Pipe: [100456522]). Ich möchte lsof verwenden, um sie anzuzeigen, aber da es keinen solchen Befehl gibt System, ich gebe auf.
Ich habe den Prozess neu organisiert und festgestellt, dass das Test-API-Programm beim Ausführen des start.sh-Skripts die folgende Anweisung verwendet hat:

out, err := exec.Command(utils.ShellToUse, "-c", "start.sh").Output()

Diese Anweisung ruft die gesamte Ausgabe des start.sh-Skripts ab. Wie macht sie das? Es handelt sich um die Verwendung von Pipelines. Das allgemeine Prinzip besteht darin, dass der übergeordnete Prozess eine Pipeline erstellt, dann den untergeordneten Prozess verzweigt und der übergeordnete Prozess das Schreiben der Pipeline abschließt. Der untergeordnete Prozess schließt das Lesen aus der Pipe und leitet die Standardausgabe an das schreibende Ende der Pipe um.

Da das Programm test-api alle stdout- und stderr-Ausgaben des untergeordneten Prozesses erhält, leitet es stdout und stderr des untergeordneten Prozesses an die Pipeline um, was wir oben gesehen haben:

bash-5.0# ls -l /proc/29764/fd
total 0
lr-x------ 1 root root 64 Jun 16 10:00 0 -> /dev/null
l-wx------ 1 root root 64 Jun 16 10:00 1 -> /dev/null
l-wx------ 1 root root 64 Jun 16 10:00 2 -> 'pipe:[100456522]'
lrwx------ 1 root root 64 Jun 16 10:00 3 -> /dev/zero

Sie können sehen, dass der Dateideskriptor 2 zur Pipe umgeleitet wird. Aber warum wird Dateideskriptor 1 nicht in die Pipe umgeleitet?

Der Grund dafür ist, dass der Start von test-bin im start.sh-Skript wie folgt erfolgt:

test-bin 1>/dev/null 

stdout und stderr des start.sh-Prozesses werden an die Pipeline umgeleitet, aber wenn der test-bin-Prozess im start.sh-Skript gestartet wird, wird stdout an /dev/null umgeleitet, aber die stderr-Pipeline wird nicht umgeleitet Der Test: Der stderr des -bin-Prozesses wird weiterhin vom start.sh-Prozess geerbt und auch an die Pipeline umgeleitet.

Theoretisch stellt die Umleitung zur Pipeline kein Problem dar. Warum wird also nur einer der Test-Bin-Prozesse gestartet, aber nicht alle? Gemäß der Codeanalyse wurde festgestellt, dass der aktuell gestartete Test-Bin-Prozess ein untergeordneter Prozess ist und derzeit auf die Initialisierung des Haupt-Test-Bin-Prozesses wartet, der Hauptprozess von Test-Bin jedoch beendet wurde und verschwunden ist Kein Grund (da keine Kerndatei gefunden werden konnte) . Warum wird der Hauptprozess beendet? Das Einsehen der Protokolldatei des Hauptprozesses ist ebenfalls normal und kann auch manuell vom Terminal aus gestartet werden. Analysieren Sie den Code erneut und stellen Sie fest, dass im start.sh-Skript Folgendes enthalten ist:

#!/bin/bash
killall test-api
kiallall test-bin

test-bin 1>/dev/null
test-api 1>/dev/null

Zuerst wird die Test-API beendet und dann der Test-Bin gestartet. Gemäß der Analyse liest die Test-API die Ausgabeinformationen des Unterprozesses aus der Pipeline und der Unterprozess (start.sh) verarbeitet dies Setzen Sie den Test – Der API-Prozess wird beendet, dann wird das Leseende der Pipeline geschlossen und der untergeordnete Prozess (das Test-Bin-Programm erbt das Pipeline-Schreibende in start.sh) schreibt Inhalte in die Pipeline, wenn dies der Fall ist Während des Startvorgangs schreibt das Test-Bin-Programm Informationen in stderr, das heißt, es schreibt Informationen in die Pipeline. Anschließend empfängt es ein SIGPIPE-Signal. Die Standardaktion dieses Signals besteht darin, das Programm zu beenden.

Schreiben Sie also eine Signalverarbeitungsfunktion in das Test-Bin-Programm, um das SIGPIPE-Signal zu verarbeiten, und es hat das Signal tatsächlich empfangen. Bisher wurde das Problem lokalisiert und es ist einfacher, es zu lösen.

Zusammenfassend lässt sich sagen, dass der Grund: Wenn das Skript start.sh in test-api ausgeführt wird, wird stderr an die Pipeline umgeleitet. Wenn das Skript start.sh ausgeführt wird, wird der Test-API-Prozess beendet und das Leseende der Pipeline geschlossen. Beim Starten von Test-Bin wird stderr nicht in andere Dateien umgeleitet, sondern in stderr geschrieben Informationen, aber da das Leseende der Pipeline geschlossen wurde, wird das SIGPIPE-Signal ausgelöst, wodurch der Prozess beendet wird.

Erweiterte Informationen

Umleiten der Eingabe und Ausgabe von Subprozess-Konsolenprogrammen – Green Wheat Field – Blog Garden

Pipeline-Erstellung und Lese- und Schreibpipeline – Beifeng- Blog Garden

SIGPIPE signal_Common kleiner Müllblog-CSDN-Blog

Supongo que te gusta

Origin blog.csdn.net/EmptyStupid/article/details/131447081
Recomendado
Clasificación