Über Signale Prozesse in der Shell steuern

Aus LinuxUser 11/2014

Über Signale Prozesse in der Shell steuern

© Kirsty Pargeter, 123RF

Klar signalisiert

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).

Abbildung 1: Starten, Anhalten und Fortsetzen eines Prozesses.

Abbildung 1: Starten, Anhalten und Fortsetzen eines Prozesses.

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.

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.

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.

Abbildung 4: Über ein einfaches Menü steuern Sie die Uhr für die Shell.

Abbildung 4: Über ein einfaches Menü steuern Sie die Uhr für die Shell.

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.

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.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 11/2014 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben