Job control
# Job control

Der Ausdruck Job control bezieht sich in Unix und unixoiden Betriebssystemen auf die Kontrolle von Jobs der Shell, insbesondere für Jobs als Darstellung einer Prozessgruppe der Shell. Der Job control beendet mittels Signale die Prozesse, setzt sie fort oder überführt sie in den Standby.

## Überblick

Ein User, welcher Unix oder ein unixoides System über ein Terminal (oder eine Terminalemulation) ausführt, erzeugt zu Beginn nur einen einzigen Prozess: die Shell selbst. Die meisten Aufgaben (Auflisten von Verzeichnissen, Bearbeiten von Dateien etc.) können dabei recht einfach ausgeführt werden, indem dem Programm die Kontrolle über das Terminal überlassen wird und die Shell die Kontrolle nach Beenden des Programms zurückerhält. Formal geschieht dies durch das Einbinden von Standard-Datenströmen in die Shell, welche vom oder in das Terminal schreiben und Eingaben der Tastatur (z. B. das Abbruchsignal der Tastenkombination Strg+C) abfangen.

Manchmal möchte ein User jedoch das Terminal weiter benutzen während ein Prozess läuft. Dabei spricht man von einem Hintergrundprozess, wenn der Prozess keine Eingaben vom Terminal erhält, im gegenteiligen Fall von einem Prozess im Vordergrund. Der Job control ermöglicht dem User, Prozesse im Hintergrund zu starten, bereits laufende Prozesse vom Hintergrund in den Vordergrund zu bringen, Prozesse in den Standby-Betrieb umzuschalten oder Prozesse zu beenden.

Ein Job verbindet das typische Shell-Verhalten eines einzelnen Shell-Kommandos mit dem Möglichkeiten des Betriebssystems mehrere Prozesse zu verarbeiten, die ein Kommando enthalten kann. Mehrprozess-Tasks treten auf, da Prozesse selbst Child-Prozesse erschaffen können und ein einzelnes Shell-Kommando eine Pipeline enthalten kann, welche mehrere Prozesse miteinander verbindet. Als Beispiel dient das folgende Kommando, welches Zeilen mit Textinhalt "title" auswählt, diese alphabetisch sortiert und anschließend mit dem Pager less anzeigt:

Das oben genannte Kommando erschafft mindestens drei Prozesse: einen für grep, einen für sort und einen für less. Der Job control ermöglicht der Shell diese Prozesse als eine einzelne Einheit zu betrachten und sollte ein User die entsprechende Tastenkombination (für gewöhnlich Strg+Z) ausführen, wird die gesamte Prozess-Gruppe in den Standby-Betrieb geführt.

Jobs werden durch das Betriebssystem als einzelne Prozessgruppe verwaltet. Ein Job ist dabei die interne Shell-Darstellung solch einer Gruppe. Die Definition lässt sich in POSIX finden:

   A set of processes, comprising a shell pipeline, and any processes descended from it, that are all in the same process group.
   Eine Menge von Prozessen, die eine Shell-Pipeline enthalten und jegliche abgeleitete Prozesse der gleichen Prozess-Gruppe.

Eine Job-ID ist eine abstrakte, vom Betriebssystem extern verwaltete Referenz der Shell auf eine Ressource (Prozessgruppe) und entspricht daher der Definition eines Handles. Die Shell-Builtin verwendet diese Job-ID um sich auf den entsprechenden Job zu beziehen. Job-IDs sind mit einem % als Anfangszeichen gekennzeichnet, dabei bezeichnet %n den n-ten Job. Der Ausdruck %% bezieht sich auf den aktuellen Job. Weitere Job-IDs sind in POSIX beschrieben. Eine eher informelle Bezeichnung von Job-ID lautet Job-Nummer und in der Bash-Dokumentation findet sich außerdem der Ausdruck „jobspec“.

Job-IDs werden für gewöhnlich nur bei interaktiven Anwendungen genutzt, wo sie lediglich auf Prozess-Gruppen verweisen. Bei Skripten werden stattdessen PGIDs verwendet, welche präziser und widerstandsfähiger sind. Tatsächlich ist der Job control als Default in Bash-Skripten ausgeschaltet.

## Geschichtliches

Seine erste Anwendung fand der Job control in der C-Shell. Die IIASA nutzte im Anschluss die Features des 4.1 BSD Kernels um den Job control auch dort einzuführen. Später wurde er in der von den Bell Labs entwickelten KornShell und in die SVR4-Version der BourneShell eingebunden. Der Job control ist in mittlerweile in den meisten modernen Unix-Shells enthalten.

## Kommandos

Der POSIX-Standard benennt zwei Kommandos um Jobs im Standby-Betrieb im Vorder- oder Hintergrund (siehe fg und bg in Hintergrundprozess) fortzuführen. Diese Kommandos hatten die Job control-Kommandos der Korn shell als Vorbild.

## Ausführung

Für gewöhnlich führt die Shell eine Job Table-Liste aller aktiven Jobs. Dabei sollte beachtet werden, dass ein Job sich auf eine Prozessgruppe bezieht, welche aus allen Elementen einer Pipeline und deren abgeleiteten Prozessen besteht. Mit dem jobs-Befehl lassen sich nun alle Hintergrundprozesse dieser Job Table-Liste einschließlich ihrer Jobnummern und ihren gegenwärtigen Zuständen (gestoppt oder aktiv) anzeigen. Im Fall einer Benutzerabmeldung (Benutzer verlässt die Shell) wird der Session leader-Prozess beendet. Der Shell-Prozess sendet dafür das Abbruchssignal SIGHUP an alle Jobs und beendet sich selbst als letzten Prozess.

Der disown-Befehl kann verwendet werden um Jobs aus der Job Table zu entfernen. Dadurch erhalten die Child-Prozessgruppen beim Beenden einer Login-Session kein SIGHUP-Signal und die Shell wartet auch nicht darauf, dass diese beendet werden. Die Prozesse werden dadurch Orphan-Prozesse und können vom Betriebssystem beendet werden. Meist werden sie jedoch vom init-Prozess adoptiert (dieser wird vom Kernel als ihr Parent-Prozess gesetzt) und arbeiten als Daemon-Prozesse weiter. Eine weitere Möglichkeit um das Beenden der Jobs zu verhindern ist der nohup-Befehl in Verbindung mit einem Terminal multiplexer.

Ein Job im Vordergrund kann durch das Standby-Zeichen (Strg-Z) gestoppt werden, welcher das Terminal-Stop SIGTSTP an die Prozess-Gruppe sendet. Standardmäßig erzwingt der SIGTSTP-Befehl den Stop eines Prozesses und die Kontrolle wird an die Shell zurückgegeben. Ein Prozess kann jedoch einen Signal-Handler zulassen um SIGTSTP zu ignorieren. Ein Prozess kann außerdem mit dem Stop-Signal SIGSTOP pausiert werden, der nicht abgefangen oder ignoriert werden kann.

Vordergrund-Jobs können mittels des Abbruch-Zeichens Strg-C unterbrochen werden. Es wird das Signal SIGINT gesendet, welches standardmäßig den Prozess beendet, aber überschrieben werden kann.

Ein gestoppter Job kann als Hintergrundprozess mit dem Befehl bg oder im Vordergrund mit dem Befehl fg fortgeführt werden. In beiden Fällen leitet die Shell die Eingabe und Ausgabe um und sendet das SIGCONT-Signal an den Prozess, welche das Betriebssystem dazu verleitet die Prozesse fortzuführen. In der Bash kann ein Programm als Hintergrund-Job gestartet werden, indem ein & an die Kommandozeile angehängt wird. Dessen Ausgabe wird verschachtelt mit anderen Prozess-Ausgaben an das Terminal weitergeleitet, kann aber keine Eingabe des Terminals einlesen.

Das Terminal sendet das Signal SIGTTIN und das Signal SIGTTOU bei Ein- und Ausgabeanweisungen von Hintergrundprozessen. Auch wenn diese Signale voreingestellt sind, können sie dennoch modifiziert werden. Manche Shells überschreiben die Voreinstellung des Stop-Befehls SIGTTOU, wodurch Ausgaben der Hintergrundprozesse im Terminal automatisch angezeigt werden.

In Bash-kompatiblen Shells kann der kill-Befehl (/bin/kill ist nur ein einzelner Anwendungsfall) Signale an Jobs oder Prozessgruppen mit bestimmter ID senden. Dabei wird das Signal an die gesamte Prozessgruppe gesendet, weswegen ein Job bestimmter ID mit dem Präfix % beendet werden sollte. kill kann jedes Signal an einen Job senden; um Prozesse aus dem System zu entfernen, sollten jedoch die Signale SIGKILL und SIGTERM verwendet werden.

