Erschienen in DESIGN & VERIFICATION 04/2001, S. 54-57 (layoutete pdf-Version mit dem "details" Button in der toolbox unter dieser Meldung download-bar.)
Chris Clark ist als Regional Systems Manager EMEA bei LynuxWorks in Frankreich für das Field Systems Engineering-Team verantwortlich.
Linux ist der Aufsteiger unter den Betriebssystemen. Rein technisch gesehen, bietet es zahlreiche Stärken der Open-Systems-Welt (speziell von Unix/Posix), der es entstammt: standardisierte APIs (Application Programming Interfaces), reichhaltige Ausstattung; Betriebsicherheit und Stabilität durch den implementierten Speicherschutz und vollständig integrierte Netzwerkfunktionen und einen reichhaltigen Bestand an Applikationsentwicklungen. Viele der Hindernisse, die dem Einsatz von Linux in Echtzeit-Systemen im Wege stehen, lassen sich auf das Design des eigentlichen Kerns zurückführen. Während sich einige der vorgefundenen Probleme relativ leicht lösen lassen, haben andere weitreichende Folgen und ihre Bewältigung würde eine grundlegende Umstrukturierung, wenn nicht gar eine völlige Neuentwicklung des Kerns erfordern. Die alternativen Lösungskonzepte der für den Echtzeitbetrieb problematischen Bereiche des Linux-Kerns werden im Folgenden erörtert. von Chris Clark
Aufbau des Linux-Kerns
Viele der Hindernisse, die dem Einsatz von Linux in Echtzeit-Systemen im Wege stehen, lassen sich auf das Design des eigentlichen Kerns zurückführen. Während sich einige der vorgefundenen Probleme relativ leicht lösen lassen, haben andere weitreichende Folgen und ihre Bewältigung würde eine grundlegende Umstrukturierung, wenn nicht gar eine völlige Neuentwicklung des Kerns erfordern. Welche Bereiche des Linux-Kerns problematisch für den Echtzeitbetrieb sind, ist allgemein bekannt:
· der Scheduler bevorrechtigt ‚Task Queues‘ und ‚Bottom Half Handler‘ unabhängig von den Echtzeitbedürfnissen der dann wartenden User-Tasks · durch Linux’ nicht preemptive Struktur müssen echtzeitbedürftige User-Tasks warten, bis der Kern wieder verlassen worden ist · während der Synchronisation mit Hardware-Geräten muss der Linux-Kern zu bestimmten Zeiten die Interrupts sperren · die Bottom Half Handler verkürzen die Sperrzeiten, werden dennoch unabhängig von der Priorität anderer Tasks erst abgearbeitet · Task-Queueing geschieht beispielsweise beim Warten auf Resources nicht nach Priorität · Linux unterstützt keine Threads, sondern bildet sie auf Prozesse ab · niederpriore Tasks halten bisweilen Daten für höherpriore Tasks unzugänglich und „umgehen“ damit Prioritäten (zwar existiert eine Reihe von Lösungen für dieses Problem, doch ist im Linux-Kern zur Zeit keine dieser Abhilfen implementiert)
Echtzeit-Linux – Eine grundlegende Entscheidung
Alle Ansätze, Linux zur Echtzeitfähigkeit zu verhelfen, basieren auf einer der drei folgenden Grundstrategien:
· Kern-Koexistenz: Ein kleiner Echtzeit-Executive, der unter dem Linux-Kern läuft, bearbeitet sämtliche Hardware-Interrupts und verarbeitet die Echtzeit-Tasks. Der im Wesentlichen unveränderte Linux-Kern wird beim Scheduling als eine Task geringer Priorität im Kontext des Echtzeit-Executives berücksichtigt. · Kern-Modifikation: Ausgehend von der Code-Basis eines standardmäßigen Linux-Kerns werden bestimmte Modifikationen vorgenommen, um eines oder mehrere der zuvor angesprochenen Echtzeit-Probleme zu lösen. · Ersatz des Kerns: Der ursprüngliche Linux-Kern wird durch einen unabhängigen Echtzeit-Kern ersetzt. Die Portierbarkeit der Applikationen zwischen Linux und dem Echtzeit-Kern ist durch die APIs gewährleistet.
Kern-Koexistenz
Das Konzept, ein Universal-Betriebssystem in einer Task zu kapseln, die auf einem Echtzeit-Executive läuft, ist durchaus nicht neu, es findet beispielsweise bei den ‚Echtzeit‘-Ergänzungen zu Windows Anwendung. Mit dieser Technik lässt sich durchaus eine gute Echtzeit-Performance erzielen, was die Interrupt-Reaktionszeit und das Antwortverhalten der Echtzeit-Tasks angeht. Diese Eigenschaften gehen allerdings auf Kosten einiger der wichtigsten Stärken von Linux. Verglichen mit dem reichen Bestand an Dienstfunktionen des Linux-Kerns ist die Funktionalität des Echtzeit-Executives stark eingeschränkt. Ein grundlegender Wesenszug des Koexistenz-Konzepts ist, dass eine Embedded-Systems-Anwendung klar in Echtzeit- und Nicht-Echtzeit-Funktionen gegliedert werden kann und dass die Echtzeit-Elemente nur sehr wenig Gebrauch von den Kern-Services machen. Der Grund für die eingeschränkte Funktionalität des Echtzeit-Executives hängt mit dem Speicherbedarf zusammen. Da der Executive zusätzlich zu Linux läuft, vergrößert er den Speicherbedarf, anstatt ihn zu reduzieren. Deshalb ist es notwendig, den Executive möglichst klein zu halten. Ein weiteres Merkmal erhöht ebenfalls den insgesamt entstehenden Speicherbedarf: ‚Service-Duplication‘. Bestimmte Mechanismen ermöglichen den Echtzeit-Tasks die Nutzung der vom Linux-Kern gebotenen Dienstfunktionen, doch profitieren diese Services nicht vom Echtzeitverhalten, denn sie werden vom herkömmlichen Linux-Kern verarbeitet. Ein Beispiel ist das TCP/IP-Networking. Obwohl Linux über einen kompletten TCP/IP-Stapel verfügt, muss für die Echtzeit-Funktionen einer Applikation ein zweiter Stack im Echtzeit-Executive implementiert werden. Daneben stellen die eingeschränkten, proprietären APIs des Echtzeit-Executives ein ernstes Hindernis für die Portierbarkeit der Applikationen dar. Das betrifft auch die Treiber, die entweder portiert oder neu geschrieben werden müssen. Die Möglichkeit, Software wiederzuverwenden und Komponenten von externen Zulieferern problemlos zu integrieren, gehört aber zu den wichtigsten Vorteilen von Linux und trägt entscheidend dazu bei, die Entwicklungszeit eines Produkts zu verkürzen und es schneller auf den Markt zu bringen. Durch das Konzept der Kern-Koexistenz wird dieser Pluspunkt erheblich eingeschränkt. Aufgrund der beschriebenen Merkmale kommt das Konzept der Kern-Koexistenz nur für relativ klein und einfach aufgebaute und eine klare Trennung zwischen Echtzeit- und Nicht-Echtzeit-Funktionen aufweisende Applikationen in Frage. Die gegenwärtigen Trends auf dem Markt für echtzeitfähige Embedded-Systeme verändern die Art der heutigen Systeme völlig, so dass das konventionelle Echtzeit-System auf eine immer größere Zahl von Anwendungen nicht mehr angewendet werden kann. Der Wert und die Differenzierung eines Produkts wird in zunehmendem Maße mit der Software erzielt. Der Einbindung fertiger Softwaremodule von externen Zulieferern kommt eine immer größere Bedeutung zu und die Portierbarkeit und Wiederverwendung vorhandener Code-Elemente ist mittlerweile unverzichtbar. Durch die Zunahme sowohl des Umfangs als auch der Komplexität der Anwendungssoftware wächst unweigerlich Anzahl an unentdeckten Fehlern. Hier bewährt sich das Speicherschutz-Konzept von Linux. Es richtet eine zuverlässige, die Aufrechterhaltung des Betriebs ermöglichende Verarbeitungsumgebung ein. Darüber hinaus verschwimmt die bisherige klare Aufteilung in Echtzeit- und Nicht-Echtzeit-Funktionen zunehmend, da zahlreiche Dienstfunktionen in beiden Kategorien in Anspruch genommen werden. Die Software einschließlich des Betriebssystems muss ausreichend flexibel sein, um mit diesen dynamischen Anforderungen fertig zu werden. Insgesamt haben die zentralen Vorteile von Linux einen immer höheren Stellenwert für den Erfolg einer großen Zahl von echtzeitfähigen Embedded-Systems-Anwendungen. Sie dürfen deshalb keinesfalls aufgegeben werden.
Kern-Modifikation
Eine nächste Möglichkeit, die Echtzeit-Eigenschaften von Linux zu verbessern, sind Modifikationen und Patches, die von dem standardmäßigen Linux-Kern ausgehen und Änderungen oder Ergänzungen am Quellcode vornehmen, um eines oder mehrere der oben erwähnten Echtzeit-Probleme zu beheben. Am häufigsten findet man Abhilfemaßnahmen für das Preemption-Problem und den Scheduler.
Preemption Points Eine Technik zum Verbessern der Echtzeit-Eigenschaften nicht preemptiver (also nicht unterbrechbarer) Kerne wie z.B. Linux, ist das Einfügen so genannter ‚Preemption Points‘. Unter einem Preemption Point versteht man einen Punkt im Kern-Code, an dem ein sicherer Kontextwechsel zu einem anderen Takt vorgenommen werden kann. Beim Erreichen eines Preemption Point prüft der Kern, ob eine andere Task mit höherer Priorität zur Verarbeitung ansteht. Ist dies der Fall, so wird umgehend mit der Verarbeitung dieser wartenden Task fortgefahren. Die Worst-Case-Latenzzeit entspricht jetzt der längsten Zeit zwischen zwei Preemption Points und nicht mehr der Gesamt-Verarbeitungszeit eines Systemaufrufs. Im Interesse einer annehmbaren Latenzzeit kann es notwendig sein, eine große Zahl von Preemption Points einzufügen, was aber als unerwünschter Nebeneffekt wiederum den Verarbeitungsaufwand des Kerns erhöht. Auch bei Anwendung dieser Technik kann allerdings keine Obergrenze für die Latenzzeit angegeben werden. Das liegt an der Implementierungsweise bestimmter Datenstrukturen im Linux-Kern. Wenn der Kern eine seiner internen Datenstrukturen (z.B. die Prozesstabelle) manipuliert, kann während dieses Arbeitsgangs keine Unterbrechung zugelassen werden, da es sonst zu einer inkohärenten Datenstruktur kommen könnte. Ist die betreffende Datenstruktur als Liste angelegt, so erhöht sich die Unterbrechungs-Latenzzeit mit der Anzahl der in der Liste befindlichen Elemente (z.B. Tasks). Dennoch wirklich echtzeitfähig ist ein Kern nur, wenn er uneingeschränkt und zu jeder Zeit unterbrechbar ist. Und das wird so auch nicht erreicht. Scheduler Der Scheduler ist ein weiterer Ansatzpunkt. Einige Lösungen zielen auf eine Neuimplementierung des gleichen, prioritätsorientierten Schedulers, allerdings mit deterministischer Performance. Andere Ansätze wiederum basieren auf einem alternativen Scheduler mit völlig anderen Algorithmen. Der Nachteil: Welchen Nutzen hat selbst der deterministischste Scheduler, wenn er nicht zum richtigen Zeitpunkt aufgerufen wird, weil der Kern gerade keine Unterbrechungen zulässt?
Bewertung Kern-Patches sind stets kurz greifende Lösungen, die nur ein oder zwei Echtzeit-Probleme angehen können und deren Vorteile durch die verbleibenden Probleme zunichte gemacht werden können. Damit die Kern-Modifikation zu einem praxisgerechten Echtzeit-Konzept wird, muss eine weiter reichende Lösung für alle existierenden Echtzeit-Probleme gefunden werden. Doch so eine Lösung dürfte wegen des Kostenaufwands für die Softwarepflege vermutlich langfristig scheitern. Die Pflege des modifizierten Kerns bringt ihrerseits wieder eine ganze Reihe von Problemen mit sich. Der Linux-Kern ändert sich mit jeder neuen Kern-Release. Der Entwickler eines modifizierten Kerns aber hat nicht die geringste Kontrolle über diese Änderungen. Und nicht selten laufen die Modifikationen der Echtzeitfähigkeit genau zuwider. Der erforderliche Arbeits- und Kostenaufwand für immer wieder neue Analyse und Anpassung wird dann ähnlich groß wie der für selbst entwickelte Kerne, von denen aber immer mehr Entwickler abgehen wollten, um einer Standardlösung wie z.B. Linux den Vorzug zu geben.
Ersatz des Kerns
So bleibt noch eine auf dem Ersatz des Kerns basierende Strategie (Abb. 3). Man entwickelt einen Echtzeit-Kern, der vom Linux-Kern vollkommen unabhängig ist. Nur so ist möglich, alle Echtzeit-Probleme des Linux-Kerns zu lösen und gleichzeitig die Kosten für die Softwarepflege auf einem akzeptablen Niveau zu halten. Das Koexistenz-Konzept bringt strenge Restriktionen für den Entwurf der Echtzeit-Applikationen mit sich, sodass es für viele Embedded-Systems-Anwendungen nicht in Frage kommt. Diese rigide Architektur wird auch langfristig verhindern, dass sich das Koexistenz-Modell analog zum Embedded-Systems-Markt weiterentwickelt. Im Gegensatz dazu ist das auf dem Austausch des vorhandenen Kerns beruhende Konzept auf die Anforderungen des sich rasch weiterentwickelnden Embedded-Systems-Sektors ausgerichtet, auf dem die Software immer ausgefeilter wird und immer mehr Features erhält. Es sind keinerlei Vorgaben nötig, welche Teile einer Applikation Echtzeit-Eigenschaften benötigen und welche nicht. Sämtlichen Applikationen steht unabhängig von ihren Echtzeit-Eigenschaften das volle Spektrum an Dienstfunktionen zur Verfügung. Wenn allerdings der ursprüngliche Linux-Kern entfernt wird, ist zu fragen, inwieweit der Ersatz-Kern eine echtzeitfähige Linux-Lösung darstellt und auf welche Weise er all die Vorzüge bietet, die für Embedded-Systems-Entwickler so attraktiv sind. Zum einen stellt der Ersatz-Kern die gleichen Services und System-Funktionen (z.B. Speicherschutz) bereit wie der ursprüngliche Linux-Kern. Zum anderen erfolgt der Zugang zu diesen Diensten mit den gleichen APIs wie bei Linux. LynxOS ist ein ausgezeichnetes Beispiel für die auf dem Austausch des Kerns beruhende Methodik, obwohl es eigentlich älter ist als Linux. Es wurde von Grund auf als Echtzeit-Kern angelegt, aber mit Industriestandard-APIs (insbesondere Posix) ausgestattet. Als Linus Torvalds Linux entwickelte, hatte er die Vorstellung, die Vorteile der Posix-Kompatibilität zu nutzen. Es sind somit die APIs, die als Bindeglied zwischen dem ursprünglichen Linux-Kern und dem an dessen Stelle tretenden Echtzeit-Kern dienen und dafür sorgen, dass beide Kerne von außen das gleiche Erscheinungsbild bieten. Trotz dieser äußerlichen Ähnlichkeit könnten sie nicht unterschiedlicher sein, wenn man ihre Internas betrachtet. Es gibt auch andere Lösungen, die auf dem Austausch des Kerns basieren, doch kann keine mit diesem hohen Maß an Kompatibilität aufwarten. In einigen Fällen wird nur ein Teil der Linux-APIs geboten, die möglicherweise durch proprietäre APIs ergänzt werden. In anderen Fällen vermisst man einige der systemorientierten Funktionen von Linux wie beispielsweise den Speicherschutz. Der LynxOS-Kern wurde mit all den Funktionen des Linux-Kerns ausgestattet. Wegen der Posix- und Linux-kompatiblen APIs können Applikationen uneingeschränkt zwischen beiden Umgebungen portiert werden. Third-Party-Software lässt sich problemlos in eine Applikation einbinden und profitiert auf transparente Weise von den durch LynxOS gebotenen Echtzeit-Eigenschaften (Abb. 4). Ungeachtet dieser äußerlichen Ähnlichkeiten implementiert LynxOS die Linux-APIs mit völlig anderen Mechanismen, da es eigenständig und mit Blick auf die Echtzeit-Fähigkeit entwickelt wurde. Zum Beispiel entwarf man den LynxOS-Kern voll preemptiv und ohne lange Sperrbereiche. Eine ganze Reihe von Techniken (Semaphoren, Sperrung der Preemption und Unterbindung von Interrupts) dient dem Schutz gemeinsam benutzter Datenstrukturen im Kern. Um sicherzustellen, dass sich die Preemption- und Blockierungsverzögerungen auf ein Minimum beschränken, wurden die LynxOS-Datenstrukturen für schnelle, deterministische Zugriffseigenschaften ausgelegt. Im Interesse einer in hohem Maße zuverlässigen Verarbeitungsumgebung macht LynxOS umfassenden Gebrauch von der Memory-Management-Hardware. Sämtliche Applikations-Tasks sind gegeneinander geschützt und der Kern selbst ist ebenso wie bei Linux als geschützter User-Code geschrieben. Eine fehlerhafte Task hat keine Möglichkeit, das System zu beeinträchtigen oder zum Absturz zu bringen, sondern führt lediglich zur Erzeugung eines Exception-Zustands, der auf geordnete Weise behoben werden kann, während das übrige System seinen Betrieb fortsetzt. LynxOS besitzt eine besondere Interrupt-Handling-Architektur, um das weiter oben angesprochene Problem der Interrupt-Verarbeitung zu lösen. Der Großteil der Interrupt-Behandlung erfolgt mit der Priorität von User-Tasks unter Verwendung eigens reservierter Kern-Threads. Die Priorität eines Kern-Threads wird entsprechend der Priorität der User-Task festgelegt, die die betreffende I/O-Einheit nutzt. Ändert die User-Task ihre Prioritätsstufe, so wird auch die Priorität des Kern-Threads entsprechend angepasst. Diese dynamische Prioritätsanpassung weicht stark von dem statischen Prioritätsmodell der Bottom-Half-Handler von Linux ab und bildet den Schlüssel für den Aufbau eines Systems, das selbst in der Gegenwart der zahlreichen Interrupt-Quellen, die typisch für moderne Embedded-Systems-Architekturen sind, vorhersagbare Eigenschaften bietet. Wichtig für Embedded-Systems-Anwendungen ist außerdem die Skalierbarkeit des LynxOS-Kerns. Viele Embedded-Systeme besitzen nur eingeschränkte Speicher-Ressourcen und sind für das Booten und die Programmverarbeitung auf Flash-Speicher angewiesen. Zwar ist Linux konfigurierbar, doch ist sein Speicherbedarf für viele Anwendungen einfach zu groß. LynxOS dagegen kann aufgrund seines modularen Designs so konfiguriert werden, dass es genau die für eine bestimmte Anwendung benötigten Funktionen enthält, während alles Übrige weggelassen wird. Speichersparend wirken sich auch die gemeinsam genutzten Bibliotheken und Texte von LynxOS sowie die Fähigkeit aus, Kern, Applikationen und ein Dateisystem direkt aus dem Flash-Speicher (d.h. ohne vorheriges Kopieren in RAM) zu verarbeiten. LynxOS ist daher selbst für die Belange sehr kleiner Embedded-Systems-Anwendungen skalierbar, lässt sich aber durch das deterministische Design seiner internen Datenstrukturen und den Speicherschutz ohne Performance-Einbußen zu sehr umfangreichen und komplexen Systemen skalieren. Überdies sind Linux-Applikationen auf all diesen Konfigurationen direkt lauffähig. Ein weiterer wichtiger Vorzug des Ersatz-Kerns ist, dass der Echtzeit-Kern unabhängig von den Linux-Kern-Releases weiterentwickelt und gepflegt werden kann. Obwohl in regelmäßigen Abständen neue Linux-Kerne erscheinen, handelt es sich bei den meisten Änderungen um Implementierungs-Details, die für den Echtzeit-Kern ohne Belang sind. Die APIs ändern sich dagegen kaum von einem Linux-Release zum anderen.
Abb. 1: Unterbrechbarkeit von Task-Operationen unter Linux Abb. 2: Linux zieht Kern-Operationen vor Abb. 3: Ersatz des Linux-Kerns Abb. 4: Plug-&-Play mittels Kern-Ersatz
|
| |
|
 |
|