4 Punkte von GN⁺ 2025-10-27 | 1 Kommentare | Auf WhatsApp teilen
  • Erklärt Schritt für Schritt, wie man durch direktes Bauen des Linux-Kernels und das Einrichten eines minimalen User Space eine „Mikro-Linux-Distribution“ erstellt
  • Behandelt von Grund auf die Rolle des Betriebssystem-Kernels, die Bestandteile einer Linux-Distribution und die Beziehung zwischen Kernel und User Space
  • Verwendet als Beispiel die RISC-V-Architektur (QEMUs Maschine riscv64 virt), aber dieselben Prinzipien lassen sich auch auf andere Architekturen wie x86 anwenden
  • Baut eine minimal lauffähige Linux-Umgebung auf, einschließlich des init-Prozesses, initramfs und einer einfachen in Go geschriebenen Shell
  • Stellt zum Schluss mithilfe des Projekts u-root vor, wie man eine tatsächlich nützliche Mikro-Distribution erstellt, und endet als Einführungshandbuch zum Verständnis des gesamten Aufbaus eines Linux-Systems

Was ist ein Betriebssystem-Kernel?

  • Der Kernel ist die Kernkomponente des Betriebssystems, die für die Verwaltung von Hardware-Ressourcen und die Steuerung der Programmausführung zuständig ist
    • Er bietet Multitasking-Verwaltung, sodass es auch in einer Single-Core-Umgebung so aussieht, als liefen mehrere Programme gleichzeitig
  • Der Kernel abstrahiert die Steuerung von Ein-/Ausgabegeräten, damit Anwendungen nicht direkt mit Hardware-Adressen oder Registerwerten arbeiten müssen
    • Ein Programm fordert zum Beispiel einfach an, „eine Nachricht auf die Standardausgabe zu schreiben“, und der Kernel übernimmt die tatsächliche Interaktion mit der Hardware
  • Über die Dateisystem-Schnittstelle stellt er eine höherwertige Form des Datenzugriffs bereit
    • Dateien sind nicht nur Daten auf einem Datenträger, sondern fungieren als logische Schnittstelle zur Kommunikation mit dem Kernel
  • Der Kernel stellt Modelle zur Isolation und Kommunikation zwischen Prozessen bereit, sodass Anwendungen unabhängig voneinander laufen oder zusammenarbeiten können
  • Der Linux-Kernel ist Open Source, läuft auf vielen verschiedenen Architekturen und ist weltweit einer der am weitesten verbreiteten Kernel

Was ist eine Linux-Distribution?

  • Mit dem Linux-Kernel allein können Benutzer weder einen Webbrowser noch GUI-Apps ausführen; es braucht mehrere Schichten von Software-Infrastruktur über dem Kernel
  • Dinge wie Netzwerkkonfiguration, IP-Zuweisung und VPN-Verwaltung werden nicht vom Kernel, sondern von übergeordneten Programmen im User Space übernommen
  • Daher wird eine Linux-Distribution als Kombination aus Kernel + User-Space-Infrastruktur definiert
  • Eine Distribution umfasst zusätzlich zu den Grundfunktionen des Kernels Pakete, Werkzeuge, Konfigurationen und den Initialisierungsprozess (init)
  • Die Komplexität von Distributionen ist sehr unterschiedlich: von minimalistischen Setups wie Arch Linux bis zu benutzerfreundlichen Varianten wie Ubuntu

Infrastruktur außerhalb des Kernels: User Space und der init-Prozess

  • Sobald der Kernel den Bootvorgang abgeschlossen hat, startet er zuerst den init-Prozess mit PID 1
    • init ist der Vorfahr aller späteren User-Space-Prozesse und startet der Reihe nach die Dienste und Werkzeuge des Systems
  • Die Gesamtheit der Prozesse und Werkzeuge, die von init gestartet werden, bildet den eigentlichen inhaltlichen Kern einer Linux-Distribution
  • Je komplexer eine Distribution wird, desto eher sammelt sie unnötige Funktionen an und wird deshalb manchmal als „bloated“ kritisiert
  • Umgekehrt lässt sich mit einer angepassten Mikro-Distribution ein schlankes System aufbauen, das nur die nötigsten Funktionen enthält

Einen Linux-Kernel für RISC-V bauen

  • In einer x86-Umgebung wird mithilfe einer Cross-Compile-Toolchain ein Kernel für RISC-V gebaut
    • Nach dem Herunterladen des Quellcodes linux-6.5.2.tar.xz von kernel.org wird make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig ausgeführt
  • Mit menuconfig lassen sich die Kernel-Einstellungen visuell bearbeiten
  • Nach einem parallelen Build mit make -j16 wird arch/riscv/boot/Image erzeugt
  • In QEMU wird mit qemu-system-riscv64 -machine virt -kernel arch/riscv/boot/Image gebootet
    • Im Boot-Log lassen sich Meldungen wie Erkennung der SBI-Schicht, UART-Initialisierung und Aktivierung von printk prüfen

Das erste Hindernis: kein Root-Dateisystem

  • Während des Kernel-Boots tritt durch den Fehler VFS: Unable to mount root fs eine Kernel Panic auf
    • Ursache: Es wurde kein Root-Dateisystem (initramfs) bereitgestellt
  • Ein Dateisystem kann nicht nur auf einem Datenträger, sondern auch RAM-basiert (initramfs) aufgebaut sein
  • initramfs wird im cpio-Format paketiert und kann in QEMU mit der Option -initrd geladen werden

initramfs aufbauen und „Hello world“ ausführen

  • Die minimale Voraussetzung ist das Vorhandensein eines /init-Binärprogramms
    • Nach dem Schreiben von init.c wird es statisch gelinkt (-static) gebaut
    • Mit cpio -o -H newc < file_list.txt > initramfs.cpio wird es paketiert
  • Beim Start in QEMU wird „Hello world“ ausgegeben, danach führt das Beenden von init erneut zu einer Kernel Panic
    • Lösung: Eine Endlosschleife hinzufügen, damit init nicht beendet wird

Eine einfache in Go geschriebene Shell hinzufügen

  • init startet mit fork und execl die /little_shell
  • little_shell.go ist eine einfache Shell, die Benutzereingaben annimmt und die Befehle per Echo ausgibt
    • Mit GOOS=linux GOARCH=riscv64 go build little_shell.go wird sie für RISC-V gebaut
  • Sowohl init als auch little_shell teilen sich die Ausgabe über UART
    • Standard-Ein- und -Ausgabe werden über File-Handles verwaltet und bei fork vererbt
  • Das Ergebnis ist eine grundlegende Linux-Umgebung, in der „Hello from init“ und die Shell-Eingaben abwechselnd ausgegeben werden

Zusammenfassung der Rolle des Kernels

  • Hardware-Abstraktion: User-Programme können Ausgaben erzeugen, ohne Details zu UART oder Geräten zu kennen
  • Bereitstellung höherwertiger Schnittstellen: Zugriff auf andere Binärdateien (little_shell) über das Dateisystem
  • Prozess-Isolation: init und die Shell laufen in getrennten Speicherbereichen
  • Der Kernel bietet auf komplexer Hardware eine stabile und hoch portable Ausführungsgrundlage

Definition eines Betriebssystems

  • Man kann entweder nur den Kernel als Betriebssystem betrachten oder die gesamte Distribution als Betriebssystem
  • Wichtig ist, die Grenzen der Rollen und die Interaktionsstruktur von Kernel und User Space zu verstehen

Mit u-root eine tatsächlich nützliche Mikro-Distribution bauen

  • Das u-root-Projekt stellt ein Go-basiertes Set von User-Space-Werkzeugen bereit
    • u-root umfasst einen im User Space laufenden Bootloader und eine Shell-Umgebung auf dem Linux-Kernel
  • Nach der Installation erstellt der Befehl GOOS=linux GOARCH=riscv64 u-root automatisch ein initramfs
    • Die Datei /tmp/initramfs.linux_riscv64.cpio kann in QEMU ausgeführt werden
  • Beim Booten erscheint zusammen mit dem Banner „Welcome to u-root!“ eine Standard-Shell-Eingabeaufforderung
    • Unterstützt grundlegende Befehle wie ls, pwd, echo sowie Tab-Vervollständigung

Netzwerkanbindung praktisch ausprobieren

  • Zu QEMU werden die Geräte virtio-net-device und virtio-rng-pci hinzugefügt
    • Verwendet werden die Optionen -device virtio-net-device,netdev=usernet -netdev user,id=usernet
  • Mit dhclient aus u-root wird per DHCP automatisch eine IP-Adresse zugewiesen
    • Beispiel: eth0 erhält 10.0.2.15/24
  • Mit wget http://google.com gelingt der Zugriff auf das externe Netzwerk, und der Download von index.html lässt sich bestätigen

Die Bedeutung von Paketmanager und init

  • Allgemeine Distributionen installieren und aktualisieren Software dynamisch über einen Paketmanager
    • Diese praktische Übung folgt dagegen einem Embedded-Ansatz, bei dem das gesamte Image neu gebaut werden muss
  • init ist nicht bloß ein einfacher Prozessstarter, sondern eine Kernkomponente für Geräteinitialisierung, Dienstverwaltung und Steuerung des Systemstarts
    • Im init-Quellcode von u-root lassen sich verschiedene Schritte zur Einrichtung von Geräten (/dev) nachvollziehen

GitHub-Repository

  • Der vollständige Code und die Beispiele aus diesem Leitfaden sind unter popovicu/linux-micro-distro verfügbar
    • Dort lassen sich initramfs-Images bauen und die Übungen nachvollziehen

1 Kommentare

 
GN⁺ 2025-10-27
Hacker News-Kommentare
  • Ich baue seit ein paar Monaten meine eigene Micro-Linux-Distribution
    Der User-Mode besteht aus genau einer einzelnen statischen Binärdatei, plus ein paar Dateien zur Unterstützung vertraulicher microVM-Container
    Besonders die Struktur von initramfs ist faszinierend. Der Ablauf, bei dem der Kernel ein cpio-Archiv entpackt, in tmpfs wechselt und dann /init ausführt, wirkt fast wie Magie
    Man kann auch mehrere cpio-Archive aneinanderhängen, jedes davon komprimieren, und sie werden der Reihe nach als Overlay angewendet
    Durch dieses einfache und elegante Design habe ich viel gelernt, indem ich den Code zum Entpacken selbst geschrieben habe

  • Vor Kurzem hat qemu begonnen, uftrace auf den wichtigsten Architekturen zu unterstützen
    Das ist genau die Antwort, wenn Experten fragen: „Wie soll man das debuggen?“
    Mehr dazu findet sich in diesem Thread

  • Ich arbeite ebenfalls an einem ähnlichen Projekt — azathos
    Es enthält ein selbstgeschriebenes toy init, eine Shell und einige Utilities
    Zum Debuggen habe ich GNU coreutils hineingepackt, und aktuell konzentriere ich mich darauf, Fenster auf den Framebuffer zu zeichnen

  • Dieses Projekt ist wirklich großartig. Es erinnert mich an die Zeit 1998, als ich eine Disketten-„Distribution“ gebaut habe, mit der Windows-PC-Images per UDP-Broadcast verteilt wurden
    „make bzimage“, Fehler in init-Skripten, Endlos-Reboots … so viele Erinnerungen
    Interessant ist, dass die heutige Vorgehensweise gar nicht so anders ist. Ein Port für Raspberry Pi wäre spaßig und lehrreich. Vielleicht probiere ich es selbst aus

    • Ich erinnere mich auch noch daran, wie ich 1998 versucht habe, Mandrake Linux über NetBIOS und ISDN zu installieren und wegen Prüfsummenfehlern dutzende Male neu anfangen musste
      Am Ende hat ein Freund den Inhalt per sftp auf CD gebrannt, um das Problem zu lösen, aber damals konnte man nur mit 2-facher Geschwindigkeit brennen
  • Ich frage mich, wie schwierig es wäre, das als Cloud-Image laufen zu lassen, z. B. auf Vultr oder DigitalOcean, oder ein GUI zu starten und Firefox auszuführen

    • Als Cloud-Image ist das relativ einfach. Man braucht nur die Standardtreiber des Kernels und installiert das Image
      Man kann auch in eine andere Distribution booten und dann per kexec den eigenen Kernel starten, um die Installation im Arbeitsspeicher durchzuführen
      Ein reales Implementierungsbeispiel ist nixos-anywhere
    • Man erstellt einfach ein Image mit virtio-Treibern für Netzwerk und Storage, konvertiert es nach qcow2 und registriert es bei DigitalOcean oder ähnlichen Diensten
      Das ist überraschend unkompliziert
  • Eine Version dieses Projekts speziell für Raspberry Pi fände ich wirklich interessant

  • Ich habe mich gefragt, warum man so etwas selbst baut, und ob es nicht reichen würde, Linux einfach mit Gentoo zu erkunden

    • Gentoo ist zwar „aus den Quellen gebaut“, aber der Paketmanager nimmt einem den Großteil der Arbeit ab
      Man kann den User Space anpassen, aber um Linux selbst zu lernen, ist es nicht ideal
      Schon das stage3-Tarball ist praktisch auf dem Niveau einer „Mini-Distribution“
  • Zum Lernen ist das wirklich großartig, und wenn man schnell etwas fertig bekommen will, ist buildroot eine gute Wahl

  • Dank dieses Artikels habe ich wirklich viel gelernt. Vielen Dank für diesen sehr informationsreichen Beitrag