Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

System Calls in Reptilian: Ein praktischer Leitfaden zur Prozessprotokollierung

Lerne, wie du benutzerdefinierte System Calls in einem Linux-ähnlichen Kernel implementierst – inspiriert von einem Projekt zur geheimen Datenverwaltung für die Echsen-Übermenschen. Perfekt für Studierende der Betriebssysteme.

System Calls Prozessprotokollierung Reptilian Betriebssystem Kernel-Programmierung COP4600 Systemaufrufe implementieren Linux Kernel Entwicklung statische Bibliothek C Prozess-Log-Level Systemprogrammierung Studium Cloud Computing Sicherheit Benutzerdefinierte System Calls Kernel-Modul Entwicklung C-API für System Calls Betriebssysteme Projekt Lizard Legion Tutorial

Einführung in System Calls und die Reptilian-Distribution

Stell dir vor, du arbeitest für eine geheime Schattenorganisation, die den Aufstieg der Silurier vorbereitet. Deine Aufgabe: Ein System zur Prozessprotokollierung in der Cloud-Plattform „Sky Skink“ entwickeln. Klingt verrückt? Genau so beginnt das Projekt COP4600 P1, das dich in die Welt der System Calls im Reptilian-Betriebssystem einführt. System Calls sind die Schnittstelle zwischen Benutzerprogrammen und dem Kernel – sie ermöglichen es, privilegierte Operationen wie das Lesen und Setzen von Systemvariablen durchzuführen.

In diesem Tutorial lernst du, wie du drei eigene System Calls implementierst, um eine Prozessprotokollierung aufzubauen. Das Konzept ähnelt dem Linux-Kernel-eigenen dmesg, erweitert jedoch um eine prozessspezifische Log-Stufe. Du wirst sehen, wie du Kernel-Code sicher erweiterst und dabei die Systemaufruf-Schnittstelle korrekt nutzt. Dieses Wissen ist nicht nur für dein Studium relevant, sondern auch für moderne Anwendungen in der Cloud-Infrastruktur oder bei der Entwicklung von KI-gestützten Überwachungstools.

Grundlagen: Was sind System Calls?

Ein System Call (Systemaufruf) ist die Brücke zwischen einem Benutzerprogramm und dem Kernel. Wenn dein Programm z. B. eine Datei öffnen oder Speicher anfordern möchte, ruft es einen System Call auf. Der Kernel führt dann die Operation im geschützten Modus aus. In unserem Projekt implementieren wir drei Calls:

  • get_proc_log_level – liest die aktuelle Log-Stufe des Systems
  • set_proc_log_level – setzt die Log-Stufe (nur für Superuser)
  • proc_log_message – sendet eine Log-Nachricht an den Kernel

Das Besondere: Du wirst diese Calls in einer statischen Bibliothek (libprocess_log.a) kapseln, die eine einfache C-API bereitstellt. So kannst du später eigene Programme schreiben, die die Protokollierung nutzen.

Projektstruktur und Vorbereitung

Bevor du loslegst, erstelle eine VM – und vergiss nicht, Snapshots zu machen! Wie im Projekt betont: Du wirst deinen Kernel wahrscheinlich mehrmals zum Absturz bringen. Ein Snapshot rettet dich vor stundenlangem Neuaufsetzen. Erstelle ein Verzeichnis process_log mit folgender Struktur:

process_log/
├── process_log.h
├── process_log.c
├── libprocess_log.a
└── Makefile

Die Header-Datei enthält die Funktionsprototypen und die Definitionen der Log-Stufen (z. B. PROC_OVERRIDE, PROC_ALERT usw.). Die Log-Stufen entsprechen den Linux-Kernel-Stufen, aber mit eigenen Namen. Tabelle 1 im Projekt zeigt die Zuordnung.

Implementierung der System Calls

1. Kernel-weite Prozesslog-Variable

Definiere eine globale Variable im Kernel, z. B. int process_log_level = 0;. Diese Variable speichert die aktuelle Log-Stufe und wird beim Booten initialisiert. Der Zugriff erfolgt über System Calls.

2. System Call: get_proc_log_level

Dieser Call liest einfach den Wert von process_log_level und gibt ihn zurück. Keine Sicherheitsprüfung nötig – jeder Prozess darf lesen.

SYSCALL_DEFINE0(get_proc_log_level) {
    return process_log_level;
}

3. System Call: set_proc_log_level

Hier ist Vorsicht geboten: Nur der Superuser (root) darf die Log-Stufe ändern. Prüfe die Berechtigung mit capable(CAP_SYS_ADMIN) oder über die User-ID. Bei ungültigen Werten (kleiner 0 oder größer 7) gib -1 zurück.

SYSCALL_DEFINE1(set_proc_log_level, int, new_level) {
    if (!capable(CAP_SYS_ADMIN))
        return -EPERM;
    if (new_level < 0 || new_level > 7)
        return -EINVAL;
    process_log_level = new_level;
    return new_level;
}

4. System Call: proc_log_message

Dieser Call erhält eine Nachricht und eine Log-Stufe. Er prüft, ob die Stufe gültig ist und ob die Nachricht (max. 128 Zeichen) protokolliert werden soll: Nur wenn die Stufe kleiner oder gleich der aktuellen Log-Stufe ist, wird sie an den Kernel-Log weitergeleitet. Die Formatierung muss exakt sein: $log_level_name [$executable, $pid]: $message.

SYSCALL_DEFINE2(proc_log_message, int, level, char __user *, msg) {
    if (level < 0 || level > 7)
        return -EINVAL;
    if (level > process_log_level)
        return level; // Nachricht wird ignoriert
    char kernel_msg[256];
    char comm[TASK_COMM_LEN];
    get_task_comm(comm, current);
    snprintf(kernel_msg, sizeof(kernel_msg), "%s [%s, %d]: %s",
             level_names[level], comm, current->pid, msg);
    printk(KERN_INFO "%s\n", kernel_msg);
    return level;
}

Beachte: Die Nachricht wird auf Kernel-Log-Level KERN_INFO ausgegeben, da die Prozesslog-Stufe unabhängig ist. Du musst die level_names als Array definieren, z. B. const char *level_names[] = {"PROC_OVERRIDE", "PROC_ALERT", ...};

Statische Bibliothek und C-API

Die Bibliothek ruft die System Calls über die syscall()-Funktion auf. Du musst die Call-Nummern kennen (die du beim Hinzufügen der System Calls zum Kernel festlegst). Beispiel für get_proc_log_level:

int get_proc_log_level() {
    return syscall(__NR_get_proc_log_level);
}

Die Header-Datei process_log.h deklariert die Funktionen und die Log-Stufen-Symbole. Die Makefile kompiliert die Quellen und erstellt die statische Bibliothek:

libprocess_log.a: process_log.o
    ar rcs $@ $^
process_log.o: process_log.c process_log.h
    gcc -c -o $@ $<

Test-Harness-Funktionen

Um die Sicherheit der System Calls zu prüfen, implementierst du Harness-Funktionen, die die Parameter für den direkten System Call zurückgeben. Sie umgehen die Bibliothek, sodass der Kernel die Sicherheitsprüfungen selbst vornimmt. Beispiel:

int *get_harness_get_proc_log_level() {
    static int params[] = {__NR_get_proc_log_level, 0};
    return params;
}

Der Testaufruf erfolgt dann mit syscall(params[0]).

Häufige Fehler und Tipps

  • Snapshots nicht vergessen: Nach jedem erfolgreichen Schritt einen Snapshot machen. Besonders vor dem Kompilieren des Kernels.
  • System Call Nummern: Füge deine Calls am Ende der System-Call-Tabelle hinzu, um Konflikte zu vermeiden.
  • Formatierung: Die Ausgabe muss exakt stimmen. Ein fehlendes Leerzeichen oder Semikolon kostet Punkte.
  • Speicherverwaltung: Verwende copy_from_user für Nachrichten aus dem Userspace.

Fazit und Ausblick

Mit diesem Tutorial hast du die Grundlagen von Systemprogrammierung und Kernel-Entwicklung kennengelernt. Das Projekt zeigt, wie wichtig Sicherheitsprüfungen und saubere Schnittstellen sind – ein Thema, das auch in der Cloud-Sicherheit oder bei der Entwicklung von Container-Laufzeiten wie Docker eine Rolle spielt. Vielleicht inspiriert dich die Idee, eigene System Calls für spezielle Anwendungen zu schreiben – sei es für ein KI-Überwachungssystem oder eine Blockchain-Plattform.

Denk daran: Die Echsen-Übermenschen zählen auf dich! Implementiere die Calls sorgfältig und dokumentiere deinen Code. Viel Erfolg!