Äh, Stefan, was machst du denn da?
Ja, wir haben von ein paar Hörern ein Audiofeedback bekommen und ich verarbeite das eben fürs nächste Intro.
Oh, cool. Und was sagen die so?
Äh, warte, ich zeige dir das eben.
Systemcall.
Ja, höre ich die jede Folge immer und die neue hier.
Echt faszinierend.
Das klingt ja total gut, aber...
Ja, die sind alle so.
Ich hab keinen Grund zu fragen, warum fragst du?
Nee, sag mal, die Striche da. Was sind das?
Ach so, ja, also das sind Schnitte.
Du schneidest das?
Ja, ja, also die Kommentare, die sind eigentlich...
Eigentlich sind die nicht ganz so positiv.
Also nein, das kann man nicht so machen. Zeig mal, wo ist denn das Original?
So, was ist mit dem Podcast? Systemcall. Ja, höre ich nie. Schon mal erzählt bekommen, aber für die Schlimmste sind die ganzen dummen Intros.
Jede Folge immer und die neue hier.
Und kaum die echt zu sehen. Das wird sich wehren.
Echt faszinierend.
Na, also wirklich, das kann man nicht machen. Wir müssen ja schon ehrlich sein.
Aber sag mal, ist das überhaupt echtes Feedback?
Nee, ich hab ausgereicht gefragt, ob er mir das fürs Intro einsprechen kann.
So, so.
Der Systemcall heute mit folgenden Themen.
Startische Systemkonditionierung und Virtualisierung für eingebettete Echtzeitsysteme.
Und die Kunst der Para-Virtualisierung.
Herzlich willkommen zu dieser siebten Ausgabe des Systemcall Podcasts.
Mein Name ist Stefan Naumann und bei mir ist wie immer der faszinierende Florian Rommel. Moin, Flo.
Hallo Stefan. Im Systemcall suchen wir uns immer mehr oder weniger aktuelle Systemsforschung und bereiten die verständlich auf.
Und diesmal haben wir uns wieder einen Gast eingeladen.
Genau, denn heute aus Regensburg zugeschaltet ist Ralf Ramsauer. Moin, Ralf.
Grünen Sie, Stefan. Grünen Sie, Flo.
Danke.
Äh, endlich hat das mal einer gesagt. Ja, dann würde ich mich vielleicht kurz vorstellen.
Genau, mein Name ist Ralf Ramsauer. Ich hab programmiert bei Dani Lohmann in Hannover und bei Wolfgang Maurer in Regensburg.
Und ich interessiere mich für Systemarchitekturen, für eingebettete Systeme, für einbettete Virtualisierung und das Ganze im Kontext Sicherheitsmitgliedersysteme, Echtzeitsysteme und Systeme gemischter Kritikalität.
Was muss man sich mit einem System gemischter Kritikalität vorstellen?
Stattdessen vorher hast du ein großes System, beispielsweise ein Kfz oder ein Flugzeug.
Da finden sich sehr, sehr viele Steuergeräte drin.
Jedes Steuergerät hat seine eigenen Professoren und hat seine eigene Steuerlogik und die übernehmen unterschiedliche Aufgaben.
Und je nach Aufgabe von dem System wird diesem System eine Kritikalität zugewiesen.
Wir haben unterschiedliche Kritikalitätslevels, je nachdem, welches Domainesystem zum Einsatz kommt.
Beispielsweise ist es offensichtlich, dass ein Infotainmentsystem in einem Automobil eine deutlich niedrigere Kritikalität hat, als beispielsweise ein Ereckssteuersystem, das eine sehr hohe Kritikalität hat.
Und gesamtbedacht hat man also ein großes System mit vielen Systemen unterschiedlicher Kritikalität und da spricht man von einem System gemischter Kritikalität oder auf englischem Mixed-Kritikalitiesystem.
Also ist mein Auto im Grunde ein Mixed-Kritikalitiesystem?
Genau.
Okay, aber was ist denn das Problem?
Ich kann ja im Grunde alle meine Software, diese verschiedenen Kritikalitäten wunderbar auf eigene Steuergeräte aufteilen, die dann unabhängig von einem anderen sind.
Richtig, genau so wird es im Prinzip auch gemacht.
Das ist der traditionelle Ansatz, eine "separation of concerns", man versucht unterschiedliche Aufgaben in eigene abgekoppelte Komponenten zu isolieren. Jetzt ist es aber so, dass wir auch gerade in den Embedded Systems immer mehr SMP-Systeme haben, also Systeme mit mehreren Kernern, mit vielen Kernern, extrem stark eingebettete Systeme.
Und es ist inzwischen schon schwierig, dass man überhaupt noch so ein Ein-Kern-System bekommt mit ordentlicher Leistung, es sind meistens schon mehr Kernsysteme.
Und irgendwie ist es schade, wenn die meisten Kerne in so einem System die größte Zeit oder die meiste Zeit nur mit Eideln beschäftigt sind oder nur mit Heizen beschäftigt sind.
Deswegen gibt es da auch wirtschaftlichen und ökonomischen Interesse bestreben, solche Systeme zusammenzuführen.
Also ich will nicht mehr 90 einzelne Steuergeräte im Automobil haben, sondern ich will nur noch ein paar wenige haben. Und das ermöglichen mir jetzt die SMP-Systeme, weil die eben genügend Rechenleistung zur Verfügung stellen, diese vielen Einzelsysteme zu einem einzigen System oder zu wenigen Systemen zusammenzuführen, man spricht davon Systemkonsolidierung.
Okay, also das heißt, ich pack jetzt meine Bremsleitung, meinen Airbag, die Steueraufwärm auf ein Rechner oder ein Steuergerät drauf.
Genau, das könnte man versuchen. Das will man auch so mehr oder weniger umsetzen.
Und da klingt der Unterton bei dir so ein bisschen mit, na eigentlich will ich das vielleicht nicht haben, dass jetzt da so ein Infotainment-System, wie jeder irgendwie sein Handy per Bluetooth-Bär haben, auf dem selben Systembus hängt wie so ein Airbag-Steuergerät oder wie so ein Brems-Steuergerät. Eigentlich will man das ja nicht haben.
Was meinst du an dem Systembus? Also in dem Steuergerät der Bus?
Auf dem Prozess war der Bus, also nicht irgendwie über Korn verbunden, über eine Kommunikationsbus, sondern wirklich der selbe Speicherbus innerhalb des Systems. Den teilt sich das Gesamtsystem.
Also wenn die auf dem gleichen Chip laufen?
Genau.
Also ich stelle mir jetzt zum Beispiel vor, wenn jetzt mein Infotainment-System irgendwie crasht, ja, was kackt ab und das dann mit CPU last, wenn dann meine Brems-Unterfassung ist eher schlecht.
Genau, das ist eher schlecht. Und eben weil das Voraussetzungen auch noch eine Zuverlässigkeit des Systemes gibt, muss sichergestellt werden, dass genau so etwas unter keinen Umständen vorkommen kann.
Das bedeutet auch, dass so ein Ausfall in ein Infotainment-System, und das kann durchaus passieren, weil der Softwareumfang in so einem Infotainment-System hier ist substanzweise größer als der Softwareumfang von so einem Airbag-Steuergerät oder so einem Fensterheber-Steuergerät, da ist es dann auch wahrscheinlich, dass da zu viele ankommen.
Das muss irgendwie sichergestellt werden, für diejenigen, die sich auf Interführen interessieren, also die Systeme dürfen sich nicht ungewollt in die Fähre kommen. Und das muss auf elektronischer Ebene, auf systemischer Ebene sichergestellt werden.
Ja gut, also pack ich einfach eine Virtualbox um mein Steuergerät, also meine Bremse und mein Infotainment-System um.
Das wäre jetzt ein Gedanke, den wir einander bekommen können. Also ich pack da so eine Virtualbox, wie du das nennst, um eine virtuelle Maschine. Was gibt mir die virtuelle Maschine? Die gibt mir Isolationskriterien.
Ich schaffe es damit, dass ich die Systeme voneinander isoliere, dass ich die voneinander trenne. Ich kann die räumlich und zeitlich voneinander trennen, ich kann den Sparcher trennen und die Ausführungszeit der Maschinen dementsprechend trennen.
Aber da kommen wir doch ziemlich schnell zu einem Problem. Man wird feststellen, dass solche sicherheitskritischen Systeme, wie es eben das Airbag-Steuergerät oder das Bremssteuergerät ESP, wie es bei den anderen Fall ist, die alle eine Gemeinsamkeit und zwar sind echt zeitkritische Systeme.
Das heißt neben der Anforderung, dass ich die Systeme voneinander stecke/isoliere, kommt auch noch die Anforderung hinzu, dass ich die technische Rechenzugrenze einhalten muss, ein echt zeitkritisches System.
Es darf also nicht passieren, dass die Ausführung von einem Infotainment-System die Ausführung von einem Airbag-System verhindern würde. Also anders ausgedrückt, ich muss garantieren können, dass die Software meiner Bremse oder meinem Airbag innerhalb einer bestimmten Zeitgrenze auch wirklich ausgeführt hat und der Airbag auch wirklich auslöst.
Genau, nicht nur innerhalb einer gewissen Zeit, sondern von der Durchausstimme auch ab und aus, und ein Lowerbound-Airbag-System muss ja im Zeitfenster die Entscheidung stehen, darf nicht zu früh dran sein, darf nicht zu schlecht dran sein, weil das Verpassen von so einer Deadline, die man reagieren muss, zu schwerwiegenden Konsequenzen führt.
Also wenn mein Gesicht schon auf dem Lenkrad eingeschlagen ist, dann ist der Airbag eher kontraproduktiv und helfen tut es an der Stelle auch nichts mehr. Genau, der Airbag ist das klassische Beispiel für echt zeitkritische Systeme. Okay, also jetzt haben wir hier eine Echtzeit-Anforderung und wir haben irgendwie den Mixed-Kritik-Halt, also wollen gerne das Infotainment-System auf dem gleichen Prozess laufen lassen, wie irgendwie unsere Bremse oder unser Airbag. Genau. Okay, wie machen wir das?
Ja, also da gibt es unterschiedliche Ansätze, wenn man das Ganze umsetzen kann. Einen Ansatz, den wir verfolgen, der ist dennoch die Virtualisierung. Also ich meinte, ja, die Virtualisierung kann man machen, gibt uns stark die Isolationskriterien, kann aber dann Probleme mit Echtzeitsystemen geben. Weil die klassischen Virtualisierungssysteme, wie wir sie kennen, die kommen ja aus dem ganzen Cloud-Unfeld, aus dem Datacenter-Unfeld, die haben ja die gleichen Probleme. Da will man auch mehrere Systeme zu einem System zusammenführen, das ist im Grunde genommen das selbe Problem, wie wir bei den Bärersystems auch haben.
Aber die haben diese Echtzeit-Anforderungen nicht?
Genau, richtig. Die haben zwei so konkurriente philosophische Prinzipien. Das eine ist, ich hab ein durchsatzorientierter System, und ich will auf Throughput, die Performance optimieren. Das ist das Optimierungsziel von klassischer Datacenter oder Cloud-Vitalisierung.
Und dann haben wir die Echtzeitsysteme, da will ich niederlegale Tänzen haben. Die beiden stehen sich da, die stehen sich immer so ein bisschen gegenüber.
Also mich interessiert es nicht, wenn ich da Gigabyte- oder Terabyte-weise Daten durch mein System durchschöpfen kann, das Virtualisiertes. Ich will niederlegale Tänzen einhalten können.
Da kann man sich dann überlegen, dass solche Virtualisierungs- Softwaresysteme grundlegend andere Designprinzipien haben, die so erstmal für die verschiedenen Domänen nicht matchen, auch wenn sie versuchen am Ende des Tages um das selbe Ziel, starke Isolierung zwischen einzelnen Auslösungen und Domänen umzusetzen.
Also wir haben einfach andere Anforderungen. Also teils sind die gleichen, aber wir haben...
Teils sind die gleichen, genau, Isolierung. Die Detailausgestaltung, die ist dann doch ein bisschen anders.
Wobei ja die Isolierung bei dir schon ein bisschen weitergeht. Also da kommen ja noch so mikroarchitekturelle Geschichten dazu, so Caches.
Also da, was wir ja nicht erreichen wollen, müssen wir eigentlich dafür sorgen, dass die Bremssoftware nicht vom Infotainment im Cache gestört wird, dass dann zusätzliche Cache-Messes dazukommen, die vielleicht unvorher- sagbar sind.
Genau, da kommen wir jetzt schon ziemlich in die einzelnen Details runter. Ja, also bei vielen Architekturen sind bestimmte Levels des Caches geteilt über mehrere Kerne.
Und sobald verschiedene Kerne verschiedene Kritikalitäten oder Aufgaben verschiedener Kritikalitäten ausführen, dann kann es natürlich dazu kommen, dass die um den selben Speicher im Cache kämpfen.
Und da muss man dann irgendwie sicherstellen, dass genau solche Situationen nicht passieren können, die eine Domäne hohe Kritikalität irgendwie beeinflussen können.
Das geht ja sogar bis dahin, wenn man an Mikroarchitektur-Attacken denkt oder so, wenn man im Infotainment-System durchaus ja vielleicht auch Code ausführen kann, der, was sich nicht unbedingt sicher wäre, dann wäre ja sogar so was möglich.
Genau, also gerade mit Spectrum and Meltdown, die haben ja Möglichkeiten aufgewiesen, wo ich auch über Prozesse hinweg auf Speicher möglicherweise lesen kann, die ich gar nicht lesen darf.
Das wird dann natürlich in welche Geheimnisse drin stehen, das gehen wir aber ein bisschen weg von Safety, wir gestalten jetzt eher so ein bisschen in die Security-Richtung.
Dann möchte ich das auch möglicherweise verhindern. Gerade solche Cache-Attacken, die haben ja in den letzten Jahren wieder an Popularität, so jetzt gehen sie langsam ein bisschen zurück, aber jetzt kommt mal die Zeit, wo die eigentlich an Popularität gewonnen haben.
Genau, also auch die feinen architektonischen Details von so einer Architektur, die muss man beim Design von so einem System, das das Ganze lösen soll, beachten.
Und um jetzt beispielsweise zu verhindern, um das Beispiel mit den Caches abzuschließen, um zu verhindern, dass sich verschiedene Ausführungseinheiten aus so einer CPU bei den Caches entgegenkommen, gibt es verschiedene Strategien und Ansätze, wie man das verhindern kann.
Auf x86 Prozesse von Intel gibt es beispielsweise eine CPU-Erweiterung, die heißt CAD.
Cache-Elocation-Technologie, da kann ich eben eins an CPUs, die dezierte Portionen aus dem Level 3-Cache und dann auch nur sie zu Verfügung dürfen zur Verfügung stellen und kann so sicherstellen, dass dieser Kern immer die nötige Menge an Cache, die er zur Ausführung braucht, auch definitiv erhöht.
Wenn die Architektur das nicht zur Verfügung stellt, gibt es andere Möglichkeiten, nur mal um Stichwort zu nennen, Cache-Coloring. Also man ordnet Speicherseiten im Virtual Speichermanagement so an, dass sie genau für das Design des Caches optimal liegen.
Also gibt es auch verschiedene Strategien, wie man solche architektonischen Detailprobleme mit an den Griff bekommt.
Okay, aber was ist denn nun eigentlich Virtualisierung und wie können wir sie in diesem Kontext benutzen?
Also Virtualisierung bedeutet eigentlich immer, dass ich ein Programm in einer Ausführungsumgebung ausführen möchte, die nicht die echte Hardware ist.
Das kann zum Beispiel bedeutend nicht für eine Interpretersprache in einer Interpreter aus, es kann aber auch bedeutend nicht für einen Prozess auf meinem Betriebssystem aus.
Ein Betriebssystem abstrahiert mir die echte Hardware von dem System weg.
Zumindest teilweise? - Teilweise, genau. Allein, dass ich mit mehreren Prozessen auf dieselbe Hardware-Komponente zugreifen kann.
Das kann im einfachen Fall eine Netzwerkkarte sein, ich habe zwei Prozesse, die mit der Netzwerkkarte kommunizieren können.
Diese Hardware-Dissource wird virtualisiert, damit mehrere Programme gleichzeitig auf diese Hardware-Dissource zugreifen können.
Das hängt also auch sehr viel mit Ressourcen-Control und Ressourcen-Sharing zusammen, Virtualisierung.
Virtualisierung kann aber auch bedeuten, dass ich die komplette Ausführungsumgebung einer Maschinen-Diffinition in eine virtuelle Umgebung überführen möchte.
Also ich will das komplette System mit kompletter Hardware in einer virtuellen Umgebung verfügen. Das kann auch Virtualisierung bedeuten.
Und das ist Virtualisierung, wie wir das in unserem Kontext auch nicht verstehen. Also ich will nicht in den Prozess auf einem Systems ausführen, sondern ich will tatsächlich die komplette Maschine in einer virtuellen Umgebung zur Verfügung stellen.
Also das quasi für das darin laufende Betriebssystem so aussieht, als wäre es auf der echten Maschine?
Genau, das ist ein Kriterium an solchen virtuellen Maschinen, das muss sich so verhalten, ich brauche identisches Verhalten, wie wenn ich auf echter Hardware ausführen.
Ich muss voll auf die Ressourcen zugreifen können, wie wenn ich auf echter Hardware sitze.
Und auch eine Anforderung, die dritte Anforderung nach Papier und Goldberg an virtualisierbare Systeme.
Ein sehr großer Anteil der Instruktionen, die in so einer virtuellen Maschine ausgeführt werden, die soll ohne jegliche Intervention durchgeführt werden können. Da komme ich schon zu so einem wichtigen Punkt. Es ist nicht immer möglich, dass so eine virtuelle Maschine ohne Eingreifen das nächste Begriff des Virtual-Maschinen-Monitors oder des Hypervisors ausführen kann.
Es gibt Szenarien, wo es eben nicht davon ist. Da muss man an der Virtual-Maschinen-Monitor eingreifen und die Ausführung von einer bestimmten Instruktion übernehmen, man spricht von moderieren von einer Instruktion.
Es kann durchaus vorkommen, was hier bei der Cloud-Virtualisierung genauso wie bei der e-Virtualisation aufgrund von archytonischen Defiziten beispielsweise.
Ok, das heißt dann zum Beispiel, dass man den Virtual-Maschinen jetzt ein Stück Speicher jeweils zubeißt oder einen Prozessor-Kern oder auch einen Teil eines Prozessors kennt, in dem man den Notierwechsel zwischendien. Aber das löst ja jetzt noch nicht unsere Anforderungen bezüglich Mixed-Quantity, die wir vorher formuliert haben. Also nur weil die jetzt auf Virtual-Maschinen laufen, heißt das nicht, dass sie sich nicht gegenseitig beeinflussen.
Genau. Das Problem dabei ist, dass eben gerade in der Cloud-Virtualisierung oder der E-Virtualisierung, die auf Performance-Fruitput optimiert ist, hardware massiv overcommitten.
Das bedeutet, ich habe üblicherweise auf so einem System deutlich mehr virtuelle Maschinen am Laufen, als ich beispielsweise CPU-Kernen zur Verfügung habe.
Das ist dann, was ich da teils leistet, mehrere VMs teilen sich in den selben CPU-Kern. Und jede bekommt dann einen Quot an Rechenzeit, in der die virtuale Maschine ausführen darf.
Wenn eine andere Virtual-Maschine zum Zug kommt, wird die verdrängt vom Hypervisor, die da eben als Stück Software unterhalb des Systemes läuft und die Maschine verdrängen kann und dann eine andere Virtual-Maschine aktiviert diese, eine andere Virtual-Maschine auf der CPU und ist dann wieder zum Zug kommen.
Die Idee ist ja auch, dass nicht immer alle Virtual-Maschinen, die darauf laufen, jetzt gleichzeitig die volle Rechenleistung brauchen und man so eben mehr Software laufen lassen kann auf einem Stück Hardware, wie man ohne Virtualisierung könnte.
Also die Idee bei Cloud-Virtualisierung ist, dass die meisten Virtual-Maschinen die meiste Zeit sowieso nur mit Nichtstun, also mit Item beschäftigt sind.
Und wenn dann mal so eine Anfrage auf so einer Webserver, die innerhalb von einer Virtual-Maschine laufen kann, reinkommt, dann kann die auch dynamisch auf der CPU aktiviert werden, die Abfrage bei der Webserver, die wird ausgeführt, die wird beantwortet und die Maschine rückt durch den Hintergrund und gibt die Rechenzeit frei für andere Maschinen, die auf der CPU laufen können.
Und je nachdem, welche Lastprophile dann auf den Virtual-Maschinen bestehen, kann ich abschätzen, wie stark ich oder bis zu welchem Grad ich das Overcommit-Ding betreiben kann.
Also deutlich mehr Virtual-Maschinen versuchen zu betreiben auf einem System, als es die Hardware physikalisch eigentlich hergeben würde.
Damit wollen wir eigentlich in Richtung von einem Mixed-Criticality-System, also irgendein System, wo das Infotainment-System ein bisschen lagged ist, nicht schön, aber wenn die Bremse gerade nicht geht, weil das Infotainment-System rechnet, das ist vielleicht schlecht.
Da haben wir ein Problem, da haben wir diesen Problem.
Wie lösen wir das jetzt?
Also allein dadurch, dass bei so Cloud-Virtualisierung die Partitionen, die Ausführungsdomänen, die Virtual-Maschinen gescheduled werden müssen, also wenn da verschiedene Virtual-Maschinen auf einem CPU-Kern hin und her schieben, dann ist das nichts anderes als ein Scheduling von Virtual-Maschinen, das kostet Overhead.
Das kostet Overhead, den ich gerade bei echtzeitkritischen Systemen möglichst vermeiden möchte, weil der immer negativ auf die Echtzeit-Eigenschaften von so einem System einschlägt. Das bedeutet, wenn ich das beispielsweise schaffen könnte, dass ich gar keine Schedule mehr habe, dann müsste ich mit dem Overhead vom Scheduling überhaupt keine Gedanken mehr machen.
Aber was würde das jetzt beispielsweise bedeuten, wenn ich auf einem System keine Schedule mehr habe?
Dann bedeutet das, dass ich auf einem CPU-Kern immer die gleichen Domäne laufen lassen muss, dafür aber eben keine Schedule mehr habe.
Und wenn man sich mal so ein klassisches eingebettetes System überlegt, dass ich beispielsweise ein 8-Kern-System, dann aus einem typischen 8-Kern-System werde ich höchstwahrscheinlich niemals mehr als 8 verschiedene Virtual-Maschinen laufen lassen oder 8 verschiedene Aufgaben laufen lassen.
Ich kann es also schaffen, bei eingebetteter Virtualisierung auf einem Scheduler komplett zu verzichten, wenn ich sage, dass eine Domäne oder eine Virtual-Maschine exakt eine oder mehrere CPUs dediziert zur Verfügung gestellt bekommt.
Also ich höre auf, die Partitionen zwischen den CPUs hin und her zu schedule, sondern ich weise einer Ausführungsdomäne aus einer Virtual-Maschine dediziert die Hardware-CPUs zu. So kann ich mit Overhead sprechen. Scheduling wird überfüßig.
Und so habe ich auch die Zusammenführung von einzelnen Systemen sehr einfach gemacht. Ich habe 4 1-Kern-Systeme vorher gehabt, die werfe ich jetzt zusammen auf einen Haufen und kommt mit einem 4-Kern-System raus.
Genau richtig. Und idealerweise kann ich die Software, die vorher auf den Systemen lief, auf der Virtual-Maschine nach wie vor ohne große Anpassungen der Payload wieder laufen lassen.
Die Aufgabe der Virtual-Maschine ist es ja, eine Hardware-Umgebung zur Verfügung zu stellen, die sich von der echten Hardware nicht unterscheidet.
Also idealerweise wird auch die Portierung von den einzelnen Systemen hin zu der Virtual-System-Umgebung dadurch deutlich vereinfacht.
Aber jetzt haben wir auch viel in letzter Zeit über Side-Channels und Caches und sowas gehört. Gibt es da irgendwie ein Einfallstor für sich gegenseitig in die Quere kommen?
Das muss man sich in Detail anschauen, wie die Angriffe funktioniert haben. Es gibt zwei verschiedene Varianten von den Angriffen. Der eine Angriff findet quasi auf der eigenen CPU statt.
Das heißt, mein Angriffsszenario wäre, dass mein Gast sich selbst angreift. Wenn der Gast meint, er soll das machen, dann kann ich ihn das nicht verhindern.
Mein Gast ist quasi völlig attraktiv vom Rest. Wenn der Meinte, er muss an sich selbst angreifen, dann kann er das natürlich machen.
Kritischer wird es, wenn wir einen Angriff haben, der auf eine benachbarte CPU losgeht.
Nachdem wir aber die Portitionen untereinander niemals schedulen und die Virtual-Maschine niemals in eigenen Sichtbarkeitsbereich verlassen, ist es nicht möglich, dass ich irgendwie auf eine benachbarte CPU wo eine fremde Domäne drauf läuft zugreifen kann.
Es gibt eine Möglichkeit, wie das passieren kann. Beispielsweise bei X86-Systemen mit Hyper-Threading-Support, wo ich eine physikalische CPU auf zwei virtuellen Threads, die den vorigen Register-Satz der CPU zur Verfügung stellen, aufdoppeln kann.
Da könnte es passieren, weil die Hardware selbst entscheidet, welchen dieser zwei virtuellen Threads gerade zum Zuge kommt.
Da kann es sein, dass Informationen an Zwischendomänen liegen können, wenn auf zwei Hardware-Threads unterschiedliche Domänen laufen.
Generell ist es aber so, dass für exakutische Systeme Hyper-Threading sowieso eine schlechte Idee ist, weil ich ihm keine Steuerung mehr darüber, welcher Thread 1 genau zum Zuge kommt.
Also das verletzt wieder die Echtzeit-Sachen?
Genau, da kommt wieder die Echtzeit-Komponenten rein.
Üblicherweise habe ich auf solche Systeme ein Mechanismen wie Hyper-Threading sowieso deaktiviert und bin auch dafür dann kein Angriffstier mehr.
Also es hat sehr viele Vorteile, wenn ich auf so Sachen wie Scaduling beispielsweise verzichte.
Wie ist es bezüglich Caches? Also es gibt ja wahrscheinlich, je nach System noch, einen gemeinsamen Cache bei alle CPU-Kerne. Der könnte ja auch noch ein mögliches Problem werden.
Ja, also müssen wir vorher einen Blick über verschaffen, und zwar wie ist denn die Architektur, die Produktivität von dem Cache, wie ist denn der aufgebaut.
Üblicherweise haben wir bei genannten Architekturen meistens jeder Kern an einem gewisserten Level 1, Level 2 Cache und Level 3 Cache. Der ist dann auf mehrere CPUs hinweg geteilt.
Das ist dann die Frage von dem Systemdesigner, wie man quasi da mit so einem System aufzieht. Also ich kann sagen, okay, ich will jetzt mein System so traditionieren, dass die Domäne genauso groß ist wie die Cache-Domäne.
Damit habe ich das Problem, dass ich das wieder zu meinem Gast verlagert, muss ich der Gast selbst umkümmern.
Müsste ich aber auch bei einer anderen echten Hardware-Architektur genauso machen, also das Problem, das es sowieso schon hat, verschiebt das Problem also zu meiner Gastin.
Der muss sich darum kümmern, aus Hyper-Weiser-Sicht, dass das für mich alles erliegt.
Oder ich habe auf einer Level-3-Cache-Domäne verschiedene Partitionen am Laufen, das habe ich am Eingang schon mal erwähnt.
Da brauche ich solche Mechanismen wie Cache-Elocation-Technologie, Resource-Director-Technologie oder Cache-Coloring, um eben zu verhindern, dass die einzelnen Domänen sich um den Level-3-Cache reißen. Okay, dann hast du dir den Jailhouse-Hyper-Weiser mal angeschaut. Was seint ihr denn eigentlich aus?
Genau, ich versuche genau dieses Konzept durchzuziehen. Ich bin 2015 bei der Entwicklung des Jailhouse-Hyper-Weisers zugestoßen und der hat einige philosophische Ansätze, die es so bislang noch nicht gab.
Also der sieht genau das nur, was ich gerade erwähnt habe. Wir wollen kein Scheduling von virtualen Maschinen auf unserem System. Wir wollen keine Virtualisierung von Hardware-Komponenten. Wir wollen keine Para-Virtualisierung von Hardware-Komponenten. Was Para-Virtualisierung ist, darauf werden wir uns später noch ein bisschen darauf eingehen.
Wir wollen kein Overcommuting von Hardware. Wir wollen nur eine 1 zu 1 Zuweisung von echten Hardware-Komponenten zu unseren Gästen.
Darüber kann man sich bei der Konfiguration des Systems entscheiden, welche Partitionen oder Domäne. Wir nennen das im Jailhouse-Chargon, also im Gefängnis-Chargon, z.B. das Zellen.
Zellen ist ein isolierter Hardware-Gereich. In den Zellen sitzt dann der Insa-Sel, der Inmate, das ist die Software, die in unserer Virtual-Maschine ausführt. Da kann man frei entscheiden, welche Hardware-Komponenten in so eine Zelle zugewiesen werden. Und auf die Nachbarte Zellen, die haben wir keine Anzugriffen mehr auf die Hardware.
Ein weiterer Aspekt, der der Jailhouse von anderen Eingebetteten hyper-weisen unterscheidet, ist, dass wir, bevor wir Jailhouse starten, also bevor wir unseren Virtual-Maschinen-Monitor aufsetzen, bevor wir das System in einen virtualisierten Zustand überführen, buchen wir erst einmal auf allen Kernen Linux an.
Der Grundgedanke ist, wenn ich so ein industriell eingebettetes System habe, dann komme ich meistens nicht an Linux vorbei. Aufgrund des Ökosystems, das uns Linux zur Verfügung stellt und der gewaltigen Software-Mäne, die es da gibt, läuft auf jedem industriellen System irgendwo ein Linux drauf. Ich werde Linux in einer Domäne verwenden wollen. Meistens in unkritischen Domänen, aber ich will auf Linux und dem Software-Ökosystem zurückgreifen.
Ein Riesenvorteil von Linux ist, dass die massiven Hardware-Supporten mitbringen. Wenn ich also erst Linux auf allen CPUs starte und danach die Virtual-Maschine starte, dann habe ich die komplette Initialisierung der Hardware, die ja trotzdem durchgeführt werden muss, komplett auf Linux geoffloadet.
Also Linux übernimmt für mich die Aufgabe, die komplette Hardware einmal hochzubuten. Und erst wenn ich das Linux ganz normal auf allen CPU-Kernen gestartet habe, komme ich mit Jell-O's und sage, okay, Jell-O's, du schiebst dich jetzt mal unter das Linux drunter und hebst die Linux zu einer Virtual-Maschine an.
Also erst nachdem Linux gebootert hat, fahre ich mit unter das System drunter und lüfte die Linux quasi in den Zustand von einer Virtual-Maschine hoch. Dabei übernimmt sich der Hypervisor Jell-O's genau eine Aufgabe und zwar, die Hardware, wie diese Vor-Virtualisierung und Nach-Virtualisierung existiert, eins zu eins zu dem Gast durchzureichen.
Also ich reiche den Zugriff auf den physikalischen Speicherbereich, so wie es vorher war und so wie es nachher war, oder so wie es nachher sein soll, eins zu eins auf den Gast durch. Jeder Interrupt, der auf dem System ankommt, wird eins zu eins direkt zu unserem Gast durchgereicht.
Das bedeutet, nachdem Jell-O's gestartet hat, merkt Linux gar nicht, dass es sich jetzt in einer virtualisierten Umgebung befindet. Also vorher war es Linux noch. Auf bare Metal hat er auf der echten Hardware ausgeführt und dann nehme ich diese Linux und setze es auf so eine Virtual-Maschine oben drauf.
Und nachdem die Anforderung an die Virtual-Maschine ist, dass sie genau so verhalten soll, wie die echte Hardware, merkt Linux gar nicht, dass es nach Aktivierung ist, weil es in einer virtualisierten Umgebung ausführt.
Das einzige, was ich mache, ist, dass ich dem ja dann Kerne und Speicher wegnehme eventuell. Das kommt dann später. Das kommt dann später, genau. Also erstmal wird alle Kerne und Hardware eins zu eins durchgereicht und irgendwann kann ich dann sagen, okay, jetzt knabber mir doch mal so ein bisschen Hardware für so eine Jell-O's Zelle ab.
So eine Zelle besteht aus Minimum einer CPU. Das ist eben diese dedizierte Ausführungs-Einheit, die die Zelle bekommen soll. Es muss mindestens eine CPU sein. Es können dann mehrere sein. Und das ist ein Stückchen Speicher.
Weil ich ja Platz für meine Software-Insassen brauche. Das kann auch nur Megabyte sein, das ist völlig egal. Ich brauche eine CPU, wo ich ausführen kann und ich brauche ein bisschen Speicher, wo ich Software reinladen kann.
Das ist alles, was eine Zelle braucht. Und damit kann ich dann Code in den Insassen in die Zelle reinladen und ich kann die Zelle starten. Funktionieren tut das Ganze, weil Linux Mechanismen mitbringt, die auch aus dem Cloud-Bereich kommen, um CPUs zur Laufzeit zu hot anplanken.
Also ich kann CPUs einfach entfernen während der Laufzeit bei Linux. Also ich schalte die CPU, die in die Zelle kommt, und dann spiele ich die ab. Ich gebe die Speicher-Bereiche aus Linux frei, rufe dann meinen Hypervisor auf und sage, hey, mach doch da mal eine neue Zelle auf.
Und im Übrigen, nimm doch mal für die Zelle CPU-3, die brauche ich nicht mehr, und nimm mal die 5 MB von dem physikalischen Speicher und gib das mal der Zelle.
Ja und damit haben wir dann die Hardware von Linux entkoppelt und der Hypervisor stellt auch sicher, dass Linux nicht mehr auf die Ressourcen der Zelle zugreifen können wird. Und das heißt aber, ich muss Linux bestimmte Geräte aufwecken können, oder? Also, irgendein Linux-Tribe initialisiert irgendein Gerät und letztendlich dem Echtzeit-System, was ich gerade hochbruchte, den neuen Zeller, dieses Gerät geben.
Genau, es gibt zwei Möglichkeiten. Entweder Linux hat das Gerät noch gar nicht angefasst, also noch nicht initialisiert, dann kann ich das dem auch einfach wegnehmen, weil du sie jetzt nicht merken willst, weil du es vorher auch nicht verwendet hast, oder Linux hat bereits auf das Gerät zugedrückt und hat da irgendwas gemacht damit.
Dann muss man natürlich den Treiber, der das Gerät claimt, vorher schon mal auswerfen. So, üblicherweise, ich habe einen Statische Setup, ich weiß genau, wie viele Domänen das ich will und welche Software in den Domänen laufen soll. Üblicherweise ist es in einem operativen Szenario dann tatsächlich so, dass die Hardware normalerweise vor Übergabe zur Zelle gar nicht angefasst haben wird. Das heißt, ich nimm den Hardware weg, auf die ich sowieso vorher noch nicht zugedrückt würde.
Was ja auf Abends zum Beispiel einfach ein bisschen Speicher, also Adressraum wegzunehmen, ne?
Ja genau, also da muss ich mal überlegen, was ist denn eigentlich eine Peripherie, die an der CPU dranhängt.
Eine Peripherie ist eigentlich nichts anderes als ein Speicherbereich, wo ich Register habe, auf die ich zugreifen kann,
ein Speicherbereich, der ist im ganz normalen Adespus vorhanden und ein Signalisierungsinterface, wo die Hardware sagen kann,
"hey ich hätte hier mal ein Paket anliegen" oder so. Das ist ein Interrupt.
Das sind die beiden Kernkomponenten mit der ich eine Hardware Peripherie beschreiben kann.
Das bedeutet, wenn ich so einen UART, wenn ich so einen Cale Interface, einen SBA, einen WDAC, was auch immer, in so eine Zelle geben muss,
da muss ich lediglich den Speicherbereich spezifizieren, zu dem das Gerät gehört und ich muss sagen, welche Interruptnummer in Zukunft nicht mehr bei Linux ankommen soll.
Genau, aber jetzt will ja Linux auch Paging selber machen, ne? Und Linux glaubt ja immer noch, er hat den kompletten physischen Adressraum. Wie geht denn das?
Also es gibt bei virtualisierbaren Architekturen üblicherweise sowas wie sogenannte Second Level Paging oder Nestor Paging.
Damit kann ich den physikalischen Speicher meiner Architektur, also den Arbeitsspeicher, also wie ich den RAM der da dran hänge und die Geräte, die sich auch auf dem Adressbus befinden,
auf den physikalischen Speicher meines Gastes, also den gastphysikalischen Speicher zuweisen.
Funktioniert genauso wie klassische digitale Speicherverwaltung, also ich hab irgendwie Page Tables, wo aufgelistet ist, welcher virtuelle Speicherbereich zu welchem physikalischen Speicherbereich zugewiesen wird.
Und der Trick bei Jellos ist, dass wir einfach sagen, okay, in dem Moment, wo wir den Hypervisor aktivieren, besteht zwischen gastphysikalischen und hostphysikalischen Speicher einfach eine 1 zu 1 Zuweisung.
Was bedeutet, ich hab da so eine Paging-Schicht dazwischen, wo ich dann immer den Lookup machen muss, da kostet auch ein bisschen Zeit, also ich hab da so eine Paging-Schicht dazwischen, die die Zuweisung von gastphysikalischen und hostphysikalischen Speicher macht,
aber Linux merkt das gar nicht, weil für Linux selbst ist der Zugriff transparent durch die Second Level Page Tables hindurch.
Den Vorteil, den ich dadurch habe, ist, dass ich mit diesem Second Level Paging dann zur Laufzeit Linux den Speicher auf Geräte oder auf Arbeitsspeicher, auf denen das dann nicht mehr zugreifen können, darf, im Nachhinein wegzunehmen.
Also ich blende dann genau diese Speicherbereiche, die ich nach anderen Romanien oder anderen Zellen zuweisen will, die kann ich dann von Linux ausblenden, eben mit diesem Second Level Paging.
Nachdem ich aber gesagt habe, Linux, du bekommst jetzt, ich nehme dir jetzt mal diesen Gigabyte Speicher weg. Genau. Und wenn dann Linux versucht darauf zuzugreifen, das könnte ja passieren, dann kriegen wir eine Exception im Hypervisor, merkt der Hypervisor, hey, da haben wir gar nicht mal laufen, wir haben versucht, auf den speicherlichen Speicher zuzugreifen, wo er eigentlich nicht hin darf, ich halte dir die CPU, die diese Violation jetzt durchgeführt hat, ich halte dir die jetzt an.
Und dann ist es eben so, dass durchaus im Infotainment-System eine schwerwiegende Verletzung stattfinden kann, also durchaus möglich, dass es dann eine Kernpanic im Infotainment-System gibt, die benachbarten Domänen und die benachbarten CPUs, sie bleiben aber völlig unbeeindruckt davon.
Völlig unbeeindruckt. Muss man erwähnen, weil eine Konkurrenzarchitektur ist beispielsweise, ich verwende gar keine Virtualisierung, ich lasse über alle CPUs hinweg Linux auf Biometall laufen und die Isolationsschicht, die ich verwende, das sind Prozesse, Linux Prozesse.
Also alle Prozesse laufen auf demselben Kernel, sind vielleicht noch dediziert an ein paar CPUs gepinnt, also die Affinität der Prozesse wird an bestimmte CPUs zugewiesen, um halt auch die Prozesse zeitlich voneinander trennen zu können.
Ja, also heißt aber auch, dass, wenn man bei einem Linux abkackt oder einen Treiber da irgendwelchen Mist macht, da rauft man das ganze System ab, ne?
Richtig, das ist es, Linux ist ein Monolith, der läuft unter allen diesen Prozessen mit unterschiedlicher Kritikalität und wenn da jetzt einer zu einem kritischen Ausnahmefehler führt, dann wird das am Ende des Tages das komplette System mit runterreißen.
Und deswegen muss man sich dann überlegen, okay, ist ein Konzept, dass man mit gewissen Grad für gewisse Kritikalitäten umsetzen kann, wenn ich aber sehr, sehr starke Anforderungen an sich als kritische Anforderungen an das System habe, dann muss ich mir schon die Frage stellen, ob ich ein Betriebssystem verwenden will, das wirklich 30 Millionen Zeilen Code hat.
Oder ob ich dann möglicherweise was Schlagere zu verwenden will.
Und bei Jailhouse hat man dann den Vorteil, dass das nur eine sehr kleine Codebase ist.
Genau, das ist so ein Designziel, so ein philosophisches Designziel von uns, wir versuchen die Codebase so schmal und kürdig wie möglich zu halten, um eben auch für Zertifizierungsvorhaben möglichst hauki zu sein und kostet dann jede Karte teuer Geld.
Der weitere Vorteil, den wir haben, ist, üblicherweise werden in diesen Jailhouse-Zellen, da wird Code-Ausgift bereits existiert.
Also ich schreibe jedes Mal das Airbag-Steuergerät neu, dieses Code existiert bereits und der wird von Variante zu Variante gepflegt und Weiterentwicklung will ich wieder verwenden können.
Und der hat bereits eine Zertifizierung.
Also statt diesen Code jetzt auf dem Linux laufen zu lassen, sagen wir, okay, wir machen eine Zelle auf und da kannst du deinen Code, der möglicherweise schon Zertifizierung hat, weiterhin laufen lassen.
Also im Idealfall, wenn man jetzt mal an die Zertifizierung von so einem System denkt, nur die dünne, dünne Hypervisor-Schicht zertifiziert werden muss, die jetzt hinzugekommen ist und der Gast, der die sicherheitskritische Software ausführt.
Üblicherweise ist der Gast schon zertifiziert, kommen noch ein paar Zehntausend Zeilen oder ein paar Tausend Zeilen Hypervisor-Code mit dazu.
Und den Linux-Code, die Linux-Zeite muss ich nicht zertifizieren, weil die ist A getrennt von dem kritischen Teil?
Genau, da wird dann eben argumentativ sichergestellt, dass die Systeme nicht umgewollt miteinander interferieren können.
Das wird eben auf unterster Hardware eben nicht durch die Virtualisierung weiterum in so einer CPU sichergestellt.
Und der Linux-Code ist sowieso nicht wirklich erzeitfähig damit?
Genau, der Linux-Code soll dann auch dafür da sein, um unkritische Aufgaben durchführen zu können.
Ich habe da am Anfang meiner Promotion mal so einen Demonstrator aufgebaut, wo wir das Konzept mal versucht haben, ein bisschen so veranschaulichen.
Das war eine Drohne, war so ein Oktober, da war dann so ein vierkehrenden Linux-System oben drüber, da lief Zelthaus drauf.
Eine CPU wurde weggeknabbert für die Fluglagerregelung, die ist offensichtlich echtzeitkritisch,
aber ich habe mir verstanden, dass das Ding irgendwie in der Luft stehen bleibt, wenn da Linux auf die Idee kommt, jetzt muss jetzt so ein Man-Pages neu generieren und das Ding fängt dann in der Luft zum Wackeln an, dann wäre das schlecht.
Deswegen haben wir dann eine delizierte Fluglagersteuerung innerhalb von einer Zellauszelle laufen lassen. Die Zellauszelle hatte dann auch Zugriff zu den jeweiligen SPI- und über-DC-Bussen, um mit der Außenwelt, mit den Sensoren und Akkoren dann interagieren zu können.
Und auf der restlichen 30-Bi-U ist dann die Linux, und die Linux hat unkritische Aufgaben übernommen. Was ist so eine unkritische Aufgabe bei so einem System?
Da war unten noch so eine Kamera dran, die Kamera hat Bildaufnahmen gemacht, also ein bisschen Image-Processing und über eine WLAN-Karte die Bilddaten an die Bodenstation geschickt.
Gleichzeitig hat die kritische Zelle, also die Fluglagerregelung, und die Linux-Zelle über einen kleinen geteilten Speicher noch eine Kommunikationsmöglichkeit gehabt, dass sie miteinander sprechen können.
Und so hat die kritische Zelle, wo man kritische Software läuft, die Möglichkeit gehabt, über die Linux-Zelle per WLAN und die Bodenstation die aktuellen Fluglagerdaten zu schicken.
Ich habe es aber geschafft, den kompletten WLAN-Stack, den kompletten TCP/IP-Stack komplett aus der kritischen Zelle auszureißen.
Also dieses Code-Umfang ist in mehreren Minimumzeiten-Code. Den kann ich dadurch, dass ich eben auch die Funktionalitäten von Linux zurückgreife, rauskriege aus dem kritischen Bereich.
Das ist auch wieder gut für Software-Zertifizierungen.
Ja, und wenn jetzt da die Kommunikation ausfällt, weil eben Linux beispielsweise abstürzt, dann geht natürlich keine Kommunikation mehr mit der Bodenstation, das ist vollkommen klar.
Aber der kritische Teil wird das erkennen und sehen, ok, da drüben bei meiner Nachbarzelle, da ist gerade irgendwas passiert und muss dann dementsprechend Aktionen ergreifen, um das System dann noch sicher zum Boden rüber zu fliegen. Also alles ist besser, dass das Ding direkt unten gesägt kommt, weil Linux in den Allpoint of D-Reference im Kern war.
Dann würde quasi nicht nur das Linux abstürzen, ne?
Genau, im Aussehen des Wortes, ja.
Das ist natürlich ein schönes Beispiel, was ist denn für eine Drohne ein sicherer Zustand?
Also klar, ich kann versuchen, im Lod, quasi nach unten auf die Erde, mich langsam runter zu bewegen. Wenn da gerade ein Sumpf oder ein See oder sowas ist, ist natürlich trotzdem Kacke.
Also unser Fokus war eher auf die Software und Systemarchitektur als auf den eigentlich sicherheitskritischen Aspekt, wie kommt die Drohne sicher zurück.
Wir wollen jetzt keine Drohne entwickeln.
Genau, wir wollen keine Drohne entwickeln, wir wollen die Systemarchitektur runterzuwickeln, also die Drohne wollen mit uns weg. Aber unser sicherer Zustand war, wir fliegen noch mal ein paar Meter nach oben, holen uns den GPS-Homepunkt, peilen den quasi auf direkten Weg an und fahren dann langsam runter. Das war bei unserer Demonstration der sichere Zustand.
Und wir haben tatsächlich dann remote, per Wlan, in der Linux-Zelle eine Kernelpanic, behandt getriggert.
Das ist dann schon schön, da gibt es noch so eine S32-Kommunikation per Funk und dann sieht man wieder noch der Crash-Dumpf von Kernel, das reinpulstet und das Ding fängt an rot zu blinken und kommt dann zurück.
Kernelpanic SSS-Service.
Man könnte ja dann auch versuchen, wenn man weiter denkt, dass das Linux neu gestartet wird, also das tatsächlich irgendwie... geht denn das? Also ich boot ja Linux und schiebe dann Jailhouse und dann kann ich jetzt Jailhouse sagen, ich boot jetzt das Linux neu?
Das ist im Moment nicht möglich, wir unterscheiden zwischen, vielleicht kommen wir so ein bisschen mit Cent-Verteilen, gibt es so eine Dom-0, wir haben bei Jailhouse die root-Zelle, also die Wurzelzelle und non-root-Zellen.
Root-Zellen gibt es genau eine, das ist das Linux, das auf dem System initial gestartet wurde und non-root-Zellen sind alle anderen Zellen, die ich dann quasi nach Linux starte. Die non-root-Zellen, die kann ich zu Laufzeit zerstören, also ich kann die beenden und wieder neu starten.
Ich kann auch mein System dynamisch zu Laufzeit umpartizieren, das ist alles möglich. Was ich aber nicht kann, das ist die non-root-Zelle zu löschen. Je nach Anwendungsfall kann man jetzt sagen, das ist ein technisches Defizit, aber für die meisten Anwendungsfälle ist es tatsächlich kein Use-Case, dass ich irgendwann mal das Linux neu starten müsste.
Das kommt normalerweise nicht vor. Was technisch nicht unmöglich ist, spricht nichts dagegen, wir haben es lediglich nicht umgesetzt.
Ja, das wäre jetzt meine Frage gewesen, was spricht dagegen, den Dienungs-Einfach den Kern wegzunehmen? Du sagst, dass du gerade gelaufen bist, wir geben diesen Kern nie wieder her. Können wir machen, es ist jederzeit möglich, dass natürlich ein Cell-House entscheidet, ich gebe meinem Gast keine Laufzeit mehr.
Also können wir für unabhängige Zelle für jede Zelle zu jeder Zeit entscheiden, wenn wir irgendwer eigenes aufgeben, das kann ja auch sein, dass ich sage, okay, es ist irgendwas extremes passiert, ich will die komplette Steuerung von meinem System gerade beenden, dann könnte ich über einen Hypercall von einer Zelle, die das auch darf, den Hyper-Wise sich konzentrieren, jetzt holt mal das komplette System an und tu nichts mehr. Und der könnte das noch machen, also technisch durchaus uns treu, aber da ist aber kein konkrete Use-Case dahinter.
Was ich vielleicht noch erwähnen sollte, was zu dem Software-Support angeht, was kann denn da in unserer Zelle laufen? Üblicherweise sollen da natürlich Echtzeit-Infektionssysteme laufen, also zwei Systeme, die wir als Gäste unterstützen, das ist beispielsweise Sapphire und Free Arthouse, das sind zwei so bisschen konterte Echtzeitsysteme, Community-Echtzeitsysteme. Was wir aber auch können, das ist, wir können ein Linux parallel zu einem Linux laufen lassen. Also wir können in so einer Cell-House-Zelle auch nur aus sagen, wir wollen ein zweites Linux auf dem System gleichzeitig buchen. Und wenn ich acht Prozessoren in dem System zur Verfügung habe, dann ist es auch kein Problem, dass ich sage, ich will acht verschiedene Linux parallel laufen haben. Es gibt durchaus Anwendungsfälle, wo sowas ganz spannend werden kann, dass ich wieder den Linux parallel auflaufen habe auf so einem System.
Habt ihr denn irgendwas gemacht, damit die sich mit Speicherbandbreite nicht in die Quere kommen? Also wenn jetzt zum Beispiel Linux sagt, ich muss jetzt, keine Ahnung, extra64, wahnsinnig speicherintensiv, ich rühle jetzt auf diesem Speicher drauf rum, aber das Free Arthouse dann quasi nicht mehr wirklich da in den Speicherbandbreite abbekommt.
Genau, das ist eines der großen Probleme von so einem Ansatz, die haben wir uns ja irgendwann schon mal erwähnt. Ich habe immer noch so einen geteilten Systembus.
Das bedeutet, wenn die CPU 0 auf den speicher zugreift, auf den physikalischen Speicher, auf Arbeitsspeicher im Raum, und die CPU 3 greift auch auf Speicher zugreift, dann geht das über den eigenen Adressbus drüber.
Speicher, der über den Adressbus läuft, der muss irgendwie arbitriert werden. Also irgendjemand muss die Entscheidung tätigen, wer darf denn als erstes auf den Speicher oder wer kommt denn als nächstes dran.
Es muss verhindert werden, dass es zu Starvation kommt, dass die Anfrage von einer CPU nach Speicher also auf Ewigkeiten niemals beantwortet wird, weil eine CPU mit höherer Priorität beispielsweise die ganze Zeit im Busraum liegt, also x264 oder was auch immer macht.
Das muss man natürlich verhindern. Dann gibt es auch den verschiedenen Architekturen, die wir so unterstützen, unterschiedliche Mechanismen, wie ich auf meinem Speicherbus beispielsweise wichtig quotieren kann.
Insol gibt es beispielsweise RTT, Resource Director Technology, da kann ich festlegen, welches Quoter, welche CPU oder welche Domäne zur Verfügung bekommt und kann es so dann eben sicherstellen, dass jeder wenigstens das bekommt, was er auch zur sicheren Ausführung benötigt.
Ja, das ist ja jetzt sehr schön, aber was kostet uns jetzt eigentlich diese Virtualisierung mit Jailhouse?
Das ist eine gute Frage, weil Virtualisierung kommt nie umsonst. Wir haben ja schon mal ein Event, wir haben ein Nested Page Table, Second Level Virtualization.
Das bedeutet, jeder Speicherzugriff, der vom Gast auf physikalischen Speicher runterbricht, der muss zweimal durch Page Table Walks durchlaufen, das kostet natürlich eine Rent Overhead, doppelte Paging Strukturen.
Das bedeutet, Speicherzugriffe als solche, die werden dezent langsamer, eben weil ich ja durch zwei Ebenen Page Tabels durch muss.
Es gibt natürlich auch wieder Möglichkeiten, um das zu optimieren, TLBs usw., man kennt das alles.
Und es gibt dann auch Situationen, wo ich es nicht verhindern kann, dass mein Hypervisor aktiv wird.
Unsere Philosophie ist eigentlich, wenn ich es schaffe, Hardware eins zu eins durchzureichen, sodass der Hypervisor nie wieder aktiv wird oder nie aktiv werden muss, dann habe ich die Echtzeit-Eigenschaften von dem System per Systemdesign oder per Architektur sichergestellt.
Der Hypervisor wird nie aktiv, der führt nie Code aus, also fällt der in meine Echtzeitbetrachtung komplett weg, womit das bedeutet, dass ich nur noch da die Echtzeit überprüfen muss, wo ich sie vorher auch schon überprüfen musste.
Das bedeutet, dass ich das Problem vorher auch schon hatte.
Also wenn ich es schaffe, dass ich Zero Overhead Virtualisation erreiche, spielt der Hypervisor als solche für die Echtzeit keine Rolle mehr.
So genau das versuchen wir. Unser Ziel mit Shell aus ist es, dass wir wirklich einen Hypervisor haben, der in einem operativen Szenario, wenn das System so läuft, nie wieder aktiv werden muss. Das ist so leider auf zeitgenössischen Architekturen so noch nicht möglich, nur in manchen Arbeitsstellen.
Man muss den Hypervisor beispielsweise aktiv werden. Schauen wir uns mal so eine klassische Armaarchitektur an, wo ich in so Geläten dran hängen habe für SBI-IQC.
Also Beispielszenario, ich habe ein SBI und ich habe ein IQC-Gelät und ich möchte beide Geläte in unterschiedlichen Domänen verwenden können, zum Beispiel SBI in der Zelle A und ich will IQC in der Zelle B.
Dann kann ich das jederzeit machen, wir haben 2nd Level Paging, das bedeutet, ich weise die Speicherseite, auf der sich das Überrassegelät befindet, nur in die eine Zelle zu und das SBI-Gelät sitzt, das weise ich nur in die andere Zelle zu und die Geläte sind voneinander so weit isoliert.
Problematisch wird es aber, wenn wir auf Plattformen unterwegs sind, die dazu tendieren, mehrere Peripheriegeläte auf der selben Speicherseite zu haben und die Geläte dann eben noch in unterschiedlichen Domänen zu verwenden.
Wir wissen ja, die feinste Granularität, um Speicher einer Virtualmaschine oder einem Prozess, was auch immer zuweisen zu können, das ist Paging im Allgemeinen, die feinste Granularität ist die Speicherseitengröße, die Page Size.
Üblicherweise bei den eben auch die Tun 4 KB. Wenn sich innerhalb dieser 4 KB 2 Geräte befinden, dann kann ich die nicht ohne Weiteres auf verschiedene Positionen zuteilen.
Was ich machen muss, ist, ich muss jetzt mit der Virtualisierung oder Moderation beginnen, ich weiß, keiner ist der Geräte einer Zelle zu, sondern sobald die Zelle versucht, auf das Gerät zuzugreifen, dann trap ich im Hypervisor, ich fange also den Speicherzugriff ab und ich stelle im Hypervisor die Frage,
kam der Zugriff denn auf einen Adressbereich, auf den diese Zelle aufzugreifen darf, also ich sage Paging, wenn man will. Im Prinzip das gleiche wie Paging, nur halt auf Byte oder Bit-Granularität, das kann man so fein machen wie man will, dazu brauche ich aber wieder zusätzliche Software im Hypervisor.
So, oder was macht die dann?
Sondern die fragt ab, ist denn die Zugriff auf diese Adresse, darf denn die von der Zelle auch kommen, wenn ja, dann reicht diesen Zugriff eins zu eins durch und wenn nicht, dann hält die CPU an, denn die haben eine schwere Zugriffverletzung.
Also wirklich, wenn ich die Frage stelle, darf ich denn auf dieses Beid zugreifen, ja nein, wenn ja, leite es durch, sei es lesen oder schreiben, völlig egal, wenn nicht, dann schütze ich die Zelle ab. Aber das kostet Zeit.
Das kostet Zeit und das Problem habe ich eben genau dann, wenn ich einen Bock vom Hab, die meint, es existiert ja so wenig physikalischer Speicher, wir müssen viele Geräte auf eine Seite quetschen.
Da sind aber dann die Hardware-Hersteller gefragt, weil es wäre natürlich jederzeit möglich, die Geräte auf unterschiedliche Speicherseiten zu packen, womit ich das Problem noch nicht mehr habe. Also um solche Systeme so zu designen, dass die für Zero-Trap-Virtualisation tauglich sind, da bedarf es dann am Ende auch so ein bisschen Hardware Software-Kuri sein, weil die Hardware-Entwickler natürlich wissen wollen, welche Dimittierungen oder welche Anforderungen wir an die Plattform stellen.
Wäre es nicht auch möglich, ich meine, du hast ja den ganzen virtuellen Speicher im Griff, diese Seite, die kannst du nicht in beide rein machen, weil dann könnte der andere auf das eine zugreifen.
Genau, das ist mein Problem, genau das ist das Problem. Ich kann nicht beide zuweisen.
Wenn du beiden vertraust, dann kannst du das machen.
Wenn das wechselseitige Zugriff keine kritischen Folgen hat, also es kommt immer auf den konkreten Anwendungsfall drauf an, dann wäre das durchaus eine gangbare Möglichkeit, über die man zumindest mal nachdenken kann. So schön und sauber ist natürlich was anderes, so wie wir es eigentlich nicht haben.
Abgesehen von diesem Fall wird dann der Hardware tatsächlich nie aktiv?
Die Antwort ist wie immer, depends. Klassische Informatiker-Antwürfe, das kommt drauf an. Also es gibt Architekturen, da passiert ansonsten neben Speicher-Zugriffsverletzungen sehr sehr wenig.
X86 ist so eine Architektur, wo bei sehr vielen Anwendungsfallen wir wirklich Zero-Trap-Virtualisation erreichen können. Es gibt aber Architekturen wie beispielsweise ARM, da müssen wir schon noch aktiv werden.
Ein Problem, das wir da beispielsweise haben, ist die Art und Weise, wie der Interrupt-Controller in unserem System funktioniert. Also per Design bei diesem, warum es denn heißt, der gigaegenerische Interrupt-Controller, per Design ist es so, dass jeder Interrupt erst mal in der Privilegstufe des Hypervisor ankommt.
So ein Hypervisor muss einen Interrupt annehmen, der kommt im Hypervisor an, der Hypervisor muss dann entscheiden, wie gehört denn der Interrupt und den Interrupt in unseren Gastreregen indizieren.
Die Antwort bei Cella ist natürlich super super einfach. Ein Interrupt, der auf eine CPU ankommt, gehört immer der Gast, der auf der CPU sitzt.
Weil wir scheduling keine CPUs, also jeder Interrupt, der auf einem CPU-Interface ankommt, wird auch dieser virtuale Maschine gehören. Also ohne der großen Auszufragung, wird der Interrupt direkt in unseren Gast weitergehen.
Weil wir vorher konfiguriert haben, dass dieser Interrupt nur auf dieser CPU ankommen kann.
Ja, wichtig, genau. Also die Frage, die sich im Moment des Interrupt-Rivals stellt, stellt sich die Frage gar nicht mehr.
Wie auch die Tours sieht es aber nicht vor, dass sich Interrupts direkt in eine Zelle oder in eine virtuale Maschine reingeben können. Die muss erst im Hypervisor ankommen und muss dann reinviziert werden.
Das kostet Overhead. Das kostet Overhead, weil wir einen Kontext switchen, es muss erst einmal erkannt werden, warum ist denn der Hypervisor gerade aktiv geworden, sie muss die ganze Register auslesen, stellen wir fest, ok, es kam ein Interrupt an, ja welcher Interrupt kam denn da an, es kam Interrupt 7 an, ok, ich muss den Interrupt aknolligen, jetzt muss ich den in meine Zelle reindizieren, gehe wieder den ganzen Stack zurück, mach wieder einen Kontext switch, in den Gast hoch, da wird der Interrupt angenommen, wird nochmal ausgelesen. Also das kostet Softwarezeit, das kostet Rechenzeit.
Und da kann ich dann über Microbenchmarks herausfinden, wie hoch ist denn eigentlich der Overhead durch so eine Interrupt-Reenizierung passiert. Das findet man dann zum Beispiel, wie wir das mal gemessen haben in unserem Look-Mum-Novia-Next-It-Paper.
Da haben wir gemessen, angenommen, ich hab keinen Hypervisor aktiv, was hab ich denn so für einen Jitter zwischen Interrupt kommt am System an und Interrupt wird vom User-Space angenommen, versus ich muss den Interrupt zwischen drinnen vorher noch einmal in den Gast reindizieren, was ist denn da alles drin dazwischen.
Das kann man dann irgendwie ein paar Stunden lang messen, dann kommt da verschiedene Eigenschaften drauf an, es dauert nicht mehr gleich lang, weils sind die Cache warm, weils sind die Cache kalt, unterschiedliche Verhalten, das wird messen, weil das ist dann mehrere Stunden weg, in der Hoffnung, dass man in allen Fällen so halbwegs abgedeckt hat.
Und gibt dann die Zahl, mit der ich im Worst-Case rechnen muss, wie lange das die Interrupt-Reenizierung dauern kann.
Je nach Architektur kann das Nanosekunden bis Mikrosekunden werden, das kann ich dann in die Echtzeitanalyse von meinem System reinfließen lassen und kann entscheiden, okay, ich muss jetzt, wenn ein Interrupt ankommt, um so viel, oder mit so viel Latenz kalkulieren im Worst-Case, passt denn das Ganze noch in den Anforderungskatalog von meinem Echtzeitsystem. Das kann ich dann da zumindest entscheiden.
Aber, also wenn ich es messe, ist ja, wer sagt mir, dass ich wirklich den Worst-Case gemessen habe?
Wir versuchen während unserer Messungen verschiedene Szenarien, verschiedene Lastszenarien auf dem System abzubilden.
Also wir setzen dabei den Nachbar-Dizellen-Unterlass, wir stressen den Systembus, wir haben verschiedene Pattern, die ich versuche abzudecken und den Rest erledigt dann Statistik und stundenlange Messungen, also die Hoffnung, den Wohl jeden Fall abgedeckt zu haben.
Ja, und auf was für Architekturen läuft denn Jailhouse eigentlich jetzt?
Also im Moment unterstützen wir zwei Architekturen, das ist x86, und zwar auf Intel und auf AMD-Systemen, da unterscheiden sich dann die Virtualisierungserweiterungen im Detail, bei AMD ist es SVM mit Zierweiterung, bei Intel ist es VTX und VTD, die unterscheiden sich dann im Detail so ein bisschen.
x86-Systeme und ARM-Systeme, bei ARM unterstützen wir ARMv7 und ARMv8, das unterstützen wir derzeit und meine Aufgabe ist es gerade, Jailhouse auf RISC-V zu portieren.
Also vor kurzem sind die Hypervisor-Extensions von RISC-V ratifiziert worden, also die sind jetzt in den Standard eingeflossen,
wobei sich alle gerade so ein bisschen aufstützen ihren Hypervisor auf RISC-V-Laufweg machen zu können, KVM funktioniert schon auf RISC-V und Jailhouse soll natürlich auch mittelfristig auf RISC-V-Systemen laufen.
RISC-V ist eine sehr schöne Architektur an und für sich, da unterscheiden sich die Details was mit Virtualisierungs angeht, stand teilweise maßgeblich von anderen Architekturen.
Bei x86 und bei ARM ist es ja beispielsweise so, dass der Hypervisor eine eigene Privilegstufe hat, also der Hypervisor sitzt irgendwie unterhalb des Betriebssystems,
bei x86 gibt es sowas wie ein Ring-1, also früher gab es irgendwie Ring 0 bis Ring 3, mit Hypervisor kommt ein Ring-1, der heute noch mächtiger war als Ring 0, der ist da hin zugekommen,
bei ARM ist es so, dass ich sogenannte Exception-Levels habe, Exception-Level 0 bis 3, genau wir haben ganz oben den User-Space sitzen, unter dem User-Space sitzt das Betriebssystem im Supervisor-Mode,
unter dem Betriebssystem sitzt der Hypervisor und unter dem Hypervisor sitzt die Systemfirma, gut glaube ich beispielsweise bei so einem System.
Bei RISC-V ist es ein bisschen anders, da habe ich ganz unten den Machine-Mode sitzen, der M-Mode, über dem M-Mode sitzt der S-Mode, der Supervisor-Mode, da läuft dann üblicherweise ein Betriebssystem drin,
kann Linux sein, unten ein User-Mode, also drei verschiedene Modi. Was RISC-V von den anderen Architekturen unterscheidet, habe ich gerade mal erzählt, wir fahren uns beim Aktivieren von Shell aus unter das System drunter
und installieren quasi den Hypervisor auf dem Privilegslabel, der unter uns sitzt und lüften so Linux von einer Virtual-Maschine hoch.
Bei RISC-V funktioniert das so, dass Linux zunächst in dem S-Mode, in dem Supervisor-Mode läuft und wir verdrängern Linux aus dem S-Mode.
Wir verwenden Linux von dem S-Mode in den sogenannten V-S-Mode, das ist der Virtual-Supervisor-Mode und der Hypervisor misst sich selbst dem S-Mode ein.
Das heißt wir schieben Linux nicht nach unten weg oder installieren uns unter den Linux drunter, sondern wir verdrängern Linux raus und heben das nach unten weg.
Funktioniert also in Detail ein bisschen anders als auf anderen Architekturen. Die klassischen Probleme gibt es bei RISC-V aber im Moment auch wie bei vielen anderen Architekturen,
beispielsweise die Interrupt-Rejection, wo der Hypervisor noch zwischendurch aktiv werden muss, die existiert bei RISC-V genauso wie bei ARM eben weil der Interrupt-Controller noch so ein paar Schwächen hat, um das ohne Traps umsetzbar zu machen.
Das ist auch bei RISC-V genauso wie bei anderen Architekturen, aber nachdem das eine offene Befehlsausarchitektur ist, gibt uns das die Möglichkeit, dass wir da jetzt sagen können, was fehlt uns denn eigentlich noch um unser großes Ziel, Zero Trav-Virtualization umzusetzen.
Wo sind denn da die Knackpunkte von der spezifischen Architektur, die sieht man immer erst, wenn man so ein System in der Alitausmann implementiert und sich das ansieht. Das kann man systematisch erfassen.
Bei RISC-V könnt ihr jetzt natürlich hingehen und das selbst implementieren, die habt ihr selber in der Altausmann-Giessen.
Genau, das ist so das Ziel, was wir bei der ganzen Arbeit haben, wir wollen uns systematisch erfassen, aber was sind denn die Probleme und wie sieht denn ein System aus, das genau unseren Anforderungen genügt.
Und das können wir auf so einem FPGA, wo man so ein Blitzfall synthetisieren kann, selbst unter Laborbedingungen natürlich nachstellen. Das klingt auf jeden Fall sehr spannend, fühlt jetzt aber eigentlich schon ein bisschen zu weit.
Ja, okay, Flo, was hast du heute hier mitgebracht?
Ja, ich habe jetzt ein bisschen den entgegengesetzten Entwurf von Trailhouse und eingebetteten Echtzeit-Systemen, also Virtualisierung für eingebettete Echtzeit-Systeme mitgebracht.
Und zwar habe ich mich mit Paravirtualisierung beschäftigt und im Gegensatz zur vollen Virtualisierung stellt jetzt eine Paravirtualisierung nicht genau die gleiche Schnittstelle, also die Hardware zur Verfügung, einer Anwendung, sondern eine leicht abweichende Schnittstelle oder auch eine teilweise erheblich abweichende Schnittstelle.
Und die Idee dahinter ist, dass man jetzt nicht einfach die Hardware emuliert, sondern eine Schnittstelle zur Verfügung stellt, die durch die Emulation von Software besser geeignet ist, also die dann einfacher ist zu implementieren.
Das ist an sich schon eine alte Idee, also die gab es schon bei alten IBM Hypervisoren oder ja, auch schon traditionell bei anderen Virtualisierungstechnologien in Form eines Hyper-Calls.
Aber so richtig zur vollen Blüte kam sie dann erst wahrscheinlich mit dem Xen-Hyper-Weiser.
Aber was heißt eigentlich Paravirtualisierung? Also dass jetzt, wenn ich ein Hardware-Gerät habe, dann nicht jeden Hesert mitnehmen, der in dem Gerät passiert, sondern nur die grobe Funktionalität implementiere oder?
Ja, also man kann sich das so vorstellen, ich habe zum Beispiel einen, ich will in meiner Virtual-Maschine einen Speicherplatz einblenden, also eine Festplatte einblenden, ein Gerät.
Und jetzt überlege ich mir, ok, ich könnte jetzt irgendeine Festplatte emulieren, beispielsweise die, die ich selber habe, aber ich will nur einen Teil der Festplatte einblenden und jetzt überlege ich mir eine ganz eigene Schnittstelle, die gar nichts mit einer echten Hardware wirklich zu tun hat.
Und die blende ich dann in der Virtual-Maschine ein und die Virtual-Maschine, also das Betriebssystem, das darin läuft, das hat jetzt ein Treiber für dieses gar nicht real existierende Gerät.
Aber dieses gar nicht real existierende Gerät ist eben einfacher für den Hyper-Weiser zu emulieren.
Genau, ja im Prinzip ist diese Idee auch gar nicht neu, wenn wir mal zurückdenken, wie Betriebssysteme entstanden sind, dann war das ja genau die Idee, ich will dem einen Prozessor, also für jeden Programm den Prozessor zur Verfügung stellen, wenn man an ein Datensystem zum Beispiel denkt, dann macht das genau das, das stellt ja eigentlich die Festplatte zur Verfügung, aber eben in einer, ja, vereinfachten Form, also in Form von Dateien.
Ja, warum will man jetzt Para-Virtualisierung haben? Da gibt es mehrere Gründe, zum einen will man Architekturen unterstützen, die gar nicht so gut geeignet sind, um eigentlich Virtualisierung darauf zu betreiben.
Das war eben der Fall bei x86, bevor die Virtualisierungserweiterungen kamen und ein anderer Grund ist, dass manchmal durchaus vorteilhaft sein kann, dass eine, dass die Anwendung in einer Virtual-Maschine weiß, dass sie in einer Virtual-Maschine ist und in gewisser Weise einen Einblick teilweise dann doch auch in echte Ressourcen hat.
Also es kann zum Beispiel bei der Systemzeit sinnvoll sein, also ich kann, in einer Virtual-Maschine habe ich eine virtuelle Zeit, die die Virtual-Maschine tatsächlich abgearbeitet hat, aber es kann sinnvoll sein manchmal auch auf die echte Zeit zu blicken, die tatsächlich vergangen ist, auch wenn in der Zwischenzeit dann andere Virtual-Maschinen ausgeführt wurden.
Genau und dann, was ja gerade schon diskutiert wurde, kann es effizient sein, Peripherie oder I/O-Geräte zu emulieren, die nicht so eine komplexe Schnittstelle haben wie die Hardware.
Genau, also dass wir quasi nicht die echte Nvidia-Titan sonst irgendwas darstellen, sondern eine generische VGA-Grafikkarte, die halt nur dies oder jenes kann, sehr, sehr einfach zu den Treiber dafür zu implementieren ist.
Genau, es gibt natürlich auch noch einen Nachteil, den wir haben, wenn wir die Hardware-Schnittstelle jetzt anders gestalten, dann muss unser Betriebssystem oder unsere Anwendung in der Virtual-Maschine das natürlich auch wissen, also sie braucht dann eben eigene Treiber um diese virtuellen Geräte dann zu benutzen.
Ja, was ich mir da jetzt konkret dazu angeschaut hat, ist das Paper "Xen and the Art of Virtualization", das ist schon 2003 erschienen und deshalb diese Para-Virtualisierung eben auf die Spitze gedrängt, die es da vorher schon gab, die schon sehr alt ist.
Ja, und was wollten die erreichen? Die wollten eine hohe Performance erreichen von Virtual-Maschinen trotz der Virtualisierung von Ressourcen und zwar nicht nur von Prozessor und Speicher, sondern auch von Geräten, auch wenn das Ganze auf diesen ungünstigen Architekturen läuft, also das damalige X86 ohne die Virtualisierungserweiterung.
Und sie wollten viele Gäste, also volle Multi-User-Betriebssysteme auf einem Rechner laufen lassen, also das waren so die Beginne dieser Cloud-Ära und damit ist es auch genau das jetzt irgendwie das entgegengesetzte von Jailhouse.
Wir wollen hier also möglichst viel auf einem Rechner unterbringen und Overcommitment betreiben und diese ganzen Dinge.
Genau, und wir wollen trotzdem, auch wenn wir deshalb eine gute Performance-Isolation haben, also auch wenn wir einen unkooperativen Gast haben, der irgendwie die ganze Rechenzeit für sich beansprucht oder irgendwie Speicher durchsetzt, macht das trotzdem die anderen Gäste nicht davon zu sehr eingeschränkt werden.
Okay, also wie hat das damals funktioniert?
Ja, damals war das dann so, also zum ersten Mal musste Xen die CPU und den Speicher virtualisieren und was man damals auf X86 hatte, ist, dass man vier Protection-Domains hat, also nicht nur den klassischen User-Mode und den Kernel-Mode, sondern es gab auch noch zwei dazwischen, mit 64 bits gibt es das jetzt nicht mehr.
Das waren also diese Intel-Ringe, ne?
Genau, genau, und wie das jetzt war, ist das auf Ring 0 eigentlich das Betriebssystem lief und nur auf Ring 0 können diese ganzen privilegierten Operationen ausgeführt werden, also die das Betriebssystem eigentlich braucht und auf Ring 3 dann die User-Prozesse und 1 und 2 wurden eigentlich zu der Zeit gar nicht benutzt oder nicht mehr benutzt.
Und was Xen jetzt macht, ist, dass es ganz ähnlich wie Jailhouse erstmal ein normales Linux bootet und dann sozusagen Xen im Nachhinein aktiviert, also aus einer ähnlichen Idee überlassen die ganze Hardware-Initialisierung Linux machen und was es jetzt macht, ist, dass es dieses Linux in den Ring 1 hebt und Xen dann in Ring 0 läuft.
Also eigentlich wieder ganz ähnlich wie wir das jetzt für RISC-V gehört haben, also da gibt es dann Parallelen.
Das Problem ist jetzt aber, dass die privilegierten Instruktionen in den Ring 1 schon verboten sind, also wir können da eigentlich nur User-Space-Zeug ausführen.
Wir können uns da Speicher einrichten, der dann auf Ring 3 nicht mehr zugleichspare ist, das geht schon, aber wir können nicht die privilegierten Instruktionen ausführen.
Und deswegen müssen wir, um diese dann doch wieder ausführen zu können, einen Hypercall-Mechanismus einführen.
Also das heißt, wenn das Betriebssystem jetzt tatsächlich diese Instruktion ausführen will, dann muss es einen Hypercall machen, also muss den Hypervisor aufrufen.
Was heißt Hypercall an dieser Stelle?
Das ist quasi das Paundor zu einem Systemcall, also wie wenn ich aus dem User-Mode in das Betriebssystem gehe, also einen Aufruf an das Betriebssystem mache, mache ich jetzt in mein Betriebssystem eine Anfrage oder einen Aufruf des Hypervisors.
Genau, so muss man sich das vorstellen.
Dann muss man sich noch überlegen, Speicher wird durch Paging-Daten-Strukturen verwaltet und jetzt hat man das Problem, wenn ich keine Virtualisierungserweiterung habe, dann habe ich die Page-Tables, mit denen man die Speicher konfiguriert. Und was Xen jetzt macht, ist, um das Ganze effizient zu gestalten, dass diese Page-Tables schon lesbar sind für das Gastbetriebssystem.
Aber die Schreibzugriffe müssen natürlich über den Xen-Hypervisor passieren, weil es gibt da bestimmte Einschränkungen.
Ich darf mir ja nicht jeden Speicher holen, weil das könnte ja Speicher sein, der einem anderen Gast gehört.
Genau, und der physikalische Speicher ist dann festpartizioniert, also ich habe auch bestimmte Abschnitte des Speichers, die bestimmten Gästen angehören, aber das kann auch im Zulauf weiter noch verändert werden.
Das heißt, Xen hat damals noch keinen, ich nehme jetzt aus dem einen Gast Speicher raus und packe ihn zum anderen gemacht?
Doch, doch, das ging schon. Man hat ihn ja schon festpartizioniert, aber man kann das machen.
Genau, jetzt gibt es natürlich noch die andere Richtung, also der Hypercall ist jetzt, der Gast will irgendwas ausführen und fragt den Hypervisor, aber jetzt kann es ja noch sein, dass der Hypervisor den Gast irgendwie, also den Gast kommunizieren will, also dann Kommunikation anstoßen zum Beispiel.
Wenn es ein Interrupt von einem Gerät durchstellen will oder wenn wir einen asynchronen Aufruf haben, also einen asynchronen Hypercall, und das geht über so ein Eventsystem, die sind quasi ähnlich aus Sicht eines Gastes wie ein Hardware Interrupt.
Genau, und da gibt es dann einen Händler, den der Gast eingerichtet hat und Xen löst dann so einen Event aus und das führt dazu, dass im Gastbetriebssystem ein Händler ausgeführt wird.
Und genau, über so eine Bitmap, die zwischen Hypervisor und Gast geteilt ist, kann der Gast dann herausfinden, welcher Typ eines Events quasi gerade ansteht.
Also im Grunde wie so ein Unix-Signal, ne?
Genau, also die Idee ist ziemlich ähnlich, die Autoren erwähnen das sogar in dem Paper, dass es wie ein Unix-Signal ist.
Genau, und das sind quasi schon die Mechanismen, wie eben alles funktioniert.
Genau, und dann ist noch ein großer Punkt, den Xen hat eben dieses Device I/O, also diese para-virtualisierten Geräte.
Und da ist die Idee, dass wir in den Gästen dann überwiegend jetzt keine Emulation von echten Geräten machen, sondern virtuelle Schnittstellen bereitstellen, die vorher schon kurz gesagt.
Und das Ganze baut dann wieder auf diesem Eventsystem auf und dem Hypercall und Shared Memory zwischen dem Hypervisor und dem Gast.
Also das heißt, die meisten Geräte, die ich quasi emuliere, sind irgendwie Shared Memory eingeblendet?
Genau.
Das heißt, die kann ich dann... kann ich die direkt durchblenden? Konnte ich die damals schon direkt durchblenden oder war das immer ein...?
Nee, nee, also das ist ja gar nicht die Idee von Xen, sondern die Idee von Xen ist ja, dass ich diese virtuellen Geräte habe, also die gar nicht reale Geräte sind, sondern wenn ich zum Beispiel eine Festplatte beim Gast haben will, dann ist das...
Dann emuliere ich quasi dieses virtuelle Gerät, das es gar nicht gibt und blende da seit meiner echten Festplatte oder ja, nicht seit meiner echten Festplatte, aber quasi eine Datei zum Beispiel ein.
Und ja, das Ganze sieht ja dann technisch über I/O-Rings, das ist so ein Ringpuffer zwischen dem Xen und dem Gast.
Und da werden dann Requests und auch die Antworten wieder darauf jeweils reingeschrieben.
Genau, darüber funktioniert das via dieses Eventsystems, werden dann Nachrichten signalisiert und dann kann der Gast diese Antworten dieser Events wieder lesen.
Genau, und das Ganze funktioniert dann ganz gut und macht das wesentlich schneller als es wäre, wenn man eine echte Hardware dann emulieren würde.
Und dann kümmert sich quasi Xen drum, dass die Dateien wirklich von der Festplatte gelesen werden?
Genau, genau. Xen greift dann tatsächlich echt auf die echte Festplatte zu.
Also er bekommt dann über diesen I/O-Ring quasi die Anfrage, ich gebe mir meine Datei so oder...
Ja, ich gebe mir meine Datei so und so und Xen findet raus, dass die Datei und die Festplatte gelesen werden.
Genau, oder eher ein Blocker ist ein Blocker. Und auch der Snetzwerkgerät läuft auf diese Weise.
Ich habe mal so eine Idee, ich muss zwingend die Existenz des Hypervisor verstecken, indem ich eine Umgebung kreiere, die sich genauso verhält wie eine echte Hardware.
Und Xen geht auf den Weg und sagt, okay, wir verstecken gar nicht, dass du nicht in einer virtualen Maschine befindest, sondern du weißt darüber Bescheid, was eine virtualisierte Umgebung ist, ich weiß auch darüber Bescheid.
Versuch man einen anderen Weg einzuschlagen, um das optimale rauszulegen und das ist in dem Fall die Paravirtualisierung.
Genau, das Ganze funktioniert auch recht gut oder hat damals auf jeden Fall recht gut funktioniert.
Also sie haben das damals eben implementiert, also wir erinnern uns, man muss ja auch das Gas-System anpassen, dass es auf diesem ganzen paravirtualisierten Zeug läuft.
Also im Prinzip war das für den Gaskörner dann so eine Art neue Architektur, die sie da eingeschüttet haben, eben Xen auf X86 zum Beispiel.
Und das haben sie gemacht für Linux, damals auch für Windows XP und auch für NetBSD und haben das gemessen.
Und damals war das vergleichbarer mit Bear Metal und deutlich schneller als damalige Standard-Virtualisierungslösungen wie VMWare, die eben das nicht gemacht haben.
Genau und klar der Vorteil war gegenüber, man lässt alles auf einem gemeinsamen Betriebssystem laufen von Prozessen, dass man natürlich viel bessere Isolation hat.
Also dass die Performance-Isolation einfach so war, dass man nicht einen anderen Prozess oder viel schwieriger aushungern konnte und solche Dinge.
Also im Grunde steht Xen dem Jailhouse ziemlich konträr gegenüber.
Also Xen virtualisiert halt nicht eins zu eins die Hardware und also irgendwie Echtzeitfähigkeit oder so spielt hier auch überhaupt keine Rolle.
Ne, ne das ist genau also in seiner klassischen Ausprägung bei Xen war das definitiv nicht erforderlich, das ist nicht vergleichbar, weil noch völlig andere Technologien da verwendet worden sind.
Aber nachdem dann irgendwann die Virtualisierungserweiterung kamen, hat auch Xen diese Virtualisierungserweiterung genutzt.
Man nutzt die auch jetzt im Moment und es gibt auch durchaus dieses Xenon ARM beispielsweise.
Und die schreiben sich schon auf die vorne, dass sie auch eben für so embedded CPUs, dass sie dort Echtzeitfähig sein wollen können.
Das sind aber dann die anderen Ausprägungen von Xen, Xen in seiner klassischen Variante nie, also da war es nicht vergleichbar.
Ja genau das führt uns auch jetzt ein bisschen dazu, was eigentlich heute damit ist.
Also es wurden ja dann irgendwann auch für X86 dann die Virtualisierungserweiterung, also Befehlseitserweiterung sind das hier eingeführt.
Und dann ist ja einiges von dem jetzt überflüssig.
Dann muss ich mich nicht mehr um dieses Paging sorgen machen, weil da gibt es dann einfach andere Möglichkeiten und über die privilegierten Instruktionen auch nicht.
Aber es gibt ein Andungsgebiet, wo Paravirtualisierung immer noch ein großes Ding ist und auch immer noch viele Vorteile bringt und das ist eben dieses Device I/O.
Nämlich Geräte, die ich dann einblenden, also die ich kreieren will und die nur einen Teil eines Gerätes widerspiegeln, also klassischerweise natürlich Festplatte.
Also wenn ich zum Beispiel eine Virtualmaschine mit QEMU oder VirtualBox laufen lassen und ich will eine Datei als Festplatte einblenden, dann kann ich Paravirtualisierung verwenden.
Also Word I/O gibt es da zum Beispiel, das wird viel verwendet.
Und da ist Paravirtualisierung wirklich immer noch ein großes Ding.
Dann hat der Gasthalm-Driver, der genau diese Möglichkeit spricht.
Bei Word I/O ist es immerhin noch ein PCI-Gerät, das emuliert wird, aber danach ist es quasi kein echtes Gerät mehr.
Da hört es dann schon auf.
Und vielleicht ist es so ein, ja quasi sagt uns das, dass bezüglich Prozessor und Speicher und so weiter sich schon einiges getan hat, bezüglich der Hardware-Unterschützung von Virtualisierung,
aber die Peripheriegeräte da immer noch keine gute Unterstützung von Seiten der Hardware bieten.
Also so, dass eben Paravirtualisierung noch benötigt wird.
Was mich ja total interessieren würde, ist, also du hast ja gerade gesagt, Xen setzt jetzt auch diese Virtualisierungsinstruktion ein.
Ist oder hat diese Art von Paravirtualisierung heute noch wirklich den Sinn?
Oder dadurch, dass wir jetzt diese Virtualisierungsinstruktion haben und alles Mögliche irgendwie partitionieren können in unserem Prozessor,
macht es eigentlich mehr Sinn, einfach die echte Maschine durchzureichen und das ist schneller und besser und sowieso viel geiler als eine Paravirtualisierung aufzubauen?
Also angenommen, ich würde, jetzt gehen wir mal zu dem Glaub-Gereich, aber da kommt jetzt ganz zu dem Zufall.
Ja, genau, also.
Also angenommen, ich würde, ich kann natürlich auch so eine Festplatte oder so eine SSD eins zu eins komplett in meine Virtualie-Maschine durchreichen.
Das ist problemlos möglich und dann kann das Ding ohne irgendwelchen Overhead, deshalb aber auf diese Platte desiziert zugreifen, aber kein anderer.
Jetzt will ich aber diesen Gas nicht unbedingt die vollen 10 Terapeit von dem Ding zur Verfügung stellen, sondern nur die Hälfte oder nur ein Quotum oder was auch immer.
Dann bin ich darauf angewiesen, dass entweder dieses Gerät mir Partitionierungsmöglichkeiten zur Verfügung stellt.
Es gibt verschiedene Möglichkeiten, wie ich echte physikalische Geräte partitionieren kann, zum Beispiel bei PCI.
Es gibt ein System "Root I/O Virtualization" SRIOV, hab ich vielleicht schon mal gehört.
Da kann ich einer physikalischen Netzwerkkarte sagen, hey, bau mir doch mal bitte 10 PCI-Funktionen raus und dann habe ich 10 Virtual-Netzwerkkarten, die gehen aber alle auf die selbe Hardware runter.
Die steuern sich aber alle gleich an, diese 10 Karten. Und eine dieser Karten könnte ich jetzt dann in so eine Partition reingeben.
Dann bräuchte ich beispielsweise keine Para-Vitualisierung mehr für diese Netzwerkkarte.
Genauso könnte ich mir auch so eine Festplatte vorstellen, die ich partitionieren kann in Hardware und braucht dann keinen Overhead mehr, deshalb aber weiß es.
Das ist aber nicht in allen Fällen gegeben und ist auch nicht so schön dynamisch rekonfigurierbar, wie man das halt in so Glaubunggebungen hätte.
Deswegen greift man da trotzdem noch sehr oft auf oder eigentlich ständig, fast auch schließlich, auf die Para-Vitualisierungs-Mechanismen zurück. Das ist eigentlich schon das, was man im Feindfeld draußen sehr sehr ruhig findet.
Aber es wäre schon interessant zu sehen, das mal auch in einer Festplatte der SSD zu sehen, solche Möglichkeiten, wie es schon bei Netzwerkkarten gibt.
Also bei Festplatten macht man üblicherweise irgendwie LVM oder sowas. Das ist natürlich halt Volume-Manager, aber das ist halt alles software- und Para-Vitualisierung, oder?
Das ist auch eine software-Sache, genau.
Das kann leider nicht die Festplatte schon selber, sodass man es einfach durchrechnen könnte.
Ja, da haben wir doch eine Mark-Glücke entdeckt.
PCI, PCI, der Busstandard, der hat halt genau dieses Problem adressiert mit diesem System, Blut-I/O-Vitualisation, wo man so ein physisches Gerät auf mehrere Geräte aufteilen kann. Am Ende des Tages ist es um SATA auch nur ein Bussystem. SATA hängt an einem SATA-Controller, der hat PCI, PCI hat die Funktionalität. Man müsste schon genau schauen, wie man das Ganze dann irgendwie auf HV abbilden kann, aber technisch möglich, ja, ist es natürlich.
Alles klar.
Okay, damit wären wir wieder am Ende der Sendung. Und wie immer in der Sendung die Frage, was haben wir eigentlich gelernt?
Also ich habe gelernt, dass Virtualisierung in Echtzeit-Systemen erst dadurch möglich wird, wenn der Hypervisor so wenig wie möglich macht und so das System und seine Echtzeitseigenschaften nicht stört.
Also ich habe viel über die Geschichte von Virtualisierung, gerade über die Geschichte von Send gelernt und wie man es damals, so vor 20 Jahren, ohne spezielle Virtualisierung so weitermachen geschafft hat, dennoch HV-Vitualisierung zu ermöglichen und wie uns genau die Konzerte, die damals geschaffen worden sind, also beispielsweise das Konzert der Power-Vitualisierung, wie uns das noch bei moderner Virtualisierung heute noch nutzen kann oder nicht.
Und wenn ihr mal in die Paper reinschauen wollt, dann sind die entsprechend in den Schaumschluss verlinkt und dann bleibt mir eigentlich noch zu sagen, Ralf Ramsauer, vielen Dank.
Gerne, ich sag auch vielen Dank, habe mich gefreut.
Und Florian Rommel, vielen Dank auch dir. Vielen Dank, Stefan. Und euch vielen Dank für die Aufmerksamkeit.
[Musik]
SWR 2020