Einstieg in die Bash-Shell - Teil 5
Vorab zwei Befehle, die Information über den eigenen Computer ausgeben:
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
# 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
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.
# "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
# 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 567 0.8 0.9 3550856 594104 tty2 SLl+ Jun26 48:30 /usr/lib/chromium-bro chris 583 0.0 0.0 446300 53404 tty2 S+ Jun26 0:00 /usr/lib/chromium-bro chris 584 0.0 0.0 446288 52540 tty2 S+ Jun26 0:00 /usr/lib/chromium-bro chris 586 0.0 0.0 446312 14316 tty2 S+ Jun26 0:02 /usr/lib/chromium-bro chris 610 1.9 0.4 2900108 263148 tty2 Sl+ Jun26 108:48 /usr/lib/chromium-bro chris 611 0.2 0.2 3088960 154608 tty2 Sl+ Jun26 14:32 /usr/lib/chromium-bro chris 621 0.0 0.1 974904 77440 tty2 Sl+ Jun26 0:47 /usr/lib/chromium-bro
USER
: Owner des ProzessesPID
: Prozess ID%CPU
: CPU-Verbrauch in Prozent%MEM
: Speicherverbrauch in ProzentVSZ
: Virtual memory sizeRSS
: Residual set size: Verbrauch des physikalischen Speichers (RAM) in kByte. START
: Startzeit bzw. StartdatumTIME
: Verbrauchte RechenzeitTTY
: 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
Hinweis: Neben top
gibt es auch das etwas neuere htop
. Dieses Programm muss aber unter Umständen nachinstallieren werden.
Studieren Sie das Kapitel 1.9 Vom Shellscript zum Prozess des Buchs Shell-Programmierung von Jürgen Wolf.
Mit &
am Ende eines Befehls oder Befehlskette laufen der Prozess bzw. die Prozesse der Befehlskette im Hintergrund.
Beispiel:
{ 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.
echo $! # gebe die PID des letzten im Hintergrund gestarteten Prozess aus
32661
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 &
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/
# Kommentare starten mit einer Raute "#"
# Importieren von Bibiotheken mittels import:
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: # Endlosschleife
time.sleep(.1) # Warte 0.1 Sekunden
print("\r{}".format(i), end="")
i += 1
Hinweise zum Skript:
Die Funktion handler
in obigen Skript bekommt zwei Argumente. Diese werde in der obigen Funktionsdefinition nicht verwendet. Die registrierten Signalhandler werden aber mit zwei Parametern aufgerufen, daher muss die Funktion diese Signatur (zwei Parameter) haben.
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):
SIGINT
Interrupt from keyboardSIGQUIT
Quit from keyboardSIGTSTOP
Stop processProbieren Sie es aus:
SIGINT
) an das Skript zu senden.
Das Skript fängt das Signal ab und gibt dies auf der Kommandozeile aus.SIGQUIT
) an das Skript senden,
um das Skript wirklich zu beenden.SIGTSTP
- signal terminal stop) können Sie den Prozess anhalten. SIGTSTP
ist die Terminalversion von SIGSTOP
(aber SIGSTOP
is unblockable).bg
(background) (siehe help bg
) können Sie den Prozess im Hintergrund weiter laufen lassen. Background-Jobs wird nicht das Signal per Tasterkombination gesendet.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.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 processesvmstat
: report virtual memory statisticstload
: graphic representation of system load averageMit Hilfe des Programms nice
können Benutzer:innen freundlich sein und die (Scheduler-)Zeitscheiben beim Start der Prozesse herabsetzen. renice
erlaubt das Herabsetzten der Prozesspriorität für bereits gestartete Prozesse.
Adminstrator:innen (su
, root) können auch das Prozess-Scheduling der Prozesse anderer verändern (und hier negative Werte setzen, d.h. höhere Priorität).
Mehr hierzu z.B. mittels man nice
bzw. man renice
.
Lesen Sie ggf.
Lesen Sie den kurzen Artikel zu Zombie-Prozessen im Linux-Journal.