Share this

Eine kurze Erläuterung von Linux-Embedded-Systemen

2026-02-21 10:55:48 · · #1
In der Vergangenheit basierten viele eingebettete Systeme nicht auf einem Betriebssystem, sondern auf einem proprietären Kernel eines Herstellers oder einer DOS-Erweiterung. Diese Ansätze sind den Anforderungen der heutigen Entwicklung eingebetteter Systeme eindeutig nicht mehr gewachsen! Zwar bieten einige kommerzielle Echtzeitbetriebssysteme kleine Kernel und Multitasking-Entwicklungsumgebungen, ihre Leistung ist jedoch nicht optimal und genügt nicht den Bedürfnissen des heutigen Marktes für Echtzeit-Eingebettete Systeme. Daher richtet sich der Fokus nun auf Allzweckbetriebssysteme (wie Windows, Solaris und Linux), in der Hoffnung, diese in Echtzeitbetriebssysteme zu transformieren. Diese Betriebssysteme sind typischerweise leistungsstark, komplex, leicht anpassbar, äußerst praxisorientiert und bieten Programmierern vertraute Standard-APIs. Darüber hinaus bieten sie Unterstützung für die Echtzeit-Softwareentwicklung. Dennoch weisen diese Betriebssysteme für die Entwicklung eingebetteter Systeme noch Schwächen auf. Eingebettete Systeme erfordern hohe Zuverlässigkeit, Anpassbarkeit an die jeweiligen Anwendungsanforderungen und eine höhere Echtzeitleistung als Allzweckbetriebssysteme. Linux bietet als Lösung für die Entwicklung eingebetteter Systeme einzigartige Vorteile gegenüber vielen anderen Allzweckbetriebssystemen. Erstens wird die Anpassung proprietärer kommerzieller Betriebssysteme wie Windows und Solaris streng von den Herstellern kontrolliert. Dies schränkt die Anpassungsmöglichkeiten für Entwickler erheblich ein. Linux hingegen unterliegt der GPL-Lizenz, wodurch der gesamte Quellcode des Systems Open Source und sehr einfach anpassbar ist. Zweitens bietet Linux im Vergleich zu anderen Open-Source-Betriebssystemen (wie FreeBSD) einen deutlichen Vorteil hinsichtlich der Unterstützung verschiedener Prozessoren, Entwicklungsboards und Softwareentwicklungswerkzeuge. Obwohl Linux ursprünglich als Allzweckbetriebssystem konzipiert und entwickelt wurde, unterstützt es Echtzeitverarbeitung. Dies umfasst die Unterstützung der meisten Echtzeitfunktionen des POSIX-Standards, Multitasking und Multithreading sowie umfangreiche Kommunikationsmechanismen. Linux bietet außerdem POSIX-konforme Scheduling-Richtlinien, darunter FIFO, Round-Robin und statisches, präemptives Prioritäts-Scheduling. Die Standard-Scheduling-Richtlinie ist die dritte. Linux bietet zudem Speichersperrung, um zu verhindern, dass Speicherseiten während der Echtzeitverarbeitung ausgelagert werden, und stellt einen POSIX-konformen Echtzeit-Signalisierungsmechanismus bereit. Ein kritisches Problem besteht darin, dass Linux präemptives Scheduling im Benutzermodus, nicht aber im Kernelmodus unterstützt. Dies bedeutet, dass im Linux-Kernel laufende Aufgaben (oder Systemaufrufe) nicht von Aufgaben mit höherer Priorität unterbrochen werden können, was zu einer Prioritätsinversion führt. Darüber hinaus sind Interrupt-Handler im Linux-Betriebssystem nicht planbar und können nicht prioritätsbasiert eingeplant werden. In Echtzeitsystemen ist es wünschenswert, dass Interrupt-Handler dieselbe Priorität wie Echtzeitaufgaben haben, damit sie vom System-Scheduler eingeplant werden können. Weiterhin sind wir besorgt über die Taktgenauigkeit im Zusammenhang mit der Aufgabenreaktionszeit und Probleme mit Prioritätsinversionen aufgrund gemeinsamer Ressourcennutzung. In Linux beträgt das Standardintervall für Hardware-Takt-Interrupts 10 ms, und alle Software-Taktgeber werden von der Hardware getriggert. Einfache Synchronisationsmechanismen (gegenseitiger Ausschluss) unterstützen keine Prioritätsvererbung und führen wahrscheinlich zu Prioritätsinversionen. Der Ansatz eines unabhängigen Kernels in Linux als Echtzeitsystem bezieht sich auf die Entwicklung eines vollständig unabhängigen Echtzeitkernels mit APIs, die mit dem Linux-Kernel kompatibel sind. Die theoretische Grundlage dieses Ansatzes ist, dass ein exzellentes Echtzeitbetriebssystem die Echtzeitanforderungen des Systems von Anfang an umfassend berücksichtigen und standardkonforme APIs bereitstellen muss. Diese Implementierungsmethode ist für viele Anbieter proprietärer, POSIX-konformer Echtzeitsysteme attraktiv. Die Einschränkung dieses Ansatzes besteht darin, dass durch die Entwicklung eines vollständig unabhängigen Echtzeitkernels ohne Verwendung des bestehenden Linux-Kernels einige Vorteile des Linux-Systems, insbesondere solche, die mit dem Linux-Kernel zusammenhängen, nur schwer übernommen werden können. Dazu gehören beispielsweise die umfassende Unterstützung einer breiten Palette von Hardware sowie die hohe Zuverlässigkeit und Stabilität des Linux-Kernels. Da diese Methode keinen Echtzeitkernel durch Modifizierung des Linux-Kernel-Codes entwickelt, sondern einen Echtzeitkernel auf Basis des Linux-Systems neu entwirft, ist für diese Entwicklung kein Open-Source-Code erforderlich. Daher werden einige der auf Open Source basierenden Vorteile von Linux zwangsläufig beeinträchtigt. Schließlich lassen sich Entwicklungsergebnisse, die auf dem Linux-Kernel basieren, nicht ohne Weiteres auf einen Echtzeitkernel übertragen. Natürlich profitiert diese Implementierungsmethode auch erheblich vom Linux-System. Dank der Linux-Unterstützung muss der Echtzeitkernel nicht „real“ implementiert werden. Entwickler mit Linux-Kenntnissen können sich zudem schnell mit Echtzeitsystemen vertraut machen, die mit dieser Methode entwickelt wurden. Linux wird daher ganz natürlich als Entwicklungsplattform für eingebettete Systeme in Betracht gezogen. Wenn die API dieses Echtzeitsystems eine Teilmenge der Linux-System-API ist, kann sie für Anwendungsentwicklung und Debugging ausschließlich auf dem Linux-Host emuliert werden, wodurch das umständliche Remote-Debugging entfällt. Die Kompatibilität mit der Linux-API ist ein wichtiges Kriterium für die Bewertung solcher Echtzeitsysteme. Ist ein Echtzeitsystem mit allen Linux-APIs kompatibel, können alle Linux-Anwendungen und -Bibliotheken darauf ausgeführt und genutzt werden. Dies bietet einen enormen Vorteil: Sämtliche unter Linux verfügbare Drittanbietersoftware kann darauf verwendet werden. Die Entwicklung eines solchen mit allen Linux-APIs kompatiblen Echtzeitsystems ist jedoch keineswegs einfach, insbesondere für einen einzelnen Entwickler. Daher lassen sich viele Drittanbieterprogramme nicht ohne Weiteres auf Echtzeitsysteme portieren – ein Nachteil, der die Vorteile von Linux deutlich schmälert. Dual-Core-Ansatz: Dieser Ansatz nutzt zwei kooperierende Systemkerne auf derselben Hardwareplattform. Ein Kernel übernimmt präzises Echtzeit-Multitasking, während der andere komplexe, nicht-Echtzeit-basierte Allzweckfunktionen bereitstellt. Dies wird durch eine Echtzeit-Kernelschicht auf der untersten Ebene des Linux-Betriebssystems erreicht. Der Echtzeit-Kernel ist für die Hardwareverwaltung und das Echtzeit-Task-Management zuständig. Er simuliert per Software die Nutzung bzw. Deaktivierung von Low-Level-Hardware in einem regulären Linux-System, anstatt tatsächlich Interrupt-Register zu manipulieren. Der Linux-Kernel wird im Echtzeit-Kernel als Task mit der niedrigsten Priorität eingeplant und nur dann ausgeführt, wenn keine Echtzeit-Tasks verfügbar sind. Ein wesentlicher Aspekt dieses Ansatzes ist, dass alle Nicht-Echtzeit-Tasks, die auf dem regulären Linux-Kernel laufen, präemptives Scheduling unterstützen müssen. Dadurch wird sichergestellt, dass die präzisen Echtzeitgarantien des Echtzeit-Kernels nicht beeinträchtigt werden. Da der Echtzeitkernel sehr klein ist, erhöht er die Systemlast nicht und bietet somit optimale Unterstützung für die Entwicklung von Echtzeitsoftware mit strengen Echtzeitanforderungen. Der Nachteil dieses Ansatzes besteht darin, dass die Entwicklung von Echtzeitaufgaben direkt auf den kleinen Echtzeitkernel mit seinen präzisen Echtzeitdiensten ausgerichtet ist, anstatt auf den leistungsstarken regulären Linux-Kernel. Daher laufen Echtzeitaufgaben auf Systemkernel-Ebene und können somit ohne Speicherschutz arbeiten. Ein einziger Fehler in einer Echtzeitaufgabe kann daher das gesamte System lahmlegen! Noch kritischer ist, dass die Entwicklung dieser Echtzeitaufgaben aufgrund des kleinen Echtzeitkernels Linux-APIs sowie Software und Laufzeitbibliotheken von Drittanbietern nicht direkt nutzen kann. Dieses Entwicklungsmodell erfordert eine statische Zerlegung der Anwendung in Echtzeit- und Nicht-Echtzeitkomponenten. In den meisten Fällen ist dies von Vorteil, da es Entwickler dazu zwingt, das Anwendungssystem in Echtzeit- und Nicht-Echtzeit-Subsysteme zu unterteilen. Es ist jedoch offensichtlich, dass dieses Entwicklungsmodell auch die Art der möglichen Anwendungen einschränkt. Da diese binäre Sichtweise von Echtzeitsystemen nicht für alle Anwendungen geeignet ist, ist ein anderer Ansatz problematisch. In manchen Anwendungen ist die Grenze zwischen Echtzeit- und Nicht-Echtzeitkomponenten nicht klar definiert, und es können unterschiedlich stark ausgeprägte Soft-Echtzeit-Komponenten vorhanden sein. Ein weiterer Nachteil dieses Ansatzes besteht darin, dass das Entwicklungsmodell zwei unabhängige Dimensionen von Echtzeitanwendungen vermischt: funktionale und Echtzeitanforderungen. Es setzt voraus, dass die Echtzeitanforderungen der Anwendung auf die vom Echtzeitkernel bereitgestellten funktionalen Anforderungen beschränkt sind. Die vom Echtzeitkernel bereitgestellte funktionale Unterstützung ist jedoch sehr begrenzt. Natürlich lässt sich die Funktionalität des Echtzeitkernels erweitern, beispielsweise durch das Hinzufügen von Echtzeit-Netzwerkfunktionen. Die neu hinzugefügten Komponenten überschneiden sich jedoch wahrscheinlich mit bestehenden Linux-Kernel-Funktionalitäten, was zu unnötiger Systemaufblähung führt und den Nutzen dieses Ansatzes mindert. Der Ansatz der Kernelmodifikation nutzt die bestehende Unterstützung von Linux-Systemen für die Echtzeit-Softwareentwicklung und modifiziert den Quellcode, um Linux in ein echtes Echtzeitbetriebssystem zu verwandeln. Dieser Ansatz entspricht der Linux-Philosophie. Jedes Produkt, das auf Modifikationen des Linux-Kernel-Quellcodes basiert, muss unter der GPL-Lizenz stehen, wodurch der Quellcode für alle Softwareentwickler frei zugänglich wird. Sobald viele Anwender ihn als nützlich erachten, wird er weiterentwickelt und entweder in den allgemeinen Linux-Kernel integriert oder als separater Echtzeit-Zweig für Linux ausgelagert. Das zentrale Prinzip dieses Ansatzes besteht darin, Modifikationen sorgfältig auszuwählen, um verschiedene Anforderungen der Linux-Echtzeitentwicklung zu erfüllen. Da diese Modifikationen relativ lokal begrenzt sind, verändern sie den Linux-Kernel nicht grundlegend. Einige Modifikationen lassen sich auch mithilfe regulärer, ladbarer Linux-Module realisieren. Das System kann diese Module bei Bedarf dynamisch laden und bei Nichtgebrauch dynamisch entladen. Ein Beispiel für eine solche Modifikation ist die präemptive Kernel-Planung. Die Umstellung des Kernels von nicht-präemptiv auf präemptiv stellt eine bedeutende strukturelle Änderung dar und könnte viele Probleme verursachen. Viele dieser Probleme wurden jedoch bereits durch die SMP-Unterstützung von Linux gelöst. Daher lassen sich präemptive Kernel-Modifikationen einfach mithilfe von SMP-Hooks implementieren. Eine weitere Modifikation sind die bereits erwähnten planbaren Interrupt-Handler. Einige Modifikationen sind global, wie beispielsweise die Anpassung des Systemuhrendienstes für präzisere „Heartbeats“ ohne zusätzliche Systemlast oder die Implementierung eines Mechanismus zum gegenseitigen Ausschluss im Kernel zur Unterstützung der Prioritätsvererbung. Ressourcenkernel-Ansatz: Dieser Ansatz entstand, um die Einschränkungen präemptiver Scheduling-Strategien mit fester Priorität in traditionellen Echtzeitbetriebssystemen zu beheben. Präemptive Scheduling-Algorithmen mit fester Priorität bieten keinen Schutz zwischen den Tasks. Daher hängen vorhersagbare Task-Reaktionszeiten von der Vorhersage der Ausführungszeiten aller Tasks mit höherer Priorität ab. In solchen Systemen ist die Vorhersagbarkeit global abhängig und kann durch einen einzelnen fehlerhaften Task beeinträchtigt werden. Darüber hinaus ist eine statische Betrachtung von Echtzeitsystemen unangebracht. In vielen Echtzeitanwendungen ist es wünschenswert, dass das Echtzeitsystem die Task-Attribute dynamisch an die verfügbaren Ressourcen anpasst, um eine optimale Leistung zu erzielen. Der Ressourcenkernel-Ansatz ist eine ressourcenzentrierte Methode, die den Echtzeitkernel anleitet, Systemressourcen präzise, ​​garantiert und präemptiv zu beschaffen. Der Echtzeitkernel ermöglicht die Konfigurierbarkeit von Echtzeitanwendungen, vorausgesetzt, die benötigten Ressourcen können vom Hintergrundprogramm des Kernels zugewiesen werden. Somit bildet der Echtzeitkernel die Grundlage für die Entwicklung von Echtzeitanwendungen – von einfachen bis hin zu komplexen Echtzeitsystemen. Alle Systeme lassen sich durch dynamische Anpassung der Attribute und Prioritäten von Echtzeitaufgaben konfigurieren. Der größte Vorteil dieses Ansatzes ist die Robustheit und die vorhersagbare Echtzeitleistung des Systems. Ein weiterer Vorteil besteht darin, dass Anwendungen ihre Attribute dynamisch an die jeweiligen Bedingungen anpassen können. Darüber hinaus eignet sich dieser Ansatz hervorragend für die Entwicklung eingebetteter Systeme. Im Folgenden finden Sie vereinfachte Schritte zur Anpassung eines Linux-Systems (nur als Referenz). Jegliche Ähnlichkeit mit realen Ereignissen ist rein zufällig! Unser Ziel-Linux-System läuft auf einem Standard-Intel-386-PC, der optional über eine Festplatte verfügt und stattdessen einen Flash-Speicher verwendet. Bei Verwendung eines Flash-Speichers muss das System das Booten von diesem unterstützen, und die Speicherkapazität des Flash-Speichers muss mindestens 16 MB betragen. Wir möchten, dass Benutzer direkt in die grafische Oberfläche von X Window booten und vordefinierte Programme ausführen können, ohne sich mit Benutzername und Passwort anmelden zu müssen. Unser Ziel ähnelt einer X-Terminal-Workstation. Mit geringfügigen Anpassungen wäre sogar ein disklesses System möglich, sodass wir den 16-MB-Flash-Speicher überflüssig machen könnten. Dies würde jedoch den Rahmen dieses Artikels sprengen. Interessierte Leser können sich gerne an mich wenden. Systemstart: Da wir das Booten vom Flash-Speicher in Betracht ziehen, haben wir uns für LILO anstelle von GRUB als Bootloader entschieden. GRUB erkennt Festplatten und Dateisysteme besser, Flash-Speicher sind jedoch keine herkömmlichen Festplatten, und GRUB erkennt das gewählte Dateisystem möglicherweise nicht; tatsächlich könnte GRUB sogar Probleme verursachen. LILO ist deutlich einfacher. Es schreibt ein kleines Programm in den MBR am Anfang der Festplatte. Dieses Programm umgeht das Dateisystem, liest das Kernel-Image direkt aus den Sektornummern der Festplatte und lädt es in den Arbeitsspeicher. Dies erhöht die Sicherheit erheblich und gibt uns die Freiheit, das Dateisystem frei zu wählen. Wie installieren wir LILO? Zuerst benötigen wir eine herkömmliche IDE-Festplatte mit 800 MB/s, die wir an den IDE-Anschluss des Zielrechners anschließen. Auf unserem Zielrechner ist IDE1 der USB-Stick und IDE2 die Arbeitsfestplatte. Wir installieren ein Debian GNU/Linux-System auf der IDE2-Festplatte. Alternativ können Sie auch Red Hat installieren. Nach der Installation des Systems entfernen wir unnötige Dienste und X Window. Dies verbessert die Systemstartgeschwindigkeit, da wir den Rechner später häufig neu starten müssen und die Startgeschwindigkeit daher entscheidend für unsere Arbeitseffizienz ist. Anschließend erstellen wir auf dem USB-Stick ein Ext2-Dateisystem mit dem Befehl `mke2fs`. Da der USB-Stick an IDE1 angeschlossen ist, hat er unter Linux die Kennung `/dev/hda`. Der Autor partitionierte den gesamten Flash-Speicher in eine einzige Partition, sodass `mke2fs` auf `/dev/hda1` aufgerufen wurde. Leser sollten in der Lage sein, direkt ein Ext2-Dateisystem auf `/dev/hda` zu erstellen, ohne vorher partitionieren zu müssen. Nach der Erstellung des Dateisystems auf dem Flash-Speicher kann eine kompilierte Kernel-Image-Datei, `vmlinuz`, darauf kopiert werden. Beachten Sie, dass diese Image-Datei `vmlinuz` vor der Installation von LILO auf den Flash-Speicher kopiert werden muss. Andernfalls wird LILO ruckeln, da es den Speicherort des Kernel-Images auf dem Flash-Speicher nicht finden kann, wodurch der Flash-Speicher nicht bootet. Wenn Leser ein komprimiertes Dateisystem auf dem Flash-Speicher verwenden, wird LILO ebenfalls auf Probleme stoßen. Obwohl es möglicherweise die Startposition des Kernel-Images auf der Festplatte korrekt findet, kann es das vom Dateisystem neu komprimierte Kernel-Image nicht verarbeiten und weiß nicht, wie es in den Speicher entpackt werden soll. Nach dem Kopieren des Kernel-Images kann die Datei `lilo.conf` bearbeitet und auf dem System abgelegt werden. Achten Sie dabei unbedingt auf die korrekten Dateinamen in `lilo.conf`. Diese Pfade entsprechen den Pfaden auf dem System. Wenn der USB-Stick beispielsweise im Verzeichnis `/mnt` gemountet ist, lautet der Pfad zu `vmlinuz` in `lilo.conf` `/mnt/vmlinuz`. Beachten Sie dies genau, da eine versehentliche Beschädigung von LILO auf dem System schwerwiegende Folgen haben kann. Führen Sie nach der Bearbeitung von `lilo.conf` den Befehl `lilo` aus und geben Sie dabei die neue Datei `lilo.conf` an, nicht `/etc/lilo.conf`. Nach der Installation von LILO können Sie das System sofort neu starten und testen. Stellen Sie im BIOS zunächst den Bootvorgang auf IDE1 ein. Erscheint die LILO-Eingabeaufforderung und werden nach dem Drücken der Eingabetaste Kernel-Ausgabemeldungen angezeigt, ist LILO erfolgreich installiert. Merken Sie sich diese Vorgehensweise: Aktualisieren Sie LILO jedes Mal, wenn Sie das Kernel-Image auf dem USB-Stick aktualisieren. Das heißt, Sie müssen den Befehl `lilo` erneut ausführen. Kernel kompilieren: Nach erfolgreicher Installation von LILO können wir mit der Kompilierung eines neuen Kernels beginnen. Dazu müssen wir zunächst unser System starten. Es gibt zwei Möglichkeiten: Entweder wir stellen das BIOS so ein, dass es von IDE2 bootet. Dafür muss LILO bei der BIOS-Installation auf `/dev/hdb` installiert werden. Oder wir booten von IDE1, ohne die BIOS-Einstellungen zu ändern. Sobald die LILO-Eingabeaufforderung erscheint, geben wir `linux root=/dev/hdb1` ein. Das `linux` am Anfang ist ein Eintrag in der Datei `lilo.conf`. Wir verwenden nur das durch diesen Eintrag angegebene Kernel-Image, aber `/dev/hdb1` als Root-Dateisystem. Manchmal ist die eine Methode besser oder bequemer als die andere. Das hängt von der jeweiligen Situation ab. Die Einstellungen schließen sich jedoch nicht gegenseitig aus. Da unser Kernel nur auf einem Rechner verwendet wird, sollten wir uns bei der Kernel-Kompilierung genau mit seiner Konfiguration vertraut machen. Um unnötige Komplexität zu vermeiden, haben wir auf die Verwendung von Kernelmodulen verzichtet und stattdessen alle notwendigen Komponenten direkt in den Kernel kompiliert. Dadurch ist der Kernel auf einem typischen 586er-Motherboard mit allen wesentlichen Funktionen in der Regel weniger als 800 KB groß. Dieser Ansatz ist daher praktikabel und reduziert die Komplexität der Init-Skripte. Da der benötigte Kernelcode ohnehin in den Speicher geladen wird, entsteht zur Laufzeit keine Speicherverschwendung. Auf unserer Zielplattform möchten wir USB-Speichergeräte verwenden. Ein weiterer wichtiger Punkt ist die Unterstützung von Framebuffern. Diese ist primär für die XFree86-Unterstützung relevant. Idealerweise wird unsere Grafikkarte direkt von XFree86 unterstützt, und die Unterstützung von Framebuffern im Kernel ist nicht erforderlich. Falls XFree86 unsere Grafikkarte jedoch nicht unterstützt, können wir den VESA-Modus in Betracht ziehen. Die VESA-Kartenunterstützung von XFree86 ist jedoch nicht optimal und weist Sicherheitslücken auf; gelegentlich treten beim Starten und Beenden von X Window Bildfehler auf. Daher können wir den VESA-Modus-Framebuffer des Kernels und anschließend den xfree86-Framebuffer-Treiber von Linux verwenden. Dies beseitigt in der Regel Bildschirmfehler und birgt keine Sicherheitsrisiken. Auch devfs ist ein interessantes Thema. Nutzt der Kernel kein devfs, muss das Root-Dateisystem alle Inhalte des Verzeichnisses `/dev` enthalten. Diese Inhalte können entweder mit dem Skript `/dev/MAKEDEV` oder manuell mit `mknod` erstellt werden. Diese Methode hat zwar Vorteile, ist aber umständlich und nicht mit dem Kernel-Zustand kompatibel. Mit devfs hingegen müssen wir uns nicht mehr um die Inhalte des Verzeichnisses `/dev` kümmern. Der Kernel-Code verwaltet diese automatisch. Im praktischen Einsatz ist der Speicherverbrauch gering. Daher entscheiden wir uns für devfs. Mit LILO und dem Kernel-Image in Busybox konfigurieren wir als Nächstes das Root-Dateisystem. Mit nur 16 MB Speicherplatz auf dem USB-Stick ist dies wohl unsere größte Herausforderung. Ich möchte Ihnen daher ein häufig verwendetes Tool zum Einrichten des Root-Dateisystems in kleinen Embedded-Linux-Systemen vorstellen: BusyBox. BusyBox wurde ursprünglich von Bruce Perens, dem bekannten Entwickler von Debian GNU/Linux, entwickelt und im Debian-Installer verwendet. Viele Debian-Entwickler trugen später dazu bei, insbesondere Erik Andersen, der aktuelle Betreuer von BusyBox, der trotz seiner Krebserkrankung ein hervorragender Entwickler freier Software ist. BusyBox wird zu einem einzigen, eigenständigen ausführbaren Programm kompiliert, das einfach BusyBox heißt. Je nach Konfiguration kann es jedoch die Funktionalität der Ash-Shell und die Funktionen zahlreicher kleiner Anwendungen ausführen. Dies umfasst einen Mini-vi-Editor, das unverzichtbare Programm `/sbin/init` sowie weitere Programme wie `sed`, `ifconfig`, `halt`, `reboot`, `mkdir`, `mount`, `ln`, `ls`, `echo`, `cat` usw. Diese sind auf einem normalen System unerlässlich, doch würde man die ursprünglichen Komponenten dieser Programme extrahieren, wäre ihre Gesamtgröße enorm. Trotz all dieser Funktionen ist Busybox jedoch nur etwa 100 KB groß. Darüber hinaus können Benutzer je nach Bedarf entscheiden, welche Anwendungsfunktionen in Busybox kompiliert werden sollen, wodurch die Größe weiter reduziert wird. Die Verwendung von Busybox ist ebenfalls einfach. Erstellen Sie einfach einen symbolischen Link, beispielsweise `ln -s /bin/busybox /bin/ls`. Wenn dann `/bin/ls` ausgeführt wird, führt Busybox die `ls`-Funktion aus und verarbeitet die Befehlszeilenargumente auf dieselbe Weise. Ein weiteres Beispiel ist `ln -s /bin/busybox /sbin/init`, wodurch das wichtige Programm `/sbin/init` zum System hinzugefügt wird. Dies setzt natürlich voraus, dass Sie die Funktionalität dieser beiden Programme in Busybox kompiliert haben. Wichtig ist, dass das von Busybox' `init`-Programm erkannte `/etc/inittab`-Format sehr einfach ist und sich vom regulären `inittab`-Dateiformat unterscheidet. Daher sollten Sie beim Schreiben von `inittab`-Dateien für Busybox' `init` auf die abweichende Syntax achten. Weitere Details finden Sie im Busybox-Benutzerhandbuch. Vom Start bis zum Aufrufen der Shell : Nach der Installation von Busybox können Sie es neu starten, bis Sie die Shell-Eingabeaufforderung sehen. Zuvor müssen Sie einige wichtige Dateien im Verzeichnis `/etc` vorbereiten und die von Busybox verwendeten Bibliotheken kopieren. Mit dem Befehl `ldd` und dem Pfadnamen des zu analysierenden Binärprogramms können Sie die Abhängigkeiten zwischen einem Binärprogramm oder einer Bibliotheksdatei ermitteln. Busybox ist beispielsweise von `libc.so` und `ld-linux.so` abhängig. Mit diesem Wissen können wir alle benötigten Bibliotheken auf den USB-Stick kopieren. Unser USB-Stick ist mit etwa 16 MB nicht besonders groß. Wir können die Glibc-Dateien daher problemlos direkt verwenden. Sollten Leser spezielle Anforderungen haben und Glibc als zu umfangreich empfinden, können sie uClibc, eine sehr kleine libc-Bibliothek, in Betracht ziehen. Obwohl ihr Funktionsumfang nicht so umfangreich ist wie der von Glibc, reicht er für ein eingebettetes System aus. uClibc wird in diesem Artikel nicht weiter behandelt. Nach dem Kopieren der Bibliotheksprogramme können wir uns den Systemstartschritten zuwenden. Beim Start wird zuerst lilo ausgeführt, gefolgt vom Kernel. Nach der Initialisierung des Kernels wird /sbin/init aufgerufen, welches dann /etc/inittab interpretiert, um verschiedene Prozesse auszuführen. inittab weist init an, das wichtigste Systeminitialisierungsprogramm, /etc/init.d/rcS, aufzurufen. In rcS werden wir die verschiedenen Dateisysteme einbinden. Zusätzlich ruft rcS das DHCP-Programm auf, um das Netzwerk einzurichten. Nachdem rcS die Ausführung abgeschlossen hat, öffnet init gemäß den Anweisungen in inittab eine Shell in der Konsole oder öffnet getty + login, sodass der Benutzer zur Eingabe eines Benutzernamens aufgefordert wird. Der Einfachheit halber starten wir hier direkt die Shell und ändern dies nach erfolgreichem Debugging, um direkt X Window zu starten. Die Syntax von inittab wurde bereits oben erläutert. Wir empfehlen Lesern, das offizielle Busybox-Benutzerhandbuch zu konsultieren. Hier beschreiben wir zunächst die Struktur des Dateisystems . Wie Sie sehen, verwendet unser Root-Dateisystem zur Vermeidung von Komplikationen das Standard-Dateisystem ext2. Da unser Festplattenspeicher mit weniger als 16 MB sehr klein ist und wir X Window darauf installieren müssen, würde die durchgängige Verwendung von ext2 den begrenzten Speicherplatz auf dem USB-Stick schnell erschöpfen. Unsere einzige Option ist die Verwendung eines geeigneten komprimierten Dateisystems. Da der Inhalt des Verzeichnisses /usr während des Systembetriebs nicht verändert werden muss, haben wir uns für das schreibgeschützte komprimierte Dateisystem cramfs entschieden, um den gesamten Inhalt des Verzeichnisses /usr zu speichern. Cramfs ist ein kleines Dateisystem, das von Linus Torvalds für eingebettete Systeme entwickelt wurde. Obwohl es zlib zur Komprimierung verwendet, ermöglicht es trotz seines Nur-Lese-Modus einen effizienten Direktzugriff. Da Cramfs die Lesegeschwindigkeit des Systems nicht beeinträchtigt und ein hochkomprimiertes Dateisystem ist, eignet es sich hervorragend für unsere Zwecke. Zunächst erstellen wir eine Cramfs-Image-Datei, die den gesamten Inhalt des Verzeichnisses `/usr` enthält. Dies geschieht mit dem Befehl `mkcramfs`. Nachdem wir die Datei `usr.img` erhalten haben, müssen wir überlegen, wie wir diese Image-Datei zur Systemlaufzeit als nutzbares Dateisystem einbinden. Da diese Image-Datei im üblichen Sinne kein Blockgerät ist, benötigen wir ein Loopback-Gerät. Konkret fügen wir den folgenden `mount`-Befehl am Anfang des zuvor erwähnten Skripts `/etc/init.d/rcS` ein: `mount -o loop -t cramfs /usr.img /usr`. Dies ermöglicht es uns, die cramfs-Image-Datei `usr.img` über das Loopback-Gerät in das Verzeichnis `/usr` einzubinden. Da wir ein Loopback-Gerät verwenden, sollten Leser beim Kompilieren des Kernels unbedingt die Unterstützung für dieses Gerät einbinden. Die Einbindung hat keine Auswirkungen auf den weiteren Systembetrieb. Die Komprimierungseffizienz von cramfs erreicht typischerweise fast 50 %. Da sich der Großteil unseres Systems im Verzeichnis `/usr` befindet, können die ursprünglich benötigten 18 MB Flash-Speicherplatz auf nur 11 MB reduziert werden. Ein 14 MB großes Verzeichnis `/usr` wurde auf lediglich 7 MB komprimiert. Neben der Komprimierung ist zu beachten, dass Flash-Speicher im Gegensatz zu herkömmlichen Festplatten nicht ideal für häufige Schreib- und Löschvorgänge geeignet sind. Daher planen wir, für Bereiche mit häufigen Schreib- und Löschvorgängen Arbeitsspeicher zu verwenden. Hierfür nutzen wir tmpfs. Auf den Vergleich zwischen tmpfs und klassischem RAM-Disk gehen wir hier nicht ein. Generell ist tmpfs flexibler; seine Größe kann, anders als bei einer RAM-Disk, je nach Bedarf angepasst werden. Wir erstellen tmpfs für Verzeichnisse wie /tmp und /var. Dazu müssen lediglich zwei Zeilen, etwa die folgenden, in /etc/fstab hinzugefügt werden: `none /var tmpfs default 0 0`. Vergessen Sie anschließend nicht, `mount -a` am Anfang von /etc/init.d/rcS einzufügen. Dadurch werden alle in /etc/fstab angegebenen Dateisysteme eingebunden. Angesichts des aktuellen Entwicklungsstands von X Window könnte man meinen, die Installation sei komplex. Dies ist jedoch nicht der Fall. Dank des bereits etablierten Frameworks ist die Installation von X Window sehr einfach; Sie müssen lediglich einige wenige wichtige Programme kopieren. In der Regel werden nur die Verzeichnisse `bin` und `lib` unter `/usr/X11R6` benötigt. Darüber hinaus können Sie die Anzahl der Programme je nach Bedarf deutlich reduzieren. Wenn Sie beispielsweise einen offenen XFS-Schriftserver in Ihrem lokalen Netzwerk betreiben, können Sie alle lokalen Schriftarten löschen und den Remote-Schriftserver verwenden. Falls Sie nur wenige Programme ausführen müssen, vergessen Sie nicht, unnötige Bibliotheken zu löschen. Zusätzlich können Sie redundante X-Window-Treiber entfernen und nur die für Ihre lokale Grafikkarte benötigten Treiber behalten. Dieser Schritt erfordert natürlich mehrere Tests. Weitere Tipps: Befindet sich Ihr Arbeitssystem auf einem anderen Rechner, der über das lokale Netzwerk mit Ihrem Rechner verbunden ist, ist SSH ein praktisches Werkzeug. Die in SSH enthaltene Funktion `scp` funktioniert ähnlich wie die reguläre Kopierfunktion `cp` und ist daher sehr komfortabel. Durch die Verwendung von SSH und SCP zum Teilen von Dateien und für Remote-Experimente entfällt das Herumlaufen im Büro. Benötigen Sie einen X-Server und einen XFS-Schriftserver unter Windows, sollten Sie das XFree86-System aus dem Cygwin Toolkit von Red Hat in Betracht ziehen.
Read next

Produktionsautomatisierungs-Überwachungssystem der Hunan Tianlong Rice Industry Co., Ltd.

Zusammenfassung: Dieser Artikel beschreibt die automatische Steuerung einer Reisraffinerieanlage mithilfe der speicherpr...

Articles 2026-02-20