Wie man ein Signal an Kinderprozesse aus einem Bash -Skript ausbreitet
- 3237
- 789
- Matteo Möllinger
Angenommen, wir schreiben ein Skript, das ein oder mehrere lang laufende Prozesse hervorbringt. Wenn dieses Skript ein Signal wie z Sigint
oder Sigterm
, Wir möchten wahrscheinlich, dass seine Kinder auch beendet werden (normalerweise, wenn der Elternteil stirbt, überlebt die Kinder). Möglicherweise möchten wir auch einige Aufräumarbeiten ausführen, bevor das Skript selbst beendet ist. Um unser Ziel erreichen zu können, müssen wir zunächst über Prozessgruppen und die Ausführung eines Prozesses im Hintergrund lernen.
In diesem Tutorial lernen Sie:
- Was ist eine Prozessgruppe
- Der Unterschied zwischen Vordergrund- und Hintergrundprozessen
- So führen Sie ein Programm im Hintergrund aus
- So verwenden Sie die Schale
Warten
eingebaut, um auf einen im Hintergrund ausgeführten Prozess zu warten - Wie man untergeordnete Prozesse beendet, wenn der Elternteil ein Signal empfängt
Softwareanforderungen und Konventionen verwendet
Kategorie | Anforderungen, Konventionen oder Softwareversion verwendet |
---|---|
System | Verteilung unabhängig |
Software | Keine spezifische Software benötigt |
Andere | Keiner |
Konventionen | # - erfordert, dass gegebene Linux -Befehle mit Root -Berechtigungen entweder direkt als Stammbenutzer oder mit Verwendung von ausgeführt werden können sudo Befehl$ - Erfordert, dass die angegebenen Linux-Befehle als regelmäßiger nicht privilegierter Benutzer ausgeführt werden können |
Ein einfaches Beispiel
Erstellen wir ein sehr einfaches Skript und simulieren den Start eines langen Laufprozesses:
#!/Bin/Bash Trap "Echo -Signal empfangen!"Sigint Echo" Die Skriptpid ist $ "Schlaf 30
Kopieren Das erste, was wir im Skript gemacht haben, war, eine Falle zum Fangen zu erstellen Sigint
und drucken Sie eine Nachricht, wenn das Signal empfangen wird. Wir haben unser Drehbuch drucken lassen PID: Wir können bekommen, indem wir die erweitern $$
Variable. Als nächstes haben wir das ausgeführt schlafen
Befehl zum Simulieren eines langen Laufprozesses (30
Sekunden).
Wir speichern den Code in einer Datei (sagen wir, er heißt prüfen.Sch
), machen Sie es ausführbar und starten Sie es von einem Terminalemulator. Wir erhalten das folgende Ergebnis:
Die SkriptpID ist 101248
Wenn wir uns auf den Terminalemulator konzentrieren und Strg+C drücken, während das Skript ausgeführt wird, a Sigint
Signal wird von unserem gesendet und behandelt fangen:
Die SkriptpID ist 101248 ^csignal empfangen!
Obwohl die Falle das Signal erwartungsgemäß behandelte, wurde das Drehbuch ohnehin unterbrochen. Warum ist das passiert? Außerdem, wenn wir eine senden Sigint
Signal auf das Skript mit dem töten
Befehl, das Ergebnis, das wir erhalten, ist ganz anders: Die Falle wird nicht sofort ausgeführt, und das Skript wird fort 30
Sekunden von „Schlafen“). Warum dieser Unterschied? Mal sehen…
Prozessgruppen, Vordergrund- und Hintergrundjobs
Bevor wir die obigen Fragen beantworten, müssen wir das Konzept von besser erfassen Prozessgruppe.
Eine Prozessgruppe ist eine Gruppe von Prozessen, die dasselbe teilen pgid (Prozessgruppe ID). Wenn ein Mitglied einer Prozessgruppe einen Kinderprozess erstellt, wird dieser Prozess Mitglied derselben Prozessgruppe. Jede Prozessgruppe hat einen Führer; Wir können es leicht erkennen, weil es es ist PID und das pgid sind gleich.
Wir können visualisieren PID Und pgid von laufenden Prozessen mit dem ps
Befehl. Die Ausgabe des Befehls kann so angepasst werden, dass nur die Felder angezeigt werden: in diesem Fall CMD, PID Und Pgid. Wir tun dies, indem wir die verwenden -Ö
Option, Bereitstellung einer von Kommas getrennten Liste von Feldern als Argument:
$ ps -a -o pid, pgid, cmd
Wenn wir den Befehl ausführen, während unser Skript den relevanten Teil der Ausgabe ausführt, die wir erhalten, ist dies Folgendes:
PID PGID CMD 298349 298349 /Bin /Bash ./prüfen.Sh 298350 298349 Schlaf 30
Wir können deutlich zwei Prozesse sehen: die PID des ersten ist 298349
, Gleich wie es pgid: Dies ist der Leiter der Prozessgruppe. Es wurde erstellt, als wir das Skript starteten, wie Sie in der sehen können CMD Spalte.
Dieser Hauptprozess hat einen Kinderprozess mit dem Befehl gestartet Schlaf 30
: Wie erwartet befinden sich die beiden Prozesse in derselben Prozessgruppe.
Als wir Strg-C drückten, während wir uns auf das Terminal konzentrierten, von dem das Skript gestartet wurde, wurde das Signal nicht nur an den übergeordneten Prozess, sondern an die gesamte Prozessgruppe gesendet. Welche Prozessgruppe? Der Vordergrundprozessgruppe des Terminals. Alle Prozesse Mitglied dieser Gruppe werden genannt Vordergrundprozesse, Alle anderen werden genannt Hintergrundprozesse. Hier ist das, was das Bash -Handbuch zu diesem Thema zu sagen hat:
WUSSTEN SIE?Um die Implementierung der Benutzeroberfläche zur Arbeitskontrolle zu erleichtern, behält das Betriebssystem den Begriff einer aktuellen Terminalprozessgruppen -ID bei. Mitglieder dieser Prozessgruppe (Prozesse, deren Prozessgruppen-ID der aktuellen Terminalprozessgruppen-ID entspricht) empfangen Tastatur-generierte Signale wie SIGINT. Diese Prozesse sollen im Vordergrund stehen. Hintergrundprozesse sind diejenigen, deren Prozessgruppen -ID von den Terminals unterscheidet. Solche Prozesse sind immun gegen Tastaturgenerierte Signale.Als wir die schickten Sigint
signalisieren mit dem töten
Der Befehl stattdessen haben wir nur auf die PID des übergeordneten Prozesses gerichtet. Bash zeigt ein bestimmtes Verhalten, wenn ein Signal empfangen wird, während es auf ein Programm wartet: Der „Trap -Code“ für dieses Signal wird erst nach Abschluss dieses Vorgangs ausgeführt. Aus diesem Grund wurde die Meldung "empfangener" erst nach der angezeigt schlafen
Befehl beendet.
Um zu replizieren, was passiert, wenn wir Strg-c im Terminal mit der Klemme drücken töten
Befehl zum Senden des Signals müssen wir auf die Prozessgruppe abzielen. Wir können ein Signal an eine Prozessgruppe senden, indem wir verwenden Die Verneinung der PID des Prozessleiters, Angenommen, die PID des Prozessleiters ist 298349
(Wie im vorherigen Beispiel) würden wir rennen:
$ Kill -2 -298349
Verwalten Sie die Signalausbreitung von innerhalb eines Skripts aus
Nehmen wir nun an, wir starten ein langjähriges Skript von einer nicht interaktiven Shell und möchten, dass das Skript automatisch die Signalausbreitung verwaltet, sodass beim Empfang eines Signals wie z Sigint
oder Sigterm
Es beendet sein potenziell langes Kind und führt schließlich einige Aufräumarbeiten aus, bevor er beendet ist. Wie wir das tun können?
Wie wir zuvor können wir die Situation bewältigen, in der ein Signal in einer Falle empfangen wird. Wie wir jedoch gesehen haben, wird der „Trap -Code“ nur nach dem Ausgang des untergeordneten Prozess.
Dies ist nicht das, was wir wollen: Wir möchten, dass der Trap -Code verarbeitet wird, sobald der übergeordnete Prozess das Signal empfängt. Um unser Ziel zu erreichen, müssen wir den Kinderprozess in der durchführen Hintergrund: Wir können dies tun, indem wir die platzieren &
Symbol nach dem Befehl. In unserem Fall würden wir schreiben:
#!/Bin/Bash Trap 'Echo -Signal empfangen!'Sigint echo' Die Skriptpid ist $ "Schlaf 30 &
Kopieren Wenn wir das Skript auf diese Weise verlassen würden, würde der übergeordnete Prozess direkt nach der Ausführung der Ausführung des Schlaf 30
Befehl, die uns ohne die Chance verlassen, Aufgaben nach dem Ende auszuführen oder unterbrochen zu werden. Wir können dieses Problem durch die Verwendung der Shell lösen Warten
eingebaut. Die Hilfe Seite von Warten
definiert es so:
Warten auf jeden durch eine ID identifizierten Prozess, der eine Prozess -ID oder eine Jobspezifikation sein kann, und meldet den Kündigungsstatus. Wenn ID nicht angegeben ist, wartet auf alle aktuell aktiven Kinderprozesse und der Rückgabestatus ist Null.
Nachdem wir einen Prozess festgelegt haben, der im Hintergrund ausgeführt werden soll, können wir seine abrufen PID im $!
Variable. Wir können es als Argument angeben Warten
Damit den übergeordneten Prozess auf sein Kind warten lassen:
#!/Bin/Bash Trap 'Echo -Signal empfangen!'Sigint echo "Die Skriptpid ist $" Schlaf 30 & warte $ $!
Kopieren Sind wir fertig? Nein, es gibt immer noch ein Problem: Die Rezeption eines Signals, das in einer Falle im Drehbuch behandelt wurde, verursacht die Warten
gebaut, um sofort zurückzukehren, ohne tatsächlich auf die Beendigung des Befehls im Hintergrund zu warten. Dieses Verhalten ist im Bash -Handbuch dokumentiert:
Um dieses Problem zu lösen, müssen wir verwenden Warten
wieder, vielleicht als Teil der Falle selbst. So könnte unser Skript am Ende aussehen:
#!/bin/bash cleanup () echo "Reinigung ..." # Unser Aufräumcode geht hierher Trap 'Echo -Signal empfangen!; Kill "$ child_pid"; Warten Sie "$ child_pid"; Cleanup 'Sigint Sigterm echo "Die Skriptpid ist $" Sleep 30 & Child_pid = "$!"Warte" $ child_pid "
Kopieren In dem Skript haben wir a erstellt Aufräumen
Funktion, wo wir unseren Reinigungscode einfügen und unsere gemacht haben fangen
fangen auch die Sigterm
Signal. Hier ist, was passiert, wenn wir dieses Skript ausführen und eines dieser beiden Signale an sie senden:
- Das Skript wird gestartet und das
Schlaf 30
Der Befehl wird im Hintergrund ausgeführt; - Der PID des Kinderprozesses wird in der „gespeichert“
child_pid
Variable; - Das Skript wartet auf die Beendigung des Kinderprozesses
- Das Skript erhält a
Sigint
oderSigterm
Signal - Der
Warten
Der Befehl kehrt sofort zurück, ohne auf die Kündigung des Kindes zu warten
Zu diesem Zeitpunkt wird die Falle ausgeführt. Drin:
- A
Sigterm
Signal (dietöten
Standard) wird an die gesendetchild_pid
; - Wir
Warten
Um sicherzustellen, dass das Kind nach Erhalt dieses Signals gekündigt wird. - Nach
Warten
Gibt zurück, wir führen die ausAufräumen
Funktion.
Propagieren Sie das Signal an mehrere Kinder
Im obigen Beispiel haben wir mit einem Skript gearbeitet, das nur einen Kinderprozess hatte. Was ist, wenn ein Drehbuch viele Kinder hat und was ist, wenn einige von ihnen eigene Kinder haben??
Im ersten Fall eine schnelle Möglichkeit, das zu bekommen Pids Von allen Kindern ist es, die zu verwenden Jobs -p
Befehl: Dieser Befehl zeigt die PIDs aller aktiven Jobs in der aktuellen Shell an. Wir können als benutzen töten
sie zu beenden. Hier ist ein Beispiel:
#!/bin/bash cleanup () echo "Reinigung ..." # Unser Aufräumcode geht hierher Trap 'Echo -Signal empfangen!; töten $ (jobs -p); Warten; Cleanup 'Sigint Sigterm echo "Die Skriptpid ist $" Sleep 30 & Sleep 40 & Wait
Kopieren Das Skript startet zwei Prozesse im Hintergrund: mithilfe der Verwendung Warten
ohne Argumente eingebaut, warten wir auf alle und halten den Elternprozess am Leben. Wenn das Sigint
oder Sigterm
Signale werden vom Skript empfangen, wir senden eine Sigterm
An beiden von ihnen, dass ihre Pids von der zurückgegeben werden Jobs -p
Befehl (Arbeit
ist selbst eine integrierte Shell. Wenn wir sie verwenden, wird ein neuer Prozess nicht erstellt.
Wenn die Kinder ihre eigenen Kinder haben und wir sie alle beenden, wenn der Vorfahr ein Signal erhält, können wir ein Signal an die gesamte Prozessgruppe senden, wie wir zuvor gesehen haben.
Dies stellt jedoch ein Problem dar, da wir durch das Senden eines Terminierungssignals an die Prozessgruppe eine Schleife „Signal-Signal-/Signal eingeschlossen“ eingeben würden. Denken Sie darüber nach: in der fangen
für Sigterm
Wir senden eine Sigterm
Signal an alle Mitglieder der Prozessgruppe; Dies schließt das übergeordnete Skript selbst ein!
Um dieses Problem zu lösen und dennoch eine Reinigungsfunktion nach Beendigung von untergeordneten Prozessen auszuführen, müssen wir die ändern fangen
für Sigterm
Kurz bevor wir das Signal an die Prozessgruppe senden, zum Beispiel:
#!/bin/bash cleanUp () echo "Reinigung ..." # Unser Aufräumcode geht hierher Trap 'Trap "" Sigterm; töte 0; Warten; Cleanup 'Sigint Sigterm echo "Die Skriptpid ist $" Sleep 30 & Sleep 40 & Wait
Kopieren In der Falle vor dem Senden Sigterm
In der Prozessgruppe haben wir die geändert Sigterm
Trap, so dass der übergeordnete Prozess das Signal ignoriert und nur seine Nachkommen davon betroffen sind. Beachten Sie auch, dass wir in der Falle die Prozessgruppe verwendet haben, um die Prozessgruppe zu signalisieren töten
mit 0
als PID. Dies ist eine Art Abkürzung: Wenn die PID weitergereicht an töten
Ist 0
, alle Prozesse in der aktuell Prozessgruppe werden signalisiert.
Schlussfolgerungen
In diesem Tutorial haben wir uns über Prozessgruppen und den Unterschied zwischen Vordergrund- und Hintergrundprozessen gelernt. Wir haben gelernt, dass Strg-c a sendet Sigint
Signal auf die gesamte Vordergrundprozessgruppe des Steuerterminals, und wir haben gelernt, wie man ein Signal an eine Prozessgruppe sendet töten
. Wir haben auch gelernt, wie man ein Programm im Hintergrund ausführt und wie man die benutzt Warten
Schale eingebaut, um zu warten, bis es ausgeht, ohne die übergeordnete Shell zu verlieren. Schließlich haben wir gesehen, wie man ein Skript so einstellt. Habe ich etwas verpasst? Haben Sie Ihre persönlichen Rezepte, um die Aufgabe zu erfüllen?? Zögern Sie nicht, es mich wissen zu lassen!
Verwandte Linux -Tutorials:
- Bash -Hintergrundprozessmanagement verleihen
- Multi-Thread-Bash-Skript- und Prozessmanagement bei der…
- Eine Einführung in Linux -Automatisierung, Tools und Techniken
- So führen Sie den Befehl im Hintergrund unter Linux aus
- Mastering -Bash -Skriptschleifen beherrschen
- So installieren Sie Signal unter Linux
- Wie man Systemanrufe verfolgt, die durch einen Prozess mit Strace auf…
- Mint 20: Besser als Ubuntu und Microsoft Windows?
- Vergleich von Linux Apache Pre -Onk -vs -Worker -MPMs
- So arbeiten Sie mit DNF -Paketgruppen
- « MySQL Ändern des Benutzerkennworts
- So bauen Sie ein Paket mit dem Arch Linux Build -System wieder auf »