Systemausfälle durch Softwarefehler eliminieren Software, heile dich selbst

Systemausfälle durch Softwarefehler eliminieren Software, heile dich selbst

In der Industrieautomatisierung können Software-Ausfälle viel Geld kosten, in der Medizintechnik können sie den Patienten gefährden. In welchem Umfeld sie auch auftreten – ein Ärgernis sind sie immer. Doch mit der richtigen Betriebssystemarchitektur und einem High Availability Framework lässt sich vorsorgen.
Es gibt prinzipiell drei Arten von Betriebssystem-Architekturen: Die klassische „Realtime Executive“ stammt aus der 8- und 16-Bit Micro-Controllerwelt, findet sich aber auch heute noch in vielen 32-Bit Systemen. Hierbei laufen alle Komponenten – Applikationen, Netzwerkstack, Treiber, Dateisysteme – gemeinsam im physikalischen Adressraum der CPU. Die Folge: Ist auch nur in einer Komponente irgendwo ein kleiner Fehler, der dazu führt, dass Code oder Daten einer anderen überschrieben werden, kann es schnell zu Problemen bis hin zum Komplettabsturz des Gesamtsystems kommen. Das führt nicht nur zu nervenaufreibenden Debug-Sitzungen, in der theoretisch jeder am System beteiligte Programmierer „schuld“ am Problem sein kann, sondern auch zu der Notwendigkeit eines kompletten Reboots im Fehlerfall – oft über einen Hardware-Watchdog ausgelöst.

Speicher-Schutz-Verletzung

Etwas Schutz hingegen bietet der konventionelle, monolithische Kernel-Ansatz, der in den 60’er Jahren als Teil von Unix entwickelt wurde und heute in Linux, Windows und auch einigen Echtzeit- und Embedded-Betriebssystemen zum Einsatz kommt. Hier wird die Memory Management Unit (MMU) des Prozessors genutzt, um für jede Applikation einen sogenannten virtuellen Adressraum bereit zu stellen. Im Fehlerfall, wenn z.B. ein Zeiger falsch initialisiert oder zu hoch inkrementiert wurde, führt dies zur heute jedem bekannten „Speicher-Schutz-Verletzung“ – die MMU stellt einen nicht erlaubten Zugriff fest, das Programm kann vom Kernel beendet werden, ohne dass irgendein weiterer Schaden entsteht – das Gesamtsystem läuft in der Regel weiter. Bei der Entwicklung von Embedded Systemen jedoch schreiben Sie nicht nur Applikationen, sie „reden“ natürlich auch mit Ihrer Hardware, schreiben eigene Treiber für selbst entwickelte Komponenten. Diese Software läuft beim Realtime Executive oder beim Monolith im Kernel-Mode der CPU. Dieser Mode erlaubt quasi Zugriff auf „Alles“, weshalb es im Fehlerfall zum Totalabsturz kommen kann: Blue Screen oder Kernel Panic sind die Folge. Solche Szenarien sind nicht nur enorm schwer und zeitintensiv zu debuggen, sondern dürfen in manchen Systemen schlichtweg nie vorkommen – sei es beim Augenlasern, in der Luftfahrt oder bei Geräten, an denen einfach zu viel „dranhängt“ – z.B. bei Terabit-Routern, die ganze Städte mit dem Internet verbinden.

Microkernel-Architektur für erhöhte Zuverlässigkeit

Für erhöhte Zuverlässigkeit hat sich deshalb die Microkernel-Architektur bewährt: Hier läuft nur ein kleiner Basis-Kern im Kernel- oder Supervisor-Mode des Prozessors , sprich der Code, dem vertraut wird, ist überschaubar und wird vom Systementwickler in der Regel auch nicht mehr angefasst. Microkernel wie z.B. QNX Neutrino bestehen nur aus Scheduler, Prozessmanager, Interprozesskommunikationsmechanismen etc., enthalten aber keinerlei hardwarenahen Treiber. Ob Netzwerkstack, CAN-Treiber, Dateisystem, USB- oder Grafiktreiber – alle laufen bei dieser Architektur im User-Mode des Prozessors, bei dem mittels der MMU voller Speicherschutz mit strikt voneinander getrennten virtuellen Adressräumen gewährleistet wird. Und eigene Treiber sind folglich lediglich auch nur „User-Land-Applikation“, die in keiner Weise mit dem Kernel verlinkt werden. Damit entfällt nicht nur das neu Kompilieren (-> keine „Dauerbaustelle Kernel“ mehr) sondern weder in der Entwicklungsphase noch beim Gerät im Feld kann ein Problem im Treiber das Gesamtsystem zum Absturz bringen. Im schlimmsten Fall stellt die MMU einen nicht genehmigten Speicherzugriff des Treibers fest, so dass dieser vom Kernel beendet wird und die zugewiesenen Ressourcen freigegeben werden können. Damit sind die Grundlagen für sich selbst heilende Software-Systeme gelegt.

Der Heilungsprozess beginnt

Jeder Entwickler wird froh sein, während der Programmierung nicht ständig neu booten zu müssen, da er mit einem Microkernel-Betriebssystem auch einen halbfertigen Treiber einfach in einen Fehler hineinlaufen lassen, diesen beenden und durch eine neue Version ersetzen kann. Doch die interessantesten Möglichkeiten liegen im Bereich der Sicherstellung von Hochverfügbarkeit bei Systemen im Feld: Aufsetzend auf dem Microkernel-Konzept liefert z.B. QNX noch ein ausgeklügeltes High Availability Framework mit. Damit lassen sich diverse Reaktionen auf verschiedene Problemfälle implementieren. Dabei wird der High Availability Manager (HAM) als ein eigener Prozess gestartet. Systemkomponenten – Treiber ebenso wie Applikationen – können von diesem überwacht werden. Dabei gibt es prinzipiell zwei Fehlerszenarios:
1. Ein Programm versucht, die Grenzen seines virtuellen Adressraums zu überschreiten, z.B. durch einen falsch gesetzten Pointer. Dies löst eine MMU-Exception aus, wodurch der Microkernel sofort in Aktion tritt und den havarierten Prozess beendet und alle zugeordneten Ressourcen wieder freigibt.
2. Ein Programm kann sich im Fehlerfall in einer Schleife „verfangen“ und somit scheinbar einfrieren oder nicht mehr reagieren. Ein Treiber z.B. könnte auf eine Anfrage einer Applikation nicht mehr antworten.
Der Systemdesigner meldet die zu überwachenden Komponenten beim High Availability Manager an. Im Szenario 1 wird der HAM im Falle einer MMU-Exception durch den Kernel informiert, dass ein überwachter Prozess gerade abgestürzt ist. Im Szenario 2 programmiert man für die High Availability sogenannte Heartbeats, also Trigger, die von der betreffenden Applikation (oder dem Treiber) regelmäßig an den HAM gesandt werden müssen. Bleibt der Heartbeat aus, tritt der HAM in Aktion.

Der High Availability Manager

Stellt der High Availability Manager also ein Problem fest, wird eine vorher definierte Aktion ausgeführt: Im einfachsten Fall ist dies ein Neustart der entsprechenden Softwarekomponente. Statt eines kompletten System-Reboots, der viele schmerzhafte Sekunden – oder gar Minuten – dauern kann, ist eine einzelne Softwarekomponente in der Regel innerhalb einiger Millisekunden wieder verfügbar. Wenn es sich dabei um einen Treiber handelt, gibt es in der Regel aber auch Applikationen, die gerade mit diesem in Verbindung gestanden und Daten ausgetauscht haben. Auch Applikationen untereinander kommunizieren in der Regel über verschiedenste Interprozess-Kommunikationsmechanismen. Das High Availability Framework von QNX bietet für solche Szenarien ebenfalls Lösungen: Der High Availability Manager kann beim Aufbau einer Verbindung von Applikation zu einem Treiber (oder einer anderen Applikation) instruiert werden, diese ebenfalls zu überwachen. Tritt ein Problem auf und ein Treiber muss neu gestartet werden, werden auch Applikationen mit aktiven Interprozess-Kommunikationsverbindungen automatisch informiert, welche dann eine vorher definierte Recovery-Funktion anspringen können. Diese stellt dann beispielsweise die Verbindung zum neu gestarteten Treiber wieder her. Da Treiber in der Regel Daten an Applikationen liefern, möchte man im Idealfall, dass die Applikation auch nach einem Neustart des Treibers nicht wieder sämtliche Daten neu einlesen muss. Umgekehrt senden Applikationen oft Daten an Treiber. Um hier nach einem Recovery-Fall den „Faden“ wiederzufinden, bietet sich ein Checkpointing-Verfahren an. Hierbei wird ein weiteres Feature des High Availability Managers genutzt: Diesem kann ein Programm nämlich regelmäßig mitteilen, in welchem Zustand es sich gerade befindet – beispielsweise welches Datenpaket gerade verarbeitet wird, welches Menü (im Falle einer grafischen Applikation) gerade angezeigt wird oder welche Relais gerade wie geschaltet sind. Der High Availability Manager hält diese Daten in einem Shared Memory Objekt vor, wodurch das Abspeichern solcher Statusinformationen seitens der überwachten Komponenten schnell, einfach und ohne irgendwelche Restriktionen bezüglich der Struktur vor sich gehen kann. Ein frisch neu gestarteter Treiber – oder eine Anwendung – kann so ohne großen Zeitaufwand den letzten bekannten Zustand wiederherstellen: Das zuletzt bearbeitete Paket kann wieder in Angriff genommen, die zuletzt geöffnete Datei weiter bearbeitet, der zuletzt abgefragte Wert übermittelt werden. Je nach Komplexität der ausgelösten Aktionen ist das System somit innerhalb von Sekundenbruchteilen bis zu wenigen Sekunden wieder voll einsatzbereit.

Einfach weiterarbeiten

Je nach Bedarf kann ein Eintrag ins System Log vorgenommen, ein Abbild des virtuellen Adressraums gespeichert oder ein übergreifender Tracing-Prozess angestoßen werden, um dem Entwickler später die Analyse solcher Problemfälle zu erleichtern – während der Systemanwender bzw. die Maschine etc. in der Regel einfach weiter arbeitet. Egal ob Treiber oder Applikationen – mit einer Microkernel-Architektur und dem passenden High Availability Framework kann sich Software im Fehlerfall tatsächlich selbst „heilen“. Systemausfälle durch Softwarefehler können somit stark reduziert, oft sogar eliminiert werden.

Autor: Malte Mundt, Field Application Engineer, QNX Deutschland

QNX Software Systems Limited
www.qnx.de

Das könnte Sie auch Interessieren