hwr-notes/Betriebssysteme/Zusammenfassung_Prozesse.md
2026-04-09 11:24:56 +02:00

120 lines
No EOL
7.1 KiB
Markdown

# Zusammenfassung: Prozesse und Threads
## Der Prozess: Definition und Konzept
1. **Bessere Definition:** „Ein Prozess ist eine Instanz eines ausgeführten Programms, einschließlich der aktuellen Werte des Programmzählers, der Register und Variablen.“ [4].
2. **Umfassende Definition:** Ein Prozess ist der laufende Programmcode plus der Prozesskontext (alle Mittel und Parameter für die Ausführung) [5].
### Der Unterschied zwischen Programm und Prozess
Dieser Unterschied ist subtil, aber entscheidend. Ein Programm ist **passiv** (Code auf der Festplatte). Ein Prozess ist **aktiv** (Ressourcenzusammenfassung + Ausführung).
- Ein Prozess führt ein Programm aus.
- Viele Prozesse können dasselbe Programm ausführen. Jeder hat dabei seine eigene Instanz im eigenen Adressraum, völlig unabhängig von den anderen [6]
### Der Prozesskontext
Der Kontext umfasst alles, was die CPU zur Ausführung benötigt [5]:
- Registerwerte der CPU.
- Belegung des Caches (Befehle/Daten).
- Belegung des Hauptspeichers (Code/Daten).
- Benutzer- und Gruppenkennungen (IDs).
- Seitentabellen (Memory Management).
- Geöffnete Dateien.
- Prioritäten und Zustandsinformationen.
## Prozessverwaltung durch das Betriebssystem
### Process Control Block (PCB)
Das OS fasst alle Informationen eines Prozesses in einer Verwaltungsstruktur zusammen, dem PCB. Für jeden Prozess gibt es einen eigenen PCB. Die Gesamtheit aller PCBs bildet die Prozesstabelle [7].
- Je komplexer das OS, desto größer der PCB.
- Unter Linux ist die Struktur in `sched.h` definiert [8].
### Ressourcennutzung (Code-Sharing)
Da Prozesse Instanzen desselben Programms sein können, optimiert das OS den Speicher:
- **Codesegment (CS):** Wird nur einmal geladen und von allen Instanzen geteilt (spart Speicher und Zeit).
- **Datensegment (DS) & Stapel (SS):** Jeder Prozess erhält seine eigene Kopie.
- **Instruction Pointer (IP):** Jeder Prozess hat einen eigenen Zeiger, wo er sich im Code gerade befindet. So können Prozesse denselben Code an unterschiedlichen Stellen ausführen, ohne sich zu stören [8], [9].
### Hierarchie
Prozesse sind hierarchisch organisiert. Ein Elternprozess (_Parent_) erstellt einen Kindprozess (_Child_). Das Kind erbt Attribute vom Elternteil. Jeder Prozess kennt seine eigene ID (**PID**) und die des Elternprozesses (**PPID**) [10].
## Lebenszyklus eines Prozesses
Ein Prozess durchläuft verschiedene Phasen [11]:
1. Erstellung.
2. Ausführung auf dem Prozessor.
3. Anforderung von Ressourcen (Warten auf Zuweisung).
4. Warten auf Ergebnisse anderer Prozesse.
5. Erhalt einer Zeitscheibe (Quantum).
6. Warten auf die nächste Zeitscheibe (nach Entzug der CPU).
7. Beendigung.
### Wichtige Zustände (States)
Nicht zu verwechseln mit den Phasen, gibt es drei Hauptzustände in der Warteschlange [12]:
- **Bereit (Ready):** Wartet auf Zuteilung der CPU.
- **Rechnend (Running):** Führt Code aus.
- **Blockiert (Waiting):** Wartet auf ein Ereignis (z.B. I/O), CPU wurde entzogen.
### Kontextwechsel (Context Switch)
Der Wechsel von Prozess A zu Prozess B erfordert das Speichern des Zustands von A im PCB-A und das Laden von B aus PCB-B.
- Dies ist sehr zeitaufwändig.
- Hardware-Unterstützung (mehrere Registersätze) kann dies beschleunigen [13], [14].
### Prozessbeendigung
Ein Prozess kann auf drei Arten enden [15]:
1. **Normal:** Geplantes Ende (letzte Anweisung, `return`).
2. **Fehler:** Ungeplant (Nulldivision, Datei fehlt, Speicherfehler).
3. **Kill:** Befehl von außen (z.B. `kill -9 PID` unter Linux) [16].
**Probleme bei der Beendigung:**
- **Speicherlecks:** Wenn `malloc` genutzt wurde, aber kein `free`, bleibt Speicher belegt. Moderne Sprachen (Java, Go) nutzen Garbage Collection
- **Zombies:** Ein Prozess ist beendet, aber der Elternprozess hat den Status nicht abgefragt. Er bleibt als "Leiche" in der Tabelle
## Prozesserstellung: Linux vs. Windows
### Linux (`fork` & `exec`)
Das Verfahren ist zweistufig und sehr flexibel:
1. **`fork()`:** Erzeugt eine exakte Kopie des Elternprozesses.
- Der neue Prozess (Kind) beginnt an der gleichen Stelle (nach dem fork).
- **Unterscheidung durch Rückgabewert:**
- `0`: Ich bin das Kind.
- `PID > 0`: Ich bin der Elternprozess (Wert ist die PID des Kindes).
- `-1`: Fehler.
2. **`exec()` (optional):** Lädt ein neues Programm in den Speicher des Kindprozesses und überschreibt den alten Code.
### Windows
Nutzt den Systemaufruf `CreateProcess()`.
## Threads
**Definition:** Ein Thread ist ein sequenzieller Kontrollfluss innerhalb eines Prozesses. Ein Prozess kann mehrere Threads haben, die parallel ausgeführt werden. Threads dienen dazu, die Programmausführung zu beschleunigen und Aufgaben zu parallelisieren (z.B. Rechnen vs. E/A).
### Ressourcenteilung
Threads und Prozesse sind unterschiedliche Konzepte. Der Prozess dient der Ressourcengruppierung, der Thread ist die Einheit der Ausführung auf der CPU.
|**Gemeinsam genutzt (Shared)**|**Privat pro Thread (Private)**|
|---|---|
|Adressraum im Speicher|Program Counter (PC/IP)|
|Globale Variablen|Stack (Stapel)|
|Geöffnete Dateien|CPU-Register|
|Untergeordnete Prozesse|Zustand & Priorität|
|Benutzer-/Gruppen-ID|Signale (teils)|
|IPC-Mittel||
**Vorteile von Threads:**
- Erstellung ist 10- bis 100-mal schneller als bei Prozessen.
- Einfacher Zugriff auf gemeinsame Daten (kein komplexes IPC nötig).
- Echte Parallelität auf Multi-Core-Systemen [24].
## Thread-Modelle: User vs. Kernel
### A. User-Threads (Benutzer-Ebene)
Das Betriebssystem weiß nichts von diesen Threads. Sie werden von einer Bibliothek (Runtime System) im User-Space verwaltet.
- **Vorteile:**
- Keine Kernel-Aufrufe nötig (schnell).
- Verwaltung rein lokal im Prozessspeicher.
- Eigenes Scheduling möglich [28].
- **Nachteile:**
- **Blockierung:** Wenn ein Thread einen blockierenden Systemaufruf macht (z.B. Warten auf Tastatur), blockiert der gesamte Prozess (alle Threads stehen).
### B. Kernel-Threads (Betriebssystem-Ebene)
Das Betriebssystem kennt und verwaltet die Threads direkt.
- **Vorteile:**
- Wenn ein Thread blockiert, laufen andere weiter.
- Echte parallele Verteilung auf mehrere CPUs [30].
- **Nachteile:**
- Langsamer (Systemaufrufe nötig für Erstellung/Wechsel).
- Belegt mehr Speicher im Kernel [30], [31].
### C. Mapping (Die Verbindung)
Es muss eine Beziehung zwischen User- und Kernel-Threads geben [32], [33]:
- **Many-to-One:** Viele User-Threads auf einen Kernel-Thread (wie reines User-Threading).
- **One-to-One:** Jeder User-Thread hat einen Kernel-Thread (WinAPI, Linux Pthreads).
- **Many-to-Many:** Hybridansatz.
## Programmierung und Standards
POSIX Pthreads (IEEE 1003.1c):
Der Standard für Unix/Linux.
- **Bibliothek:** `pthread`
- **Funktionen:** `pthread_create`, `pthread_join` (warten auf Ende).
WinAPI Threads:
Der Standard für Windows.
- **Funktionen:** `CreateThread`, `WaitForSingleObject` (statt join), `CloseHandle`.
**Java & Go:**
- **Java:** Threads werden vom Runtime-System verwaltet (früher User-Threads, heute oft Mapping auf Kernel-Threads). Klasse `Thread` oder Interface `Runnable`. Starten mit `.start()` (ruft `.run()` auf) [33].
- **Go:** „Goroutinen“ (`go function()`). Sehr leichtgewichtig, vom Go-Runtime-System verwaltet [38], [39].