Process API – Erstellen und Verwalten von Prozessen¶
📚 1️⃣ Was ist ein Prozess?¶
Ein Prozess ist eine laufende Instanz eines Programms mit eigenem Speicherbereich und Registerzustand.
💡 Zwei Möglichkeiten zur Prozesserstellung:
-
Neuen Prozess von Grund auf erstellen
- Lade Code & Daten in den Speicher
- Erstelle einen leeren Call Stack
- Initialisiere den Prozesskontrollblock (PCB)
- Setze den Prozess auf die Ready-Queue
✅ Vorteil: Keine unnötigen Kopien
❌ Nachteil: Komplizierter einzurichten -
Vorhandenen Prozess klonen & anpassen
-
Unix fork(): Erstellt eine exakte Kopie des Elternprozesses
- exec(): Ersetzt den aktuellen Code durch ein anderes Programm
✅ Vorteil: Schnell durch Copy-on-Write (CoW)
❌ Nachteil: Erst Kopieren, dann Überschreiben ist ineffizient
🔄 2️⃣ Der fork()
System Call – Prozesse duplizieren¶
fork()
erstellt eine exakte Kopie des laufenden Prozesses.
📌 Wichtig:
- Das Kind erhält seine eigene Speicherzuweisung.
- Elternprozess bekommt die PID des Kindes, Kind bekommt
0
.
📌 Syntax in C:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("Hallo Welt (pid:%d)\n", getpid());
int rc = fork();
if (rc < 0) {
fprintf(stderr, "Fehler: fork() fehlgeschlagen\n");
exit(1);
} else if (rc == 0) {
printf("Ich bin das Kind (pid:%d)\n", getpid());
} else {
printf("Ich bin der Elternprozess von %d (pid:%d)\n", rc, getpid());
}
return 0;
}
💡 Ergebnis (nicht-deterministisch!):
⏳ 3️⃣ Der wait()
System Call – Warten auf Kindprozesse¶
wait()
sorgt dafür, dass ein Elternprozess auf das Ende eines Kindprozesses wartet.
📌 Beispiel:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int rc = fork();
if (rc < 0) {
fprintf(stderr, "fork fehlgeschlagen\n");
exit(1);
} else if (rc == 0) {
printf("Ich bin das Kind (pid:%d)\n", getpid());
sleep(1);
} else {
int wc = wait(NULL);
printf("Elternprozess von %d (wc:%d) (pid:%d)\n", rc, wc, getpid());
}
return 0;
}
✅ Ergebnis (deterministisch!):
🔹 Der Elternprozess wartet, bis das Kind beendet wurde.
🏃♂️ 4️⃣ Der exec()
System Call – Ein anderes Programm starten¶
Mit exec()
kann ein Prozess ein anderes Programm ausführen, indem er sein eigenes ersetzt.
📌 Beispiel:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int rc = fork();
if (rc < 0) {
fprintf(stderr, "fork fehlgeschlagen\n");
exit(1);
} else if (rc == 0) {
char *args[] = {"ls", "-l", NULL}; // "ls -l" ausführen
execvp(args[0], args);
printf("Dies wird nicht mehr ausgeführt\n");
} else {
wait(NULL);
printf("Elternprozess beendet\n");
}
return 0;
}
✅ Erklärung:
- Elternprozess bleibt gleich.
- Kindprozess ersetzt sich selbst mit
ls -l
. - Falls
exec()
erfolgreich ist, läuft der alte Code nicht weiter.
🔪 5️⃣ kill()
– Prozesse beenden¶
Ein Prozess kann mit kill(pid, SIGNAL)
ein anderes Programm beenden.
📌 Beispiel:
#include <signal.h>
#include <unistd.h>
int main() {
pid_t child = fork();
if (child) {
sleep(5);
kill(child, SIGKILL);
} else {
while (1); // Endlosschleife, bis es beendet wird
}
return 0;
}
✅ Erklärung:
- Elternprozess wartet 5 Sekunden und beendet das Kind dann mit
SIGKILL
.
🔗 6️⃣ Kommunikation zwischen Prozessen: pipe()
¶
Mit Pipes können Prozesse miteinander kommunizieren.
📌 Beispiel:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd[2];
pipe(fd);
pid_t child = fork();
if (child == 0) { // Kindprozess
close(fd[1]); // Schreibe-Ende schließen
char buffer[100];
read(fd[0], buffer, 100);
printf("Empfangene Nachricht: %s\n", buffer);
} else { // Elternprozess
close(fd[0]); // Lese-Ende schließen
char message[] = "Hallo, Kindprozess!";
write(fd[1], message, sizeof(message));
wait(NULL);
}
return 0;
}
✅ Ergebnis:
🚀 7️⃣ Fazit¶
fork()
erstellt einen neuen Prozess.wait()
sorgt für synchronisierte Ausführung zwischen Eltern und Kind.exec()
startet ein neues Programm im selben Prozess.kill()
kann Prozesse beenden.pipe()
ermöglicht Interprozesskommunikation.
Diese System Calls sind das Fundament jedes modernen Betriebssystems! 🔥