Prozesse in klassischen Betriebssystemumgebungen

Kommentieren Drucken
Teil 2 von 19 aus der Serie "Virtualisierungstechniken"

Die Hersteller von Virtualisierungssoftware zeigen immer so kleine Bildchen mit kleinen virtuellen Maschinchen. Das ist mehr als ärgerlich, denn erstens ist es fundamentaler Unsinn und, als sei das noch nicht genug, verdeckt es problematische Stellen. Aus didaktischer Perspektive ist es viel günstiger, sich dem Thema über eine Prozesssicht zu nähern. Daran kann man alles erklären. Betriebssysteme haben sich über mehrere Jahrzehnte entwickelt. Dadurch haben auch an und für sich gleiche oder ähnliche Dinge unterschiedliche Namen bekommen. Für diese Erklärung verwende ich eine abstrakte Nomenklatur. Sie werden verstehen, was ich meine, aber es kann in Ihrem konkreten Betriebssystem anders heißen.

Wir beginnen direkt mit der Bauweise eines konventionellen Betriebssystems, siehe Bild 1:

Wir sehen die Abstraktion grundsätzlicher Funktionen eines Betriebssystems, die bei allen realen Betriebssystemen von Windows über Unix bis hin zu Großrechner-Betriebssystemen ähnlich heißen und vergleichbar funktionieren. Unten haben wir die Hardware, die hier durch einen Prozessor, einen seitenorientierten Speicher, einen Massenspeicher und einen Host Bus Adapter für die Kommunikation vertreten ist. Diese zwei Sorten Speicher haben sich historisch entwickelt. In jedem Prozessor gibt es Register und elektronisch implementieren RAM. Diese Speichertypen sind zwar von ihrer Zahl her begrenzt, aber dafür umso schneller. In jedem Rechner gibt es darüber hinaus Massenspeichersysteme wie Festplatten. Diese sind historisch auf magnetischer Basis realisiert und dementsprechend langsamer, aber wesentlich billiger.

Diese beiden Speichertypen werden durch die so genannte Seitenspeicherarchitektur verbunden. Eine laufende Anwendung benötigt zu einem Zeitpunkt nur einen relativ geringen Teil ihres Gesamt-Speichervolumens. Diese kleine Teil wird sinnvollerweise in RAM bereitgestellt, die Adressierung kann durch die Register geschehen. Der Rest des Speicherbereiches kann auf der Festplatte bleiben. Während die Anwendung läuft, ändert sich der kleine Teil, auf den sie zugreifen möchte, laufend. Also müssen Daten von der Festplatte in den RAM nachgeladen werden. Es hat sich in diesem Zusammenhang eingebürgert, von Seiten zu sprechen, die einen zusammenhängenden kleinen Speicherbereich repräsentieren. Diese Seiten müssen also laufend nach den Anforderungen der Anwendung ersetzt werden. Je reibungsloser das funktioniert, desto schneller kann die Anwendung laufen, bei einer ungeschickten Strategie müsste sie ja immer warten. Also ist die Qualität der sog. Seitenersetzungsstrategie (engl.: Paging) von zentraler Bedeutung. Natürlich sind auch die Größe des RAMs und die Qualität des Massenspeichermediums entscheidend. Das kennt jeder PC-Besitzer: alles läuft über einen Zeitraum sehr gut. Dann kauft man eine Anwendung mit besonderem Speicherhunger. Mindestens der RAM muss dann aufgerüstet werden, oft hilft aber nur noch die Anschaffung eines neuen PCs.

Sie können jetzt sagen, dass das doch eigentlich jeder weiß. Aber ich habe leider gelernt, dass das nicht so ist. Grade der Speicherzugriff ist aber auch ein wesentlicher Stolperstein für die Virtualisierung, so dass das klar sein muss.

Viele (nicht alle) Betriebssysteme verwenden dann ein Hardware Abstraction Layer HAL. Dieses abstrahiert den Befehlssatz und man kann das Betriebssystem auf einem weiten Bereich von Systemen laufen lassen. Mit dem HAL muss man aber auch wieder aufpassen, es kann sein, dass ein Prozessor bestimmte wünschenswerte spezielle Eigenschaften hat. Diese könnten vom HAL verdeckt werden.

Eine Anwendung ist grundsätzlich erst einmal ein Stück ausführbarer Code, der irgendwo auf dem Massenspeicher steht. So würde sie nicht laufen können. In dem Moment, wo man eine Anwendung startet, wird für sie eine sog. Laufzeitumgebung geschaffen. Diese besteht aus einem oder mehreren anwendungsorientierten Prozessen, die den ausführfähigen Code repräsentieren, einer Zuordnung von Seiten, die für den Start der Anwendung sinnvoll sind und einer Startbelegung für die Register. Das alles läuft immer noch nicht, ist aber eine sehr wesentliche Vorbereitung.

Normalerweise wird ein einzelner Prozessor zu einem multitaskingfähigen System organisiert. Das bedeutet, dass er scheinbar mehrere Dinge gleichzeitig verrichten kann. In Wahrheit kann er zu einer Zeit aber nur eine Aufgabe erledigen, was sich erst bei den Multi-Core-Systemen ändert, dazu gleich mehr.

Hier kommt der Scheduler ins Spiel. Der Scheduler erzeugt auf einem Prozessor ein System mit mehreren sog. Elementarprozessen, die abwechselnd vom Prozessor bearbeitet werden können. Diese Anzahl ist endlich, normalerweise einige Dutzend. Es kann immer nur einen sog. aktuellen Elementarprozess geben, der dann auch tatsächlich läuft. Der Scheduler hat eine sog. Scheduling-Strategie, davon gibt es Unmengen, für die Zwecke dieser Darstellung reicht Round Robin: jeder der Elementarprozesse kommt für eine kurze Zeit an die Reihe, entweder bis seine Zeit abgelaufen ist oder ein Unterbrechungsereignis passiert. Ein typisches Unterbrechungsereignis wäre eine Ein- oder Ausgabe. Kommt der aktuell laufende Elementarprozess an eine Stelle, an der er z. B. eine Eingabe wartet, wird er sofort stillgelegt und der nächste Elementarprozess ist dran. Man kann ihn in dieser Umgebung nicht solange warten lassen, bis die Eingabe tatsächlich erfolgt ist, das dauert zu lange. Er kommt erst dann wieder an die Reihe, wenn er nach Round Robin dran ist. Ist für die Eingabe dann z. B. immer noch kein Wert da, wird er sofort wieder schlafen gelegt.

In jedem Betriebssystem gibt es auf dieser Ebene zwei verschiedene Arten von Elementarprozessen, die einander abwechseln müssen:

  • anwendungsunterstützende Elementarprozesse und
  • systemunterstützende Elementarprozesse.

Jede Ein- und Ausgabe auf oder von einem externen Element (Speicher, Netzwerk, …) benötigt Rechenleistung. Diese wird von den systemunterstützenden Elementarprozessen erbracht. Der Scheduler oder auch das Paging-Verfahren sowie viele andere, hier nicht genannte Funktionen zur Systemsteuerung und Verwaltung sind systemunterstützende Elementarprozesse.

Sie benötigen einen nennenswerten Anteil an der Prozessorleistung. Nur der „Rest“ steht dann tatsächlich für die Anwendungen zur Verfügung.

Möchte man eine Anwendung laufen lassen, so muss also ihre Laufzeitumgebung an einen oder mehrere anwendungsunterstützende Elementarprozesse gebunden werden. Das macht der sog. Dispatcher. Wie wir wissen, arbeiten diese immer nur sehr kurz. Der Sinn der Laufzeitumgebung ist es also vor allem, den jeweiligen Zwischenstatus der Anwendung bei einem Prozesswechsel festzuhalten. Dies geschieht normalerweise in den assoziierten Registern. Das sind auch bei einem normalen System virtuelle Register, denn in dem Moment , wo der aktuelle anwendungsunterstützende Elementarprozess aufhören muss, übernehmen sie den Zustand der tatsächlichen Register im Prozessor. Kann der anwendungsunterstützende Prozess dann weitermachen, werden die Registerinhalte aus der Laufzeitumgebung auf den physikalischen Prozessor übertragen.

Also, fassen wir zusammen:

  • Eine Anwendung muss in eine Laufzeitumgebung überführt werden.
  • Die Laufzeitumgebung muss an einen oder mehrere anwendungsunterstützende Elementarprozesse gebunden werden.
  • Der anwendungsunterstützende Elementarprozess kann dann immer nur kurz laufen, weil der Scheduler viele Elementarprozesse verwalten muss.

Dadurch gibt es mindestens folgende Sorten von Prozessen in einer Betriebssystemumgebung:

  • Anwendungsprozesse, die eine Anwendung im Rahmen einer Laufzeitumgebung unterstützen,
  • anwendungsunterstützende Elementarprozesse, die die Anwendungsprozesse unterstützen,
  • systemunterstützende Elementarprozesse, die alle Systemfunktionen unterstützen, die nicht Anwendungsprozesse sind.

Dieses Prozessmodell ist didaktisch vereinfacht, weil es innerhalb der oben genannten Sorten von Prozessen noch jeweils Abstufungen gibt, die aber nicht weiter zum Verständnis beitragen.

Ganz wesentlich in diesem Zusammenhang ist dann noch die elementare Prozesskommunikation, weil sich die Elementarprozesse durchaus etwas mitzuteilen haben können. Nehmen wir ein Beispiel: Eingabe. Ein anwendungsunterstützender Elementarprozess ist an einer Stelle angekommen, wo er gerne eine Eingabe hätte. Wir wissen, dass er darauf nicht warten darf. Außerdem wäre das auch zwecklos, der bei einer Eingabe interessierende Wert muss von einem systemunterstützenden Elementarprozess geholt werden, denn der anwendungsunterstützende Elementarprozess hat keinerlei Zugriff auf die physikalischen Medien. Also benötigen wir eine Kommunikation zwischen diesen beiden Prozessen. Bevor er stillgelegt wird, formuliert der anwendungsunterstützende Elementarprozess einen Auftrag an den nächsten I/O-Prozess, z. B. hole mir bitte den aktuellen Wert für a aus dem Speicher und lege ihn für mich an Stelle x bereit. Das wird er in der Praxis kürzer formulieren, da gibt es titanische Unterschiede in der praktischen Implementierung. Der I/O-Prozess sieht, nachdem man ihn geweckt hat, nach, ob für ihn ein I/O-Auftrag da ist und führt ihn aus. Er legt dann den aktuellen Wert für a nach x. Wird der beauftragende anwendungsunterstützende Elementarprozess dann aufgeweckt, holt er sich als erstes den Wert ab und kann dann sofort damit weiterarbeiten.

Dieses Beispiel zeigt eine ganz elementare Interprozesskommunikation IPC. Das ist natürlich im Laufe der Jahrzehnte wesentlich erweitert worden. Die wichtigste Erweiterung ist sicherlich, dass nicht nur anwendungsunterstützende Elementarprozesse und systemunterstützende Elementarprozesse kommunizieren können, sondern auch diese Prozessarten untereinander.

Bleibt jetzt nur noch die Frage zu klären, inwieweit die Einführung mehrerer Prozessoren oder einer Multi-Core-Architektur dem Betriebssystem hilft. Bis vor wenigen Jahren hatte ein Prozessor nur einen einzigen Rechenkern. Die Multi-Core Architektur besagt, dass ein physikalischer Prozessorchip mehrere vollständige und an und für sich autonome Rechenkerne besitzt, Bild 2:

Wie bereits dargestellt, gibt es Elementarprozesse, die von Scheduler gesteuert werden. Ist nur ein Prozessor vorhanden, muss der Scheduler alle Elementarprozesse auf diesen einen Prozessor abbilden, es kann also zu einer Zeit wirklich nur ein Prozess laufen. Die Verfügbarkeit mehrerer Prozessoren führt dazu, dass der Scheduler nunmehr mehr Möglichkeiten hat. Er kann jetzt zwei, vier, acht oder mehr Prozesse gleichzeitig laufen lassen, je nach Anzahl der Prozessoren oder Cores. Das hört sich jetzt einfach an, wird aber in der Praxis dadurch kompliziert, dass nicht alle Kombinationen parallel laufender Elementarprozesse wirklich sinnvoll sind.

Bei nur einem Prozessor konnten sie sich nicht gegenseitig stören, weil ja nur immer ein Elementarprozess laufen konnte. Wir erinnern uns an den Lebenszyklus eines Elementarprozesses. Normalerweise ist er nicht aktiv, sondern wartet darauf, ausgeführt zu werden. Dieser Zustand wird durch die Inhalte eines bestimmten Speicherbereiches, der diesem und nur diesem Prozessor zugeordnet ist, beschrieben. Im Wartezustand des Elementarprozesses beschreibt dieser Speicherbereich den Zustand VOR der Ausführung des Elementarprozesses. Darum heißt er Vorbereich. Wird der Elementarprozess ausgeführt, wird sich der Inhalt des Speicherbereiches ändern. Nach Ablauf seiner möglichen Rechenzeit oder beim Eintritt eines Unterbrechungsereignisses wird der Zustand des Speicherbereiches sozusagen wieder eingefroren. Dies ist dann der sog. Nachbereich. Das Bearbeiten aller Elementarprozesse nach einem Scheduling-Verfahren durch den Scheduler nennt man auch Scheduling-Zyklus. Der Scheduling-Zyklus beschreibt letztlich auch die Zeit, die notwendig ist, bis alle Prozesse einmal dran waren. Alle in einem Scheduling-Zyklus erzeugten Nachbereiche sind die Vorbereiche des nächsten Zyklus.

Entwirft man nun ein System aus mehreren parallelen Prozessen für eine Multi-Prozessor oder Multi-Core-Umgebung, muss man darauf achten, dass sich die Prozesse nicht gegenseitig stören. Dafür gibt es eine einfache Grundregel: zwei Prozesse können sich genau dann nicht stören und parallel ausgeführt werden, wenn ihre Nachbereiche disjunkt sind. Es gibt dazu eine ganze umfangreiche Theorie mit noch viel mehr Regeln, aber das führt jetzt hier nicht weiter.

Pauschal kann man sagen, dass sich z. B. anwendungsunterstützende Elementarprozesse und systemunterstützende Elementarprozesse gegenseitig selten stören. Das benutzen Sie jeden Tag: Sie schreiben z. B. einen Text mit Word und lassen den Explorer für das Internet im Hintergrund laufen. Oder Sie drucken etwas aus und sehen sich gleichzeitig ein Video an, um die Wartezeit zu verkürzen. Eine Dual-Core-Architektur kann man immer gut brauchen. Genauso gibt es viele Prozesse, die im Hintergrund laufen, z. B. zum Aufbau des Bildschirms, zur Optimierung des Speichers oder für das Sicherheitssystem. Auch diese haben einerseits untereinander und andererseits mit den Anwendungsprozessen wenig zu tun. Also auch über die Auslastung einer Quad-Core-Architektur braucht man sich keine Sorgen zu machen, auch das gibt es ja jetzt schon in Notebooks.

Natürlich gibt es auch Prozesse, die unmittelbar voneinander abhängen und nacheinander ausgeführt werden müssen. Ich hatte ja eben die Ein- und Ausgabe vermöge entsprechender Elementarprozesse im Zusammenhang mit der IPC besprochen. Das ist genau so ein Beispiel: ein Prozess, der auf eine Ein- oder Ausgabe wartet und deshalb im aktuellen Scheduler-Zyklus schlafen gelegt wird, kann auch im nächsten Zyklus nicht weitermachen, wenn nicht mittlerweile der I/O-Elementarprozess das von ihm gewünschte Ergebnis geholt hat.

Man kann auch Anwendungen entwerfen, die sich eine mögliche Parallelität von Prozessen zunutze machen, um eine spezielle, umfangreiche Aufgabe zu erledigen. Das ist aber hier generell nicht das Thema und gehört auch auf eine andere Ebene.

Klar wird aber, dass eine Multi-Core-Architektur sehr nützlich sein kann, wenn der Scheduler entsprechend angepasst arbeitet. Ich hatte eingangs erwähnt, dass ein Scheduler nur eine begrenzte Anzahl von Elementarprozesse unterstützt. Sind mehr Prozessoren oder Cores vorhanden, kann man diese Zahl steigern, aber nicht einfach durch Multiplikation mit der Anzahl der Cores, weil die Prozesswechsel auch immer Zeit kosten.

Natürlich gibt es über das Ganze noch viel mehr zu sagen, für die Erklärung der Virtualisierung reicht das aber erst mal.

« Teil 1: Einführung und ÜberblickTeil 3: Vom Urvater „IBM VM“ zu moderner Virtualisierungssoftware »


zugeordnete Kategorien: Virtualisierung
zugeordnete Tags:

Sie fanden diesen Beitrag interessant? Sie können



Anmerkungen, Fragen, Kommentare, Lob und Kritik:

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

.