Skip to content

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:

  1. 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!):

Hallo Welt (pid:29146)  
Ich bin der Elternprozess von 29147 (pid:29146)  
Ich bin das Kind (pid:29147)  

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!):

Ich bin das Kind (pid:29267)  
Elternprozess von 29267 (wc:29267) (pid:29266)

🔹 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:

Empfangene Nachricht: Hallo, Kindprozess!

🚀 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! 🔥