Nach kurzem Nachdenken fiel mir auf, dass es bereits zahlreiche Betriebssysteme dieser Art bereits gibt. Am bekanntesten dürfte Windows NT sein, das, als hoffnungsvoller Nachfolger des unkaputtbaren VMS durch den legendären OS-Architekten Dave Cutler entworfen wurde, leider aber später (nach NT 4.0 SP6a) mit dem Consumer-Betriebsystem aus Redmond (dem MS-DOS-basierten Windows 3.11) zu einem, sagen wir: semiprofessionellen OS verschmolzen wurde.
Don’t Panic
Auch Linux, das von Beginn an als “obsolet” gescholten wurde, hat inzwischen einen Ballast angesammelt, der einen respektablen Speckgürtel aus Treibern und Kompatibilitätsschichten bildet. Viele dieser Konfigurationen sind nicht mehr notwendig, aber dennoch im aktuellen Monolithen enthalten.
„A microkernel is minimal if and only if it is impossible to move a part of it into user space without losing the system’s ability to satisfy its requirements.“ — Jochen Liedtke
Inspiriert von Microkernel-Systeme wie seL4 und Systemen wie SculptOS , habe ich mir die Frage gestellt: Was, wenn man den Kernel erstmal strippt und dann Schritt für Schritt wieder aufbaut, bis das minimal bootbare System entsteht? Ein Bootcamp, sozusagen.
Das Experiment
Wir beginnen mit dem Checkout der aktuellen Kernel Sourcen von Github. Guck mal auf das Profil von Torvalds : Hunderttausende Follower, aber kein Follow…
Sobald die Toolchain installiert ist
sudo dnf install -y gcc make flex bison elfutils-libelf-devel openssl-devel bc debootstrap qemu-system-x86 qemu-img ncurses-devel
geben wir den Befehl, erstmal alle Optionen loszuwerden:
make allnoconfig
Das deaktiviert wirkungsvoll sämtliche Optionen des Kernels und hinterlässt einen wunderbar minimales bzImage von weniger als 1MB. Bootet allerdings auch nicht.
Richtig durchstarten
Um ein Linux starten zu können, braucht es neben einem Kernel bzImage auch ein Root-Dateisystem. Das kann man am schnellsten herstellen, indem man z.B. eine Minimaldistribution wie Alpine verwendet und diese mit cpio zu einem Initramfs zusammenstellt. Diese bringt auch BusyBox mit, ein statisch gegen Musl gelinkte Binary, die ein fertiges Init und einige POSIX-kompatible Tools mitbringt. Bei Embedded-Entwicklern ist BusyBox sehr beliebt.
Ich habe diesen Ansatz auch verfolgt, dann allerdings noch einen Schritt weiter in Richtung Minimalismus getrieben:
mkdir -p rootfs/{proc,sys,dev,var,bin,sbin,etc,tmp,usr/bin,usr/sbin}Ich installiere die BusyBox-Tools weiterhin, ersetze PID 1 aber durch ein eigenes Binary, das in Zig geschrieben ist. Im Wesentlichen müssen die Systempfade (proc, sys, dev) für den Kernel gemountet und eine Shell gestartet werden.
BusyBox kann mit sh eine Shell starten, aber auch hier habe ich eigene Ideen: Die Zish (Zig Shell) bietet eine Command Line, allerdings keine Skripting-Fähigkeiten.
Unter Verwendungs des Alpine-Userlands (siehe Infobox) kannst du den kompilierten Kernel mit Qemu testen:
qemu-system-x86_64 \
-kernel build/bzImage \
-initrd build/initramfs.cpio.gz \
-m 640 \
-nographic \
-netdev user,id=net0 \
-device virtio-net-pci,netdev=net0 \
-append "console=ttyS0 quiet rdinit=/init"Für ein Betriebssystem, das
- booten,
- Netzwerkverbindung herstellen,
- Dateisystem mounten,
- mit dem ich per Terminal kommunizieren und
- das OCI-kompatible Container starten kann benötigt man etwa zwei Dutzend Optionen. Die Details dazu findest du in der Build-Vorschrift von StyxOS.
Das fertige OS ist im Gegensatz zu einer “minimalen” Distribution wie Fedora IoT oder Debian Minimal, die mit einigen hundert Megabyte daherkommen, geradezu lächerlich klein:
.rw-r--r--. 3.4 MB bzImage
.rw-r--r--. 3.2 MB initramfs.cpio.gz
# just run
/ # free -h
total used free shared buff/cache available
Mem: 617.9M 14.1M 594.0M 0 9.8M 587.5MWir reden hier von 6.6 MB OS, das nach dem Start etwa das Doppelte, nämlich 14 MB an RAM belegt. Mein Laptop mit Fedora Silverblue, gestartetem Browser, Editor, etc. kommt auf locker 15 GB, also etwa das tausendfache. Letztlich mache ich dort aber auch nicht viel mehr als die Shell zu nutzen und Vi (na gut: Zed ist super!) zu starten.
Userland
Sobald der Kernel läuft, Dateisysteme und Netz da sind, möchte man mit dem OS was vernünftiges anfangen können:
Beschreibbares Dateisystem
Das Initramfs ist komplett zustandslos; bei jedem Boot wird dasselbe System geladen. Das bietet gegen Angriffe maximalen Schutz, allerdings will man ja auch Daten speichern. Unter /var erstellen wir einen beschreibbaren Datenträger:
truncate -s 1G var.img
mkfs.ext4 -d build/var_skel var.imgIch habe schon einige Verzeichnisse vorbereitet unter var_skel. Die Idee von StyxOS ist, dass die gesamte Konfiguration, Logs, Shellumgebung und Prozesskommunikation (IPC) in SQLite-Datenbanken abgelegt ist. Dazu hat jeder elementare Prozess die SQLite Amalgamate statisch eingebunden. Das frisst zwar wieder etwas mehr Platz, dafür sind die Abhängigkeiten minimal. Man kann eben nicht alles haben.
Init (PID 1)
Am Anfang war PID 1 und PID 1 war beim Kernel und der Kernel startet PID 1. Allerdings muss sich der Init-Prozess auch um die Hinterlassenschaften anderer Prozess kümmern: In einem nicht-perfekten OS können Prozesse sterben, deren Kinder wiederum sind dann automatisch an PID 1 angehängt und füllen die Prozesstabelle. Das bedeutet, wir müssen uns um verwaiste Prozesse (“Zombies”) kümmern. Das erhöht den Aufwand einer minimalen Implementierung.
She sells C Shells
Der erste Kontakt mit dem OS kommt über die Shell. Meine Implementierung “Zish” begnügt sich mit einem minimalen Satz an “Built-ins”, speichert dafür aber ihren Zustand (Environment und History) in einer Datenbank, die mit anderen System getauscht werden kann. Scripting ist absichtlich nicht eingebaut.
Observablen-Algebren
Monitoring ist für Produktivsysteme Pflicht, deshalb kommt mit StyxOS ein eigener Datensammler “Pluto”, der ganz einfach zeitgesteuert aus proc und sys Kernelmesswerte ausliest und im JSON-Format in einer SQLite-DB versenkt. Die Daten können später voraggregiert in ein zentrales Monitoring überführt werden.
Vi oder Emacs – keine Frage
Ebenfalls in Zig implementiert ist der Editor “Lethe”, der ein fast vergessenes Prinzip von OpenVMS wiederbelebt: die automatische Dateiversionierung. Jede Speicherung erzeugt eine neue Version (datei.txt.1, datei.txt.2), anstatt das Original stumpf zu überschreiben. In einer Welt, in der wir uns auf komplexe Git-Snapshots verlassen, ist dieser “Physical Undo” auf Dateisystemebene erfrischend ehrlich. Erst ein expliziter Aufruf von squash (einem Symlink auf das Lethe-Binary) räumt auf und konsolidiert den Stand. Es ist oldschool und hat auch Nachteile. Aber Du kannst immer noch Vi benutzen.
Totale Auflösung
Ein immerwährendes Ärgernis für mich ist die DNS-Auflösung, vor allem in Cloud-nativen Umgebungen. Mit Zig war es ein leichtes, auch DNS zu implementieren. Der Systemdienst “Charon” arbeitet als Resolver und autoritativer Server in definierten Domains. Die Zonen liegen ebenfalls in einer SQLite-Datenbank. Damit entfällt das Parsen kryptischer Zonenfiles; ein einfacher SQL-Insert reicht, um die Infrastruktur zu aktualisieren. Charon ist der Fährmann, der die Anfragen sicher an ihr Ziel bringt – oder sie im digitalen Hades verschwinden lässt, wenn sie den Sicherheitsrichtlinien widersprechen.
Config as Data (CaD)
Während die Welt von „Config as Code“ schwafelt und damit meist nur meint, unstrukturierte Textwüsten in Git-Repositories zu werfen, geht StyxOS den entscheidenden Schritt zur strukturierten Wahrheit.
ACID statt Zufall: Durch die Nutzung von SQLite als zentralem Konfigurationsspeicher genießen wir echte Transaktionssicherheit. Wenn du das Netzwerk umstellst, passiert das entweder ganz oder gar nicht. Es gibt keinen „halbgaren“ Zustand, der dein System im digitalen Nirgendwo stranden lässt.
Relationale Integrität: In StyxOS ist eine IP-Adresse kein loser String in einer /etc/network/interfaces, sondern ein validiertes Datenfeld. Wir nutzen SQL-Constraints, um sicherzustellen, dass die Konfiguration logisch korrekt ist, bevor der erste Dienst sie liest.
Kein Parser-Overhead: Dienste wie Charon oder Pluto müssen keine komplexen, fehleranfälligen Parser für exotische Textformate mitschleppen. Sie stellen eine SQL-Anfrage und erhalten saubere, strukturierte Daten. Das spart Codezeilen, RAM und Nerven.
Fazit und Ausblick
StyxOS ist kein Betriebssystem für den Massenmarkt. Es ist der Beweis, dass wir uns in einer Welt aus DiluentOS-Instanzen an den Schmerz gewöhnt haben. Wir akzeptieren Gigabytes an Overkill, weil Hardware billig geworden ist. Doch die Eleganz eines Systems, das in 14 MB RAM atmet, zeigt, wie weit wir uns vom Ideal der Effizienz entfernt haben.
Ein eigenes PID 1 in Zig zu schreiben und den Kernel manuell aus den Trümmern von allnoconfig zusammenzusetzen, ist eine Übung in digitaler Askese. Es ist befreiend.
Quo vadis, Styx?
Das Projekt steht erst am Anfang. Auf der Roadmap für die Organisation unter styxos.org stehen als nächste Schritte:
-
Container-Orchestrierung: Ein minimalistischer Runner, der OCI-Images direkt auf dem nackten Styx-Kernel startet, ohne den Overhead von Docker-Daemons.
-
Hardening: Da die Angriffsfläche durch den Verzicht auf tausende Treiber und Dienste bereits minimal ist, möchte ich die Rust- und Zig-Sicherheit auf Kernel-Ebene weiter ausreizen.
-
Selbsthostend: Das Ziel ist es, die StyxOS-Infrastruktur auf einem Cluster aus Kleinstrechnern zu betreiben, die nur über Tailscale kommunizieren.
StyxOS ist kein Ziel, es ist ein Weg. Ein Weg weg von der Verdünnung, hin zur Konzentration. Don’t Panic.
Quellen und Links
- Linux Kernel Programming von Kaiwan Billimoria
- StyxOS Homepage
Kommentare