So, schönen guten Morgen.
Ich begrüße Sie zur Fortsetzung dieser Grundlagen der Informatik 2.
Heute am Mittwochmorgen.
Und wir gucken kurz zurück, was wir letztes Mal gemacht haben.
Da habe ich das Kapitel, oder wir hatten das schon letztes Mal
beendet, das Kapitel über Rechnerarchitektur, Speicher und so weiter.
Wir hatten uns dann angeschaut, dieses Kapitel hier über Architektur
von Rechensystemen.
Und hatten uns nicht über Programmierung, meine ich natürlich.
Teil 2 ist Architektur von Rechensystemen, dieses Kapitel ist
Programmierung.
Ich hatte Ihnen ganz kurz gesagt nochmal, dass es verschiedene
Sprachebenen gibt.
Wir hatten uns angeschaut, dass es Übersetzer und Interpreter gibt,
mit denen wir uns heute noch genauer beschäftigen werden.
Wir hatten dann kurz gesehen, diese verschiedenen Ebenen der
Programmierung, von problemorientiert bis runter zur Hardware.
Also Assembler-Sprache, Maschinen-Sprache, Mikroprogrammierung.
Ich hatte Ihnen dazu ganz bisschen was erzählt, hatte Ihnen
Mikroprogrammierung vorgestellt.
Dass dort eben darum geht, die Steuersignale, also die Steuerung der
Abläufe innerhalb der CPU zu steuern, damit dieser
Maschinenbefehlszyklus tatsächlich sinnvoll ausgeführt werden kann.
Dazu hatten wir uns die Komponenten hier der Mikroprogramm-Einheit
angeschaut, die eben die feinen, elementaren Operationen dann steuert,
entweder vertikal oder horizontal.
Die beiden Vor- und Nachteile hatte ich Ihnen erläutert.
Also horizontal, schneller Ausführung, aber mehr Aufwand, weil die
Befehlsregister größer werden, Programme werden größer.
Also die Breite der Operation ist einfach erhöht.
Dann kamen wir zu den Maschinensprachen.
Da hatte ich Ihnen Beispiele gegeben von den verschiedenen Formaten, 1
-Adress bis 3-Adress.
Wir hatten dann kurz gesehen, welche Arten von Befehlen es gibt.
Ich hatte Ihnen eine einfache Maschinensprache vorgestellt, die 16-Bit
-Befehle hat.
Das heißt, hier 4-Bit für die Operationen, 12-Bit für den Operanten.
Wir hatten dann ein kurzes Beispiel angeschaut.
Das war das Beispiel mit der Fakultätsfunktion.
Sie sehen, wie man eben dort ganz weit unten im Rechner programmieren
kann.
Wir hatten dann ein bisschen unterhalten, welche Arten von Operationen
es gibt.
Größere Befehlssätze, Vorteile, wenn man das also etwas reduziert,
kann man die Einheiten etwas schneller machen.
Und dann hat man Vorteile bei der Ausführung von Programmen.
Das ist halt immer so ein Trade-off zwischen Umfang des Befehlssatzes
und Komplexität der Ausführung von Einzelbefehlen.
Aber Sie wissen, dass das Wesentliche natürlich ist, dass wir bei der
Ausführung der Befehle im Rechner heutzutage in der Regel Pipelining
-Verfahren haben, sodass man zwar lange Zeit hat, um einzelne
Operationen auszuführen, aber dadurch das Pipelining trotzdem einen
hohen Durchsatz an Befehlen erzielen kann, sofern die Pipelines nicht
leer laufen.
Das wissen Sie aus dem vorigen Kapitel.
Dann hatte ich Ihnen die Adressierungsarten vorgestellt von
unmittelbarer Adressierung, wo der Wert im Befehl direkt schon der
Operant ist, über die direkte Adressierung, wo man eben erstmal in
Speicherreihen muss einen Operanten holen, oder indirekt, wo man
zweimal auf den Speicher zugreifen muss, nämlich erstmal die Adresse
holen und dann zu dem eigentlichen Operanten kommt.
Das ging der Reihe nach hier durch.
Das war also die absolute und die direkte Adressierung.
Indirekte Adressierung, dass man eben nachschaut im Speicher, wo ist
die Adresse.
Oder dann die indizierte und relative Adressierung, wo man auf einen
Wert, einen Basiswert erst noch den tatsächlichen Wert des Operanten
drauf addieren muss, oder den Wert der Adresse, die man im Befehl hat,
drauf addieren muss, sodass man dann erst die Adresse eigentlich im
Speicher hat.
Das heißt, man muss erst noch eine arithmetische Operation
durchführen.
Das verzögert natürlich alles die Ausführung eines Befehls.
Und dann noch die implizite Adressierung.
Wir hatten uns dann konkreter nochmal Assembler-Sprachen angeschaut,
die dann eben nicht nur die üblichen Maschinenbefehle haben, sondern
auch noch Pseudo-Befehle, organisatorische administrative Befehle, und
die vor allen Dingen etwas besser lesbar aussehen.
Hier am Beispiel dargestellt, gleiche Programm wie vorher in Maschinen
-Sprache, jetzt als Assembler-Programm, wo man dann noch einige Pseudo
-Befehle hat, um erstmal überhaupt Variablen einzuführen, um nicht
immer Absolut-Adressen angeben zu müssen, sondern die auch symbolisch
notieren zu können.
Und dann die Marken, auch eventuell Kommentare, und damit dann also
ein lesbares Programm bekommt.
Nächste Stufe waren die problemorientierten Programmiersprachen, und
dort hatten wir auch schon kurz gesehen, dass es dort natürlich die
imperativen Programmiersprachen gibt, dann die funktionalen oder
applikativen Programmiersprachen, bei denen man sehr viel näher an den
Problemprogramme formulieren kann.
Dann die prädikativen Programmiersprachen, wo man Programme als Folge
von Prädikaten aufschreibt.
Hier als sehr kleines Beispiel die Berechnung der Fakultätsfunktion,
nochmal jetzt als Prolog-Programm formuliert, obwohl es eigentlich
ungewöhnlich ist, eine solche einfache Funktionsberechnung in Prolog
zu formulieren.
Man kann es aber machen.
Normalerweise setzt man das eben ein, um logische Formeln darzustellen
und eigentlich eher in der künstlichen Intelligenz, in der
Wissensverarbeitung Abläufe, logische Abhängigkeiten zu formulieren.
Dann kamen die objektorientierten Programmiersprachen, die Sie über
Java ja schon kennen.
Und schließlich kamen wir dann als letzte Folie zu der
Sprachübersetzung, wo es eben als erste Einheit zur Sprachübersetzung
die Notwendigkeit gibt, vom Assembler auf die Maschinensprache zu
übersetzen.
Und da kommen wir jetzt auf die erste Folie, die neu ist.
Das hatte ich schon letztes Mal gesagt.
Wenn wir ein Assembler-Programm übersetzen wollen, dann kann es eben
sein, dass wir dabei so ein Programm durchlaufen.
Dann kann es eben sein, dass hier die ersten Spalten stehen, hier
immer die Marken, dass wir hier einen Befehl haben, wo am Ende steht,
wir sollen zu einer Marke laufen und die Marke ist noch gar nicht
bekannt.
Bei Vorwärtssprüngen kann es eben sein, dass man dort erstmal
durchlaufen muss.
Das heißt, wir müssen einen sogenannten Zweipass, zweifachen Durchlauf
machen, Zweipass-Assemblierer müssen zweimal durchlaufen, damit wir
erst beim ersten Mal erstmal alle Symbole kennenlernen.
Und danach können wir dann, wenn wir die Symbole kennen, entsprechend
die Vorwärtsreferenzen direkt in Adressen im Maschinenprogramm
übersetzen.
Das heißt, zunächst mal baut man eine Symboltabelle auf, in der man
für alle formalen Symbole, die dort eingeführt sind, dann den
jeweiligen Wert einträgt und vielleicht noch irgendwelche weiteren
Dinge, die wichtig sein können für die Übersetzung.
Also ob das eben relative oder absolute Adressen sind, das ist ja
wichtig nachher für die konkrete Adressierung, wenn das Programm
ausgeführt werden soll.
Und wenn man dann diese Tabelle hat, dann kann man entsprechend den
Assembler halt das zweite Mal durchlaufen lassen.
Also beim ersten Mal gibt es diese Symboltabelle und danach im zweiten
Pass, dann kann man das Programm nochmal durchlaufen, kann das
Maschinencode übersetzen.
Dabei kann es sein, dass man irgendwelche weiteren Dinge erkennt, die
vielleicht notiert werden sollen.
Dann kommt noch der Protokolldurchlauf.
Man will dem Anwender Informationen zurückgeben, was dabei passiert
ist.
Also der erste Durchlauf, wie gesagt, Aufbau der Symboltabelle, zweite
Durchlauf, Einsetzen der Werte und Generierung des Maschinencodes und
der Protokollpass, dann Erstellung des Protokolls.
Und da können dann Fehlermeldungen drinstehen.
Also dann bei den Fehlermeldungen, Zeilennummer, Adresswert,
Assemblerbefehl und so weiter.
Da habe ich alle Informationen darüber, wie dieses Maschinenprogramm
eigentlich dann, oder wie das übersetzt wird.
Ich weiß, ob das Ding jetzt in Ordnung ist oder nicht.
Also ein Protokoll ist halt wichtig und das sind die Dinge, die bei
einem solchen Assembler halt erstellt werden müssen.
Rückmeldung für den Anwender, damit er weiß, inwieweit er das Programm
verändern muss.
Und damit sind wir schon beim Assemblierer durch.
Die nächste Stufe sind dann die höheren Sprachen, die wir übersetzen
müssen, jetzt runter in Assembler- oder Maschinensprache direkt.
Und da gibt es halt entweder den Interpreter, der ein Programm direkt
ausführt.
Das heißt, wenn wir hier ein Programm haben, irgendeine Anweisung,
wird die direkt ausgeführt in unserem Rechner.
Und so laufen wir da der Reihe nach durch, durch unser Programm, von
Befehl zu Befehl.
Und es wird jeweils unmittelbar ausgeführt.
Das heißt, es werden die Variablen in dem Befehl mit Werten versehen.
Entsprechend werden die Befehle ausgeführt.
Und das ist klar, dass das etwas ist, was bei der Programmentwicklung
einem sehr gut hilft.
Aber wenn man so etwas mehrfach machen will, muss man eben jedes Mal
wieder die einzelnen Befehle interpretieren.
Das dauert sehr lange.
Und das ist für die effiziente Programmausführung natürlich nicht
sinnvoll.
Das macht nur Sinn bei der Programmentwicklung.
Und danach sollte man besser kompilieren.
Also für Fehlererkennung ist das alles sehr gut.
Aber es hat keinen Sinn für einen Dauerbetrieb, ein Programm immer nur
zu interpretieren.
Deswegen ist es am sinnvollsten, dass man zunächst mal interpretiert
und dann übersetzt.
Beim Testen kann man das machen mit der Interpretierung.
Und dann kann man später das Programm übersetzen.
Jetzt müssen wir uns anschauen, was ein Übersetzer macht.
Der Kompilierer, der muss halt jetzt das Programm direkt übersetzen
von einer höheren Programmiersprache in ein Programm einer niedrigeren
Programmiersprache.
Und dann will man natürlich auch Hinweise bekommen, ob das Ganze in
Ordnung ist oder nicht.
Der Nachteil ist natürlich, dass ich nicht so ohne weiteres die
Rückmeldungen bekomme bezüglich der Dinge, die ich im Programm gemacht
habe.
Wenn ich hier einen Befehl habe in meinem Quellprogramm, dann ist
nicht unbedingt klar, an welcher Stelle, zu welchem Befehl genau das
in meinem Quellprogramm gehört.
Also die Rückwärts-Operation von dem Zielcode hier zurück zu dem
Quellcode ist etwas schwierig.
Das heißt, das Debuggen ist dann nicht ganz so einfach.
Dafür gibt es auch gute Werkzeuge, um das zu unterstützen.
Aber es ist ein etwas aufwändigerer Vorgang, um immer noch zu wissen,
wenn ich bei der Testphase mit Übersetzer arbeite oder bei der
Entwicklung, wirklich genau zu sehen, was passiert eigentlich genau
bei dem Durchlauf durch das Programm.
Hier in dem Quellprogramm möchte ich ja etwas verändern, möchte ich
korrigieren.
Ausgeführt wird das Maschinenprogramm oder Semlerprogramm und da muss
man halt dann entsprechend sehen, dass man geeignete Informationen
gibt über das, was dort gerade ausgeführt wird und von welchen Teilen
meines Quellprogramms das beeinflusst wurde.
Das Übersetzungsprogramm ist natürlich auch rechnerspezifisch.
Einzelne Rechner, einzelne Betriebssysteme haben unterschiedliche oder
einzelne Compiler können auf unterschiedliche Programme abbilden.
Das heißt, sie haben unterschiedliche Arten der Ausführung.
Sie wissen das, wenn Sie zum Beispiel ein Java-Programm ausführen, das
mehrfädig programmiert ist, kann es sein, dass Sie auf dem Windows
-Rechner eine ganz andere Ausführung bekommen, als auf dem Unix
-Rechner oder Linux-Rechner, weil dort das Scheduling der Fäden anders
ist.
Das sind alles so Dinge, die eine Rolle spielen können.
Da ist natürlich wichtig, dass im Prinzip die Semantik gleich ist,
aber man muss aufpassen, was man da macht.
Jetzt gibt es beim Kompilieren insgesamt vier Schritte prinzipiell.
Zunächst mal macht man eine lexikalische Analyse, dann muss man die
Sündachs überprüfen, dann muss man entsprechend den Code generieren
und schließlich wird er noch optimiert.
Und diese einzelnen Schritte gehen wir ganz kurz durch.
Wir haben also erstmal die lexikalische Analyse.
Was macht diese lexikalische Analyse?
Ich habe im Programm eine ganze Reihe von Dingen drin, die
normalerweise für die Übersetzung nicht unbedingt genau so übernommen
werden müssen.
Ich habe benutzerdefinierte Namen für Variablen und so weiter.
Ich habe irgendwelche Grundsymbole, also Terminalzeichen.
Wir haben Terminalzeichen, die festgelegt sind.
Wir haben einige Zeichen, die sind variabel von uns festlegbar.
Also Terminalzeichen, feste syntaktische Einheiten, Begin, If,
Doppelpunktgleich und so weiter.
Das sind einfach festgelegte Begriffe, die in einem Programm vorkommen
müssen.
Und dann gibt es natürlich die Variablen, die man selber definiert.
Es gibt benutzerdefinierte Namen, es gibt Zahlen, es gibt Kommentare
da drin.
Kommentar brauche ich für die Übersetzung nicht.
Die brauche ich nur für das Quellprogramm.
Und deswegen ist hier ein kleines Beispiel angegeben, was da im
Prinzip passiert, wenn das hier unser Ausgangsprogramm ist.
Kleine If-Anweisung oder ein Statement.
Wird halt ein Test gemacht und dann steht hier eine Anweisung und am
Ende noch ein kleiner Kommentar.
Lexikalische Analyse, die wirft einfach alles das, was nicht unbedingt
erforderlich ist, raus.
Also er setzt hier diese Variable durch einfach nur Name, die Zahl
durch einfach Realzahl und dann diese Variablen, die entsprechen
einfach nur auf diese syntaktische Struktur Name gleich Name plus
Name.
Welche das sind, ist jetzt im Augenblick egal.
Das braucht man zwar später noch für die Überprüfung der Frage, ob
Variablen auch tatsächlich so benutzt wurden.
Das sind aber dann semantische Überprüfungen.
Zunächst mal wird das so überführt in einfach nur diese Struktur und
anschließend kann ich dann überprüfen, ob mein Programm tatsächlich
richtig ist, ob das syntaktisch richtig ist, ob es den Regeln der
entsprechenden Grammatik entspricht.
Da muss ich dann entsprechend, wie Sie wissen, eine solche Anweisung
überführen.
In den Ableitungsbaum muss feststellen, ob diese Anweisung so
tatsächlich erzeugt werden kann.
Hier der Ableitungsbaum gibt eben für eine IF-Anweisung dann
entsprechend, oder aus meinem Beispiel hier, IF Name kleiner real
Zahl, then Name gleich Name, da unten steht das Plus, Name, und dann
else Teil haben wir nicht, dann haben wir hier ein Entrennen.
Also das wäre eine Struktur, die wir im Prinzip erzeugen durch
Reduktion unserer Anweisung auf den Ableitungsbaum.
Das haben wir uns alles angeschaut in den Teilen über Formale
Sprachen, wie das funktioniert.
Das kennen Sie schon.
Also das ist die syntaktische Überprüfung, da brauchen wir dieses
Parsing des Programms.
Dann haben wir einen Ableitungsbaum für das ganze Programm, sofern das
Programm korrekt formuliert worden ist.
Und dann muss ich diesen Ableitungsbaum hernehmen, muss noch ein
bisschen semantische Analyse machen, sehen, ob die Namen, die benutzt
werden, auch alle deklariert worden sind.
Das sind die ganzen Abhängigkeiten, die da im Programm noch vorkommen.
Ich muss sehen, ob die Typdeklaration, Typverwendung alles richtig
ist.
Das muss alles überprüft werden, damit das auch vernünftig
funktionieren kann.
Und anschließend kann ich den Code generieren.
Der Code wird generiert anhand des Ableitungsbaumes.
Da werden also den einzelnen Nicht-Terminalsymbolen im Ableitungsbaum,
den kann man im Prinzip Prozeduren zuordnen, die dann jeweils den Code
generieren.
Und dann habe ich also aus meinem Programm, über die ermittelte
Struktur, die sich im Ableitungsbaum niederschlägt, kann ich dann den
Code generieren, das Maschinenprogramm, das das gleiche Programm,
semantisch das gleiche Programm dann darstellt und ausgeführt werden
kann.
Und wenn ich dieses Maschinenprogramm habe, dann will ich das
natürlich bestmöglich ausführen.
Nun kann es sein, dass ich manchmal meinetwegen irgendwie, habe ich
meinetwegen irgendeine Schleife, hier irgendeinen Test, dann kommen
hier ein paar Anweisungen und danach gehe ich hier meinetwegen wieder
zurück.
Wenn man feststellt, hier drin sind Befehle, die jedes Mal ausgeführt
werden, die aber eigentlich immer genau die gleiche Operation
darstellen.
Also Sie addieren auf irgendeine Variable irgendeinen festen Wert und
das könnten Sie genauso gut nach oben ziehen, dann wird das bei der
Code -Optimierung gemacht.
Das heißt, da werden Befehle rausgezogen aus einem Schleifenrumpf,
werden nach oben geschoben oder es werden unnötige, wenn also ein
Ausdruck, der gleiche Ausdruck mehrfach ausgewertet wird in Ihrem
Programm, dann wird das erkannt und kann entsprechend vereinfacht
werden, dass man den Ausdruck nur einmal auswertet und dann den Wert
später wieder verwendet.
Das sind alles so Dinge, die man in der Code-Generierung machen kann.
Wenn man weitergeht, dann macht man gleichzeitig noch eine
Parallelisierung.
Das sind parallelisierende Compiler, die entsprechend noch versuchen,
ihr Programm so umzubauen, dass es bestmöglich auf einem
Mehrkernprozessor ausgeführt werden kann.
Das ist aber schon eine sehr weitgehende Veränderung des Programms,
die nicht notwendigerweise immer passiert.
Also das macht die Code-Optimierung.
Hier steht es nochmal drin.
Die Optimierung des erzeugten Programms, Laufzeitoptimierung,
Speicherplatzoptimierung, damit Ihr Programm bestmöglich ausgeführt
wird.
Da muss man natürlich aufpassen.
Sie erinnern sich daran, dass wir über Gleitpunkt-Operationen
gesprochen haben.
Wenn eine Code-Optimierung Reihenfolgen von Operationen verändert,
kann das sein, dass dadurch auf einmal Effekte auftreten bezüglich der
Korrektheit oder der Genauigkeit von Werten, die Sie berechnen bei
Gleitpunkt -Operationen.
Deswegen muss man aufpassen, dass die Code-Optimierer tatsächlich auch
sinnvoll arbeiten.
Wenn Sie also numerische Probleme haben nach Ausführung der Code
-Optimierung, ist es vielleicht sinnvoll, dass Sie die Optimierung
ausschalten.
Also das ist nicht mehr ganz unproblematisch.
Im Regelfall bringt Ihnen das Laufzeitgewinne.
Gut, das also zur Code-Optimierung.
Natürlich, wenn Sie ein Code-optimiertes Programm haben, von dem
wieder zurückzuschließen darauf, an welcher Maschinenbefehl auf genau
welche Anweisungen in Ihrem Programm sich bezieht, das wird dann noch
schwieriger, weil eben an dem Code tatsächlich etwas verändert worden
ist.
Also diese Verlagerung von Anweisungen aus einer Schleife heraus hat
natürlich auch Auswirkungen auf das Quellprogramm, nur da wird
natürlich nichts verändert.
Das zum Übersetzer war also relativ kurz.
Und dann haben wir noch die virtuelle Maschine.
Java Virtual Machine, eine Besonderheit bei Java, Sie wissen das, da
wird zunächst mal das Programm übersetzt in Bytecode.
Das ist also eine Übersetzung.
Dann entsteht hier das Programm in Bytecode.
Das wird ausgeführt werden auf der Java Virtual Machine.
Die Java Virtual Machine ist eben eine so definierte Maschine,
Befehlssatz über den Bytecode definiert.
Und jetzt wird diese Virtual Machine ausgeführt oder interpretiert in
die Hardware hinein.
Das heißt, dann haben Sie einen Interpretierer, der dann das Bytecode
-Programm interpretiert und auf Ihrem Rechner ausführt.
Das soll gewährleisten, dass eben wirklich auf jedem Rechner die
Programme gleichartig ausgeführt werden.
Aber es führt natürlich zu einer langsameren Programmausführung, weil
Sie nicht direkt von Ihrem Java-Programm runter in das
Maschinenprogramm gehen, sondern erstmal in den Bytecode und dann erst
in das Maschinenprogramm.
Und deswegen gibt es eben auch die Just-in-Time-Compiler, heutzutage
weitgehend üblich.
Man übersetzt ein Programm zunächst mal in Bytecode und wenn dann
tatsächlich nach der Ausführung, wenn das dann tatsächlich richtig
ausgeführt werden soll, dann wird das Programm, dieser Bytecode,
direkt in Maschinensprache übersetzt, sodass Sie dann tatsächlich bei
dem Just-in-Time-Compiler am Ende ein Programm bekommen, das von Java
in die Maschinensprache dann übersetzt.
Aber es wird eben zunächst mal in den Bytecode übersetzt.
Das ist diese Besonderheit bei Java, die eine Plattformunabhängigkeit
bringen soll.
Sie können auf jedem Rechner diese Java Virtual Machine
implementieren.
Wenn Sie das gemacht haben, dann können Sie dort Ihr Java-Programm
nutzen und das wird überall gleichartig in Bytecode übersetzt.
Okay, das zu Java und damit sind wir schon bei der Programmierung
durch und kommen schon zum nächsten Kapitel, nämlich zum Kapitel über
Betriebssysteme.
Programmierung ist auch etwas, was Sie schon lange kennen durch die
Vorlesungen über Einführungsprogrammieren.
Einige von Ihnen werden auch schon die Vorlesung gemacht haben,
Programmierung kommerzieller Systeme.
Und vielleicht noch ganz kurz etwas hier an dieser Stelle, nochmal
ganz kurz hier rein.
Sie sollten daran denken, dass Sie sich anmelden für die Klausur bis
spätestens 2.2.
Also spätestens bis Montag haben Sie noch Zeit sich anzumelden.
Denken Sie daran, dass Sie sich rechtzeitig anmelden.
Nachträgliche Anmeldungen werden in der Regel nicht angenommen.
Also das ist wirklich extrem schwierig, das noch einzubauen.
Das sind sehr viele Hörsäle, die dort gebraucht werden.
Wir müssen das alles organisieren und das ist extrem ärgerlich, wenn
da anschließend noch Anmeldungen kommen.
Deswegen können wir das nicht immer wirklich gewährleisten.
Wir machen Termine und ich bitte Sie, sich daran zu halten, dass es
keine Beliebigkeit, solche Termine, dass man sagt, ich kann mich daran
halten, aber muss das ja nicht.
Die werden das schon irgendwie hinkriegen.
Seien Sie bitte kooperativ und melden Sie sich an.
Okay, damit sind wir jetzt wirklich am Ende von diesem Kapitel und wir
kommen zum nächsten Kapitel.
Und dann gehen wir hier auf die erste Folie und können damit starten.
So, Betriebssysteme.
Jetzt haben wir uns alles Mögliche angeguckt.
Wir haben uns bei Rechnerarchitektur angeschaut, die Grundlagen,
Schaltnetze, Schaltwerke, wie man überhaupt Bauteile entwerfen kann.
Wir haben uns Rechnerarithmetik angeschaut, Zahlendarstellungen,
Rechnerarchitektur von Neumannrechner.
Wir haben uns jetzt die ganze Speicherhierarchie angeschaut,
Programmierschnittstellen und jetzt kommen wir zu den
Betriebssystemen.
Wie kann ich also das, was dort dahinter steckt, hier kurz Übersicht,
was dort alles gemacht werden soll.
Eine ganze Reihe von Einzelthemen, die ich hier schnell verfahren
einigermaßen Ihnen darstelle und versuche Ihnen dabei die wesentlichen
Punkte zu vermitteln.
Worum geht es?
Wir wollen dafür sorgen, dass der Rechner bestmöglich ausgenutzt
werden kann.
Der Rechner hat eine Reihe Ressourcen, die sollen bestmöglich
ausgenutzt werden.
Das heißt, sie sollen möglichst immer bestmöglich ihre Arbeit tun
können, sollen nicht aufgehalten werden durch irgendwelche
Verzögerungen, dadurch, dass irgendwelche Daten nicht da sind oder
ähnliches.
Es gibt ja in Regel viele Programme, die ausgeführt werden sollen.
Also will man den Rechner möglichst immer beschäftigt halten, Dinge
verteilen, die einzelnen Aufgaben verteilen in der Rechnerarchitektur
und also dafür sorgen, dass die Ressourcen bestmöglich eingesetzt
werden, um die Informationsverarbeitung zu gewährleisten.
Alle solche Komponenten, die das garantieren, die dafür sorgen, dass
unsere Programme bestmöglich ausgeführt werden, das sind die
Programme, die insgesamt das Betriebssystem ausmachen.
Und das werden wir uns jetzt genauer angucken, wie so ein
Betriebssystem aufgebaut ist, um also diese Betriebsmittel, Prozessor,
Speicher, Ein- und Ausgabegeräte, Programme und Daten am besten
einzusetzen.
Und das schauen wir uns genauer an.
Was muss da gemacht werden?
Ich muss dafür sorgen, dass der Prozessor sinnvoll verwaltet wird oder
heutzutage die Prozessoren beim Mehrkernprozessor.
Welcher Prozessor wird für welche Aufgabe eingesetzt?
Wie kann ich meine Programme, die anstehen, sinnvoll verteilen auf die
vorliegenden Programme?
Wobei ich hier in diesem Kapitel weitestgehend auf die Parallelität,
also auf Parallelverarbeitung nicht eingehe.
Das haben wir vorhin mal gemacht.
Aber bei der Architektur spielt hier prinzipiell eigentlich erstmal
keine Rolle.
Ich muss den Hauptspeicher geeignet verwalten.
Ich muss ja dafür sorgen, dass dort immer die richtigen Programme drin
liegen.
Wir wissen, dass wir den Sekundärspeicher haben, Hintergrundspeicher,
wo eigentlich unsere Daten alle permanent drin liegen.
Die ganzen Programme, unsere Daten, wir wollen damit rechnen, müssen
die also in den Hauptspeicher hineinbekommen.
Der Hauptspeicher ist deutlich kleiner als das, was wir insgesamt an
Informationen im Rechner gespeichert haben.
Und es muss aber, damit ein Programm ausgeführt werden kann, das
Programm und die zugehörigen Daten müssen verfügbar sein im
Hauptspeicher.
Und möglichst im Cache, der ist noch kleiner.
Da hatte ich schon gesagt, wie man das im Wesentlichen macht.
Aber jetzt schauen wir uns im Weiteren auch noch genauer an, wie kann
man tatsächlich dafür sorgen, dass die Informationen aus dem
Hintergrundspeicher immer zur rechten Zeit im Hauptspeicher sind.
Dann muss ich die Programmausführung geeignet organisieren, muss den
Zugriff auf ein Ausgabegerät steuern, Netzzugriffe organisieren.
Also einiges muss hier gemacht werden und das geschieht natürlich
durch Programme.
Das heißt, ich habe die Systemprogramme, ich habe die
Anwenderprogramme.
Die Systemprogramme sorgen dafür, dass die Anwenderprogramme sinnvoll
ausgeführt werden.
Natürlich möchte ich erreichen, dass der größte Teil meiner
Rechenkapazität für die Anwenderprogramme genutzt wird.
Das heißt, ein Betriebssystem muss möglichst effizient sein, möglichst
klein sein, die Aufgaben mit möglichst wenig Aufwand erledigen, damit
die Rechenkapazität weitgehend dem Benutzer zur Verfügung steht.
Das ist also eine ziemlich schwierige Angelegenheit, die aber ganz gut
gelöst ist mittlerweile.
Und ich fange jetzt mal an mit so einer Übersicht über
Betriebssysteme.
Das erste ist ein sehr historisches Betriebssystem MS-DOS, das
Microsoft Disk Operating System mit einigen Annahmen, die damals Bill
Gates gemacht hat.
Es hatte zum Teil wirklich haarsträubende Eigenschaften, wie diese
Annahme, dass man niemals mehr als 640 Kilobyte Speicher braucht.
Das steckt noch heute in dem Betriebssystem von Microsoft drin, wird
irgendwie immer umgangen.
Da gibt es eben nette Aussagen, dass man sagt, man wird eigentlich nie
mehr Hauptspeicherplatz brauchen, als diese 640 Kilobyte.
Das andere kann man dann auslagern.
Es gibt eine ganz andere Reihe von seltsamen Annahmen in diesem
Betriebssystem, die in Konkurrenzprodukten der damaligen Zeit ganz
anders und viel besser und eleganter gelöst waren.
Aber es wurde halt von einer großen Firma entschieden, sie nehmen
dieses MS-DOS und damit hat sich das durchgesetzt.
Und zumindest dieses CPM, das ist leider verschwunden.
macOS hat sich halt entsprechend weiterentwickelt und war eigentlich
weitgehend immer, was die Eigenschaften anging, sehr stark den
Microsoft -Produkten voraus.
Microsoft hat natürlich viele andere Dinge entwickelt, das wissen Sie
alle.
Von MS-DOS ging es dann zu Windows und zu allen möglichen Varianten.
Mittlerweile sind wir hier über eine ganze Reihe von
Zwischenstationen.
Demnächst bei Windows 10, das wird jetzt gerade vorgestellt,
demnächst.
Also es entwickelt sich immer weiter, die Eigenschaften werden
hoffentlich immer besser.
Ein wichtiger Effekt ist natürlich, dass man immer wieder seine
Rechner umrüsten muss und dann auch neue Software braucht.
Das heißt, das ist ein stetiger Treiber der Geschäfte für alle, die
Software produzieren, weil man immer wieder neue Varianten den
Anwendern verkaufen kann.
Also ein tolles Geschäftsmodell, wenn ich die Basis für die Ausführung
von Informationsverarbeitung ständig verändere, sodass jeder wieder
was Neues kaufen muss.
Was Besseres kann man nicht machen.
Also das ist eine sehr geniale Art, wie man immer wieder Geld
einnehmen kann.
Und wenn man sich anschaut, es gibt so schöne Beispiele dafür, wie man
bestimmte Aufgaben erledigen kann mit einem Rechner im alten
Betriebssystem, einem sehr alten Betriebssystem und einem im neuen
Betriebssystem.
Und es fällt sich heraus, dass man das nicht notwendigerweise im neuen
Betriebssystem deutlich besser machen kann, wird deutlich schneller.
Es ist die ganze Darstellung wesentlich besser, es ist viel mehr an
Funktionen drumherum verfügbar.
Aber die einzelne Aufgabe wird nicht notwendigerweise dadurch besser
ausgeführt.
Trotzdem will ich keineswegs sagen, dass das unnötig gewesen ist,
diese Entwicklung zu machen.
Das was wir hier jetzt in Programmen wie Windows 8, 8.1 oder Windows
10 zukünftig machen können, ist schon sehr sehr sehr viel mehr, als
man früher erledigen konnte.
Also das sind jeweils alle möglichen Varianten.
Mittlerweile eben überall mehr Benutzerbetrieb, was bei Microsoft
lange ausgeschlossen war.
Das hat man sich nicht vorstellen können, dass mehr als eine Person so
einen Rechner nutzt.
Und auch, dass man mehr als ein Programm ausführen möchte, das ist
alles erst später dazugekommen.
Konkurrenten hatten das viel früher eingebaut.
Auch, dass man das Netz benutzt, auch das ist irgendwann erst später
dort gemacht worden.
Aber es gibt das alles mittlerweile.
Dann gibt es die Varianten von Windows für alle möglichen anderen
Geräte.
Also für kleine Geräte, für Smartphones, für Smartcards, für
Komponenten im Auto gibt es das auch.
Also im Automotive-Bereich, dass man dort auch Steuergeräte mit einem
Windows -System betreiben kann.
Also das hat sich schon sehr breit durchgesetzt für alles, was heute
im Bereich so cyberphysikalische Systeme gebraucht wird.
Dann gibt es die Alternative, Macintosh Operating System, macOS oder
mittlerweile das iOS für iPhone, iPad und so weiter.
Macintosh Operating System, Apple war immer, was so die Funktionalität
anging, oder fast immer den anderen voraus.
Die haben sehr früh Wert gelegt auf Vernetzung, auf mehr
Benutzerbetrieb, mehr Programmbetrieb und ähnliches.
Dieses iOS ist gleichzeitig Unix-basiert, das heißt, Sie können mit
dem macOS auch die anderen Betriebssysteme simulieren, emulieren.
Ist ein sehr interessantes Betriebssystem, das einige von Ihnen
wahrscheinlich auch nutzen auf Ihren Geräten.
Was ich hier nicht aufgeführt habe, ist jetzt Android, es gibt eben
auch noch ein paar weitere, aber ich habe mich auf die wesentlichen
hier beschränkt.
Dann gibt es auf den Großrechnern oder auf den Workstations die Unix
-Systeme, haben sich auch sehr stark durchgesetzt, wo man eben
Multiprogramming, Webbenutzerbetrieb macht, auf Workstations und auf
Servern in Rechnernetzen.
Da haben die einzelnen Hersteller eigene Varianten entwickelt.
In Berkeley entwickelte Variante, dort gibt es System 5, das ist auch
eine Unix-Variante.
Von HP, von IBM, von verschiedenen Firmen gibt es eigene Unix
-Varianten, die sich auf den Rechnern durchgesetzt haben.
Dann gibt es für PCs das Linux und entsprechende freie Varianten für
PCs.
Unix -Derivate, die also etwas einfacher sind und auf PC einsetzen
kann, die im Prinzip kostenlos verfügbar sind, aber wenn Sie einen
Anbieter haben, wird er Ihnen normalerweise noch einiges an Service
bieten dazu und dann müssen Sie dafür bezahlen.
Ein wesentlicher Punkt ist, dass Sie bei Linux eine selbstorganisierte
Qualitätssicherung haben, das heißt, die haben die Linux
-Entwicklerforen und wenn Sie irgendwelche neuen Komponenten dazu
entwickeln, dann können Sie das dort über das Entwicklerforum im
Prinzip testen und bekommen sehr schnell Antworten von zig
verschiedenen Leuten, die halt da einfach reinschauen, Komponenten
sich anschauen, testen und Fehlermeldungen zurückgeben.
Das ist also eine interessante Art der Qualitätssicherung.
Im Prinzip, man redet häufig über Crowdsourcing, hier wäre es also so
ein Crowdtesting, dass man einfach die Entwickler daran arbeiten
lässt, die sich dort freiwillig in diese Entwicklerliste eingetragen
haben.
Dann gibt es die Großrechner, das ist mehr eine historische
Aufzählung.
MVS, VMS haben alle ähnliche Namen.
Multiprogrammierung, virtuelle Systeme und ähnliches.
BS2000, Betriebssystem 2000 von Siemens, hatte eine große Bedeutung
bei Großrechnern, die von Siemens angeboten wurden und haben sich sehr
lange gehalten.
Mir ist nicht bekannt, ob es immer noch BS2000 bei Siemens-Rechnern
tatsächlich gibt.
Ich würde mich nicht wundern, wenn davon immer noch welche laufen.
Ist halt ein sehr weit verbreitetes Betriebssystem gewesen bei
Großrechnern.
Da hat sich aber sehr viel getan in den letzten Jahren.
Ich gehe auf die Einzelheiten da gar nicht weiter ein.
Ich gehe aber gleich noch auf die wesentlichen Anforderungen an
Betriebssysteme ein.
Nur, dass Sie Begriffe einmal hier gesehen haben.
Die Konfiguration der Rechenanlagen ist natürlich sehr
unterschiedlich.
Wenn Sie ein Tablet haben, brauchen Sie eigentlich nichts weiter.
Da haben Sie nur einen Bildschirm, einen Prozessor und sicherlich noch
einen Flash-Speicher da drin.
Wenn Sie einen PC haben, haben Sie halt auch noch eine Festplatte,
Drucker angeschlossen und ähnliches.
Beim Großrechner haben Sie alle möglichen Terminals, die an den
Rechner angeschlossen sind oder alle möglichen anderen Rechner.
Das sind halt unterschiedlich zu konfigurieren, unterschiedlich
komplex.
Sie haben verteilte Systeme, beliebig große Rechner, die
zusammengebaut werden.
Heutzutage können Sie Cloud-Computing nutzen oder können Sie virtuelle
Rechner so konfigurieren, wie Sie das für Ihre Aufgaben brauchen.
Also in der Cloud ist vieles möglich heutzutage.
Da brauchen Sie nicht mehr Rechner, die sich selbst hinzustellen,
sondern Sie können sich die konfigurieren bei den Cloud-Anbietern und
können dann und auf Rechnern nach Ihrem Bedarf auf vielen Rechnern
Ihre Aufgaben erledigen.
Das ist halt eine ganz wichtige Errungenschaft, dass man dynamisch
sich Rechenkapazität schaffen kann, dafür Nachbedarf bezahlt und wenn
man den Dienst nicht mehr braucht, dann hat man halt den Rechner nicht
rumstehen, sondern hat nur den Dienst genutzt.
So, das war jetzt nur so ein kurzer Überblick über Namen von
Komponenten von großen Betriebssystemen.
Wie sind diese Betriebssysteme aufgebaut?
Natürlich auch systematisch strukturiert.
Sie haben eine ganze Reihe verschiedener Schichten mit
unterschiedlichen Aufgaben.
Also die unterste Schicht, die ist dafür verantwortlich, direkt auf
die Rechner-Hardware zuzugreifen, konkret die Komponenten zu steuern.
Da werden also die Prozesse auf den Prozessor zugeordnet und die
Schicht oben drüber, die nutzt dann die Möglichkeiten der unteren
Schicht.
Sie haben dann hier auf der Schicht 2 die Betriebsmittelverwaltung,
Steuerung von Betriebsmitteln.
Aber das Konkrete, wenn ein Betriebsmittel zugeteilt ist, dann konkret
die Ausführung ihrer Aufgaben auf den Betriebsmittel zu steuern.
Das ist dann etwas, was in der unteren Schicht passiert.
Und oben drüber haben Sie dann noch wieder die Auftragssteuerung, dass
Sie große Aufträge erstmal für den Rechner steuern, die Reihenfolge
der Auftragsbearbeitung festlegen und ähnliches.
Also da ist einiges, was da erledigt wird, alles schön schichtenartig
angeordnet und die Benutzer, nicht nur einer, sondern viele wollen
bestmöglichen Service haben.
Der Benutzer möchte möglichst direkt auf die Hardware zugreifen
können.
Das Betriebssystem muss dafür sorgen, dass viele Benutzer den Eindruck
haben, sie hätten exklusiven Zugriff und die Betriebsmittel werden
dabei bestmöglich ausgenutzt.
Dann gibt es eine ganze Reihe von Betriebsarten, da kommen wir gleich
drauf.
Also wir haben, wenn wir einen Auftrag formulieren, es kann sein, dass
man da eine größere Software ausführen möchte, das ist ein Auftrag,
also ein Programm zusammen mit irgendwelchen Daten, die benutzt werden
müssen.
In diesem größeren Auftrag kann es Teilaufträge geben und dann müssen
die entsprechend ausgeführt werden.
Und jetzt ist die Frage, wie macht man das eigentlich?
Also Sie geben einen Auftrag ab, Sie wollen ein Programm ausführen.
Wie wird dafür gesorgt, dass dieses größere Programm tatsächlich
ausgeführt wird?
Das vielleicht noch viele Einzelkomponenten hat.
Und dafür gibt es halt je nach Anforderungen an die Ausführung
unterschiedliche Arten des Betriebes eines Rechners.
Und die schauen wir uns jetzt im weiteren genauer an.
Und es geht los mit dem Stapelbetrieb.
Ein Stapelbetrieb ist ganz einfach.
Ich möchte den Rechner möglichst gut nutzen.
Das heißt, ich habe im Prinzip hier einen Schreibtisch und da packe
ich die ganzen Aufträge rauf.
Und die werden jetzt der Reihe nach ausgeführt.
Wobei der Schreibtisch vielleicht nicht ganz richtig ist.
Vielleicht sollte ich lieber eine Warteschlange malen.
Also machen wir das anders.
Wir haben hier eine Warteschlange.
Hier kommen die Aufträge rein.
Und da gehen sie irgendwann raus.
Und ich muss halt jeden Auftrag vollständig definiert haben, inklusive
aller Eingabedaten.
Und dann habe ich anschließend, wenn ich hier also viele solche
Aufträge habe, entweder so als Warteschlange angeordnet.
Das sieht nach FIFO aus.
Oder es kann auch sein, ich komme jetzt doch wieder zu meinem Stapel
zurück.
Da muss ich mir überlegen, wenn ich hier sitze, wenn hier also jemand
sitzt und er soll jetzt entscheiden, welches Programm er ausführen
möchte.
Nach welchen Kriterien macht er das eigentlich?
Nimmt er einfach hier nach diesem FIFO-Prinzip, nimmt er da einfach
immer die einen nach dem anderen raus, so wie sie angekommen sind?
Oder nimmt er immer den, der als letztes oben drauf gelegen hat?
Oder zieht er sich hier irgendwo einen raus, der irgendwie eine
größere Priorität hat?
Da gibt es unterschiedlichste Verfahren.
Und das muss man sich später nochmal genauer anschauen, wie man das im
Prinzip macht.
Auf jeden Fall muss man dafür sorgen, dass eben diese Aufträge, die
abgegeben werden, bestmöglich ausgeführt werden.
Häufig innerhalb einer gewissen Zeit.
Also es kann sein, dass man durchaus sagt, das soll bis zum nächsten
Morgen erledigt sein, aber eben nicht sofort.
Solche Betriebsart braucht man immer dann, wenn es darauf ankommt, den
Durchsatz zu maximieren, aber wenn es eben nicht darauf ankommt, dass
Sie interaktiv noch mit dem Programm irgendwelche Daten nachliefern.
Interaktion mit einem Programm, das im Batch-Betrieb ausgeführt wird,
ist nicht möglich.
Weil Sie keinen Einfluss darauf haben, wann das Programm ausgeführt
wird.
Sie brauchen solche Programme zum Beispiel, um typische
betriebswirtschaftliche Sachen zu machen.
Lohn- und Gehaltsabrechnungen, statistische Auswertung, technisch
-wissenschaftliche Berechnungen.
Da wollen Sie nicht interagieren, da wollen Sie nur irgendwelche
Testläufe machen, wenn Sie wissenschaftliche Tests machen von
irgendwelchen Programmen, dann ist ein Batch-Betrieb völlig
ausreichend.
Das ist die eine Sache.
Das andere ist Multiprogrammbetrieb.
Sie wollen natürlich in der Lage sein, dass Sie mehrere, möglichst
mehrere aktive Prozesse haben, die nebeneinander ausgeführt werden
können, die vielleicht nur immer bis zu einem gewissen Stück
ausgeführt werden, bis zu dem gerade alle Daten da sind.
Dann muss vielleicht auf irgendetwas gewartet werden.
Also hier ist es so, dass Sie mehrere Prozesse haben, die gleichzeitig
im Hauptspeicher resident sind.
Das heißt, die Programme und die zugehörigen Daten liegen im
Hauptspeicher, ich kann darauf zugreifen.
Wenn ein Programm zugreifen möchte auf Daten, die nicht im
Hauptspeicher verfügbar sind, dann muss das unterbrochen werden, dann
müssen erstmal die Daten in den Hauptspeicher reingeladen werden und
dann kann es wieder ausgeführt werden.
Auf jeden Fall kann bei einem Multiprogrammbetrieb die Bearbeitung der
Prozesse zeitlich verzahnt werden.
Das schauen wir uns gleich nochmal an, was das eigentlich bedeutet.
Hauptaufgabe hier ist, dass man einerseits die Betriebsmittel an
Prozesse zuteilt, sie entsprechend aktiviert, blockiert, je nachdem,
ob alles, was erforderlich ist zum Ausführen eines Prozesses, auch da
ist.
Ich muss dafür sorgen, dass die Programme sich nicht gegenseitig
behindern oder sich stören.
Gegenseitige Isolation der Prozesse, möglichst unterschiedliche
Speicherbereiche sollten Sie verwenden, damit Sie keine unerwünschten
Nebenwirkungen haben, dass ein Programm etwas schreibt, was das andere
eigentlich gerade noch gebraucht hätte oder den alten Wert gebraucht
hätte.
Also da muss man aufpassen.
Vorteil ist, dass Sie die Betriebsmittel besser ausnutzen können, wenn
Sie mehrere Programme verwenden können gleichzeitig.
Und dieses mit der Verzahnung will ich Ihnen gleich nochmal
darstellen, hier auf diesem kurzen Bild.
Eigentlich etwas, was wir uns schon mal angeschaut haben.
Im Prinzip beim Pipelining ist das Szenario ein bisschen anders.
Hier betrachten wir einfach nur die sehr einfachen Abläufe.
Ich habe ein Programm, einen Auftrag, der zunächst mal Daten einlesen
muss, der dann etwas verarbeitet, aufgrund dieser eingelesenen Daten
und anschließend kann etwas ausgegeben werden.
Nehmen wir mal an, wir haben eine solche strikte Trennung zwischen
diesen drei Phasen.
Dann würde bei einer rein sequentiellen Ausführung dieser Aufträge es
dazu führen, dass Sie zunächst mal die Eingabe machen.
In der Regel haben Sie dafür unterschiedliche Verarbeitungseinheiten.
Die würde also arbeiten, dann hätten Sie die Verarbeitung, dann hätten
Sie die Ausgabe und erst danach kann der nächste Auftrag starten.
Wenn Sie aber in der Lage sind, gleichzeitig zu einer Eingabe auch
Ihre Verarbeitungseinheit laufen zu lassen oder auch gleichzeitig
etwas auszugeben, wenn Ihr Rechner so aufgebaut ist, dann könnte man
natürlich sagen, wenn der erste Auftrag mit der Eingabe fertig ist,
könnte ja der nächste gleich starten, die Eingabe zu machen.
Und in der Zeit kann der erste Auftrag schon ausgeführt werden, kann
also seine Verarbeitung machen.
Zufällig ist es jetzt hier gerade so, dass wenn der mit seiner
Verarbeitung fertig ist, auch gerade der Auftrag 2 mit seiner Eingabe
dann gerade fertig ist, er kann sofort weitermachen und ausführen.
Der dritte Prozess kann seine Eingabe machen, dann gibt es hier eine
kleine Lücke.
Aber insgesamt ist es klar, wir haben jetzt die Betriebsmittel
deutlich besser ausgenutzt.
Sie sind fast durchgehend aktiv und für die Gesamtzeit ergibt sich
eben eine deutliche Verbesserung.
Wenn Sie die Zeiten abschätzen für Eingabe, Verarbeitung und Ausgabe,
dann haben Sie halt bei diesen verschiedenen Varianten genau diese
unterschiedlichen Gesamtzeiten.
Wenn Sie das nicht verzahnen, haben Sie die Summe der einzelnen Zeiten
für Eingabe, Verarbeitung und Ausgabe.
Wenn Sie zeitlich verzahnen, dann ist das so in etwa das Maximum der
Gesamtzeiten für Eingabe, Verarbeitung und Ausgabe.
Wie Sie ja hier sehen, natürlich kommt noch ein bisschen was dazu,
aber das kommt so in etwa hin.
Wenn Sie viele Programme haben, dann ist das Wesentliche, wie lang
sind im Prinzip diese Gesamtzeiten für Eingabe, Gesamtzeit für
Verarbeitung und für Ausgabe.
Und Sie sparen deutlich Zeit ein.
Das ist das alte Prinzip, das Sie kennen, das Pipelining, dass man
mehrere Einheiten hat, die nebeneinander arbeiten können,
unterschiedliche Aufträge erledigen können, die Sie eben nacheinander
in Ihrem Auftrag erledigen müssen.
Und dann haben Sie genau wieder den Zeitgewinn wie beim Pipelining.
Und deswegen ist es eben wichtig, dass man Multiprogrammbetrieb hat,
weil man dadurch in der Lage ist, wirklich seine Betriebsmittel besser
einzusetzen.
Das war jetzt Multiprogrammbetrieb und Stapel.
Jetzt kommt der Dialogbetrieb, Timesharing-Mode, Dialogbetrieb.
Sie wollen direkt mit dem Rechner kommunizieren, interaktiv ein
Programm ausführen.
Und hier müssen Sie Interaktion ermöglichen.
Stapelbetrieb geht dann nicht.
Und da gibt es jetzt unterschiedliche Arten.
Kann sein, dass Sie alle das gleiche Programm verwenden.
Kann aber auch sein, dass Sie den Rechner völlig unabhängig nutzen.
Und wenn Sie jetzt also mehrere hundert Benutzer haben, die an einem
Rechner sitzen, die wollen alle den Eindruck haben, dass der Rechner
ihnen ständig zur Verfügung steht.
Und deswegen sagt man Timesharing.
Wenn Sie sich hier so eine Zeitscheibe anschauen, dann bekommt also
jeder einen Teil der Zeit für sich.
Und jeder Nutzer kann eine gewisse Zeiteinheit zugreifen.
Dann kommt der nächste Benutzer dran.
Und dadurch hat man als einzelner Benutzer den Eindruck, dass man
einen die ständigen Service hat.
Aber der Rechner, der bedient eben alle nebeneinander.
Anwendungen für diese beiden verschiedenen Varianten sind beim
Teilhaberbetrieb.
Wenn halt alle ein gleichartiges Programm verwenden, wie
Platzbuchungssystem eines Reisebüros oder ähnliches.
Oder Sie haben die Programme für Bankanwendungen.
Da müssen alle auf ein gleichartiges Programm zugreifen.
Die machen keine unterschiedlichen Anwendungen.
Ansonsten Teilhaberbetrieb, interaktive Programmentwicklung, etwas was
Standard ist.
Und hier ist eben das Wesentliche, dass Sie einerseits die
Rechenressourcen bestmöglich ausnutzen, aber den Benutzern auch
bestmöglichen Service bieten, dass die Antwortzeit möglichst gering
ist.
Wenn Sie jeweils lange warten müssen, bis Sie auf ein eingetipptes
Symbol eine Antwort kriegen, dann werden Sie unzufrieden sein.
Das ist also das, was beim Timesharing-System dann gemacht wird.
Und dann haben wir den Echtzeitbetrieb, prinzipiell eine andere Art.
Bei Echtzeit geht es darum, dass Sie Zeitrestriktionen haben.
Sie müssen innerhalb einer vorgegebenen Zeit tatsächlich reagieren
können.
Also typisches Beispiel, Sie haben ein Programm, das das Bremsen
steuert in Ihrem Auto.
Dann muss klar sein, wenn Sie dieses Programm ausführen, das reagiert
auf das Treten eines Gaspedals, dann muss das Programm innerhalb einer
festen Zeit tatsächlich reagieren.
Also das ist jetzt ein Extrembeispiel, aber das ist eben
Echtzeitanforderung, wenn Sie Break-by-Wire haben, also
softwaregestütztes Bremsen oder auch Steuern oder ähnliches im Auto.
Diese Programme müssen innerhalb einer festen Zeit tatsächlich
reagieren können.
Und das heißt zum Beispiel, dass die Verwendung eines Caches hier
problematisch ist, weil wenn Sie einen Cache haben in Ihrem Rechner,
dann kann es sein, dass Sie einen Befehl ausführen wollen und wenn das
Datum nicht im Cache ist, müssen Sie es auf den Hauptspeicher
zugreifen.
Dann dauert auf einmal die Ausführung eines Befehls länger, als wenn
dieser Befehl im Cache drin gewesen wäre.
Und das heißt, dann wäre es so, dass Sie eine sehr konservative
Zeitschranke hätten.
Nämlich, da müssen Sie immer berücksichtigen, dass Sie eigentlich bei
jedem Befehl potenziell auf den Hauptspeicher zugreifen können.
Das heißt, Sie können die Vorteile des Caches bei Ihrer
Zeitabschätzung gar nicht ausnutzen.
Und dann ist es sinnvoller von vornherein auf den Cache zu verzichten
und dann eben effizient den Hauptspeicher auszunutzen, bestmöglich.
Also bei Echtzeitanforderungen muss man wissen, dass man Garantien
geben muss für die Ausführungszeit der einzelnen Programme.
Bei allen Prozessanwendungen, also wenn Sie Flugzeuge steuern oder
ähnliches, in allen Anwendungen, wo es darum geht, dass Sie sofort
reagieren können, brauchen Sie Programme, bei denen Sie eben
garantiert innerhalb einer gewissen Zeit Ihre Funktion erfüllt haben.
Und Anwendungen hatte ich eben schon erwähnt, ein paar.
Also Mess- und Regelungsaufgaben, Kraftfahrzeugelektronik,
Telekommunikation.
Wobei bei Telekommunikation, also beim Telefonieren, wenn da mal ein
Programm nicht richtig funktioniert, ist es nicht ganz so schlimm,
dann wählen Sie vielleicht neu.
Das ist nicht so sicherheitsrelevant.
Aber im Automotive-Bereich, Kraftfahrzeugelektronik ist sehr wichtig.
Generell bei cyberphysikalischen Systemen haben Sie es mit
Echtzeitanforderungen zu tun.
Und da muss man sich auch genau überlegen, kann man eigentlich für
Echtzeitanforderungen die Cloud verwenden?
Das ist genauso problematisch wie der Cache.
Wenn Sie Cloud-Anwendungen verwenden, Cloud-Computing bei
Echtzeitanwendungen ist in der Regel eigentlich nicht sinnvoll, sofern
Sie eben diese scharfen Echtzeitanforderungen haben.
Dass Sie innerhalb einer kurzen Zeitspanne etwas erledigen müssen,
weil Sie bei Cloud-Anwendungen ja erstmal ins Netz gehen müssen.
Dann haben Sie erstmal die Verzögerung durch die Kommunikation übers
Netz mit Ihrem virtuellen Server und Sie haben dafür keine
Zeitgarantien.
Bei Cloud-Anwendungen kann Ihnen niemand Zeitgarantien geben.
Es gab neulich mal so eine Meldung, dass irgendwelche Entwickler
gesagt haben, also durchaus höherrangige Leute gesagt haben, man
könnte ja auf eine ganze Reihe von Steuergeräten im Fahrzeug
verzichten, also in den Autos verzichten und das einfach in die Cloud
verlagern.
Ein solches Fahrzeug möchte ich nicht fahren, weil Sie einfach keine
Zeitgarantien haben, dass dort die Funktionen zeitgerecht erledigt
werden.
Unterhaltungsfunktionen, die können Sie gerne in die Cloud verlagern,
aber sicherheitsrelevante Dinge natürlich nicht.
Da muss man sehr genau aufpassen, was man da macht.
Aber dieses ganze Thema Cyber-Physical-Systems, Industrie 4.0, das ist
ein Bereich, wo Sie Prozessrechner brauchen.
Heutzutage redet man nicht mehr von Prozessrechnern, sondern von Cyber
-Physikalischen Systemen, die auch noch vernetzt sind.
Und das ist ein, wie Sie wissen, hoffentlich ein ganz wesentliches
Gebiet, heutzutage die Industrie wettbewerbstauglich zu machen für die
Zukunft.
Der fordert, dass wir die ganzen Möglichkeiten der Integration von
Informationsverarbeitung in industrielle Prozesse bestmöglich nutzen.
Da müssen wir vorne dran sein und müssen also dieses Thema Industrie 4
.0 bestmöglich erledigen.
Wirtschaftsingenieure haben dafür eigentlich genau die richtigen
Kompetenzen, weil sie sowohl die ingenieurwissenschaftlichen
Fähigkeiten oder Kompetenzen haben, als auch die
informationstechnischen Kompetenzen.
Also, sie brauchen für Echtzeitbetrieb in der Regel eben auch
besondere Ein- und Ausgabegeräte.
Sie haben Sensoren, Steuerungsventile, Messfühler, brauchen also
Sensorenaktoren.
Sie haben eingebettete Systeme und, wie gesagt, prioritätsgesteuert
mit scharfen Zeitrestriktionen.
Hauptaufgabe ist also, die zeitgerechte Verfügbarkeit der
Informationsverarbeitung zu gewährleisten.
Da können Sie nicht Stapelbetrieb machen, können auch kein Timesharing
-System dort einsetzen.
Das sind andere Arten der Steuerung von Rechnern.
Und dann kommen wir noch kurz wieder zu dem kleinen Serverbetrieb, den
haben Sie schon mal kennengelernt.
Das habe ich schon mal angedeutet bei den verteilten Systemen.
Für verschiedenste Aufgaben brauchen Sie Kommunikation zwischen Client
und Server, um Benutzerverwaltung zu machen, Dateiverwaltung,
Datenbanken zu managen, Ihre E-Mails abzurufen und Ähnliches.
Also verschiedenste Server sind für Sie eingesetzt und Sie können über
Remote Procedure Call, das ist der allgemeine Begriff, oder speziell
wenn Sie Java verwenden, können Sie das mit Remote Method Invocation
machen, können Sie halt auf die Rechner zugreifen und während also der
Server eine Aufgabe erledigt, die der Client ihm gegeben hat, kann der
Client andere Aufgaben erledigen und kann dann wieder, wenn der Server
fertig ist, die Aufmerksamkeit auf das Ergebnis lenken.
Das sind also die kleinen Serversysteme.
Ich hatte schon mal darauf hingewiesen, dass man eben mittlerweile
sehr viel auch von Peer-to-Peer-Systemen redet, bei denen Server und
Clients gleichberechtigt sind.
Das heißt, jeder kann im Prinzip auch Serveraufgaben übernehmen.
Das sind dann selbstorganisierende Service-Architekturen, die Sie in
einigen Bereichen schon sehr häufig sehen können.
Also Verkehr im Internet ist häufig heutzutage bedingt durch Peer-to
-Peer -Systeme.
Aber darauf kann ich im Einzelnen jetzt nicht mehr eingehen.
Hauptaufgaben hier bei dem kleinen Serversystem ist natürlich, dass
Sie in der Lage sind, diese entfernten Prozeduraufrufe bestmöglich zu
erledigen.
Sie müssen die Anstoßnachricht, also den Aufruf einer entfernten
Prozedur, weiterleiten an den entsprechenden Server.
Die Prozedur muss ausgeführt werden und Sie müssen anschließend das
Ergebnis wieder zurückschieben können.
Das heißt, Sie brauchen hier einiges an Kommunikation.
Deswegen haben Sie auch hier wieder verschiedene Schichten.
Sie haben jetzt noch zusätzlich zu dieser Schichtenarchitektur, die
ich Ihnen vorhin dargestellt habe, mit den Schichten in jedem
Betriebssystem, von der Schicht, die auf die Hardware zugreift, dann
hoch auf die anderen, haben Sie eben hier einerseits eine
Kommunikationsschicht, wobei die also nur dafür da ist, die ganzen
Nachrichten zu versenden, zu empfangen und sie dann entsprechend
abzuliefern an dem Ort, wo sie ankommen sollen, in Empfang genommen
werden sollen.
Sie haben die Maklerschicht.
Was macht ein Makler?
Der vermittelt zwischen verschiedenen Interessenten, also jemand
möchte einen Dienst nutzen, andere bieten Dienste an und der Makler
entscheidet, welcher Dienstanbieter genommen wird, zu wem man
vermittelt wird.
Das ist die Maklerschicht.
Weitergabe von Aufträgen an den geeigneten Dienst.
Da gibt es eine ganze Reihe Standardpakete, die sowas machen.
Ein klassisches solches Softwarepaket ist CORBA, Common Object Request
Broker Architecture, dient also dazu, die Nutzung entfernter Dienste
bestmöglich zu gestalten.
Ein Object Request Broker ist ein Makler, der die Nachfrage nach
Objects makelt, vermittelt und die Objects sollen Dienste ausführen.
Das ist eine Common Object Request Broker Architecture.
Die hat sich weltweit durchgesetzt, ist ein Standard-Maklersystem.
Unten drunter haben Sie die Services, die ausgeführt werden sollen und
die werden dann in dem konkreten Rechner angeboten.
Das ist typische Architektur, so genannte Multi-Tier-Architektur.
Da fehlt aber nicht von Schichten, sondern von einer Tier 1, 2, 3 und
so weiter.
Und das, was hier Kommunikationsschicht und Maklerschicht macht, das
heißt häufig auch oder in der Regel auch Middleware.
Die Middleware ist also dafür zuständig, genau diese Kommunikation
zwischen verschiedenen Rechnern zu erledigen, damit sie ihre Aufgaben
sinnvoll erfüllen können.
Eine moderne Art, kleinen Serverbetriebssystemen oder auch solche
Middleware -Systeme ist eine Service-orientierte Architektur, bei der
Sie eigentlich das gleiche Bild wiedersehen wie vorher.
Sie haben hier generische Basisdienste, die Funktionen der technischen
Infrastruktur nutzen, meinetwegen Zugriff auf Platten, auf
irgendwelche Speicherbereiche, auf Programme.
Und Basisdienste können zum Beispiel auch Sicherheitsdienste sein,
dass Sie überhaupt einen sicheren Zugriff erlauben, dass Sie
verschlüsseln können und ähnliche Dinge.
Das sind Basisdienste, die Sie kombinieren müssen in einer geeigneten
Form, um dann anwendungsspezifische Dienste zu bekommen auf der Ebene
der Anwendungsdienste.
Sie können also Basisdienste konfigurieren zu Anwendungsdiensten,
können das immer wieder modifizieren.
Und wir haben eine solche Architektur hier in Karlsruhe aufgebaut, das
ist das Beispiel, was hier angegeben ist.
Das KIM, das Karlsruher Integrierte Informationsmanagement, setzt also
auf so eine Architektur auf oder hat die hier umgesetzt.
Darin haben wir in einem längeren Projekt das Studierendenportal
entwickelt, das ja leider, also dieses Frontend, jetzt weitgehend
ersetzt wird durch ein anderes System, aber es ist auf jeden Fall ein
gutes Beispiel für eine serviceorientierte Architektur gewesen.
Okay, dann, wenn Sie jetzt einen Rechner konkret einsetzen, werden Sie
in der Regel unterschiedliche oder häufig unterschiedliche Aufgaben,
Profile haben.
Es kann sein, dass Sie tagsüber Dialogbetrieb haben, nachts ein
Betrieb, wo Sie im Batchbetrieb Sachen laufen lassen.
Typischer Fall bei Finanzdienstleistern, die halt nachts alle
möglichen Verwaltungsdinge ablaufen lassen und dann tagsüber natürlich
den Dialogbetrieb in ihren Filialen unterstützen müssen.
Und Sie haben dann eben mehrere Betriebsarten manchmal gleichzeitig in
einem Rechner zur Verfügung und müssen dann dafür sorgen, dass die
fair die Ressourcen Ihres Rechners zur Verfügung bekommen.
Also, heutzutage sind eben solche Rechner sehr komplexe Strukturen.
Wenn Sie Gelegenheit hatten, vielleicht mal Praktikum zu machen bei
einem Finanzdienstleister, also einem Service-Dienstleister wie
Fiducia in Karlsruhe oder so, einer der größten Dienstleister, die die
genossenschaftlichen Banken, für die die Software anbieten, das sind
sehr komplexe Rechensysteme, die dort vorgehalten werden müssen.
Okay, und damit kommen wir jetzt nach dieser Übersicht über das Grobe,
über so die Gesamtdinge, kommen wir jetzt wieder runter auf die
einzelnen Teile, auf die einzelnen Prozesse.
Was ist eigentlich ein Prozess?
Das ist ein zeitlicher Ablauf einer Folge von Aktionen eines Rechners,
die zu einer identifizierbaren Einheit, also einer funktionellen
Einheit zusammengefasst sind.
Das kann ein Sortierprogramm sein oder ähnliches, kann aber eben sein,
dass Sie einen größeren Auftrag haben, der mehrere solcher Prozesse
enthält.
Charakterisiert durch das Programmstück, durch die Daten und den
Ausführungskontext.
Und dann hat jeder Prozess einen eigenen Kontrollfluss, einen eigenen
Adressraum.
Und es gibt eben dann auch noch diese leichtgewichtige Variante von
Prozessen.
Das sind dann die Threads, die einen eigenen Kontrollfluss haben, aber
einen gemeinsamen Adressraum.
Und das ist halt eine wichtige Variante.
Mit Threads kann man halt sehr gut programmieren und durch den
gemeinsamen Adressraum können die eben auch Informationen sehr gut
austauschen.
Es gibt aber dann sofort auch eine Reihe von Problemen, auf die wir
gleich noch genauer eingehen.
Also das sind Prozesse, Einheiten, die einen eigenen Kontrollfluss
haben, ausführbar sind, zusammen mit den Daten, die dort verarbeitet
werden sollen.
Und ich kann dann eben mehrere solcher Prozesse, auch dann, wenn es
geht, zeitlich verzahnt ausführen.
Die leichtgewichtigen Prozesse haben den Vorteil, dass ich schneller
umschalten kann zwischen den einzelnen Threads.
Ich kann schneller umschalten, weil ich nicht die Adressbereiche
erstmal noch schützen muss, sondern die arbeiten halt auf dem gleichen
Speicherbereich.
Aber man muss eben aufpassen, dass man keine Konflikte erzeugt.
Ich kann über diese mehreren Programmier- oder Programmfäden, diese
Threads, eben den Prozesse auch besser auslasten.
Da hatten wir schon mal drüber gesprochen, bei dem Hyperthreading,
dass wir dort in der Lage waren, durch das verzahnte Ausführen von
solchen Programmfäden in der Lage waren, die zeitlichen Abstände
zwischen der Ausführung einzelner Anweisungen eines Threads zu
verlängern, sodass wir weniger Probleme mit dem Leerlaufen einer
Pipeline haben, weil die logischen Abhängigkeiten nicht mehr zu einem
Leerlaufen der Pipeline führen.
Und jetzt müssen wir uns die einzelnen Zustände eines Prozesses
anschauen.
Es kann sein, dass ein Prozess initiiert ist zunächst mal, das ist
also das Erste, dann wird er irgendwann bereit.
Das heißt, er besitzt, also initiiert ist er, er muss erstmal bekannt
gemacht werden.
Ich muss ihn starten, er muss abgelegt sein irgendwo, aber er ist
nicht notwendigerweise schon so weit, dass er ausgeführt werden kann.
Er ist bereit in dem Augenblick, wo alle seine Daten im Hauptspeicher
zur Verfügung stehen und also nichts mehr erforderlich ist, also ihn
nichts mehr daran hindert, ausgeführt zu werden.
Und aktiv ist er erst dann, wenn der Prozessor tatsächlich die Befehle
dieses Prozesses ausführt.
Dann wird er tatsächlich ausgeführt, davor wartet er noch auf die
Ausführung.
Wenn jetzt während der Ausführung zugegriffen wird, auf einmal auf
einen anderen Speicherbereich, der nicht oder auf ein anderes Datum,
das noch nicht im Speicher oder im Hauptspeicher zur Verfügung steht,
dann wird er blockiert, dann muss zunächst mal die entsprechende Seite
aus dem Hintergrundspeicher geladen werden.
Erst wenn das erfordert oder wenn das erfolgt ist, kann er wieder
deblockiert werden, kann wieder bereit werden.
Irgendwann wird er hoffentlich fertig, dann ist er terminiert.
Und das schauen wir uns jetzt nochmal genauer an.
Das sind die unterschiedlichen Übergänge, die hier möglich sind.
Sehr vereinfacht, also von initiiert bis irgendwann terminiert und
dann eben hier eventuell paar mal eine Schleife, bis man dann
tatsächlich fertig ist.
Also von initiiert nach bereit geht man dann über, wenn halt alle
Betriebsmittel da sind.
Das wird im Betriebssystem gesteuert durch den sogenannten Job
Scheduler, der also sagt, hier diese Prozesse sind jetzt bereit und
können ausgeführt werden.
Und von bereit nach aktiv.
Das ist etwas, was der CPU Scheduler macht, der Prozessumschalter, der
dann eben konkret jeden Prozess, der hier drin sitzt und bereit ist,
den dann hier rüber schiebt zu den Aktiven, die ihm die Prozesse
zuteilt.
Dann kann es sein, dass er aktiv ist, aus irgendeinem Grund
unterbrochen werden muss, vielleicht weil ein höher priorisierter
Prozess ausgeführt werden muss.
Dann wird er wieder unterbrochen, wird wieder bereit sein.
Und es sei denn, er hat eben selbst einen Grund, warum er noch auf
etwas anderes warten muss, dann ist er halt blockiert.
Von bereit nach aktiv ist es so, der wird dann ausgeführt.
Wenn jetzt ein Prozess unterbrochen wird, von aktiv nach bereit, dann
muss natürlich dafür gesorgt werden, dass der Prozess anschließend
auch wieder sinnvoll ausgeführt werden kann.
Das heißt, die aktuelle Information über den Prozess muss
abgespeichert werden, damit ich anschließend den unterbrochenen
Prozess weiter ablaufen lassen kann.
Diese Informationen kommen in einen sogenannten Prozesskontrollblock
und gehen dann in einen Speicherbereich, den man auch Monitor nennt.
Da sind also die Prozesskontrollblöcke der gerade bereiten Prozesse,
die schon einmal ausgeführt worden sind, damit sie wieder fortgesetzt
werden können.
Dann haben wir den Übergang von aktiv nach bereit.
Hatte ich schon gesagt, ein dringender Prozess muss ausgeführt werden.
Betriebssystemprozess oder was immer.
Oder ein Echtzeitprozess.
Und das macht wiederum die Prozessauferwaltung.
Der Übergang dann aktiv nach blockiert passiert halt dann, wenn zum
Beispiel eine Eingabe erforderlich ist durch den Benutzer oder eben
eine Seite aus dem Hintergrundspeicher in den Hauptspeicher geladen
werden muss.
Das macht dann die Betriebsmittelverwaltung, die dafür sorgt, dass die
Ressourcen bereitgestellt werden.
Und von blockiert nach bereit geht es dann halt wieder, wenn die
Ressource da ist, die Blockadegründe aufgehoben sind.
Auch wieder Betriebsmittelverwaltung für zuständig.
Das sind diese unterschiedlichen Möglichkeiten der Zustandsübergänge.
Und richtig, es fehlt noch der letzte von aktiv nach terminiert.
Nur von aktiv wird man nach terminiert gehen können.
Und danach muss natürlich dafür gesorgt werden, dass alles, was über
die Ausführung oder während der Ausführung des Prozesses noch
zusätzlich an so Verwaltungseinheiten abgelegt wurde, auch wenn dann
gelöscht wird.
Es müssen die vorher belegten Betriebsmittel freigegeben werden.
Und das sind manchmal Probleme, wenn es dazu kommt, dass Prozesse
gestartet werden, aber nicht ordnungsgemäß terminieren.
Dann kann es passieren, dass die Rechner irgendwann stehen bleibt,
weil der einfach zu viele noch aktiven Prozesse im System hat.
Und dann läuft auf einmal der Bereich über, in dem sie Prozesse
verwalten.
Das kann zu Problemen führen.
Es sind beliebte Angriffe auf Rechner, dafür zu sorgen, dass man
beliebig Prozesse erzeugt und die man dann irgendwann abbricht, sodass
die nicht ordnungsgemäß terminieren.
Es ist wichtig, dass die Betriebsmittel festgelegt werden.
Das muss der Prozesskontrollblock gelöscht werden.
Und dann ist wieder Ressource da für den nächsten Prozess.
Auch das macht die Betriebsmittelverwaltung.
Und damit können dann diese einzelnen Prozesse ausgeführt werden, über
diese verschiedenen Zustände, die sie annehmen können.
Und jetzt können wir zu den konkreten Betriebssystemaufgaben kommen
und sie genauer anschauen, die wir haben.
Wir hatten gerade gesehen, wir haben Betriebsmittelverwaltung und
ähnliches.
Und jetzt ist die Frage, wie wird denn eigentlich Rechenzeit jetzt
zugeteilt?
Also der Prozessor ist frei.
Welcher Prozess, aus der Menge der bereiten Prozesse, wird denn als
nächstes jetzt genommen?
Dafür gibt es verschiedene Arten.
Ich hatte es vorhin schon angedeutet.
Es kann sein, dass man eine einfache Warteschlange hat.
First in, first out.
Ich möchte aber dafür sorgen, dass das Ganze auch möglichst fair
zugeht.
Wenn jetzt hier ein sehr dicker Prozess drin ist, der sehr lange
dauert, und hier ein paar sehr kleine Prozesse, dann müssen diese
kleinen Prozesse lange warten, bis dieser dicke Prozess irgendwann
fertig ist.
Und insbesondere, wenn jetzt hier ein Prozess wäre, der dringend
ausgeführt werden muss, es wird gerade einer ausgeführt, der sehr lang
ist, dann muss man sehr lange warten.
Dieses FIFO ist eine sehr einfache Art, oder First Come, First Serve,
aber es ist nicht unbedingt immer sinnvoll, das zu machen.
Und deswegen hat man sogenannte Prioritätszuteilungsverfahren.
Und hier hat jeder Prozess eine Priorität.
Es sei eine Priorität P von P dem Prozess gegeben, irgendeine Zahl,
und die mit der höchsten Priorität werden ausgeführt.
Ich habe hier ein Beispiel.
Also höchste Priorität haben die Betriebssystemprozesse, niedrigste
Priorität die Batch-Prozesse, und dazwischen sind dann halt die
verschiedenen anderen interaktiven Übersetzungsprozesse,
Editierprozesse, die entsprechend unterschiedliche Anforderungen haben
an die Antwortzeiten, mit denen sie ausgeführt werden.
Dann hat man entsprechend eben solche Klassen, unterschiedliche
Priorität, von 1 bis K, und die natürlich jeweils des Jungs sind.
Und es wird dann jeweils Rechenzeit zugeteilt an die Klasse, die die
höchste Priorität hat.
Wenn die leer ist, kommt dann die nächste dran.
Und innerhalb einer Klasse, gemäß First Come, First Serve.
Das also zu dieser Ausführung, wenn es prioritätsgesteuert ist.
Es gibt auch Prioritäten, also das eine ist hier, dass sie das nach
Funktion machen, das andere wäre, dass sie sagen, das hatte ich vorhin
schon angedeutet, kürzere Prozesse vielleicht eher auszuführen,
Shortest Job First.
Das führt dazu, dass die langen Prozesse eventuell ewig warten müssen.
Geht natürlich auch nicht.
Dann müsste man Wartezeit und erwartete Laufzeit kombinieren.
Dass also ein Prozess zwar eine lange Laufzeit hat, wenn er aber lange
wartet, bekommt er trotzdem eine höhere Priorität und kann dann
ausgeführt werden.
Dafür gibt es verschiedene Verfahren.
Und ein sehr Standardverfahren, das in den meisten Rechnern
durchgeführt wird, ist das sogenannte Round Robin Verfahren.
Round Robin heißt, wir haben sowas ähnliches wie eine Zeitscheibe.
Also wir haben eine Zeitscheibe mit einer festen Zeitspanne und jedem
Prozess wird Rechenzeit zugeteilt entsprechend dieser Zeitscheibe,
dieser Zeitspanne.
Das heißt, wir haben hier diese Scheibe, die sich so im Prinzip
virtuell dreht.
Und das ist der Prozessor, der also jeweils für einen gewissen
Zeitraum für einen Prozess zur Verfügung steht und danach kommt der
nächste Prozess dran.
Das heißt aber auch, ich kann einen Prozess nur eine bestimmte Zeit
ausführen und dann muss er unterbrochen werden.
Ein kleines Beispiel dafür.
Stellen Sie sich vor, Sie haben vier Prozesse, die brauchen
unterschiedlich viel Zeit.
Sie haben eine Zeitscheibe von 16 Millisekunden und jetzt wollen Sie
diese vier Prozesse mit Zeitanforderungen von 56 bis runter zu 12
Millisekunden ausführen.
Sie fangen an mit dem ersten Prozess.
Prozess 1 bekommt seine 16 Millisekunden, wird danach unterbrochen.
Dann kommt Prozess 2 dran, der braucht nur 12 Millisekunden, ist also
zum Zeitpunkt 28 fertig.
Prozess 3 kommt dran, der braucht auch nur 12 Millisekunden, ist also
nach 40 Sekunden insgesamt fertig.
Danach kommt Prozess 4.
Prozess 4 braucht 32 Millisekunden, der braucht also eine ganze
Zeitscheibe.
Und dann ist Prozess 4 fertig.
Dann kommt wieder Prozess 1 dran, der kann nochmal 16 Sekunden
ausgeführt werden.
Dann anschließend, Prozess 2 und 3 waren fertig, Prozess 4 braucht
nochmal 16 Millisekunden und danach kann zwei Zeitscheiben lang, wobei
die nächste nicht mehr ganz ausgenutzt wird, der Prozess 4 fertig
ausgeführt werden.
Also die 112 sind natürlich da hinten, die 104 ist der Zeitpunkt, da
sind die Zahlen ein bisschen verrutscht.
Also das ist so eine typische Ausführung bei diesem Round-Robin
-Verfahren.
Hier hat man so einen Kompromiss zwischen dem Shortest-Job-First und
dem First-Come-First-Served.
Das heißt, lange Prozesse werden nicht zu lange benachteiligt, aber
sie können die anderen nicht blockieren.
Ein kurzer Prozess bekommt auch seine Chance, entsprechend dieser
Round -Robin-Zeitscheibe, die da rumläuft.
Wenn er kurz ist, ist er innerhalb einer Zeitscheibe fertig und der
nächste kann ran.
Wenn er lang ist, muss er halt warten, bis wieder diese Zeitscheibe
bei ihm angekommen ist.
Also das ist das Zeitscheiben-Verfahren, das Round-Robin-Verfahren.
Es setzt natürlich vor, dass man in der Lage ist, die Prozesse zu
unterbrechen.
Deswegen nennt man das auch Preemptive Scheduling.
Das heißt, Sie unterbrechen einen Prozess, bevor er ausgeführt ist,
brauchen dann den Prozesskontrollblock, müssen den abspeichern und
können ihn später nochmal neu ausführen.
Das andere, das Non-Preemptive Scheduling, das waren also die
Verfahren, die ich Ihnen auf der vorigen Folie vorgestellt habe.
Prioritätsgesteuerte Verfahren, bei denen Sie einfach First-Come,
First -Served machen oder Prioritätsweise die aussuchen, das ist dann
Non -Preemptive.
Da wird ein Prozess nicht unterbrochen.
Beides kann man machen und sind aber eben völlig unterschiedliche
Arten der Prozessausführung.
Und es ist klar, Sie brauchen diese ganze Mimik mit den
Prozesskontrollblöcken und den Monitorbereichen bei dem Preemptive
Scheduling.
Aber Sie haben eine größere Fairness für die Ausführung der Programme.
So, das ist die Zuteilung des Prozessors.
Das andere ist die Zuteilung des Speichers.
Wer bekommt eigentlich Platz im Hauptspeicher?
Wie kann man dafür sorgen, dass der Hauptspeicher bestmöglich
ausgenutzt wird?
Dazu gibt es eben ein Standardverfahren, das sogenannte Paging
-Verfahren, ein Seitenwechsel.
Klassisches Verfahren, um den Speicherbereich, die Speicherhierarchie
bestmöglich zu administrieren.
Und dafür gehen wir zunächst mal davon aus, dass wir einen Adressraum
haben.
Wir haben K-Bit-Adressen.
Dann hätten wir zwei Hoch-K-Adressen.
Und jeder Adresse ist eine Zahl aus dem Adressraum.
Und dann gibt es noch den Speicherraum.
Das ist der konkret zur Verfügung stehende Platz auf Ihrem
Hauptspeicher.
Also Sie haben Ihren Hauptspeicher.
Da haben Sie einen gewissen Adressumfang.
Und dann gibt es aber außenrum den gesamten virtuellen Raum, den
Adressraum.
Das sind die Adressen, die Sie prinzipiell ansprechen könnten.
Also bei einer 32-Bit-Maschine können Sie 2 hoch 32 verschiedene
Adressen im Prinzip ansprechen.
Und damit ist das dann Ihr Adressraum.
Der Speicherraum kann kleiner sein.
Wenn der Speicherraum größer wäre, könnten Sie den gar nicht
adressieren.
Das hatte ich schon mal darauf hingewiesen.
Bei einer 32-Bit-Maschine macht es keinen Sinn, mehr als 4 GB
Hauptspeicher zu verwenden.
Man kann insgesamt natürlich damit von einem virtuellen Speicher
reden.
Man muss ihn entsprechend dann abbilden auf den Hauptspeicher.
Und Sie haben zunächst mal virtuelle Adressen und müssen dann dafür
sorgen, dass Sie die reale Adresse aus der virtuellen Adresse geeignet
berechnen können.
Sie müssen also diese Überführung geeignet abbilden.
Sie haben die virtuellen Adressen eines Programms.
Und die Vereinigung aller virtuellen Adressräume muss natürlich
irgendwo im Adressraum abbildbar sein.
Und der Speicherraum ist in der Regel eine Teilmenge der virtuellen
oder der Adressbereiche eines Programms.
Das heißt, es kann sein, wenn ein Programm einen größeren Adressraum
hat oder benötigt, als der Speicherraum zur Verfügung stellt, dann
kann das Programm nicht vollständig im Hauptspeicher sein.
Dann müssen Sie ständig während der Ausführung des Programms die
gerade benötigten Bereiche in den Hauptspeicher nachladen.
Das also jetzt zu diesem Adressraum, Speicherraum.
Sie haben den Hauptspeicher, Sie haben den Hintergrundspeicher.
Und jetzt müssen Sie dafür sorgen, dass Sie geeignet Informationen aus
Ihrem Hintergrundspeicher in den Hauptspeicher reinladen können.
Dazu ist das alles angeordnet in Blöcke gleicher Länge, Seiten im
Hauptspeicher, im Hintergrundspeicher.
Man nennt also im Adressraum den Block eine Seite.
Im Hauptspeicher, in dem Speicherraum, nennt man den Block einen
Seitenrahmen.
Warum einen Seitenrahmen?
Weil das hier der Rahmen ist, in den man die Seiten reinladen kann.
In jeden solchen Seitenrahmen können Sie irgendeine Seite reinladen.
Und es ändert sich der Inhalt.
Deswegen ist es ein Rahmen für unterschiedliche Inhalte.
Und die einzelnen Seiten aus dem Hintergrundspeicher können also in
diesen Hauptspeicher hineingeladen werden.
Wie man das macht, muss man sich genauer angucken.
Ich denke, das machen wir besser am Montag, nicht mehr heute.
Ich danke Ihnen für die Aufmerksamkeit und wir sehen uns am Montag
wieder.
Denken Sie daran, sich anzumelden für die Klausur.
Das war es für heute.
Ich wünsche Ihnen einen schönen Tag.