bash_5

Prozessverwaltung und Signale

Einstieg in die Bash-Shell - Teil 5

Vorab zwei Befehle, die Information über den eigenen Computer ausgeben:

In [27]:
uname -a
Linux platon 5.4.0-87-generic #98~18.04.1-Ubuntu SMP Wed Sep 22 10:45:04 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
In [28]:
# wie lange ist der Rechner schon an
uptime
 10:52:07 up 20 days,  2:00,  1 user,  load average: 0,09, 0,10, 0,34

Wiederholung: Prozess

Ein Prozess) (aus dem Lateinischen procedere, „vorwärts gehen“) ist ein laufendes Programm. Dabei kann dasselbe Programm mehrfach quasi-parallel ausgeführt werden. Prozesse besitzen eine eindeutige Identifikationsnummer (ID), die Prozess-ID (PID).

Mit Hilfe des Befehls ps kann man Informationen zu laufenden Prozessen auflisten. Aus der Manpage:

   By default, ps selects all processes with the same effective user ID
   (euid=EUID) as the current user and associated with the same terminal
   as the invoker.  It displays the process ID (pid=PID), the terminal
   associated with the process (tname=TTY), the cumulated CPU time in
   [DD-]hh:mm:ss format (time=TIME), and the executable name (ucmd=CMD).
   Output is unsorted by default.
In [41]:
# "ps" ohne Schalter zeigt nur alle laufenden Prozesse in der laufenden Terminalsession 
ps
  PID TTY          TIME CMD
 4970 pts/7    00:00:00 bash
32156 pts/7    00:00:00 ps
In [59]:
# Schalter u für alle eigenen Prozesse (User)
ps u | head -8 | colrm 87

# Erinnerung: Mittels head -8 werden nur die ersten 8-Zeilen ausgegeben
#             colrm 87 zeigt nur die ersten 86 Zeichen jeder Zeile an
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chris     2333  0.0  0.2 22573484 156648 tty2  Sl+  Nov05   0:31 /usr/lib/chromium-bro
chris     2936  0.0  0.0  23116  5328 pts/0    Ss+  Nov05   0:00 /bin/bash --rcfile /h
chris     3450  0.0  0.2 22583332 153732 tty2  Sl+  Nov01   2:34 /usr/lib/chromium-bro
chris     3838  0.0  0.2 22570820 147768 tty2  Sl+  Nov09   1:21 /usr/lib/chromium-bro
chris     3941  0.0  0.3 22617112 238532 tty2  Sl+  Nov12   0:13 /usr/lib/chromium-bro
chris     4768  0.0  0.4 1377512 291524 tty2   Sl+  Nov15   0:04 evince /tmp/mozilla_c
chris     4872  0.0  0.2 22607888 191716 tty2  Sl+  Nov02   9:33 /usr/lib/chromium-bro
  • USER: Owner des Prozesses
  • PID: Prozess ID
  • %CPU: CPU-Verbrauch in Prozent
  • %MEM: Speicherverbrauch in Prozent
  • VSZ: Virtual memory size
  • RSS: Residual set size: Verbrauch des physikalischen Speichers (RAM) in kByte.
  • START: Startzeit bzw. Startdatum
  • TIME: Verbrauchte Rechenzeit
  • TTY: Control Terminal von dem der Prozess gestartet wurde. ? für no control terminal.

Information über STAT aus der ps man-Page:

PROCESS STATE CODES
   Here are the different values that the s, stat and state output
   specifiers (header "STAT" or "S") will display to describe the state of
   a process:

           D    uninterruptible sleep (usually IO)
           R    running or runnable (on run queue)
           S    interruptible sleep (waiting for an event to complete)
           T    stopped by job control signal
           t    stopped by debugger during the tracing
           W    paging (not valid since the 2.6.xx kernel)
           X    dead (should never be seen)
           Z    defunct ("zombie") process, terminated but not reaped by
                its parent

Übung

Aufgabe

Listen Sie mit

  • ps -af

Prozesse auf. Wofür stehen die Optionen? Sehen Sie sich hierzu die Manpage an.

top

Aufgabe

Sehen Sie sich die Manpage zu topan und führen Sie den Befehl top aus.

Hinweis: Mit der Taste h erhalten Sie wieder Hilfe und q dient wieder zum Beenden.

Hinweis: Neben top gibt es auch das etwas neuere htop. Dieses Programm muss aber unter Umständen nachinstallieren werden.

Aufgabe

Studieren Sie ggf. das Kapitel 1.9 Vom Shellscript zum Prozess des Buchs Shell-Programmierung von Jürgen Wolf.

Prozesse im Hintergrund laufen lassen

Mit & am Ende eines Befehls oder Befehlskette laufen der Prozess bzw. die Prozesse der Befehlskette im Hintergrund.

Beispiel:

In [97]:
{ sleep 5; echo "Hallo"; } &
[1] 32661

Hier wird die Nummer des Hintergrundprozesses die Job-Id in eckigen Klammern und die Prozess-ID (des Prozessgruppenleaders) ausgegeben.

In [98]:
echo $! # gebe die PID des letzten im Hintergrund gestarteten Prozess aus
32661

Hinweis

Manchmal kommt es vor, dass Prozesse, die im Hintergrund laufen Fehlermeldungen (oder normale Ausgaben) auf die laufende Kommandozeilenumgebung schreiben. Mit Hilfe der Dateiumleitungen können solche Ausgaben in Dateien umgeleitet werden, wie in diesem Beispiel:

rm /etc/not-exists 2>errorlog &

Signale

Signale) dienen als Kommunikationsmittel (unter UNIX) und können zu Prozessen gesendet werden. Signale sind Software Interrupts.

Wir verwenden folgendes python-Skript (adaptiert von Missing Semester ...), um zu zeigen, wie Signale funktionieren:

#!/usr/bin/env python

# adaptiert von https://missing.csail.mit.edu/2020/command-line/

import signal, time
import os

# Eine Funktion in Python mit zwei Argumenten
def handler(signum, time):
    print("\nI got a SIGINT, but I am not stopping")

print("Meine Prozess-ID ist: ", os.getpid())

# registrieren des signal-handlers
signal.signal(signal.SIGINT, handler)

i = 0
while True:
    time.sleep(.1)
    print("\r{}".format(i), end="")
    i += 1

Erzeugen Sie eine Datei mysignal.py mit dem obigen Inhalt. Starten Sie obiges Python Skript auf der Kommandozeile im Vordergrund. Sie sollten wissen, wie das funktioniert.

Der Endlos-Loop bewirkt, dass das Skript sich nicht einfach selbst beendet.

Solchen Prozessen, die in Vordergrund laufen, kann man über folgende Tastenkombinationen Signale senden (Standard - kann aber mit dem Befehl stty geändert werden):

  • Strgc: SIGINT Interrupt from keyboard
  • Strg\\: SIGQUIT Quit from keyboard
  • Strgz: SIGTSTOP Stop process

Probieren Sie es aus:

  • Drücken Sie Strgc, um das Signal (SIGINT) an das Skript zu senden. Das Skript fängt das Signal ab und gibt dies auf der Kommandozeile aus.
  • Mit Strg\\ können Sie ein anderes Signal (SIGQUIT) an das Skript senden, um das Skript wirklich zu beenden.
  • Mit Strgz (SIGTSTP - signal terminal stop) können Sie den Prozess anhalten. SIGTSTP ist die Terminalversion von SIGSTOP (aber SIGSTOP is unblockable).
    • mit bg (background) (siehe help bg) können Sie den Prozess im Hintergrund weiter laufen lassen. Background-Jobs wird nicht das Signal per Tasterkombination gesendet.
    • mit fg (foreground) (siehe help fg) können Sie den Prozess wieder "nach vorne" schieben (Vordergrund), um ihn beispielsweise mit Strg\\ (SIGQUIT) zu beenden.

kill Befehl

Mit dem Befehl kill kann man ein Signal zu einem Prozess schicken, siehe auch man kill.

Starten Sie wieder das obige Python Skript im Hintergrund. Hierzu dient ein & am Ende der Eingabe. Senden Sie dann ein Signal per kill Befehl:

  • kill -SIGQUIT <process-id> bzw. kill -3 <process-id>: Der Schalter -3 steht dabei auch für das Signal SIGQUIT. Die noch benötigete Prozess-ID gibt das Skript aus. Sie können diese aber auch mit ps oder top erhalten. Diese Befehle kennen Sie ja bereits. Außerdem ist per echo $! die PID des letzten background-Jobs erhältlich.

Das Signal SIGQUIT hat die 3 als Signal-ID. Welche Signal-IDs für welche Signale sehen, finden Sie z.B. in der Signal Manpage (man signal). Hier ein kurzer Auszug:

```First the signals described in the original POSIX.1-1990 standard.

   Signal     Value     Action   Comment
   ──────────────────────────────────────────────────────────────────────
   SIGHUP        1       Term    Hangup detected on controlling terminal
                                 or death of controlling process
   SIGINT        2       Term    Interrupt from keyboard
   SIGQUIT       3       Core    Quit from keyboard
   SIGILL        4       Core    Illegal Instruction
   SIGABRT       6       Core    Abort signal from abort(3)
   SIGFPE        8       Core    Floating-point exception
   SIGKILL       9       Term    Kill signal
   SIGSEGV      11       Core    Invalid memory reference
   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers; see pipe(7)
   SIGALRM      14       Term    Timer signal from alarm(2)
   SIGTERM      15       Term    Termination signal
   SIGUSR1   30,10,16    Term    User-defined signal 1
   SIGUSR2   31,12,17    Term    User-defined signal 2
   SIGCHLD   20,17,18    Ign     Child stopped or terminated
   SIGCONT   19,18,25    Cont    Continue if stopped
   SIGSTOP   17,19,23    Stop    Stop process
   SIGTSTP   18,20,24    Stop    Stop typed at terminal
   SIGTTIN   21,21,26    Stop    Terminal input for background process
   SIGTTOU   22,22,27    Stop    Terminal output for backgr
   ound process

   The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.```
  • SIGCONT (18) wird bespielsweise von bg und fg gesendet.
  • Mit dem Signal SIGKILL können Sie nicht mehr reagierende Prozesse -als letzes Mittel- beenden. Hier wird aber nicht mehr richtig aufgeräumt. Das kann z.B. dazu führen das Kind-Prozesse verweist übigbleiben. Wenn dies beenden (terminieren) wird dies typischerweise dem aufrufenden Prozess mitgeteilt. Da dies dann nicht mehr möglich ist, bleiben Zombie-Prozesse übig.

Weitere Befehle zum Senden von Signalen sind:

  • pgrep bzw. pkill
  • killall

Wenn das Terminal bzw. das Kommandozeilenfenster (Terminalemulation) geschlossen wird, erhalten die Prozesse die im Hintergrund gestartet wurden das Signal SIGHUP. Dadurch werden sie typischerweise beendet. Um zu vermeiden, dass das Signal SIGHUP beim Beenden des Terminals gesendet wird, dient nohup (ein Wrapper der das Signal auffängt). Eine Alternative ist disown. Mehr hierzu siehe https://unix.stackexchange.com/questions/3886/difference-between-nohup-disown-and

Folgende Beispielsession zeigt diese (aus https://missing.csail.mit.edu/2020/command-line): Mit jobs werden dabei alle Hintergrundprozesse der laufenden Shell angezeigt.

$ sleep 1000
^Z
[1]  + 18653 suspended  sleep 1000

$ nohup sleep 2000 &
[2] 18745
appending output to nohup.out

$ jobs
[1]  + suspended  sleep 1000
[2]  - running    nohup sleep 2000

$ bg %1
[1]  - 18653 continued  sleep 1000

$ jobs
[1]  - running    sleep 1000
[2]  + running    nohup sleep 2000

$ kill -STOP %1
[1]  + 18653 suspended (signal)  sleep 1000

$ jobs
[1]  + suspended (signal)  sleep 1000
[2]  - running    nohup sleep 2000

$ kill -SIGHUP %1
[1]  + 18653 hangup     sleep 1000

$ jobs
[2]  + running    nohup sleep 2000

$ kill -SIGHUP %2

$ jobs
[2]  + running    nohup sleep 2000

$ kill %2
[2]  + 18745 terminated  nohup sleep 2000

$ jobs

Weitere Kommandos mit Prozess-Bezug:

  • pstree: display a tree of processes
  • vmstat : report virtual memory statistics
  • tload: graphic representation of system load average

Prozesspriorität verändern

Mit Hilfe des Programms nice können Benutzer:innen freundlich sein und die (Scheduler-)Zeitscheiben beim Start ihrer Prozesse herabsetzen. renice erlaubt dies für bereits gestartete Prozesse. Adminstrator:innen (su) können natürlich auch das Prozess-Scheduling der Prozesse anderer verändern (und hier negative Werte setzen - höhere Priorität). Mehr hierzu z.B. mittels man nice bzw. man renice.

Aufgabe: Vertiefung und Wiederholung (optional)

Lesen Sie ggf.