Mit der Aufteilung einer leistungsfähigen physikalischen Maschine in mehrere virtuelle Maschinen lässt sich die Komplexität der Software reduzieren. Nebenbei wird auch noch die Auslastung der Ressourcen effizienter verteilt. Diese Effekte, die in Rechenzentren schon seit Jahren genutzt werden, sind auch für Embedded-Systeme immer wichtiger.
Heute verfügt jedes durchschnittliche Mobiltelefon über eine Rechenleistung, die noch vor wenigen Jahren einer Workstation Ehre gemacht hätte. Mit dieser generellen Leistungssteigerung eingebetteter Systeme geht ein Anstieg der Software- Komplexität einher: Anwendungen, die das Potential aktueller Plattformen auszunutzen vermögen, sind nicht mehr vergleichbar mit den kompakten, überschaubaren Programmen, für die die klassischen Embedded-Betriebssysteme einst konzipiert wurden. Sie bestehen häufig aus verschiedenen Komponenten, die – mehr oder weniger unabhängig voneinander – verschiedene Teilaspekte der zu erbringenden Gesamtfunktionen realisieren.
Wenn diese Teilsysteme von verschiedenen Anbietern stammen und mit unterschiedlichen Betriebssystemen arbeiten, muss die System-Software für eingebettete Systeme gleichzeitig diese Betriebssysteme anbieten können. Eine effiziente Kommunikation über Teilsystemgrenzen hinweg ist dabei für die Anwendungen ebenso wichtig wie das sichere Unterbinden von unkontrollierten Wechselwirkungen über dieselben Teilsystemgrenzen.
Diese Problemlage ist ähnlich der, die im Bereich der Serverkonsolidierung mit Hilfe der Virtualisierung gelöst werden konnte. Nun steht also eine Konsolidierung eingebetteter Systeme an (Bild 1). Es erscheint logisch, auch hier die Virtualisierung als Lösungsweg ins Auge zu fassen. Zuvor ist jedoch zu prüfen, ob und inwieweit Virtualisierung mit den übrigen Anforderungen eingebetteter Systeme vereinbar ist.
Virtualisierung bezeichnet allgemein die Bereitstellung eines Modells eines Rechners in Form eines Programms. Ein solches Modell zeigt definitionsgemäß das gleiche Verhalten wie der von ihm nachgebildete Rechner. Auf diesem Rechner ausgeführte Programme können – abgesehen vom Zeitverhalten – keinen Unterschied zwischen Modell und realer Maschine feststellen.
Emuliert oder nativ?
Das wahrscheinlich bekannteste Beispiel eines solchen Modells ist eine „Java Virtual Machine“ (JVM), die eine hypothetische Rechnerarchitektur emuliert. Analog dazu lässt sich aber auch eine real existierende Rechnerarchitektur durch Emulation nachbilden.
Ein Beispiel hierfür ist „bochs“, ein Programm, das einen PC so detailgenau nachbildet, dass darin nahezu alle PC-Betriebssysteme und -Anwendungen direkt ausgeführt werden können [1]. Eine solche Emulation (Bild 2) kann auf einem beliebigen Wirtsrechner eine beliebige (andere) Rechnerarchitektur bereitstellen, sie erfordert allerdings das relativ aufwendige Interpretieren einzelner Maschinenbefehle.
Verfügt der nachzubildende Rechner über denselben Befehlssatz wie der Wirtsrechner, so können die meisten Maschinenbefehle ebenso gut auch direkt „in Hardware“ ausgeführt werden. Nur einige wenige, so genannte „sensitive“ Befehle müssen bei dieser nativen Virtualisierung weiterhin emuliert werden. Abfangen und Emulieren dieser Befehle geschehen durch einen Hypervisor: Jeder Versuch eines Programms, einen sensitiven Befehl direkt auszuführen, bewirkt eine Ausnahmebedingung (Trap), die den als Trap-Handler arbeitenden Hypervisor aufruft. Für das ausgeführte Programm ist dieser Vorgang transparent. Unter der Voraussetzung, dass sensitive Befehle hinreichend selten vorkommen, erreicht diese, als „Vollvirtualisierung“ bezeichnete Methode annähernd die gleiche Leistung wie ein realer Rechner.
Für die Vollvirtualisierung muss die zugrunde liegende Rechnerarchitektur gewisse Eigenschaften aufweisen (vgl. [2]). Insbesondere müssen alle sensitiven Befehle zugleich privilegiert sein, damit sie durch Traps abzufangen sind. Da dies jedoch bei einigen Rechnerarchitekturen nicht der Fall ist, wurde die Methode der Paravirtualisierung entwickelt. Hierbei wird in Kauf genommen, dass der im privilegierten Modus auszuführende Programmcode, also in der Regel der Betriebssystemkern, für die Virtualisierung modifiziert werden muss. Anwenderprogramme können hingegen unverändert bleiben. Alle im Betriebssystemkern enthaltenen sensitiven Befehle werden durch explizite Systemaufrufe in den Hypervisor (so genannte Hypercalls) ersetzt. Dies kann erfolgen entweder
In den ersten beiden Fällen muss dazu der Quellcode des Betriebssystems vorliegen, im dritten Fall ist das nicht erforderlich, jedoch bedingt diese „Just-in-time“-Paravirtualisierung einen zusätzlichen Laufzeitaufwand.