Was sind die Mindestanforderungen an Root-Dateisystemanwendungen, um Linux vollständig zu starten?

16

Es ist eine Frage zu User Space-Anwendungen, aber hör mir zu!

Um eine funktionierende Linux-Distribution zu booten, sind sozusagen drei "Anwendungen" erforderlich:

  1. Bootloader - Für eingebettete Systeme ist dies in der Regel U-Boot, obwohl dies keine zwingende Voraussetzung ist.

  2. Kernel - Das ist ziemlich einfach.

  3. Root-Dateisystem - Ohne dieses System kann nicht in eine Shell gebootet werden. Enthält das Dateisystem, in das der Kernel bootet, und wo inites Form heißt.

Meine Frage bezieht sich auf # 3. Wenn jemand ein extrem minimales rootfs erstellen wollte (für diese Frage sagen wir mal keine GUI, nur Shell), welche Dateien / Programme sind erforderlich, um von einer Shell zu booten?

MDMoore313
quelle
Minimal definieren. Sie können nur eine einzelne ausführbare Datei verwenden und nichts anderes, wie unter der folgenden Adresse erläutert: superuser.com/a/991733/128124 Nur dass dies nicht bei jedem Beenden der Fall ist oder Sie in Panik geraten, sodass Sie eine Endlosschleife oder einen langen Ruhezustand benötigen. Similar question: unix.stackexchange.com/questions/17122/…
Ciro Santilli am

Antworten:

31

Das hängt ganz davon ab, welche Dienste Sie auf Ihrem Gerät haben möchten.

Programme

Sie können Linux direkt in eine Shell booten lassen . In der Produktion ist es nicht sehr nützlich - wer möchte nur eine Shell haben -, aber es ist nützlich als Interventionsmechanismus, wenn Sie einen interaktiven Bootloader haben: Übergeben Sie ihn init=/bin/shan die Kernel-Befehlszeile. Alle Linux-Systeme (und alle Unix-Systeme) haben eine Bourne / POSIX-ähnliche Shell /bin/sh.

Sie benötigen eine Reihe von Shell-Dienstprogrammen . BusyBox ist eine sehr häufige Wahl; es enthält eine Shell und gemeinsamen Dienstprogramme für die Datei- und Textmanipulation ( cp, grep, ...), Netzwerk - Setup ( ping, ifconfig, ...), Prozessmanipulation ( ps, nice, ...) und verschiedene andere Systemtools ( fdisk, mount, syslogd, ...). BusyBox ist äußerst konfigurierbar: Sie können auswählen, welche Tools Sie möchten, und sogar einzelne Funktionen zum Zeitpunkt der Kompilierung auswählen, um den richtigen Kompromiss zwischen Größe und Funktionalität für Ihre Anwendung zu erzielen. Neben shdem bloße Minimum , dass Sie nicht wirklich etwas tun können , ohne ist mount, umountund halt, aber es wäre untypisch nicht auch haben cat, cp, mv, rm,mkdir, rmdir, ps, syncUnd ein paar mehr. BusyBox wird als einzelne Binärdatei busyboxmit einem symbolischen Link für jedes Dienstprogramm installiert .

Der erste Prozess auf einem normalen Unix-System wird aufgerufen init. Seine Aufgabe ist es, andere Dienste zu starten. BusyBox enthält ein Init-System. Zusätzlich zu der initBinärdatei (in der Regel in /sbin) benötigen Sie die Konfigurationsdateien (in der Regel als /etc/inittab- einige moderne Init-Ersetzungen beseitigen diese Datei, aber Sie werden sie nicht auf einem kleinen eingebetteten System finden), die angeben, welche Dienste gestartet werden sollen und wann. Bei BusyBox /etc/inittabist dies optional. Wenn es fehlt, erhalten Sie eine Root-Shell auf der Konsole und das Skript /etc/init.d/rcS(Standardverzeichnis) wird beim Booten ausgeführt.

Das ist alles, was Sie brauchen, abgesehen von den Programmen, die Ihr Gerät zu etwas Nützlichem machen. Auf meinem Heimrouter, auf dem eine OpenWrt- Variante ausgeführt wird, sind die einzigen Programme BusyBox nvram(zum Lesen und Ändern von Einstellungen im NVRAM) und Netzwerkdienstprogramme.

Sofern nicht alle Ihre ausführbaren Dateien statisch verknüpft sind, benötigen Sie den Dynamic Loader ( ld.soder abhängig von der Wahl von libc und den Prozessorarchitekturen unter verschiedenen Namen aufgerufen werden kann) und alle dynamischen Bibliotheken ( /lib/lib*.somöglicherweise einige davon in /usr/lib), die von benötigt werden diese ausführbaren Dateien.

Verzeichnisaufbau

Der Filesystem Hierarchy Standard beschreibt die allgemeine Verzeichnisstruktur von Linux-Systemen. Es richtet sich an Desktop- und Serverinstallationen: Viele davon können auf einem eingebetteten System weggelassen werden. Hier ist ein typisches Minimum.

  • /bin: ausführbare Programme (einige können /usr/binstattdessen in sein).
  • /dev: Geräteknoten (siehe unten)
  • /etc: Konfigurationsdateien
  • /lib: gemeinsam genutzte Bibliotheken, einschließlich des Dynamic Loader (sofern nicht alle ausführbaren Dateien statisch verknüpft sind)
  • /proc: Einhängepunkt für das proc-Dateisystem
  • /sbin: ausführbare Programme. Die Unterscheidung mit /binist /sbinfür Programme, die nur für den Systemadministrator nützlich sind, aber diese Unterscheidung ist auf eingebetteten Geräten nicht sinnvoll. Sie können /sbineinen symbolischen Link zu erstellen /bin.
  • /mnt: Praktisch, um schreibgeschützte Root-Dateisysteme als Scratch-Mount-Punkt während der Wartung zu verwenden
  • /sys: Einhängepunkt für das sysfs-Dateisystem
  • /tmp: Speicherort für temporäre Dateien (oft ein tmpfsMount)
  • /usr: Enthält Unterordner bin, libund sbin. /usrexistiert für zusätzliche Dateien, die sich nicht im Root-Dateisystem befinden. Wenn Sie das nicht haben, können Sie /usreinen symbolischen Link zum Stammverzeichnis erstellen.

Gerätedateien

Hier sind einige typische Einträge in einem Minimum /dev:

  • console
  • full (Beim Schreiben wird immer "kein Platz mehr auf dem Gerät" gemeldet.)
  • log(Ein Socket, über den Programme Protokolleinträge senden), wenn ein syslogdDaemon (z. B. BusyBox) davon liest
  • null (verhält sich wie eine Datei, die immer leer ist)
  • ptmxund ein ptsVerzeichnis , wenn Sie Pseudo-Terminals verwenden möchten (dh ein anderes Terminal als die Konsole) - z. B. wenn das Gerät vernetzt ist und Sie telnet- oder ssh-in möchten
  • random (liefert zufällige Bytes, riskiert das Blockieren)
  • tty (bezeichnet immer das Terminal des Programms)
  • urandom (Gibt zufällige Bytes zurück, blockiert niemals, kann aber auf einem frisch gebooteten Gerät nicht zufällig sein.)
  • zero (enthält eine unendliche Folge von Null-Bytes)

Darüber hinaus benötigen Sie Einträge für Ihre Hardware (mit Ausnahme der Netzwerkschnittstellen, in die keine Einträge eingehen /dev): serielle Anschlüsse, Speicher usw.

Bei eingebetteten Geräten erstellen Sie die Geräteeinträge normalerweise direkt im Root-Dateisystem. High-End-Systeme verfügen über ein Skript MAKEDEVzum Erstellen von /devEinträgen. Auf einem eingebetteten System ist das Skript jedoch häufig nicht im Image enthalten. Wenn eine Hardware per Hotplug angeschlossen werden kann (z. B. wenn das Gerät über einen USB-Host-Port verfügt), /devsollte dies von udev verwaltet werden (möglicherweise ist im Root-Dateisystem noch ein Mindestsatz festgelegt).

Boot-Time-Aktionen

Über das Root-Dateisystem hinaus müssen Sie für den normalen Betrieb einige weitere bereitstellen:

  • procfs on /proc(so ziemlich unverzichtbar)
  • Sysfs auf /sys(so ziemlich unverzichtbar)
  • tmpfsDateisystem ein /tmp(damit Programme temporäre Dateien erstellen können, die sich im RAM befinden, und nicht im Root-Dateisystem, das sich möglicherweise in Flash oder schreibgeschützt befindet)
  • tmpfs, devfs oder devtmpfs on /devif dynamic (siehe udev in " Gerätedateien " oben)
  • devpts on, /dev/ptswenn Sie [Pseudo-Terminals verwenden möchten (siehe Bemerkung ptsoben)

Sie können eine /etc/fstabDatei erstellen und aufrufen mount -aoder mountmanuell ausführen .

Starten Sie einen Syslog- Daemon (sowie klogdfür Kernel-Protokolle, wenn das syslogdProgramm sich nicht darum kümmert), wenn Sie einen Platz zum Schreiben von Protokollen haben.

Danach ist das Gerät bereit, anwendungsspezifische Dienste zu starten.

So erstellen Sie ein Root-Dateisystem

Dies ist eine lange und abwechslungsreiche Geschichte. Alles, was ich hier tun werde, ist ein paar Hinweise zu geben.

Das Root-Dateisystem kann im RAM (geladen von einem (normalerweise komprimierten) Image in ROM oder Flash) oder auf einem festplattenbasierten Dateisystem (gespeichert in ROM oder Flash) oder gegebenenfalls vom Netzwerk (häufig über TFTP ) geladen werden . Wenn sich das Root-Dateisystem im RAM befindet, machen Sie es zum initramfs - einem RAM-Dateisystem, dessen Inhalt beim Booten erstellt wird.

Es gibt viele Frameworks zum Zusammenstellen von Root-Images für eingebettete Systeme. In den BusyBox-FAQ gibt es einige Hinweise . Buildroot ist ein beliebtes Tool , mit dem Sie ein vollständiges Root-Image mit einem ähnlichen Setup wie der Linux-Kernel und die BusyBox erstellen können. OpenEmbedded ist ein weiteres solches Framework.

Wikipedia hat eine (unvollständige) Liste der gängigen Embedded Linux-Distributionen . Ein Beispiel für Embedded Linux in Ihrer Nähe ist die OpenWrt- Betriebssystemfamilie für Netzwerkgeräte (beliebt bei Heimroutern von Bastlern). Wenn Sie aus Erfahrung lernen möchten, können Sie Linux von Grund auf ausprobieren. Es richtet sich jedoch eher an Desktopsysteme für Bastler als an eingebettete Geräte.

Ein Hinweis zu Linux vs. Linux-Kernel

Das einzige Verhalten, das in den Linux-Kernel eingearbeitet ist, ist das erste Programm, das beim Booten gestartet wird. (Ich werde hier nicht auf die Feinheiten von initrd und initramfs eingehen.) Dieses Programm, das traditionell als init bezeichnet wird , hat die Prozess-ID 1 und verfügt über bestimmte Berechtigungen (Immunität gegen KILL-Signale ) und Verantwortlichkeiten (ernten von Waisen ). Sie können ein System mit einem Linux-Kernel ausführen und als ersten Prozess das starten, was Sie möchten. Dann haben Sie jedoch ein Betriebssystem, das auf dem Linux-Kernel basiert, und nicht das, was normalerweise als „Linux“ -  Linux im allgemeinen Sinne - bezeichnet wird Der Begriff ist ein Unix- ähnliches Betriebssystem, dessen Kernel der Linux-Kernel ist. Beispielsweise ist Android ein Betriebssystem, das nicht wie Unix ist, sondern auf dem Linux-Kernel basiert.

Gilles 'SO - hör auf böse zu sein'
quelle
Hervorragende Antwort. Ich habe nur das Booten erwähnte in Linux im Titel b / c , das , was wahrscheinlich gesucht werden, so große Bereicherung über Linux vs Linux Kernel, dass der Bedarf weiter verbreitet Wissen.
MDMoore313
@BigHomie Denken Sie daran, die Free Software Foundation möchte, dass wir alle GNU / Linux nennen, da auf den meisten (allen?) "Linux-Distributionen" die Software GNU ist, obwohl der Kernel Linux ist (daher GNU / Linux).
BenjiWiebe
Meh, niemand hat Zeit dafür. Dann sollte meine Distribution Busybox / Linux heißen ?? Ich weiß, ich weiß, du bist es nicht, Stallworth, nur Entlüften;)
MDMoore313
1
@BenjiWiebe Oder GNU / X11 / Apache / Linux / TeX / Perl / Python / FreeCiv . Abgesehen von RMS wird es von allen als "Linux" bezeichnet.
Gilles 'SO- hör auf böse zu sein'
@ Gilles Nun, abgesehen von Debian, denke ich. :)
ein CVn
5

Sie benötigen lediglich eine statisch verknüpfte ausführbare Datei, die isoliert auf dem Dateisystem abgelegt wird. Sie benötigen keine weiteren Dateien. Diese ausführbare Datei ist der Init-Prozess. Es kann Busybox sein. Das gibt Ihnen eine Shell und eine Vielzahl anderer Dienstprogramme, alles an sich. Sie können zu einem voll funktionsfähigen System wechseln, indem Sie die Befehle in busybox manuell ausführen, um das Root-Dateisystem mit Lese- / Schreibzugriff einzubinden, Knoten zu erstellen / zu entwickeln, echte Init-Befehle auszuführen usw.

Setzen Sie Monica wieder ein
quelle
Ja, ich wusste, dass die Busybox kommt. Mal sehen, ob noch etwas auftaucht.
MDMoore313
4

Wenn Sie keine Shell-Dienstprogramme benötigen, ist eine statisch verknüpfte mkshBinärdatei (z. B. gegen klibc-130K unter Linux / i386) ausreichend. Sie benötigen ein /linuxrcoder /initoder /sbin/initSkript, das nur mksh -l -T!/dev/tty1in einer Schleife aufruft :

#!/bin/mksh
while true; do
    /bin/mksh -l -T!/dev/tty1
done

Die -T!$ttyOption ist eine mkshNeuerung, die es anweist, eine neue Shell auf dem angegebenen Terminal zu erzeugen und darauf zu warten. (Vorher war es nur -T-ein Programm zu dæmonise und -T$ttyzu laichen auf einem Terminal aber nicht warten. Das war nicht so schön.) Die -lOption einfach erzählt ein Login - Shell ausgeführt werden ( in dem es heißt /etc/profile, ~/.profileund ~/.mkshrc).

Dies setzt voraus, dass Ihr Terminal ein /dev/tty1Ersatz ist. (Mit mehr Magie kann das Terminal automatisch herausgefunden werden. Sie erhalten /dev/consolekeine vollständige Auftragssteuerung.)

Sie benötigen ein paar Dateien, /devdamit dies funktioniert:

  • / dev / console
  • / dev / null
  • / dev / tty
  • / dev / tty1

Wenn Sie mit der Kernel-Option booten, ist devtmpfs.mount=1kein gefülltes /devVerzeichnis mehr erforderlich. Es muss lediglich ein leeres Verzeichnis sein (das als Mountpoint verwendet werden kann).

Normalerweise benötigen Sie einige Dienstprogramme (von klibc, busybox, beastiebox, toybox oder toolbox), die jedoch nicht wirklich benötigt werden.

Möglicherweise möchten Sie eine ~/.mkshrcDatei hinzufügen , in der $ PS1 und einige grundlegende Shell-Aliase und -Funktionen eingerichtet werden.

Ich habe einmal eine 171K komprimierte (371K unkomprimierte) initrd für Linux / m68k erstellt, wobei ich nur mksh (und dessen Beispiel-mkshrc-Datei) und klibc-utils verwendet habe. (Dies war jedoch, bevor -T! Zur Shell hinzugefügt wurde, sodass /dev/tty2stattdessen die Anmeldeshell gestartet und eine Nachricht an die Konsole gesendet wurde, die den Benutzer auffordert, die Terminals zu wechseln.) Es funktioniert einwandfrei.

Dies ist ein wirklich minimales Setup. Die anderen Antworten bieten hervorragende Tipps für Systeme mit etwas mehr Funktionen. Dies ist ein echter Sonderfall.

Haftungsausschluss: Ich bin der MKSH-Entwickler.

Mirabilos
quelle
Dies ist eine großartige Antwort, danke für das Teilen und auch danke für mksh.
JoshuaRLi
2

Minimales init hallo Weltprogramm Schritt für Schritt

Bildbeschreibung hier eingeben

Kompilieren Sie eine Hallo-Welt ohne Abhängigkeiten, die in einer Endlosschleife enden. init.S:

.global _start
_start:
    mov $1, %rax
    mov $1, %rdi
    mov $message, %rsi
    mov $message_len, %rdx
    syscall
    jmp .
    message: .ascii "FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n"
    .equ message_len, . - message

Wir können nicht sys_exitoder sonst die Kernel Panics verwenden.

Dann:

mkdir d
as --64 -o init.o init.S
ld -o init d/init.o
cd d
find . | cpio -o -H newc | gzip > ../rootfs.cpio.gz
ROOTFS_PATH="$(pwd)/../rootfs.cpio.gz"

Dies schafft ein Dateisystem mit unserer Hello World unter /init, dem ersten Userland-Programm, das der Kernel ausführen wird. Wir hätten auch weitere Dateien hinzufügen können, auf d/die über das /initProgramm zugegriffen werden kann, wenn der Kernel ausgeführt wird.

Dann cdin den Linux-Kernel-Baum einbauen wie gewohnt und in QEMU ausführen:

git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
git checkout v4.9
make mrproper
make defconfig
make -j"$(nproc)"
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -initrd "$ROOTFS_PATH"

Und Sie sollten eine Linie sehen:

FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR

auf dem Emulatorbildschirm! Beachten Sie, dass es nicht die letzte Zeile ist, Sie müssen also etwas weiter nachschlagen.

Sie können C-Programme auch verwenden, wenn Sie sie statisch verknüpfen:

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR FOOBAR\n");
    sleep(0xFFFFFFFF);
    return 0;
}

mit:

gcc -static init.c -o init

Sie können auf echter Hardware mit einem USB-Anschluss ausgeführt werden /dev/sdXund:

make isoimage FDINITRD="$ROOTFS_PATH"
sudo dd if=arch/x86/boot/image.iso of=/dev/sdX

Hervorragende Quelle zu diesem Thema: http://landley.net/writing/rootfs-howto.html Hier wird auch erklärt, wie Sie dieses gen_initramfs_list.shSkript aus dem Linux-Kernel-Quellbaum verwenden, um den Prozess zu automatisieren.

Nächster Schritt: Richten Sie BusyBox so ein, dass Sie mit dem System interagieren können: https://github.com/cirosantilli/runlinux

Getestet unter Ubuntu 16.10, QEMU 2.6.1.

Ciro Santilli ist ein Schauspieler
quelle