5.2 Vorder- und Hintergrundprozesse
Normalerweise werden in der Shell eines Benutzers Programme gestartet, die bestimmte Eingaben des Benutzers erwarten, dann etwas berechnen und schließlich das bzw. die Ergebnisse ausrechnen und ausgeben. Dieses EVA-Prinzip (Eingabe – Verarbeitung – Ausgabe) kann natürlich auch abgewandelt sein, sodass wie bei ls gleich die »Berechnung« der Verzeichniseinträge startet und darauf dann die Ausgabe folgt.
Solche interaktiven, für den Benutzer bzw. die Benutzerin steuerbaren Programme werden als Vordergrundprozesse bezeichnet. Jedoch gibt es eine weitere Möglichkeit für den Ablauf eines Programms innerhalb einer Session: den Hintergrund. Ein Hintergrundprozess läuft zwar, bekommt aber keine Eingaben seitens des Benutzers. Der Benutzer kann, während ein Hintergrundprozess läuft, die Shell weiter zur Arbeit nutzen und neue Prozesse starten… zumindest gibt es in der Regel keine störenden Ausgaben des Hintergrundprozesses, doch selbst diese könnte man unterdrücken. Hierzu später mehr in Abschnitt 5.2.3.
Stellen Sie sich einmal Folgendes vor: Sie benötigen eine Datei, starten also eine Suche. Das dazugehörige Kommando find durchsucht das Dateisystem – das eine große Anzahl an Dateien enthält – über einen längeren Zeitraum nach dieser Datei. Sie selbst sind genervt und möchten eigentlich mit der Arbeit fortfahren. Genau hier setzt das Prinzip des Hintergrundprozesses an: Sie starten den Befehl im Hintergrund oder befördern ihn nachträglich dorthin – und können weiterarbeiten.
Um einen Prozess im Hintergrund zu starten, hängen Sie ein kaufmännisches »Und« (&) an das Kommando an:
$ Prozess & [1] 14215 … [1] + done /usr/local/bin/Prozess
Listing 5.3 Schreibweise zum Starten eines Prozesses im Hintergrund
Das Listing zeigt einen Prozess, der im Hintergrund gestartet wird. Nach dem Start wird die Nummer des Hintergrundprozesses – die sogenannte Job-ID – in eckigen Klammern (in diesem Fall Nummer 1), gefolgt von der Prozess-ID (hier 14215), ausgegeben.
Nach einiger Zeit ist der Prozess mit der Abarbeitung seiner Aufgaben fertig. Dem Benutzer bzw. der Benutzerin wird dies durch die done-Zeile mitgeteilt.
Bei der Arbeit mit Prozessen steht also oft die Frage im Vordergrund, ob ein Prozess bzw. ein Programm Ihre direkte Aufmerksamkeit erfordert oder ob es still im Hintergrund seine Arbeit verrichten kann.
Eine spezielle Art von Prozessen sind die sogenannten Daemonprozesse. Sie arbeiten im Hintergrund und werden vorwiegend für Aufgaben genutzt, die keiner direkten Kontrolle bedürfen. Das sind Serverdienste wie beispielsweise Webserver oder Mailserver.
Daemonprozesse und Shell-Hintergrundprozesse
Dummerweise werden Daemonprozesse oftmals mit den Hintergrundprozessen der Shell verwechselt. Wie wir oben erläutert haben, sind Daemonprozesse jedoch eigene Sessionführer und unabhängig von einer Shell.
Solche Daemonprozesse werden normalerweise während des Bootens gestartet und erst beim Shutdown des Systems beendet, indem der Kernel ein TERMINATE- bzw. KILL-Signal an den Prozess sendet.
Wie wir bereits erwähnt haben, haben Hintergrundprozesse ebenfalls eine Nummer zur Identifikation. Daraus lässt sich schließen, dass es möglich ist, mehrere Prozesse parallel im Hintergrund ablaufen zu lassen.
$ sleep 10 & [1] 10203 $ sleep 10 & [2] 10204 $ sleep 10 & [3] 10205 $ sleep 1 [3] + Done sleep 10 [2] - Done sleep 10 [1] Done sleep 10
Listing 5.4 Parallele Hintergrundprozesse
Es ist wichtig zu wissen, dass ein Hintergrundprozess automatisch laufen muss. So ist es beispielsweise nicht möglich, Tastatureingaben an diesen Prozess zu senden.
Des Weiteren werden die Ausgaben des Hintergrundprozesses einfach zwischen die Ausgaben anderer Shellprogramme gemischt, was Ihnen einiges durcheinanderbringen könnte. Um dieses Problem zu lösen, sollten Sie die Ausgabeumlenkung (siehe Abschnitt 4.7) benutzen, die wir zu diesem Zweck in Abschnitt 5.2.4 anwenden werden.
5.2.1 Prozessgruppen mit mehreren Prozessen
Durch Klammerung kann eine Prozessgruppe, die aus mehreren Prozessen besteht, in den Hintergrund gepackt werden.
$ (sleep 1; echo "Hallo") & [12] 9790 $ Hallo
Listing 5.5 Nach einer Sekunde »Hallo« sagen: eine ganze Prozessgruppe im Hintergrund
5.2.2 Wechseln zwischen Vorder- und Hintergrund
In einigen Shells, wie der bash oder der ksh, ist es möglich, zwischen Vorder- und Hintergrundprozessen zu wechseln. Damit aus einem Vordergrundprozess ein Hintergrundprozess wird, muss er erst einmal angehalten (gestoppt) werden. Das wird mittels der Tastenkombination (Strg) + (Z) realisiert. Testen können Sie das folgendermaßen:
$ sleep 10 ^Z[1] + Stopped sleep 10
Listing 5.6 Stoppen eines Vordergrundprozesses
Sollte die Tastenkombination (Strg) + (Z) bei Ihnen nicht funktionieren, ist Ihr Terminal wahrscheinlich auf eine andere Tastenkombination eingestellt. Prüfen Sie die Terminalkonfiguration mit dem Kommando stty. Die Kombination für susp (suspend) ist zum Anhalten eines Prozesses vorgesehen. Die Zeichen vor den Großbuchstaben stehen hierbei für die Tastenkombination (Strg) + Buchstabe.
$ stty -a
…
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z…
…
Listing 5.7 Was tun, wenn's nicht funktioniert?
Um den obigen Vordergrundprozess nun in den Hintergrund zu befördern, muss das bg-Kommando (background) aufgerufen werden. Der Aufruf ist wirklich simpel und erfolgt mittels bg %<Job ID>:
$ sleep 10 ^Z[1] + Stopped sleep 10 $ bg %1 [1] sleep 10 [1] + Done sleep 10
Listing 5.8 Einen Prozess in den Hintergrund befördern
Hin und wieder möchte man jedoch einen Prozess wieder (zurück) in den Vordergrund bringen. Dazu wird das fg-Kommando verwendet:
$ sleep 120 ^Z[1] + Stopped sleep 120 $ bg %1 [1] sleep 120 $ kill -STOP %1 [1] + Stopped (signal) sleep 120 $ fg %1 sleep 120 $
Listing 5.9 Einen Prozess in den Vordergrund holen
Wenn Sie die Prozessnummer eines Hintergrundprozesses mit einem Programm wie kill, fg oder bg verwenden, müssen Sie das Prozentzeichen (%) vor die ID setzen:
$ bg %1 $ fg %1 $ kill -STOP %1 && kill -CONT %1
Listing 5.10 Beispielaufrufe mit Modulo
Alternativ können Sie auch mit der Prozess-ID arbeiten, aber die ist meist eine größere Zahl, und die Gefahr, dass man sich vertippt, ist daher recht hoch. Außerdem sind Informatiker faul.
5.2.3 Jobs – behalten Sie sie im Auge
Oftmals hat man mehrere Prozesse oder gar Prozessgruppen parallel im Hintergrund laufen. Wird die Shell jedoch beendet, werden alle Hintergrundprozesse »mit in den Tod gerissen«. Um dies zu vermeiden, geben die meisten Shells, etwa die Z-Shell, bei dem ersten Versuch, die Shell zu beenden, eine Warnung aus, sofern noch Hintergrundprozesse ablaufen.
Im nächsten Beispiel starten wir in der laufenden Shell eine weitere, um nach der Beendigung der neu gestarteten die Ausgabe der letzten Shell zu sehen.
bash$ zsh … zsh$ sleep 10000& [1] 782 zsh$ exit zsh: you have running jobs. zsh$ exit zsh: warning: 1 jobs SIGHUPed bash$
Listing 5.11 Warnmeldungen der Shells beim Beenden
Nachdem wir uns nicht um den noch laufenden Hintergrundprozess (auch »Job« genannt) gekümmert haben, wird er über ein sogenanntes HUP-Signal (Hang-up) beendet.
Jobs
Um sich eine Übersicht über die momentan laufenden Hintergrundprozesse (Jobs) zu verschaffen, wird das Kommando jobs verwendet.
Dieses Kommando listet alle Hintergrundprozesse der aktuellen Shell auf – also nicht alle Hintergrundprozesse eines Benutzers, denn Benutzer und Benutzerinnen können gleichzeitig mehrere Shells verwenden.
$ jobs [1]+ Running sleep 10000 & $ jobs -p 214 $ jobs -l [1]+ 214 Running sleep 10000 &
Listing 5.12 Das Kommando jobs
Wird der Parameter -p verwendet, werden die Prozess-IDs der Hintergrundprozesse ausgegeben, und bei -l werden sie zur Default-Ausgabe hinzugefügt.
5.2.4 Hintergrundprozesse und Fehlermeldungen
Es kommt sehr häufig vor, dass Hintergrundprozesse störende Fehlermeldungen auf die Konsole schreiben. Dies kann durch eine nicht gefundene Datei, eine Zugriffsverletzung oder Ähnliches hervorgerufen werden. In diesem Fall sollten Sie sich mit der Ausgabeumlenkung der Fehlerausgabe behelfen – natürlich bevor Sie den Prozess starten.
Standarddeskriptoren: Prozesse verfügen über verschiedene Deskriptoren. Die drei Standarddeskriptoren sind die Standardeingabe (0) zum Eingeben von Zeichen, die Standardausgabe (1) zum Ausgeben der normalen Programmausgabe und eine Fehlerausgabe (2) zur Ausgabe der Fehlermeldungen. Diese Deskriptoren werden über ihre Nummern (0–2) angesprochen und können über Pipes und Ausgabeumlenkungen von der Konsole »verbannt« werden.
Schauen wir uns einmal folgendes Beispiel an: Die Benutzerin startet ein Programm im Hintergrund, das versucht, die Datei /etc/blub zu löschen. Dabei wird jedoch eine Fehlermeldung direkt auf die Konsole geschrieben, da diese Datei gar nicht vorhanden ist:
$ rm /etc/blub & [1] 132 $ rm: cannot remove '/etc/blub': No such file or directory [1]+ Exit 1 rm /etc/blub
Listing 5.13 Fehlermeldungen bei Hintergrundprozessen
Stellen Sie sich ein Programm vor, das kontinuierlich einmal pro Sekunde eine ähnlich lästige Meldung ausgibt. Über die Umlenkung des zweiten Deskriptors (also der Fehlerausgabe) kann dies vermieden werden. Leiten wir die Fehlerausgabe einmal in die Datei Logdatei um:
$ rm /etc/blub 2>Logdatei & [1] 133 $ [1]+ Exit 1 rm /etc/blub $ cat Logdatei rm: cannot remove '/etc/blub': No such file or directory
Listing 5.14 Fehlermeldungen umlenken
Auf diese Weise haben wir gleich zwei Fliegen mit einer Klappe geschlagen: Die lästigen Ausgaben sind weg, und wir haben den Fehler archiviert. Jetzt könnten wir sogar den gestressten Support mit der exakten Fehlermeldung nerven!
5.2.5 Wann ist es denn endlich vorbei?
Keine Sorge, dies ist der letzte Abschnitt zum Thema Hintergrundprozesse. Die Überschrift gilt jedoch einer anderen Angelegenheit: dem Warten auf die Beendigung eines Hintergrundprozesses.
Hierfür wird ganz einfach das Kommando wait verwendet. Als Parameter wird der gewünschte Hintergrundprozess, besser gesagt dessen Nummer, angegeben. Wie Sie bereits wissen, muss für Hintergrundprozesse der Modulo-Operator verwendet werden:
$ sleep 10& [1] 237 $ jobs [1]+ Running sleep 10 & $ wait %1 [1]+ Done sleep 10 $
Listing 5.15 wait