Statt Prozesse einfach abzuschießen, fangen Profis deren Kommunikation geschickt ab und lenken mit den passenden Signalen die Arbeit des Rechners gekonnt in die gewünschte Richtung.
Meist kommt der Befehl kill als letztes Mittel der Wahl zum Einsatz: Sofort und ohne Pardon beenden Sie damit einen Prozess. Meist setzen Sie dazu das Kommando kill -9 Prozess-ID ab. Als Option übermitteln Sie das Signal 9, als Argument die Prozess-ID (PID), welche Sie meist aus der Ausgabe des Kommandos ps erhalten. Aber das ist nur eine der Möglichkeiten, die der Mechanismus bietet.
Signale
Die unter Linux gebräuchlichen Signale finden Sie in der Tabelle “Signale”. Verwandte Systeme wie FreeBSD oder Solaris zeigen hier Abweichungen. Bei allen Systemen sind nur die Signale 1, 3, 9 und 15 identisch. Welche das von Ihnen verwendete System unterstützt, erfahren Sie mittels kill -l im Terminal. Den Befehl kill können Sie in der Form kill -ZahlPID oder kill -SignalnamePID anwenden. Beim Einsatz des Signalnamens verwenden Sie den Namen ohne vorangestelltes SIG.
Für einige Beispiele dient daemon.sh aus Listing 1 als Ziel für die Signale. Das Programm zeigt in einer Schleife seinen Shell-Aufruf, die eigene PID und die Prozess-ID des Elternprozess (PPID) an.
Listing 1
#! /bin/sh while true; do echo $0 $$ $PPID sleep 1 done
Signale
| Nummer | Signal | Wirkung | Beispiel/Hinweis |
|---|---|---|---|
| 1 | SIGHUP |
Trennt den Kind- vom Elternprozess | Beendet Prozess |
| 2 | SIGINT |
Beendet Prozess | Entspricht [Strg]+[C] |
| 3 | SIGQUIT |
Beendet Prozess | Prozess Darf Core-Dump erstellen |
| 4 | SIGILL |
Beendet Prozess | nach falschem Aufruf (Rechtemangel, unbekannte Funktionen) |
| 5 | SIGTRAP |
Beendet Prozess | fordert Trace/Debugger an (geht vom Prozess selbst aus) |
| 6 | SIGABRT |
Beendet Prozess | (geht vom Prozess selbst aus) |
| 7 | SIGBUS |
Beendet Prozess | Aufruf durch System nach Speicherzugriffsfehler |
| 8 | SIGFPE |
Beendet Prozess | Bei Division durch 0 |
| 9 | SIGKILL |
Beendet Prozess | Schreibt keine Dateien mehr, Gefahr von Datenverlust |
| 10 | SIGUSR1 |
Benutzerdefiniertes Signal | – |
| 11 | SIGSEGV |
Beendet Prozess | Nach Speicherzugriffsfehler |
| 12 | SIGUSR2 |
Benutzerdefiniertes Signal | – |
| 13 | SIGPIPE |
Beendet Prozess | Problem mit einer Pipe |
| 14 | SIGALRM |
Beendet Prozess | Nach Timer-Ablauf |
| 15 | SIGTERM |
Beendet Prozess | Prozess schreibt Dateien zurück |
| 16 | SIGSTKFLT |
Beendet Prozess | Nach Stack-Fehler am Co-Prozessor |
| 17 | SIGCHLD |
Beendet Kindprozess | Geht vom Elternprozess aus |
| 18 | SIGCONT |
Setzt gestoppten Prozess fort | – |
| 19 | SIGSTOP |
Hält Prozess an | – |
| 20 | SIGTSTP |
Hält Prozess an | entspricht [Strg]+[Z] |
Sanft entfernt
In der Grundform beenden Sie einen Prozess mit SIGHUP. Dabei geht das System so vor, als hätten Sie das betreffende Terminal beendet. Umgekehrt verhindern Sie genau dies, indem Sie beim Programmstart ein nohup voranstellen. Damit läuft die Anwendung weiter, obwohl Sie sich anschließend abmelden, wie bei einer Sitzung via SSH.
Solange niemand etwas gegen den Prozess unternimmt oder den Rechner abschaltet, läuft das Skript, bis es von sich aus terminiert. Die Ausgabe lesen Sie in der Protokolldatei nohup.out, deren Inhalt Sie mittels tail -f nohup.out mitverfolgen.
Auf die gleiche Weise starten Sie bei Bedarf die meisten Daemons im System neu: kill -HUP Daemon-PID. Es empfiehlt sich aber trotzdem, das für den Daemon vorgesehene Init-Skript beziehungsweise den Start/Stop-Mechanismus des Systems zu verwenden.
Bei Bedarf halten Sie Prozesse an, um für den weiteren Verlauf notwendige Ressourcen bereitzustellen. Im Beispiel läuft das Skript daemon.sh und nach einigen Durchgängen erhält es das Signal 19. Das hält den Prozess an. Mittels kill -18 PID setzen Sie dessen Arbeit fort (Abbildung 1).
Signale abfangen
Es gibt keine Möglichkeit, die Signale SIGKILL und SIGSTOP per Trap abzufangen, zu blockieren oder zu ignorieren. Alle anderen dürfen Sie innerhalb von Shell-Skripten nutzen, um eine abweichende Reaktion zu erzeugen. Das Trap-Kommando bauen Sie in der Form trap 'Reaktion' Signale in ein Shell-Skript ein.
Die Zeile trap '' 2 ignoriert ein Signal. Ohne Option und Reaktion liefert es bei fast allen Signalen beim Beenden des Shell-Skripts deren Bezeichnung. Das Signal 10 bricht das Skript aus Listing 2 mit der Ausgabe [2]+ Benutzerdefiniertes Signal 1 ./anzeige.sh ab. Die nutzbaren Signale zeigt trap -l an.
Listing 2
#! /bin/sh trap while true; do clear echo $0 $$ sleep 1 done
Trap eröffnet viele Möglichkeiten: So ermöglicht es etwa Skripts, beim versehentlichen Abmelden von der Shell temporäre Dateien zu löschen. Genauso verhindern Sie einen Abbruch eines Skripts durch fehlerhaftes Bedienen, indem Sie es Signale ignorieren lassen. Umgekehrt greifen Sie mit dieser Technik in den Ablauf eines Shell-Skripts ein, ohne es neu zu starten.
Listing 3 (reaktion.sh) zeigt in knapper Form, wie Sie dies in Form einer Funktion unterbringen. In diesem Beispiel belegt das Skript eine Variable neu und zeigt diese anschließend an. Abbildung 2 zeigt Ihnen den Ablauf.
Listing 3
#! /bin/sh
a="Vorbelegung"
# Funktion definieren
signalabfaenger() {
echo -n "Neuen Wert eingeben: ";read a
if [ -z "$a" ];
then
echo "Keine Eingabe -> Abbruch!"
exit
fi
continue
exit 0
}
# Signal 2 ([Strg]+[C]) abfangen
trap 'signalabfaenger' 2
while true;
do
echo "$$: $a"
sleep 3
done

Abbildung 2: Mittels eines Signals steuern Sie den Ablauf eines Skripts, indem Sie wie in diesem Beispiel eine Variable neu belegen.
Nach dem Start gibt das Skript neben der Prozess-ID den Wert der Variablen $a aus. Durch Drücken von [Strg]+[C] senden Sie das Signal 2, woraufhin das Skript dazu auffordert, einen neuen Wert einzugeben. Den manuellen Eingriff erkennen Sie an den Zeichen ^C im Terminal.
Aufgrund der Anweisung continue für die Schleife läuft das Skript anschließend mit dem neuen Wert für die Variable weiter. Von einer anderen Shell erhält es mittels kill -2 das entsprechende Signal. Im Terminal mit der Anwendung nehmen Sie nun die Eingabe vor – das Skript läuft weiter. Nach erneutem Drücken von [Strg]+[C] und leerer Eingabe beendet sich das Programm.
Eine Uhr für die Shell
Sie finden so etwas auch als schon vorgefertigtes Programm, das vermutlich hübscher aussieht, als ein selbst geschriebenes Skript im ersten Anlauf. Aber im konkreten Fall handelt es sich um eine Retro-Uhr, komplett mit Ausgabe von Tönen im Stil eines C-64, des Klassikers unter den Heimcomputern (Abbildung 3). Diese setzt noch zwei Programme voraus: sysvbanner und beep.

Abbildung 3: Mit ein paar einfachen Hilfsmitteln programmieren Sie eine Uhr für die Shell im Stil eines C-64.
Haben Sie diese Tools installiert, bieten die Skripts aus Listing 4 (uhrmenu.sh) und Listing 5 (uhr.sh) die Möglichkeit, weiter damit zu experimentieren. Starten Sie zuerst uhr.sh. Es legt eine Datei mit dem Namen .uhr.pid ab, die die PID enthält. Rufen Sie dann uhrmenu.sh auf. Es holt sich die Nummer des anderen Prozesses aus der Datei. Nun besteht die Möglichkeit, die Uhr zu steuern.
Listing 4
#! /bin/sh
# Prozess-ID der Uhrzeit holen
uhrpid=$(cat .uhr.pid)
while true;
do
clear
echo "Steuerung der Shell-Uhr"
echo " "
echo "(l) lokale Zeit"
echo "(u) UTC"
echo "(w) Weckzeit setzen"
echo "(e) Uhr beenden"
echo "(E) Steuerung beenden"
echo " "
echo -n "Funktion auswählen: ";read f
if [ "$f" = "E" ]; then
exit
elif [ "$f" = "e" ]; then
kill -15 $uhrpid
elif [ "$f" = "w" ]; then
echo -n "Weckzeit eingeben (Leer für Löschen): ";read wz
echo "$wz" > .uhr.wz
kill -1 $uhrpid
elif [ "$f" = "l" ]; then
kill -10 $uhrpid
elif [ "$f" = "u" ]; then
kill -12 $uhrpid
fi
done
Listing 5
#! /bin/bash
# Ignorieren der Signale 2 und 20
trap '' 2 20
# Ausgeben der Prozess-ID für die Steuerung "uhrmenu.sh"
echo $$ > .uhr.pid
# Leere Variable fuer Weckzeit $wz anlegen
wz=""
# Zeitzone bestimmen: Lokale Zeit
zz="l"
# Weckzeit holen, ggfs. leer lassen
if [ -e .uhr.wz ]; then
wz=$(cat .uhr.wz)
else
touch .uhr.wz
fi
# Hauptschleife
while true;
do
# Signale Aktionen zuordnen, wichtig:
# damit die Schleife weiterlaeuft,
# continue nicht vergessen!
trap 'wz=$(cat .uhr.wz); continue' 1
trap 'zz="l"; continue' 10
trap 'zz="u"; continue' 12
# Zeiten gewinnen
zeit=$(date +%H:%M:%S)
utc=$(date -u +%H:%M:%S)
kurzzeit=$(date +%H:%M)
datum=$(date +%A\ %d.%m.%Y)
woche=$(date +%V)
stundenschlag=$(date +%I)
viertelschlag=$(date +%M)
# Laufbalken Sekunden:
# Die For-Schleife kann mit "08" und "09" nichts anfangen.
# Daher # werden die Sekunden zuerst in $a abgelegt und fuer
# $sekunden über den Rechner bc um 0 vermindert ("cast").
a=$(date +%S)
sekunden=$(echo $a -0 | bc)
clear
# Anzeige Uhrzeit
if [ "$zz" = "l" ]; then
banner $zeit
zzone="Lokale Zeit"
elif [ "$zz" = "u" ]; then
banner $utc
zzone="UTC"
fi
# Anzeige weiterer Informationen
echo " "
echo "gesetzte Weckzeit: $wz"
echo "$datum : $woche. Kalenderwoche Zeitzone: $zzone"
echo "----------------------------------------------------------------"
# Sekundenbalken
for((i=0; i<$sekunden; i++)); do
echo -n "#"
done
# Schlagwerk
if [ "$viertelschlag" != "$merker" ]; then
if [ $viertelschlag -eq 15 ]; then
beep -f 800
elif [ $viertelschlag -eq 30 ]; then
beep -f 800 -r 2 -l 500 -d 500
elif [ $viertelschlag -eq 45 ]; then
beep -f 800 -r 3 -l 500 -d 500
elif [ $viertelschlag -eq 0 ]; then
beep -f 800 -r 4 -l 500 -d 500
beep -f 700 -r $stundenschlag -l 500 -d 600
fi
merker=$viertelschlag
fi
# Wecker
if [ "$kurzzeit" = "$wz" ]; then
if [ "$wz" != "$weckmerker" ]; then
for (( i=1; i <= 5; i++ )); do
beep -f 1000 -n -f 2000 -n -f 1500 -n -f 500 -n -f 300 -n -f 3000
done
weckmerker=$wz
fi
fi
sleep 1
done
# Aufräumen
rm .uhr.pid
Abbildung 4 zeigt das Menü für die Uhr. Es ist recht spartanisch gehalten, verfügt aber über alle notwendigen Funktionen. Das Stellen der Weckzeit erfolgt durch Eingabe der Uhrzeit in der Form SS:MM.
Die Uhr verfügt dank banner über eine große Anzeige. Falls Sie eine Weckzeit setzen, zeigt das Skript diese an. Der Ton beim Wecken hört von selbst wieder auf, die Folge der Töne ist unverkennbar. Mit beep zaubern Sie verschiedene Töne aus dem Rechner.
Gleiches gilt für den Klang des Stundenschlags: Wie eine klassische Uhr erklingen die Viertelstunden mit einem hohen Ton und die Stunden mit einem tieferen. Dieser Stundenschlag teilt den Tag allerdings in zweimal 12 Stunden ein. Es besteht die Möglichkeit, die Weltzeit (UTC) oder die lokale Zeit zu verwenden. Mit der Eingabe von e in uhrmenu.sh beenden Sie die Uhr.
Beachten Sie, dass es notwendig ist, nach einem Neustart der Uhr die kontrollierende Software ebenso zu beenden und neu aufzurufen: Sonst sendet diese Signale an einen falschen oder nicht mehr vorhandenen Prozess. Die Töne, die beep hervorbringt, gibt es standardmäßig auf dem PC-Lautsprecher aus. Bei Bedarf steuern Sie die Uhr (mit Ausnahme der Weckzeit), aber auch die anderen Beispiele, mittels Htop (Abbildung 5).

Abbildung 5: Das Konsolentool Htop arbeitet ebenfalls mit Signalen und erlaubt es auf diese Weise, Prozesse zu steuern.
Fazit
Ein Linux-System bietet mit seinem Konstrukt aus Prozessen den Signalen eine Fülle von Möglichkeiten. Wer sich mit dem Thema etwas mehr beschäftigt, stellt schnell fest, dass die Robustheit und die langen Laufzeiten von Linux/Unix-Rechnern vor allem auf diesen ausgeklügelten Mechanismen beruhen, die ein fortwährendes Neustarten unnötig machen.
Darüber hinaus ergeben sich für den ambitionierten Programmierer faszinierende Möglichkeiten, um ein Shell-Skript nach außen mit einer Schnittstelle zu erweitern, über die Sie während des Ablaufs mit dem Programm kommunizieren.
Der Autor
Harald Zisler beschäftigt sich seit rund 20 Jahren mit FreeBSD und Linux. Zu Technik- und EDV-Themen verfasst er Bücher und Beiträge für Zeitschriften. Aktuell ist sein Werk “Computer-Netzwerke”, erschienen bei Galileo Press.







