Linux/Prozess

Aus Foxwiki
Version vom 28. Mai 2023, 10:38 Uhr von Dirkwagner (Diskussion | Beiträge) (Textersetzung - „  “ durch „ “)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Linux- Prozessverwaltung

  • Prozesse bilden das tragende Konzept eines jeden Betriebssystems. Prozesse werden gestartet, angehalten, reaktiviert, beendet, ihre Ausgaben unterbunden usw.
  • Alle Maßnahmen, die die Arbeit der Prozesse beeinflussen, fasst man daher unter dem Begriff der Prozesssteuerung zusammen.
  • Die Shells tragen der Bedeutung solcher Mechanismen Rechnung, indem sie unterschiedlichste Möglichkeiten bieten, Prozessen die Richtung zu weisen.
  • Wenn die Festplatte kreischt, oder der Bildschirm flimmert, wenn die Soundkarte tönt oder der Prozessor sich erhitzt,... - immer dann zeichnet ein Prozess dafür verantwortlich.
  • Programme sind die Ablaufpläne, nach denen etwas zu verrichten ist und Prozesse die Instanzen, die letztlich die Arbeit verrichten.
  • Wann immer im System sich etwas dreht, dann ist ein Prozess am werkeln und selbst wenn der Prozessor scheinbar ruht, ist ein Prozess - der Idle-Prozess - aktiv und tut nichts anderes, als zu warten, dass sich wieder etwas tut.
  • So ein Prozess im System steht nicht nur für sich allein, sondern ist in die Gesellschaft von seinesgleichen integriert, mit denen er auch Informationen austauschen kann.
  • Die dabei grundlegende Beziehung ist die Eltern-Kind-Verwandtschaft zwischen einem Prozess und den von ihm erzeugten Prozessen.
  • Der Ursprung aller Prozesse und »Prozessfamilien« liegt im init-Prozess, der als erster Prozess beim Start des Systems mit der Prozessnummer (PID) 1 ins Leben gerufen wird, und dann die elementaren Programme des Systems startet.
  • Wenn man alle im System laufenden Prozesse auf Basis dieser Beziehung betrachtet, erhält man einen Prozessbaum. Die Hierarchie lässt sich z. B. mit dem Kommando pstree darstellen:
pstree
 init-+-actived
      |-atd
      |-cron
      |-in.identd---in.identd---5*[in.identd]
      |-inetd
      |-innd-+-archive
      |      |-controlchan
      |      |-nnrpd
      |      `-overchan
      |-kdm-+-X
      |     `-kdm---fvwm2-+-FvwmButtons
      |                   |-FvwmPager
      |                   |-netscape---netscape
      |                   |-xosview.bin
      |                   |-xterm---tail
      |                   |-xterm---bash-+-bash1
      |                   |              `-objectman
      |                   |-2*[xterm---bash]
      |                   |-xterm---bash-+-su---bash
      |                                  `-xterm---bash
      |-kflushd
      |-klogd
      |-kpiod
      |-kswapd
      |-kupdate
      |-lockd---rpciod
      |-login---sh

Was ist ein Prozess?

  • Ein Stück Programm-Code, das zur Ausführung in den Hauptspeicher geladen wurde, und im System durch Parameter wie Prozessnummer (PID), Elternprozessnummer (PPID), Besitzer, Priorität, Nice-Level, Status usw. gekennzeichnet ist.

Einige Parameter bringt das Kommando ps zum Vorschein:

ps -ef | head
 UID      PID  PPID  C STIME TTY        TIME CMD
 root       1     0  0 May24 ?      00:00:03 init [3]
 root       2     1  0 May24 ?      00:00:02 [kflushd]
 root       3     1  0 May24 ?      00:00:00 [kupdate]
 root       4     1  0 May24 ?      00:00:00 [kpiod]
 root       5     1  0 May24 ?      00:00:11 [kswapd]
 root       6     1  0 May24 ?      00:00:00 [md_thread]
 bin       90     1  0 May24 ?      00:00:00 [portmap]
 root      98     1  0 May24 ?      00:00:06 /usr/sbin/scanlogd
 root     104     1  0 May24 ?      00:00:01 /usr/sbin/syslogd 
  • Die PID (Prozessnummer) ist die eindeutige Kennzeichnung eines Prozesses während der Laufzeit des Systems, PPID ist die Prozessnummer seines Elternprozesses und UID (Nutzerkennung) der den Prozess startende Benutzer.
  • Wichtig zu wissen ist, dass ein Prozess, dessen Elternprozess nicht mehr existiert, automatisch dem init-Prozess (PID=1) zugeordnet wird. Die Priorität (C) ist ein während der Laufzeit der Prozesses dynamisch bestimmter Wert, der für die Zuteilung von CPU-Zeit verwendet wird.
  • Des Weiteren sehen wir bei der Ausgabe von ps die Startzeit STIME, das mit dem Prozess verbundene Terminal TTY (in obigem Beispiel sind allerdings nur Prozesse dargestellt, die keine Ausgaben auf ein Terminal tätigen; diesen Sachverhalt kennzeichnet das Fragezeichen) und den Namen der Programme, die von den Prozessen ausgeführt werden.

Wie wird eine Prozess generiert

und wie sieht sein weiterer Weg aus?

  • Die beiden wesentlichen Aktionen bei der Prozessentstehung sind die Systemaufrufe fork() und exec(). Mittels fork() wird ein neuer Prozess erzeugt, der zunächst dasselbe Programm wie sein Elternprozess ausführt.
  • Erst mit dem Aufruf von exec() wird ein Prozess das alte Programm durch ein neues ersetzen und dessen Ausführung beginnen.
  • Der exec() Aufruf hat seinen Weg auch in den Funktionsumfang der in die Shell eingebauten Kommandos gefunden:
exec ls
login: 
Erklärung
  • Im Beispiel wurde das Programm des laufenden Prozesses durch das Kommando ls ersetzt.
  • Da der aktive Prozess die Shell selbst ist, wird diese beendet und nachdem nun auch ls seine Tätigkeit abgeschlossen hat, finden wir uns auf der Login-Konsole wieder (sofern es sich um die Login-Shell selbst handelte).

fork() existiert nicht als eigenständiges Kommando. Eine Shell wird diesen Systemruf immer tätigen, um ein Programm innerhalb eines neuen Prozesses auszuführen.

  • Eine Shell vermag (fast) beliebig viele Prozesse zu starten, jedoch wartet sie in den häufigsten Fällen auf die Terminierung des zuletzt gestarteten Prozesses:
sleep 100
# 100 Sekunden verstreichen, die Shell wartet...
  • Uns als Anwender steht es nun zu, einem Prozess zu signalisieren, dass er sich z. B. regulär beenden [Ctrl]-[D] , ist i.A. programmabhängig) oder seine Verarbeitung abbrechen ([Ctrl]-[C]) soll:
cat
 Die Eingabe muss mittels [Ctrl]-[D] beendet[Enter]
 Die Eingabe muss mittels [Ctrl]-[D] beendet
 oder mit [Ctrl]-[C] abgebrochen werden[Enter]
 oder mit [Ctrl]-[C] abgebrochen werden
 [Ctrl]-[C] 

Anmerkung 'Das reguläre Beenden eines Prozesses kann auf verschiedenen Wegen erfolgen. '

  • In obigem Beispiel ist das Programm cat eigentlich dazu gedacht, aus einer Datei zu lesen, und beendet sich, sobald das Dateiende erreicht ist.
  • Wird jedoch von der Standardeingabe eingelesen, und ist diese wie in diesem Fall mit der Tastatur verbunden, so muss der Nutzer dem Programm signalisieren, dass das »Dateiende« erreicht wurde.
  • Dies erfolgt mit dem End-of-File-Zeichen (kurz EOF), das auf der Tastatur mit der Tastenkombination [Ctrl]-[D] generiert wird.
  • In solchen Fällen bestünde keine Möglichkeit, beliebig viele Prozesse quasi-parallel zu starten, da der soeben initiierte Prozess die Shell für weitere Eingaben blockiert.
  • Die Lösung des Problems liegt im Verschieben des Prozesses in den Hintergrund.
  • Dabei wird er von der Ein- und Ausgabe der Shell abgekoppelt und läuft im Hintergrund weiter, bis er sich selbst beendet, oder aber auf Grund einer notwendigen Interaktion mit dem Benutzer zum Anhalten gezwungen ist.
# Start eines Hintergrundprozesses, der keine Eingaben erwartet
ls -lR > /dev/null &
[1] 706
"beliebiges Arbeiten auf der Kommandozeile" [Enter]
[1]+  Done          ls $LS_OPTIONS -lR >/dev/null

# Start eines Hintergrundprozesses, der Eingaben erwartet
(ls -Rl >/dev/null; cat)&
[1] 720
[Enter]

[1]+  Stopped       (ls $LS_OPTIONS -Rl >/dev/null; cat)
fg
(ls $LS_OPTIONS -Rl >/dev/null; cat)
hallo[Enter]
hallo
[Ctrl]-[D]
  • Jedes im Hintergrund laufende Programm wird als Job bezeichnet.
  • Nach Beendigung der Eingabe des Kommandos mit [Enter] gibt die Bash auf der Standardfehlerausgabe eine Zeile mit Jobinformationen aus, wie am Beispiel zu erkennen ist.
  • Diese beinhaltet in eckigen Klammern eine fortlaufende, von der Shell vergebene Jobnummer und die vom System dem Prozess zugeordnete Prozessnummer (PID).
  • Mit dem Kommando jobs kann man Informationen über die derzeit auf einer Shell laufenden Hintergrundprozesse erlangen:
cat &
 [1] 1343
 
 [1]+  Stopped            cat
 
 time dd count=1000000 if=/dev/zero of=/dev/null &
 [2] 1346
 jobs
 [1]+  Stopped            cat
 [2]-  Running            time dd count=1000000 if=/dev/zero of=/dev/null &
 1000000+0 Records ein
 1000000+0 Records aus
 
 real    0m13.817s
 user    0m9.430s
 sys     0m4.040s
 [2]-  Done               time dd count=1000000 if=/dev/zero of=/dev/null
 exit
 exit
 There are stopped jobs.
 jobs
 [1]+  Stopped            cat
 exit
# Shell wurde beendet

Ohne direkten Kontakt

  • Die bisher vorgestellten Fälle betrachteten nur die Möglichkeit, Programme während einer Sitzung parallel von einer Shell aus zu starten, und im Hintergrund ablaufen zu lassen.
  • Interessant ist aber auch der Weg, Programme auf der Shell zu starten, und diese nach dem Abmelden, sprich dem Beenden der benutzten Shell, weiter laufen zu lassen.
  • Bisher waren in allen Beispielen die gestarteten Programme an die aufrufende Shell gebunden. So dass bei Beendigung selbiger auch die an sie gebundenen Prozesse den Aufruf zum Beenden erhalten.
  • Um Programme von der Shell abzukoppeln, wird das Kommando nohup verwendet.
  • Dieses schirmt das Programm vom HUP-Signal der Shell ab, so dass es nach dem Beenden der Shell unabhängig weiter laufen kann:
nohup ls -lR / >/dev/null &
 [1] 2511
 ps -ef | grep 2511
 user     2511  2502 20 00:28 tty5    00:00:02 ls -lR /
 user     2514  2502  0 00:28 tty5    00:00:00 grep 2511
 exit
 
 ...auf einer anderen oder neuen Shell:
 Have a lot of fun...
 ps -ef | grep 2511
 user     2511     1 14 00:28 ?       00:00:05 ls -lR /
 user     2524  2516  0 00:29 tty5    00:00:00 grep 2511

nohup startet das Programm nicht selbstständig im Hintergrund.

  • So ist es möglich, ein Programm analog zum »normalen« Vorgehen (ohne »nohup«) zu starten, eventuell notwendige Eingaben vorzunehmen und erst im Anschluss das Programm durch Eingaben von [Ctrl]-[Z] und nachfolgendem bg in den Hintergrund zu schicken.
  • Erst jetzt existiert der das Programm ausführende Prozess tatsächlich abgenabelt von seinem Vorfahren.
  • Ein Prozess, der keine Eingaben benötigt, lässt sich bequem durch ein der Kommandozeile nachgestelltes & unverzüglich vom Elternprozess entkoppeln.
  • Und welche Möglichkeiten bleiben mir nun, um solch einen Prozess nachträglich zu beeinflussen?
  • Eine direkte Verbindung zum Terminal wie bei fg ist nicht mehr möglich. Hier hilft nur noch eine indirekte Kommunikation über Signale, die u.a. mit dem Kommando kill übermittelt werden können:
dd if=/dev/zero of=/dev/null &
 [1] 20098
 
 ps -axuw | grep 20098
 user    20098 30.5  0.3  1204  448 pts/4    R    11:49   0:36 dd if=/dev/zero of=/dev/null
 kill -19 20098
 ps -axuw | grep 20098
 user    20098 75.9  0.3  1204  448 pts/4    T    11:49   1:01 dd if=/dev/zero of=/dev/null
 kill -18 20098
 ps -axuw | grep 20098
 user    20098 55.5  0.3  1204  448 pts/4    R    11:49   1:54 dd if=/dev/zero of=/dev/null
 kill -15 20098 

Erläuterung

  • Das Kommando »dd« (zum »Low-Level«-Kopieren) wird im Hintergrund gestartet. Dass es aktiv ist, bestätigt das »R« (running) in der Ausgabe des Kommandos ps. In einer weiteren Eingabe wird dem Prozess die Aufforderung zum Stoppen signalisiert (Signal Nr. 19).
  • Dass es tatsächlich funktioniert hat, beweist nun das »T« (traced oder gestoppt) in der »ps«-Ausgabe.
  • Mit dem Signal 18 schließlich wird der Prozess reaktiviert; das »R« ist wieder in der Ausgabe zu sehen.
  • Damit unser Beispielkommando nicht für alle Zeit die Nullen in den Mülleimer befördert, brechen wir es mit dem Signal 15 (Aufforderung zum Beenden) ab.

Entstehen und Vergehen

Ein Prozess entsteht, indem ein Elternprozess mittels des Systemrufes fork() einen neuen Prozess erzeugt (Ausnahme ist init, der einzige "von Hand generierte" Prozess). Dieser Kindprozess teilt sich alle Ressourcen mit dem Elternprozess, wesentliche Unterschiede sind nur ein eigener Stack und eine eigene PID.

  • Anhand des Rückgabewertes von fork() ist es den beiden Prozessen nun möglich, zu unterscheiden, ob es sich um den Vorfahren oder einen Nachkommen handelt.
  • In Abhängigkeit hiervon wird nun ein Kindprozess mittels des Systemrufes exec() ein neues Programm laden.
  • Irgendwann wird ein Kindprozess seine Arbeit beenden und signalisiert diesen Zustand seinem Elternprozess durch eine entsprechende Nachricht.
  • Obwohl der Kindprozess bereits jetzt aus Speicher und Prozesstabelle entfernt ist, muss dieses Signal noch verarbeitet werden. Für gewöhnlich zeichnet der Elternprozess dafür verantwortlich.
  • Zwei Situationen könnten den "normalen Werdegang" durcheinander bringen:
  • Der Elternprozess ist andersweitig beschäftigt (befindet sich im Zustand D o.a.).
  • In einem solchen Fall symbolisiert der Zustand Z das noch zu behandelnde Signal. Genau genommen durchläuft jeder Kindprozess die Laufbahn eines Zombies (Zeitspanne zwischen Ableben und Signalbehandlung), jedoch ist sie meist von so kurzer Dauer, dass man gar nichts davon mitbekommt.
  • Der Elternprozess existiert nicht mehr (Programmfehler o.a.). Jetzt ist der init-Prozess für der Signalbehandlung verantwortlich

Was ist ein Prozess?

   Ein Stück Programm-Code, das zur Ausführung in den Hauptspeicher geladen wurde, und im System durch Parameter wie Prozessnummer (PID), Elternprozessnummer (PPID), Besitzer, Priorität, Nice-Level, Status usw. gekennzeichnet ist.

Einige Parameter bringt das Kommando ps zum Vorschein:

ps -ef | head

UID      PID  PPID  C STIME TTY        TIME CMD
root       1     0  0 May24 ?      00:00:03 init [3]
root       2     1  0 May24 ?      00:00:02 [kflushd]
root       3     1  0 May24 ?      00:00:00 [kupdate]
root       4     1  0 May24 ?      00:00:00 [kpiod]
root       5     1  0 May24 ?      00:00:11 [kswapd]
root       6     1  0 May24 ?      00:00:00 [md_thread]
bin       90     1  0 May24 ?      00:00:00 [portmap]
root      98     1  0 May24 ?      00:00:06 /usr/sbin/scanlogd
root     104     1  0 May24 ?      00:00:01 /usr/sbin/syslogd 
   Die PID (Prozessnummer) ist die eindeutige Kennzeichnung eines Prozesses während der Laufzeit des Systems, PPID ist die Prozessnummer seines Elternprozesses und UID (Nutzerkennung) der den Prozess startende Benutzer.
   Wichtig zu wissen ist, dass ein Prozess, dessen Elternprozess nicht mehr existiert, automatisch dem init-Prozess (PID=1) zugeordnet wird. Die Priorität (C) ist ein während der Laufzeit der Prozesses dynamisch bestimmter Wert, der für die Zuteilung von CPU-Zeit verwendet wird.
   Des Weiteren sehen wir bei der Ausgabe von ps die Startzeit STIME, das mit dem Prozess verbundene Terminal TTY (in obigem Beispiel sind allerdings nur Prozesse dargestellt, die keine Ausgaben auf ein Terminal tätigen; diesen Sachverhalt kennzeichnet das Fragezeichen) und den Namen der Programme, die von den Prozessen ausgeführt werden.