So, schönen guten Tag.
Ich begrüße Sie zur letzten Vorlesung Grundlagen der Informatik 2 in
diesem Semester.
Ich hätte gerne noch in viel mehr Vorlesungen erzählt, weil ich noch
so viel Stoff habe, den ich präsentieren muss, aber wir kriegen das
schon irgendwie hin.
Es gibt schon wieder eine Frage.
Was ist das hier?
Das ist schon die zweite Frage.
Remaining questions, fünf.
Was ist das?
Und alles, da ist überall nichts drin.
Hat irgendjemand fünf solcher Fragen?
Ich hoffe, das ist damit erledigt.
Ich habe Ihnen mehrere Sachen zu erzählen heute Morgen, und zwar
einmal nächste Woche gibt es die Klausur, das wissen Sie alle, Sie
sind alle gut darauf vorbereitet, bereiten sich auch darauf vor.
Damit Sie sich noch besser darauf vorbereiten können, haben wir etwas
für Sie vorbereitet, und zwar diesen Extraservice zur Vorbereitung.
Wir wollen, dass Sie noch mehr Informationen, also noch mehr
Möglichkeiten haben, schon mal zu sehen, was Sie eigentlich können.
Und dafür haben wir unser schönes Nukid-Werkzeug ein bisschen
erweitert.
Es gibt eine so genannte Survey-Funktion oder auch Test-Funktion, wo
Sie Fragen bekommen, also speziell jetzt hier zu den Kapiteln 9 und 10
heute, weil heute gerade erst Kapitel 9 fertig gemacht wird und 10 in
Ausschnitten vorgestellt wird.
Und dann kommen Dienstag fünf Fragen zu Kapitel 7 und 8, Mittwoch fünf
Fragen zu Kapitel 5 und 6 und so weiter, die hier aufgeführt ist.
Und wo kommen diese Fragen hin?
Bekommen Sie, wenn Sie auf die Nukid-Apps draufgehen.
Also Sie müssen Ihr Android-App einmal aktualisieren.
Ich mache das vielleicht automatisch, wenn Sie das so eingestellt
haben, sonst müssen Sie es explizit tun.
Dann bekommen Sie diese Funktion mit da rein, sodass Sie diesen Survey
aufrufen können.
Da sind also dann fünf Fragen freigeschaltet und diese fünf Fragen
können Sie dann beantworten.
Das sind altes Multiple-Choice-Fragen und Sie müssen also entsprechend
dann so viele Punkte anklicken, wie Sie für richtig halten und können
also dort zumindest die Antworten geben.
Sie bekommen dann natürlich auch Rückmeldung, was das richtige
Ergebnis jeweils gewesen ist.
Das wird aber erst später dann jeweils passieren, dann Ende der Woche.
Und wie wird das also aussehen?
Es ist so, dass Sie zwei Möglichkeiten haben, entweder übers Web
reinzugehen.
Da müssen Sie halt erst mal Ihren Namen angeben.
Sie müssen den Kurs wählen und dann gibt es eben diesen neuen Punkt
dort, dass Sie den Survey oder den Test auswählen.
Und dann müssen Sie außerdem das Passwort angeben für diesen Kurs.
Sie wissen, dass das Info 2 ist und so wie es hier steht.
Und dann beantworten Sie die Fragen.
Also der Survey wird angezeigt.
Da müssen Sie auch aufklicken.
Dann werden die Fragen gezeigt und dann sehen Sie hier solche Fragen.
Hier ist es angedeutet auf dieser Seite.
Da steht zum Beispiel die erste Frage, auf welche Art, nach welchen
Verfahren und so weiter.
Also welche Themenarten gibt es?
Das werden Sie heute noch kennenlernen.
Oder dann etwas über Assembler-Sprachen.
Sie haben also eine Reihe von Fragen, die Sie gestellt werden können.
Und dann müssen Sie irgendwann dann tatsächlich unten auf Answer
Survey klicken.
Sie können also hier die Antworten anklicken und schicken das dann ab.
Und das Gleiche können Sie, ja genau, dann können Sie die Survey
Results angeben.
Die werden nach dem Ende freigeschaltet.
Das ist ein T stehen und kein N.
Und dann sehen Sie die Ergebnisse, wie Sie alle geantwortet haben auf
diese Frage.
Ich weiß nicht, wann die richtigen Ergebnisse mitgeteilt werden.
Stehen die auch dabei?
Okay, also die richtigen Antworten Ende der Woche.
Und da gibt es wieder eine Frage.
Dann gibt es also hier entsprechend die Ergebnisse und dann wird, also
die Ergebnisse der Umfrage.
Dann können Sie sehen, ob alle einer Meinung waren oder
unterschiedlicher Meinung.
Und dann geht das weiter mit dem Zugang über die App.
Kennen Sie auch, Sie können dort entsprechend Ihren Namen eingeben,
Kurs wählen, das Passwort wieder angeben, dann die Fragen beantworten,
wobei Sie auf Folgendes achten müssen.
Ich habe das gestern ausprobiert und festgestellt, man sitzt dann hier
und sagt, auf welche Art und Weise und so weiter können hier also
Rechner betrieben werden.
Das sind ein paar Sachen, die Sie anklicken können.
Dann wundern Sie sich, was muss ich jetzt machen?
Ich weiß tatsächlich nichts.
Wir müssen es einfach einmal in die Richtung schieben.
Einfach das einmal rüberschieben, dann kommt die nächste Frage.
Das ist hier leider nicht angegeben im Menü, aber das ist so.
Die weiteren Fragen sind dann angegeben.
Hier oben ist ein kleiner Punkt und da sind also mehrere Felder.
Da sehen Sie dann im Prinzip, wie weit Sie bei dem Survey bereit sind.
Also einfach immer nach links schieben und dann haben Sie entsprechend
die nächsten Fragen, können die beantworten.
Und bei der letzten Frage steht dann unten drunter dieser Button zum
Abschicken.
Das ist, ah ja, sehen Sie, das ist hier Fragen beantworten und dann,
das hier ist Ihnen auch schon alles und Sie können dann also am Ende
hier unten auch noch angeben, dass Sie das abschicken wollen.
Und dann können Sie wieder, also diese Sache fehlt hier, dass man, da
müsste eigentlich noch ein Punkt rein, dass man die Antworten
abschicken soll.
Und dann können Sie anschließend sich die Umfrageergebnisse anschauen.
Gut, also, das ist ein Ding.
Dann hat hier jemand, es ist ja nett, dass Sie mich hier nerven mit
Fragen, die nicht lesbar sind.
Aber okay, das war das, was wir hier im Test Ihnen noch anbieten
wollten.
Und dann kommen wir zum Inhalt der Vorlesung.
Lassen Sie mich gleich da reingehen.
Genau, letztes Mal haben wir uns beschäftigt mit Programmieren.
Ich hatte Ihnen den Rest erzählt, Speicher und ähnlichen Dingen.
Wir hatten uns dann mit Programmiersprachen beschäftigt und hatten
gesehen, dass da verschiedene Ebenen gibt, in denen man Programme
schreiben kann, problemorientiert bis runter zur Maschinensprache oder
Mikroprogrammierung, also sehr maschinennah.
Ich hatte Ihnen etwas erzählt über die Strukturen bei der
Mikroprogrammierung, über diese Mikroprogramm-Einheit, die im Prinzip
nochmal das Gleiche nachbildet, was wir in der CWU auch gesehen haben,
diesen Ablauf, Maschinenbefehlszyklus.
Hier wird ein einzelner Befehl geholt, interpretiert und dann wird ein
Mikroprogramm aufgerufen, um die einzelnen Steuersignale zu erzeugen
für die Ausführung der konkreten Operationen.
Ich hatte Ihnen etwas erzählt über Maschinensprache, über den Aufbau,
ein Adress, zwei Adress, drei Adressbefehle geben kann.
Und dann hatten wir auch ein Beispiel gesehen, das war dieses Beispiel
mit einer einfachen Sprache, 4-Bit-Op-Code, 12-Bit-Operantenadresse,
Operantenadresse M, das war hier oben angegeben.
Und da hatte ich dieses einfache Programm Ihnen dargestellt, um die
Fakultätsfunktion zu berechnen, also in einer sehr einfachen Art und
Weise, sukzessive eine Zahl immer wieder um 1 reduziert und dann das
Zwischenergebnis wieder mit dem neuen Wert multipliziert, sodass dann
am Ende alle Werte von X bis 1 multipliziert sind.
Wir hatten dann gesehen, dass das, das war hier, richtig, das war
hier, Entschuldigung, das erste war ja hier nur die Liste, das war nur
der Befehlssatz, das hier ist das Programm, das X-Fakultät berechnet
und ich hatte Ihnen dann das gleiche nochmal dargestellt in einem
Assembler -Programm, aber bevor ich dazu kam, habe ich Ihnen erstmal
einen wichtigen Teil vorgestellt, nämlich die Adressierungsarten.
Sie wissen, dass wir hier eben die Operandadressen hatten und ich
hatte Ihnen dann die verschiedenen Adressierungsarten vorgestellt.
Es gibt noch durchaus Varianten davon.
Das Wesentliche sind hier zunächst mal diese drei auf der linken
Seite, das unmittelbare, absolute oder direkte und auch die indirekte
Adressierung und dann hier die indizierte, die relative und die
implizite Adressierung.
Auf die Art und Weise kann ich auf die Werte von Operanden zugreifen,
die in einem Befehl spezifiziert werden.
Entweder unmittelbar, direkt, indirekt oder indiziert oder eben
relativ.
Okay, das war dazu.
Da waren drei Beispiele genannt worden, wie das funktioniert, die
implizite Adressierung.
Dann kamen die Assembler-Sprachen.
Ich habe Ihnen gesagt, dass man dort eben etwas mehr Struktur hat.
Man kann besser lesen, was eigentlich passiert.
Das war das Beispiel nochmal mit dem Maschinenprogramm der Ex
-Fakultät.
Ist hier angegeben, jetzt als Assembler-Programm lesbar, nicht einfach
binär notiert mit hier vorne eine Marke, dass ich weiß, wo ich mich
eigentlich befinde, wenn ich irgendwo hinspringen will.
Die Marken sind deutlich angegeben und die Möglichkeit über Pseudo
-Befehle Variablen zu deklarieren, denen man Werte zuweist, die dann
im Programm, in weiteren Programmen verwendet werden.
Und das ist also sehr nah am Maschinenprogramm, aber eben etwas besser
lesbar und dadurch fürs Programmieren besser geeignet als die
Maschinen -Sprache.
Dann kamen die demontierten Programme, auch das habe ich Ihnen letztes
Mal schon kurz erzählt, die Aufteilung in die verschiedenen
Sprachklassen.
Und ich hatte Ihnen dann begonnen, etwas zu erzählen über funktionale
und applikative Programmiersprachen.
Lassen Sie mich hier wieder reingehen in die Präsentation und dann
machen wir weiter.
Hier hatte ich auch Ihnen schon einiges erzählt.
Das war ein Beispiel für ein Programm Merge Sort, bei dem man im
Prinzip rein funktional programmiert, Funktionen definiert und nicht
explizit die einzelnen Anweisungen, die ausgeführt werden müssen.
Eine rekursive Struktur, genau wie man ein Problem eben mathematisch
formulieren kann, kann man das so auch in der Programmiersprache
formulieren.
Dadurch hat man Vorteile hinsichtlich der Korrektheit der Programme.
Allerdings kann es eben passieren, dass man dann eventuell eine nicht
-effiziente Ausführbarkeit kriegt, weil einfach die Darstellung des
Problems so in diesen Gleichungen natürlich auch logische
Abhängigkeiten impliziert, die nicht notwendigerweise genau auf diese
Art und Weise bestehen müssen.
Aber das ist ein anderes Thema, das wir in anderen Vorlesungen dann
genauer eingehen.
Dann gibt es die prädikativen Programmiersprachen.
Prädikativ, Prädikatenlogik kennen Sie aus Grundlageninformatik 1.
Und jetzt ist die Frage, was habe ich mit logischen Formeln, wie kann
ich logische Formeln in Programmen ausdrücken?
Beispiel für so eine Sprache ist Prolog.
Hier kann man auch Dinge ausdrücken, die eigentlich nicht logische
Ableitungen sind.
Also ich kann sogar die Funktion Fakultät in Prolog formulieren, wobei
das eigentlich nicht die klassische Anwendung ist von Prädikaten,
logischen Kalkülen.
Aber ich kann es machen.
Und wie würde ich so ein Prolog-Programm schreiben?
Ich muss in dem Fall ein Prädikat formulieren.
FAK für Fakultätsfunktion mit Argumenten N und M.
Dieses FAK N M bedeutet, dass dieses Prädikat FAK N M ist wahr, wenn N
Fakultät gleich M ist.
Das heißt also einfach, dass der zweite Parameter M ist das Ergebnis
der Berechnung von N Fakultät.
Und das kann ich jetzt als Programm so ausdrücken, dass ich eine Sache
hier postuliere.
Dieses hier ist wahr.
Fakultät von 1 ist 1.
Und Fakultät von N-1, F ist wahr.
Oder wenn das wahr ist, wenn Fakultät von N-1, F wahr ist, dann
impliziert das, dass Fakultät von N, N mal F wahr ist.
Das ist offensichtlich, dass die Fakultätsfunktion, wenn also F die
Fakultät von N-1 ist, dann ist N mal F Fakultät von N.
Ich muss ja nur Produkte 1 bis N-1 noch multiplizieren mit N.
Also diese beiden logischen Formen, diese beiden Prädikate, die hier
so formuliert sind, definieren die Funktion N Fakultät.
Alles, was ich brauche.
Sehr elegante Art, das aufzuschreiben.
Aber jetzt stellen Sie vor, das hier muss jetzt durch ein internes
System umgesetzt werden in entsprechende Abläufe im Rechner.
Dazu müssen Sie das alles auflösen, zurückführen auf die
entsprechenden, also entsprechend diesen Regeln, das alles auflösen.
Sie müssen also dann hier entsprechend, wenn Sie wissen wollen, ob
Fakt N-1, F erfüllt ist, müssen Sie immer weiter runtergehen, bis Sie
dann tatsächlich den richtigen Wert dort rauskriegen.
Und wenn Sie jetzt 6 Fakultät berechnen wollen, dann fragen Sie im
Prinzip, welche Zahl F erfüllt das Prädikat Fak 6 F.
Und das ist halt, basiert dann alles im Prinzip hier.
Am Ende geht das zurück bis auf die Aktion, die hier am Anfang steht,
dass die Fakultätsfunktion von 1 gleich 1 ist.
Alles andere ergibt sich dann über das zweite Aktion, das das.
Also das ist eine logische Programmiersprache, nicht geeignet für
effiziente Berechnungen, überhaupt nicht.
Führt zu sehr aufwändigen Berechnungen, sehr ineffiziente
Berechnungen.
Aber ich kann logische Zusammenhänge dort sehr deutlich, sehr gut
ausdrücken.
In der Wissensverarbeitung braucht man solche Dinge.
Allerdings muss man dann natürlich sehen, dass man das irgendwie auch
effizienter ausführen kann.
Gibt durchaus Transformationen von solchen Programmen auch in
effiziente Form und dann ist es besser zu machen.
Aber zunächst mal so eine kompakte Darstellung halt gut für die
Problemnähe, aber schlecht für die Ausführung im Rechner.
Das muss dann auch umgesetzt werden.
Dann kennen Sie objektorientierte Programmiersprachen.
Das Wesentliche sind hier die Objekte und entsprechende Methoden und
Eigenschaften der Objekte.
Das wissen Sie alles.
Die wesentlichen Prinzipien, die man hier hat, bei objektorientierten
Programmiersprachen ist, dass ich einerseits ein Geheimnisprinzip
habe.
Das heißt, der Zugriff auf die Objekte geht nur über definierte
Methoden.
Ich kann also nur, wenn ich eine Methode zur Verfügung habe, um auf
eine entsprechende Eigenschaft eines Objekts zuzugreifen, kann ich die
anwenden.
Ich kann ansonsten nicht einfach auf Teile zugreifen.
Ich brauche entsprechend die Methoden.
Ich habe ein Vererbungsprinzip.
Eigenschaften von einer Klasse an Unterklassen weitergeben.
Wenn ich also weiß, dass ein geometrisches Objekt eine Fläche hat,
dann kann ich eben auch über irgendwelche Dreiecke, Vierecke, Kreise
Flächen definieren, aber sie werden entsprechend dann weiter
verfeinert.
Das Vererbungsprinzip kennen Sie, da brauche ich nicht genau darauf
einzugehen.
Dynamische Bindung ebenso wichtig.
Sie können eine Funktion Fläche, ich sag mal einfach hier Fläche von
geometrisches Objekt.
Stellen Sie sich vor, Sie haben eine solche Variable, geometrisches
Objekt und dann würde entsprechend, ob das jetzt ein Dreieck ist oder
ein Kreis oder ein Quadrat, würde entsprechend zur Laufzeit die
richtige Methode für die Berechnung dieser Fläche halt entsprechend
dazu geladen werden.
Das heißt, die Zuordnung von Methoden zu Objekten erfolgt dynamisch
zur Laufzeit.
Welche Methode das F dann tatsächlich ist, wird entsprechend dem
konkreten Objekt, das dort drin ist, ob das jetzt Dreiecke sind oder
Kreise oder Quadrate oder Rechtecke, entsprechend ausgewählt.
Sie kennen das.
Das sind die drei wesentlichen Konzepte der objektorientierten
Programmierung.
Alles andere ist natürlich auch da mit drin, aber das sind so die
wesentlichen Unterscheidungsmerkmale zu anderen Programmiersprachen.
Sie schütteln den Kopf da oben.
Ich weiß nicht, warum Sie den Kopf schütteln, aber das ist halt sehr
stark reduziert auf so einige wesentliche Konzepte bei der
objektorientierten Programmierung.
Sie kennen Java, eine Vielzahl anderer Sprachen.
So die klassische Sprache für Objektorientierung ist Smalltalk 80.
Das ist also eine sehr streng objektorientierte Sprache.
Java ist nicht ganz so streng objektorientiert, aber es hat sich halt
sehr stark verbreitet.
C++, auch objektorientiert, aber hat auch sehr viele prozedurale
Elemente da drin und vor allen Dingen ist die Sprache C++ dermaßen
komplex, dass es dort schon manchmal schwierig ist, Software zu
erzeugen, bei der Sie, also wenn Sie hier so lange C++-Programme
haben, da am Ende ein Zertifikat drauf zu machen und zu sagen, das
Ding ist korrekt, ist eine ganz schwierige Aufgabe, weil Sie so viele
Möglichkeiten haben, sehr komplizierte Beziehungen aufzubauen in C++.
Das ist in Java wesentlich einfacher.
Grundsätzlich unterstützen objektorientierte Sprachen den
systematischen Entwurf korrekter, effizienter und wiederverwendbarer
Programme und deswegen, je einfacher eine Sprache ist, desto klarer
können Sie etwas formulieren und wenn Sie sehr viele Möglichkeiten
haben, Ausdrucksmöglichkeiten in einer Sprache, wie zum Beispiel in
C++, dann haben Sie da natürlich auch viele Fehlerquellen.
Das Wesentliche ist immer, wenn irgendjemand anderes als der
ursprüngliche Autor auf so ein Programm raufguckt, muss der in der
Lage sein zu verstehen, was dort geschrieben ist.
Und es hat mal Wettbewerbe gegeben, wer das kleinste C-Programm
schreibt für irgendeine besondere Funktion und da kommen kryptische
Programme raus, die eigentlich niemand verstehen kann auf den ersten
Blick.
Das ist völlig widersinnig, solche Aufgaben zu stellen.
Das kürzeste Programm, das irgendeine Funktion darstellt.
Das Wesentliche ist, dass Programme lesbar, verständlich sind und
wiederverwendet werden können.
Ansonsten ist ein Programm, das einmal geschrieben wurde, wenn es
nicht von anderen gelesen werden kann, völlig wertlos.
Deswegen wissen Sie, Programme, die nicht dokumentiert sind, die keine
Erklärung haben, sind im Prinzip für viele Zwecke wertlos, weil sie
keine Nachhaltigkeit haben.
Werden nicht verändert, die werden neu geschrieben und deswegen ist
das eine verschwendete Investition.
Also man schreibt immer so, dass man möglichst korrekt, effizient und
wiederverwendbare Programme erzeugt.
Das heißt, sie müssen lesbar sein, gut dokumentiert sein und ähnlich.
Das ganz kurze Querschnitt zu Programmen, gehe heute sehr schnell vor,
schneller, noch schneller als sonst, tut mir leid, geht nicht anders.
Ich muss ganz kurz ansprechen, den Aspekt der Sprachübersetzung.
Wenn ich diese verschiedenen Ebenen habe, der Programmierung, muss ich
von problemorientiert runterkommen können auf Maschinensprache.
Und die erste Ebene, die ich angucken muss, da kommt schon der erste,
der sagt, bitte etwas langsamer, geht das durchaus, dann muss ich mir
angucken, wie wird eigentlich ein Assembler-Programm übersetzt in ein
Maschinenprogramm.
Das ist doch ganz einfach.
Also ein Assembler-Programm, wenn da irgendwie steht, LOAD M, das ist
doch praktisch genau der Maschinenbefehl.
Das ist doch ganz einfach.
Also das ist nicht weiter schwierig, muss zunächst mal das erkennen,
muss den Text analysieren und erkennen, dass hier LOAD steht und
dahinter ein M.
Das ist die Syntax-Analyse.
Was Syntax ist, wissen Sie, Grammatiken und ähnliches haben Sie ja
alles mal kennengelernt.
Dann muss ich Pseudo-Befehle ausführen.
Pseudo-Befehle, das war sowas wie X, Beispiel 17 oder so.
Das sind Pseudo-Befehle.
Das kommt dann irgendwann hier vielleicht nicht M vor, sondern kommt
vielleicht irgendwann hier im Befehl.
Da steht X.
Dann weiß ich, aha, da gibt es eine Rolle.
Also Pseudo-Befehle ausführen.
Das Maschinenprogramm kennt keine Pseudo-Befehle.
Dann muss ich Konstanten in Binärdarstellung konvertieren.
Auch das ist klar, wenn hier jetzt steht 17, muss ich entsprechend die
Binärdarstellung geben.
Ich muss den Maschinen-Code erzeugen für den Operationsteil des
Befehls.
Ich muss LOAD umsetzen, entsprechend den Code.
Wissen, wie das geht.
So und so viele Bits dafür zur Verfügung.
Es gibt eine Tabelle, in der man das macht.
Ich muss Adressen berechnen, die durch Symbole oder Ausdrücke gegeben
sind.
Also wenn hier X steht, muss da entsprechend die 17 drin stehen.
Ich muss also das ersetzen.
Das ist aber auch die Ausführung der Pseudo-Befehle.
Hängt auch damit schon zusammen.
Ich muss ein Protokoll machen.
Über einen Assemblierungslauf, also eine Übersetzung, möchten sie
gerne als derjenige, der die Assemblierung angestoßen hat, ein
Ergebnis haben.
Und in diesem Bericht muss drinstehen, insbesondere, ob es dort Fehler
gegeben hat.
Und es muss da stehen, als Protokoll der Analyse dieses Assembler
-Programm.
Ich muss natürlich die Adressrechnungen machen.
Was heißt denn dieses 17 hier überhaupt?
Wenn das eine relative Adresse ist, bezogen auf den Anfang meines
Programms, dann muss ich natürlich relativ das noch umsetzen in das
Maschinenprogramm an der entsprechenden Basisadresse.
Dann muss ich hier überlegen, ist das richtig, das 17 hinzuschreiben.
Das wäre also dann relativ zum Programmanfang.
Da hätte ich nur relative Adressen.
Oder wenn ich weiß, wo das Programm im Hauptspeicher stehen würde,
dann werde ich gleich die expliziten Adressen reinschreiben.
Also die Adressen im Hauptspeicher, was man normalerweise nicht macht,
sondern diese endgültige Umsetzung von relativen Adressen in
tatsächliche physikalische Adressen, das wird erst später durch den
Binder, den Lader gemacht.
Was ist ein Binder und ein Lader?
Ein Programm kann bestehen aus mehreren Teilprogrammen und die müssen
zusammengebunden werden zu einem großen Programm, das dann tatsächlich
in den Rechner geschoben wird und dort ausgeführt, in der CPU.
Dieses Binden, das Zusammenbinden von Teilprogrammen zu einem größeren
Programmstück und dann, wenn ich also Unterprogramme habe und
ähnliches, müssen die an entsprechenden Stellen eingebunden werden und
dann kommt hier die Abbildung irgendwann oder die Aufnahme in den
Hauptspeicher.
Dann weiß ich, dadurch dass das Programm geladen wird, weiß ich,
welche Adresse ein Programm steht und dann kann ich die relativen
Adressen umführen oder überführen in absolute Adressen.
Kann aber auch sein, dass ich absolute Adressen schon von vornherein
drin habe, die müssen entsprechend dann auch dort drin stehen.
Also, was muss hier also gemacht werden?
Ich habe mein Programm und jetzt habe ich in meinem Programm hier
irgendeinen Befehl und da steht ein Sprung drin, das sprengt irgendwo
hin, da ist irgendeine Marke in meinem Befehl.
Diese Marke taucht hier vorne auf.
Aber wenn ich jetzt von oben nach unten hier durchlese, komme ich zu
dieser Marke dorthin und es steht jetzt GOTO irgendwie label.
GOTO meinetwegen L, L für label.
Ich weiß auch gar nicht, wo das ist, weil ich da noch gar nicht
gewesen bin.
Ich habe ja diesen Befehl noch gar nicht gesehen, beim Durchlaufen von
oben nach unten.
Das heißt, ich habe manchmal etwas an Problemen.
Ich habe sogenannte Vorwärtsreferenzen.
Ich mache eine Referenz an dieser Stelle hier, dort oben, auf eine
Marke, die steht erst hier unten.
Das heißt, ich kann die Adresse, die dort eigentlich stehen müsste, im
Maschinenprogramm, dort erst reinschreiben, nachdem ich bis hier unten
durchgelesen habe und erst dann weiß ich, was hier oben stehen müsste.
Deswegen kann ich gar nicht in einem Durchlauf von oben nach unten
alle symbolischen Adressen durch echte Adressen ersetzen, alle Werte
so entsprechend umsetzen.
Ich muss da noch erstmal eine Tabelle aufbauen und in der Tabelle muss
dann eben stehen, ich habe hier eine Marke L und dann muss ich
irgendwann, wenn ich da hinkomme, den konkreten Wert eintragen.
Den Wert irgendwie 17 oder 587.
Und im zweiten Lauf, wenn ich diese Tabelle gebaut habe, kann ich dann
die Werte aus dieser Tabelle, also was immer hier alles drinsteht,
kann ich dann nehmen und das dann konkret übersetzen in die konkreten
Adressen in meinem Programm.
Also erstmal baue ich eine Symboltabelle auf, in die ich die ganzen
Vorwärtsreferenzen erst mal eintrage und sobald ich an der richtigen
Stelle angelangt bin und weiß, auf welchen konkreten Wert sich das
bezieht, dann kann ich das entsprechend umsetzen.
Und im zweiten Durchlauf kann ich dann alles ersetzen und kann das
Programm entsprechend aufschreiben als Maschinenprogramm.
Und das ist hier also so eine Symboltabelle, also zum Beispiel hier
diese Symbole, die alle auftreten, werden mit der Tabelle
reingeschrieben, verwende ich dann, um die Werte hier anschließend
reinzuschreiben, dann kann ich im zweiten Lauf greifen und kann das
Maschinenprogramm erzeugen.
Und dann gibt es noch weitere Angaben, wie Sachen, dass meinetwegen
Adresswert relativ oder absolut ist und ähnliche Dinge, was man noch
an Eigenschaften braucht.
Also das Wesentliche ist, dass Sie sehen, wir haben diese beiden
Läufe, der erste Lauf erzeugt eine Symboltabelle, der zweite Lauf
greift darauf zu und produziert dann im zweiten Pass, im Durchlauf,
das Maschinenprogramm.
Außerdem wird ein Protokoll erzeugt und im Protokoll stehen dann halt
die ganzen Ergebnisse meiner Assemblierung drin, mit Kommentaren dazu
und Angaben, was dort alles so gemacht ist, eine Nummerierung der
Einzelnen, da steht, was dort alles gemacht wird.
Und der Protokollpass erstellt also das Protokoll, gibt dort
Fehlermeldungen an und macht schöne Zeilennummern rein und ähnliche
Dinge.
Zeilennummern stehen natürlich nicht im Maschinenprogramm, aber
inhaltlich im Protokoll.
Es stehen dort Kreuzreferenztabelle, wird angegeben, die Zeilennummer
des Vorkommens jedes Symbols.
Also in der Symboltabelle ist hier irgendein Wert angegeben, irgendein
Wert X.
Es steht ja auch da in dem Protokoll nachher, an welchen Stellen
dieser Wert X überall im Programm auftaucht, also in dieser
Kreuzreferenz.
Und dann kann es noch weitere Dinge geben, für Sonderfunktionen, für
Codeoptimierung, ähnliche Dinge.
Das ist hier jetzt aber nicht angesprochen.
Also Assemblierung, zwei Durchläufe wegen der Vorwärtsreferenz.
Dann kommen wir zum nächsten Schritt, höhere Ebene über Assembler.
Da geht es über Maschinensprache natürlich auch.
Frage, wie kann ich Programm interpretieren?
Ich habe hier mein Programm und gehe hier von oben nach unten durch
und jeder Befehl soll direkt interpretiert werden im Rechner und soll
dort ausgeführt werden.
Also hier wird nicht das gesamte Quellprogramm übersetzt
Maschinensprache, sondern jede Anweisung wird unmittelbar ausgeführt.
Das macht man bei einigen Sprachen.
Typische Beispiele sind BASIC, LISP, HOLOC, APL.
In denen kann man das machen.
Das macht natürlich nur Sinn zur Entwurfszeit.
Wenn Sie zur Ausführungszeit immer wieder ein Programm so
interpretieren müssten, wäre das furchtbar, weil insbesondere dann
Nachteile, wenn Sie irgendeine Schleife haben, müssten Sie bei jedem
Durchlauf die Befehle der Schleife neu interpretieren.
Das macht natürlich auch keinen Sinn.
Für das Debugging eines Programms, für das Überprüfen, ob das, was Sie
geschrieben haben, richtig ist, ist es durchaus sehr hilfreich, solch
eine Interpretation zur Verfügung zu haben.
Dann sehen Sie sofort, ob eine Anweisung, die Sie geschrieben haben,
etwas negativ gewesen ist.
Und deswegen kann man beides kombinieren.
Zunächst interpretiert man, dann übersetzt man und dann hat man ein
effizientes Programm und kann das dann immer wieder ausführen.
Also das ist das, was man normalerweise dann machen kann.
Wobei man dann natürlich, wenn man das so macht, wenn man nur mit
einem Interpreter arbeitet, hat man etwas Schwierigkeiten, Aussagen zu
machen zur Leistungsfähigkeit des Programms.
Aber das kann man ja später noch machen, dann mit der ausgeführten,
mit der übersetzten Version.
Das ist der Interpreter und das, was wir normalerweise einsetzen, ist
ein Übersetzer, ein Kompilierer oder Compiler, der halt das Programm
einer höheren Programmiersprache übersetzt, anschließend ausführt.
Programm einer höheren Programmiersprache heißt also, ich muss hier
einiges erstmal machen.
Ich kann natürlich das Programm, wenn ich hier mein Programm habe, das
wird übersetzt in ein Maschinenprogramm und das kann jetzt konkret
ausgeführt werden.
Das ist also mein Maschinenprogramm, das ist mein Quellprogramm und
das kann immer wieder ausgeführt werden.
Aber wenn ich mir hier irgendwelche Anweisungen anschaue, das wird
ausgeführt im Rechner und da treibt irgendwo ein Fehler auf.
Ist die Frage, an welcher Stelle tauchen oder sind denn dann die
Quellen für diese Fehler im Originalprogramm zu suchen?
Wie wird dann eigentlich so ein Programm tatsächlich übersetzt?
Also Fehlererkennung und Programmtest ist ein bisschen schwieriger,
weil das übersetzte Programm rechnerspezifisch sein kann und deswegen,
es gibt dafür auch entsprechende Werkzeuge, um eine Rückmeldung zu
bekommen, wie sich Fehler konkret auf Quelltext oder aus
Quelltextstücken zusammensetzen.
Aber das erfordert also extra Debugger-Funktionalität.
Beim Kompilieren habe ich normalerweise mehrere verschiedene Schritte.
Das erste ist die lexikalische Analyse.
Das heißt, ich muss erstmal erkennen, dass das ein Programm ist.
Erinnern Sie sich?
Formale Modelle der Informatik.
Wesentliche Frage war, was sind die formalen Modelle, um
Programmiersprachen zu beschreiben?
Dazu muss ich zunächst mal die Bestandteile erkennen.
Also wenn da steht, meinetwegen ein If oder hier eine Klammer auf,
irgendwann Klammer zu.
Solche Teile müssen erstmal erkannt werden als wesentliche
Bestandteile einer Programmiersprache.
Das ist die lexikalische Analyse.
Da habe ich eine Folge von Token, Sie kriegen gleich ein Beispiel
dazu.
Syntaktische Analyse ist das, was wir genannt haben, das Wortproblem.
Bei Grammatiken ist das Programm, das dort steht, ein gültiges Wort
der Programmiersprache.
Also ein gültiges Programm.
Entspricht das den Regeln der Grammatik?
Das heißt, hier muss ich den entsprechenden Ableitungsbaum erzeugen,
der zu diesem Programm gehört.
Aus dem Ableitungsbaum kann ich dann über eine semantische Analyse,
also was bedeutet das alles, entsprechend einen Code generieren in
Assembler oder Maschinensprache oder halt dem Zielprogramm, das ich
hier haben möchte.
Dann kann dahinter noch eine Code-Optimierung kommen.
Also zum Beispiel, wenn Sie eine Schleife in Ihrem Programm haben,
hier ist ein Stück, das soll immer wieder ausgeführt werden.
Und Sie hätten hier einen Befehl, der immer wieder ausgeführt wird,
der aber immer das gleiche macht.
Dann wäre es sinnvoller, so etwas nach oben zu ziehen, einmal den
Befehl oben drüber auszuführen und das Ergebnis dann entsprechend
umzuverwenden.
Solche Dinge gehören mit zur Code-Optimierung.
Bei der Code-Optimierung wird also die Reihenfolge von Befehlen
verändert.
Es werden eventuell Ausdrücke, die immer wieder ausgewertet werden
müssen, hier nur einmal vor der Schleife ausgewertet und dann unten
nur eine Variable verwendet.
Das ist also ein wichtiger Punkt, wobei man auf eine Sache aufpassen
muss.
Sie erinnern sich, dass wir bei dem Thema Gleitpunktzahlen gesagt
haben, bei Gleitpunktoperationen ist es wichtig zu wissen, in welcher
Reihenfolge die Operationen ausgeführt werden, weil
Gleitpunktarithmetik nicht associativ ist, nicht notwendigerweise.
Wenn auf einmal die Code-Optimierung die Reihenfolge von Anweisungen
verändert, kann es sein, dass das Auswirkungen hat auf die Genauigkeit
ihres Programms.
Deswegen gibt es immer die Möglichkeit zu sagen, ich schalte Code
-Optimierung aus, ich möchte, dass genau das so ausgeführt wird, wie
ich das in meinem Quellprogramm hier oben formuliert habe.
Also das sind so wichtige Dinge.
Code-Optimierung kann zu Problemen führen bei Genauigkeit von
Ergebnissen.
Entsprechend diesem Problem, die ich Ihnen genannt hatte, mit der
Ausführungsreihenfolge von arithmetischen Operationen.
Wir hatten das Beispiel da eben dort riesige Fehler auftreten können.
Okay, das zu diesen verschiedenen Schritten.
Jetzt ganz kurz zu diesen verschiedenen Schritten, die hier stehen.
Die lexikalische Analyse, das ist manchmal auch der Scanner, lexical
analysis.
Hier geht es darum, die ganzen Namen in einem Programm entsprechend zu
erkennen, Grundsymbole zu erkennen, Zahlen zu erkennen, Kommentare zu
ignorieren.
Beispiel ist hier, Sie haben ein Programmstatement, if x kleiner 1,5,
wenn irgendwas end in einer anderen Sprache geschrieben als Java,
sondern irgendwas Algol oder Modular oder was immer ähnlichen.
Also eine einfache Formelsprache.
Da muss sowas erkannt werden, muss also stehen hier, if ist natürlich
ein Element der Sprache, dass x ist ein Name, 1,5 ist eine reelle
Zahl, wenn ist ein Element der Sprache und dann steht hier Name gleich
Name plus Name.
Das heißt, bei der lexikalischen Analyse werden die einzelnen Elemente
überführt in eigentlich ihre syntaktischen Elemente, die Platzhalter,
die ich dann brauche, um zu erkennen, ob das ein gültiges Wort der
Sprache ist.
Ein gültiges Wort der Sprache heißt, ich mache ein Passing.
Bei dem Beispiel, hier if Name kleiner reelle Zahl, wenn Name gleich
Name plus Name, brauche ich also entsprechend einen Ableitungsbaum,
den erzeuge ich, bei der so aussehen müsste, dass eben hier aus einer
Anweisung, if-Anweisung gemacht wird und dann entsprechend if Name
kleiner reelle Zahl, wenn Name gleich Name plus Name.
Also, ich habe hier entsprechend einen Ableitungsbaum, Sie kennen
Ableitungsbäume, gottesfreien Grammatiken, wissen Sie, habe ich für
jedes Wort der Sprache einen würdigen Ableitungsbaum, den muss ich
halt erzeugen.
Das heißt, ich übersetze das Programm, ich erzeuge aus dem Programm
den gültigen Ableitungsbaum oder ich reduziere das Wort auf das
Startsymbol und habe dann nebenbei einen Ableitungsbaum erzeugt und
dann wird der Baum genommen und anhand der Struktur dieses Baumes, da
wird also der Syntaxbaum durchlaufen, entsprechend dieser Struktur die
richtigen Assembler- oder Maschinenbefehle zu erzeugen.
Das heißt, ich ordne hier jeder Meta-Variable in jedem Symbol in
meinem Ableitungsbaum entsprechend eine Prozedur zu, die den Code für
dieses Symbol erzeugt.
Das ist alles Dinge, die werden standardmäßig gemacht, kann ich Ihnen
hier nicht genau erzählen, ist Standardinhalt einer Vorlesung über
Compilerbau, die wir Ihnen gar nicht anbieten, weil Compilerbau ein
Standardstoff ist, können Sie in vielen Büchern gut nachlesen, es gibt
Standardfunktionen, um das aufzurufen.
Das ist im Augenblick nicht Thema aktueller Forschung.
Dann kommt Codeoptimierung.
Optimierung ist einerseits die Laufzeitoptimierung und dann die
Speicherplatzoptimierung, habe ich Ihnen kurz angedeutet.
Da geht es darum, dass man mehrfach auftretende arithmetische
Ausdrücke, wenn die immer wieder auftreten, nur einmal ausführen
möchte und deswegen macht man solche Vereinfachungen von
Programmteilen und wie gesagt, das kann dazu führen, dass auf einmal
sich die numerischen Ergebnisse etwas verändern.
Dadurch muss man aufpassen, ob das passiert, wenn ja, muss man
entsprechend vorsichtig damit umgehen.
Codeoptimierung soll eben dazu führen, dass ein Programm besser
ausgeführt wird.
Was natürlich heutzutage ganz wichtig ist, wäre zum Beispiel eine
automatische Parallelisierung.
Wenn Sie einen Multicore-Rechner haben oder einen Minicore-Rechner,
dann wollen Sie, dass Ihr Programm möglichst gut verteilt wird auf
viele einzelne Programmteile.
Das kann man automatisch machen, aber auch da muss man natürlich
aufpassen, dass es gut passiert.
Das wäre aber auch etwas, was Richtung Codeoptimierung gehen würde,
nämlich Optimierung des Programms für die Rechnerarchitektur, die bei
der Ausführung zugrunde liegt.
Das ist ein aktuelles Thema der Forschung.
Wie kann ich möglichst effektiv Programme führen, transformieren,
sodass sie sehr gut parallel ausgeführt werden können und die
richtigen Funktionen immer noch am Ende erzeugt werden.
Okay, dann ganz schnell noch Java Virtual Machine.
Ein besonderes Thema.
Java hat eine besondere Eigenschaft.
Ich möchte gerne dafür sorgen, dass Java plattformunabhängig ist.
Das ist etwas, eine wichtige Eigenschaft, plattformunabhängige
Programmausführung oder plattformunabhängige Erzeugung von Code.
Dazu gibt es auf jedem Rechner, der Java ausführen kann, eine
sogenannte Java Virtual Machine.
Diese Virtual Machine hat einen speziellen Befehlssatz, einen
sogenannten Bytecode.
Wird also jedes Java-Programm in ein Bytecode-Programm überführt auf
dieser Java Virtual Machine und diese Java Virtual Machine wird
interpretiert in entsprechend Maschinenbefehlen auf ihrem konkreten
Rechner.
Das heißt im Prinzip führen Sie etwas auf einer virtuellen Maschine
aus, auf der Java Virtual Machine.
Konkret muss es umgesetzt werden in Befehle auf ihrer konkreten
Architektur, in Maschinensprache.
Das kann möglichst effizient geschehen.
Das wird dann unmittelbar ausgeführt.
Aber zunächst mal wird das Programm übersetzt in entsprechenden
Bytecodes.
Es gibt auch durchaus Programme, die Java-Programme übersetzen in
Maschinencode.
Das sind sogenannte Just-in-Time-Compiler.
Wenn Sie eine Routine in Java aufrufen, wird entsprechend
normalerweise der Bytecode erzeugt.
Wenn die Methode das erste Mal benutzt wird, dann wird der
entsprechende Bytecode in Maschinensprache übersetzt und danach wird
entsprechend diese in Maschinensprache übersetzte Version verwendet
und nicht mehr der Bytecode.
Das heißt, ich habe dann keine Mehrfachübersetzung mehr, sondern habe
sofort den entsprechenden Code auf der Hardware.
Also Just-in-Time-Compiler setzt man ein in Java um das Deutlichen.
Kennen Sie aus den Java-Vorlesungen.
Und manche sind doch echte Compiler.
Und dann habe ich eben nicht mehr die Plattform-Unabhängigkeit für das
Programm.
Dann brauche ich einen entsprechenden Compiler für eine spezielle
Plattform.
Okay, das ist Ihnen alles im Wesentlichen bekannt.
Das war etwas zu Programmiersprachen.
Und damit sind wir mit dem Teil erst mal soweit durch.
Ein paar wesentliche kurze Aspekte der Programmiersprachen.
Und jetzt kommt ein wichtiges Kapitel, das ich Ihnen eigentlich eine
Woche früher hätte vorstellen müssen.
Zuerst mal eine Frage.
Wird im Java-Compiler auch eine Code-Optimierung durchgeführt?
Was werden Sie dort als Option haben, dass Sie den einschalten oder
ausschalten können?
Hängt vom Java-Compiler ab, den Sie zur Verfügung haben.
Verschiedene Varianten.
Und ein vernünftiger Compiler würde Ihnen die Option anbieten, dass
Sie Code-Optimierung machen können.
Und das heißt als Option, das heißt Sie müssen dann auch in der Lage
sein, Code-Optimierung wieder auszuschalten.
Das ist etwas, da hatte ich schon drauf hingewiesen, das sollte man
sprechen.
Ich kann also nicht sagen, im Java-Compiler.
Da gibt es verschiedene Compiler.
Das ist nicht nur einer.
Und damit kommen wir jetzt zum nächsten Kapitel, das ich Ihnen auf
jeden Fall noch präsentieren muss.
Das darauffolgende Kapitel werde ich Ihnen nicht präsentieren.
Kapitel 11, das Kapitel nachher über Dateiorganisation.
Da ist der Inhalt doch für die Klausur am Ende des Sommersemesters,
aber nicht bei der Klausur am Ende des Mittersemesters, weil ich das
hier nicht in der Vorlesung mit präsentieren konnte.
Das war auch ein Standard Vorgehen, weil ich sonst dieses Kapitel
nicht in der letzten Vorlesungsstunde zeige.
Das Betriebssystem ist normalerweise in der vorletzten, spätestens in
der vorletzten Vorlesungsstunde.
Worum geht es bei Betriebssystemen?
Wir haben jetzt alles mögliche angeguckt.
Wir haben bei Rechnerarchitekturen, wir haben uns die Hardware
angeguckt, wir haben uns die Strukturen angeguckt, Speicher,
Programmiersprachen.
Jetzt müssen wir wissen, wie wird eigentlich ein Programm tatsächlich
ausgeführt?
Wir wissen, wie ein einzelner Befehl aus einem Programm geholt wird in
den CPU.
Aber woher weiß ich, wann ein Programm ausgeführt werden muss?
Dazu, oder damit beschäftigt sich das Betriebssystem, die Zuordnung
von konkreten Ressourcen zu den Programmen, die ausgeführt werden
sollen.
Da steht einiges drin.
Ich werde Ihnen nur einen Teil dieser Themen noch wirklich darstellen.
Was macht ein Betriebssystem?
Es muss dafür sorgen, dass die Rechenanlagen einerseits möglichst
einfach und effizient betrieben werden können.
Möglichst wenig Overhead, möglichst sinnvoll ausgenutzt.
Nicht nur von einem, sondern von mehreren Anwendern.
Dazu muss ich wissen, welche Betriebsmittel habe ich eigentlich?
Was sind Betriebsmittel?
Das sind Hardware- und Softwarekomponenten, die ich brauche, um
Programme auszuführen.
Also, ich brauche den Prozessor, ich brauche einen Speicher, ich
brauche ein Ausgabegerät.
Vielleicht brauche ich noch irgendwelche Standardbibliotheken,
Standardprogramme, irgendwelche Bibliotheksroutinen.
Oder ich muss ein Programm haben, um irgendwas drucken zu können.
Es sind bestimmte Dinge noch erforderlich.
Zunächst mal sind es Hardwarekomponenten, aber eben auch einige können
auch Softwarekomponenten.
Und dann muss ich in der Lage sein, den Zugriff von Benutzerprogrammen
auf die Betriebsmittel sinnvoll zu organisieren.
Es darf natürlich nicht so sein, dass ein Benutzerprogramm auf
irgendein Betriebsmittel zugreift und alle anderen, die auch darauf
zugreifen wollten, stehen hier hinter einer Warteschlange und drehen
Däumchen, obwohl der gar nicht so viel mit den Betriebsmitteln macht.
Ich muss dafür sorgen, dass dieses Betriebsmittel möglichst gut
genutzt wird und gleichzeitig jeder Benutzer den Eindruck hat, er hat
exklusiven Zugriff auf die Betriebsmittel.
Das sieht aus wie ein Widerspruch, wird aber weitgehend erreicht durch
intelligente Betriebsmittel.
Also, es geht darum, dass man alle Verwaltungs-, Steuerungs- und
Dienstleistungsaufgaben eines Rechners in Systemprogrammen formuliert.
Und das sind also die Elemente eines Betriebssystems, sind in jeder
Rechenanlage vorhanden.
Das Betriebssystem ist also im Wesentlichen, ist nur Software, das
Operating System.
Alle Systemprogramme einer Rechenanlage sind da zusammengenommen.
Was machen diese alle?
Wir müssen den Prozessor verwalten.
Also, wir müssen organisieren, dass ein Programm Zugriff auf den
Prozessor erhält.
Manchmal auch mehrere.
Ich hatte mal was erzählt über Hyperthreading.
Da waren mehrere Prozesse, die gleichzeitig nebenläufig auf einen
Prozessor zugreifen können.
Ich muss den Hauptspeicher verwalten.
Hauptspeicherverwaltung.
Ich habe Ihnen erzählt, dass wir Festplatten haben,
Hintergrundspeicher oder auch SSDs und müssen Dateien von der
Festplatte in den Hauptspeicher kommen.
Da müssen irgendwann auch andere wieder ausgelagert werden.
Das ist Hauptspeicherverwaltung.
Welches Programm darf wann, auf welchem Platz im Hauptspeicher?
Kommen wir noch kurz darauf zurück.
Programmausführung.
Das ist der Maschinenbefehlszyklus, Laden der Befehle und Daten in den
Hauptspeicher.
Und dann vom Hauptspeicher natürlich, das wäre die konkrete Zugriff
auf den Rechner.
Programmausführung heißt zunächst mal, muss alles erstmal in den
Hauptspeicher haben und dann vom Hauptspeicher noch wieder in die CPU.
Das kennen Sie als Maschinenbefehlszyklus.
Muss auf die Ein-Ausgabe-Geräte zugreifen können.
Entweder direkt oder indirekt.
Lese-Schreib-Operationen auf Ein-Ausgabe-Geräte.
Muss aufs Netz zugreifen können.
Brauche Software, um auf die Sockets, auf die Ports zuzugreifen und
Anwendungsprogrammen zu ermöglichen, dass sie eine Verbindung aufbauen
über TCP und ähnliche Dinge.
TCP, IP, Print-Protokolle, das sind umgesetzt in Systemprogramme, die
halt den Netzzugriff regeln.
Zugriff auf Dateien.
Wie kann ich auf eine Datei zugreifen, auf dem einen Medium, auf dem
anderen Medium?
Ich muss entsprechende Datei-Beschreibungsinformationen
interpretieren, um dann die richtigen Daten rauszuholen.
Muss dafür sorgen, dass nur gewisse berechtigte Personenprogramme
Zugriff haben auf die Betriebsmittel oder auch auf Dateien.
Zugriffsschutz ist ein wichtiger Punkt.
Muss buchführen darüber, was auf dem Rechner passiert.
Wer hat wann was womit gemacht.
Das muss aufgeschrieben werden.
Da hat man ein langes, langes Logbuch und da steht alles drauf, was
gemacht wurde.
Der Systemadministrator kann dann, wenn ein Fehler aufgetreten ist,
nachvollziehen, was dort konkret geschehen ist.
Es sind dann noch eine Reihe weiterer Dienste da.
Editor, Compiler, Debugger, Browser und so weiter.
Alle diese Komponenten sind Programme, werden auch im Rechner
ausgeführt, aber sie haben natürlich, sind nicht genauso Programme wie
die Anwendungsprogramme, sondern sie haben Priorität bei der
Ausführung.
Darauf kommen wir gleich noch zurück.
Also, wenn ich ein Programm ausführe, laufen im Hintergrund alle
möglichen Systemprogramme ab, die die Ausführung meines
Nutzerprogramms dann konkret organisieren.
Gehen wir ganz kurz eine Liste bekannter Betriebssysteme durch.
Das erste, das MS-DOS, Microsoft Disk Operating System.
Ein klassisches Betriebssystem, das sehr große Berühmtheit erlangt
hat, weil es halt die Basis war für viele PCs und es ist halt das
Betriebssystem für PCs gewesen.
Es hatte im Vergleich mit damals verfügbaren anderen Alternativen
haarsträubende Eigenschaften.
Begrenzung auf 640 Kilobyte im Speicher und die Nutzungsschnittstellen
waren katastrophal im Vergleich zu dem, was gleichzeitig bei CPM oder
bei macOS verfügbar war.
Aber die Entscheidung wurde halt getroffen für ein einfaches, ganz
einfaches Betriebssystem für PCs.
Ist ja nur für PCs und für Leute, für Rechner, die Leute direkt
benutzen.
Es war ja nichts für Großrechner, sondern für einzelne PCs.
Da ist ja nicht so wichtig, dass man Nutzerschnittstellen so etwas
anguckt.
Da hat sich die Einstellung heute geändert.
Darauf aufbauen gab es halt eine lange Palette von weiteren Microsoft
-Betriebssystemen, Microsoft Windows.
Fenstertechnik ist irgendwann auch von Microsoft entdeckt worden.
War in Immerstore alles nicht vorhanden.
Menüsteuerung von irgendwelchen Befehlen und ähnliches hat man alles
nach und nach, hat man das alles in die Microsoft-Programme
übernommen.
Das war bei den Konkurrenten, insbesondere bei macOS, alles von Anfang
an mit drin.
Und irgendwann ist alles übernommen worden in die Windows-Version.
Mittlerweile sind wir bei Windows 8.
Ob das immer Vorteile sind, wenn man von einem Betriebssystem ins
nächste geht, kann man sich überstreiten.
Wir haben halt immer wieder neue Versionen mit neuen Eigenschaften,
die immer besser auf unsere Anforderungen eingestellt sind.
Ich will auf die Einzelheiten gar nicht genauer eingehen, aber es gibt
halt hier inzwischen insbesondere mehr Benutzerbetrieb.
War früher bei den Microsoft-Betrieben nicht vorhanden.
Also zum Beispiel bei Windows 95, 98 war es so, dass es auch einen
Zugriffsschutz gab.
Man musste sich anmelden, wenn man einen Rechner nutzen wollte.
Aber wenn man das Passwort vergessen hatte, hat man einfach dieses
Anmeldefenster weggeklickt und konnte darauf zugreifen.
Das heißt, da war einfach ein Fenster aufgemacht worden, das einem
vorspiegelte, ich muss mich erstmal anmelden, um den Rechner zu
nutzen.
Da war keine wirkliche Funktion dahin.
Und das ist einfach ein Witz.
Und das war anderen selbstverständlich immer so, dass es verschiedene
Benutzer gab, die auch von Ferne auf den Rechner zugreifen konnten.
Ist inzwischen alles anders.
Sie wissen das.
Inzwischen hat Microsoft ein ganz fantastisches Zugriffssystem.
Die haben auch Nutzerverwaltungssystem, Active Directories und so
weiter.
Sehr gutes System, besser als manche andere.
Also ich will nicht schlecht reden über die Microsoft-Produkte, aber
es war einfach so historisch, dass die nicht immer diejenigen waren,
die die innovativen Ideen als erste gehabt haben, sondern die sind
häufig von anderen gekommen.
Mehr bei der Luxusvariante hier von Apple.
Und dann gibt es natürlich die Betriebssysteme, auch auf kleineren
Geräten, auf unseren Smartphones, Handys, kleineren Dingen.
Selbst auf Smartcards können Sie Windows-Betriebssystem laufen lassen.
In Ihrem Auto sind einige, sind auch Windows-Programme, kann auch
Windows -Programm laufen.
Also es gibt Windows-Betriebssysteme für eine Vielzahl von
verschiedenen Geräten, auf denen Informationsverarbeitung ausgeführt
wird.
Macht natürlich Sinn, weil man dann innerhalb einer
Betriebssystemumgebung arbeiten kann und nicht zwischen verschiedenen
Betriebssystemen hin- und herspringen.
Dann ganz kurz aufgeschrieben hier, obwohl ich es schon gesagt habe,
Macintosh Operating System, also die Alternative von Apple.
Hier wurde wirklich Pionierarbeit geleistet, was so Fenstertechnik,
Maus - und Menügeführte, Benutzungsschnittstellen angeht.
Da war also Windows weit von weg.
Multiprogrammbetrieb, Netzwerkfähigkeit.
Es gab Apple Talk als ein ganz wichtiges Protokollseiten, als ein
entfernter Zugriff auf PCs, die mit Windows-Betriebssystemen gar nicht
möglich war.
Da haben die Apple-Rechner schon über Apple Talk miteinander
kommuniziert.
Man konnte verteilte Systeme aufbauen, was mit PCs damals nur sehr,
sehr, sehr, sehr schwer möglich war.
Mittlerweile ist eben das Betriebssystem der Macs oder von Apple auch
Unix -basiert.
Ab OSC war das der Fall.
Jetzt gibt es das sogenannte iOS für iPhone, iPad und ähnliche Dinge.
Alles Weiterentwicklungen der ursprünglichen Systeme.
Dann, das sind die ganzen kleinen, Personal Computer oder Handheld
-Computer.
Dann gibt es das Unix-Betriebssystem für größere Rechner, überwiegend
also für Workstation und Server und Rechnernetzen mit allen möglichen
Varianten.
Da haben die einzelnen Firmen unterschiedliche Varianten entwickelt.
Also sie haben HP, UX, IBM AIX, Solaris kommt von Sun.
Und dann gibt es noch das BSD 4.X, das System 5.
Das sind Betriebssysteme, die wurden an der University of Berkeley
entwickelt.
Also University of California at Berkeley.
Das BSD steht für Berkeley.
Und dann halt eine Variante von Unix.
Dieses Betriebssystem Unix ist halt eine akademische Entwicklung
gewesen, die dann in die Industrie in Großrechner weiterentwickelt
wurde.
Die Variante für PCs war Linux oder auch FreeBSD oder Solaris.
Das sind Derivate, die auf PCs eingesetzt werden oder auf allen
möglichen anderen Rechnern.
Die sind normalerweise kostenlos verfügbar, aber wenn sie das richten,
wenn sie entsprechende Software oder wenn sie Services haben wollen,
zusätzlich zu der Installation von Linux, dann ist es so, dass sie
noch extra dafür zahlen müssen, um eben die Qualitätssicherung zu
bekommen.
Wobei Linux einen wesentlichen Teil ist, ich habe geschrieben,
selbstorganisierte Qualitätssicherung, was heißt das?
Linux unterstützt eine User- oder eine Nutzer-Community oder eine
Entwickler -Community.
Es gibt Linux-Nutzergruppen und wenn sie ein Programm schreiben für
Linux, neuen Systemsoftware für Linux, dann lassen sie das dort über
diese Entwickler-Community im Prinzip testen, kriegen Rückmeldungen.
Sie haben also eine sehr gute Rückmeldung bezüglich
Fehlersuche, Qualitätssicherung von Programmen, während bei den
anderen Betriebssystemen, wie meinetwegen HP-UX oder IBM-IEX, alle
Entwickler von einer Firma kommen und deren Auftrag arbeiten, ist eben
bei Linux das so, dass sie eine riesige Entwicklergemeinde haben, die
Software entwickeln und entsprechend dann auch dafür sorgen, dass die
Qualität stimmt.
Also Linux ist ein bisschen mehr so das frei verfügbare,
selbstorganisierte, die anderen sind halt zentral entwickelt und haben
auch viele gute Eigenschaften.
Und dann gibt es die Großrechnerbetriebssysteme, das MVS, das VMS, den
sehr ähnlichen Namen.
Eigentlich könnte BS 2000 auch MVS heißen, aber es heißt halt BS 2000,
weil es die deutsche Variante des IBM-Betriebssystems ist, hat auch
gewisse andere Eigenschaften, ist auch weiterentwickelt worden und ist
in der Industrie immer noch wahrscheinlich sehr weit verbreitet ein
klassisches Großrechnerbetriebssystem.
Dann kann ich Rechner natürlich sehr unterschiedlich konfigurieren,
wenn ich von Betriebsmitteln rede.
Beim Tablett habe ich hier nur einen Bildschirm und Prozessor plus
SSD.
Beim PC habe ich einen Bildschirm, Festplatte, Drucker.
Beim Großrechner habe ich eventuell hunderte von Bildschirmen,
hunderte von Teilnehmerstationen, an denen gleichzeitig gearbeitet
wird und das muss ich entsprechend alles verwalten können.
Verteiltes System ist halt noch wesentlich mehr.
Ich habe ein riesiges Rechnernetz und muss dieses Rechnernetz geeignet
verwalten.
Macht man zum Beispiel in Grid Computing oder ähnlichen Dingen oder
Sie bauen ein anderes Rechnernetz auf.
Das sind alles Dinge, die dann noch Cloud dazu,
Informationsverarbeitung, Service auf virtualisierten
Rechnerressourcen, die nutzen einen Dienst irgendwo in der Cloud und
ob da jetzt ein Rechner da ist, der das macht oder eine Vielzahl von
Rechnern, die ihre Services ausführen, das wissen sie nicht so.
Also diese Nutzung der Cloud.
Ein Thema ist da zum Beispiel die Nutzung von Dropbox.
Kennen Sie alle Dropbox?
Ein virtueller Speicher, der von Google zur Verfügung gestellt wird.
Öffentlichen Einrichtungen in Deutschland ist es nicht erlaubt,
Dropbox zu nutzen, weil die Daten in Dropbox alle in den USA liegen.
Geht natürlich nicht.
Könnten auch in den USA liegen.
Möchte man nicht, dass unsere Daten alle irgendwo in den USA liegen,
deswegen darf man Dropbox nicht einsetzen.
Hier für das KIT wird gerade eine Variante entwickelt, die virtuellen
Speicher halt auch so zur Verfügung stellt, aber unabhängig von
firmenspezifischen Dingen.
Also Dropbox sollte man nicht nutzen, beziehungsweise wenn man kein
Risiko eingehen möchte, wenn man sagt, das ist mir egal, ob jemand
meine Fotos sieht oder meine Daten liest, kann man das
selbstverständlich nutzen, ist ein ganz praktischer Dienst.
Also, das sind so die verschiedenen Möglichkeiten, die man dort
einsetzen kann.
Wie ist ein Betriebssystem aufgebaut?
Natürlich nicht als ein monolithisches Programm.
Viele verschiedene Programme und ich hatte Ihnen schon bei der
Kommunikation in Rechnern gesagt, da gibt es verschiedene Schichten.
Bei Betriebssystemen gibt es auch Schichten.
Eine Schichtenhierarchie der Module, die verschiedene Aufgaben haben.
Ganz unten geht es zunächst mal um die konkrete Steuerung der harten
Rechnerressourcen, direkter Zugriff auf Hardware.
Die nächste Schicht greift dann auf diese Funktion zu und die wieder
auf Funktion der Schicht und das heißt, ich kann mich in der
jeweiligen Schicht konzentrieren auf die Objekte, die dort zur
Verfügung stehen und was dann in den konkreten Objekten konkret
passiert, das macht die Schicht unten drunter.
Das ist also etwas, was man generell so macht, eine schichtenweises
Vorgehen bei der Ausführung von größeren Aufgaben und wenn ich also
einen Auftrag ausführen möchte einem Rechner, kann es sein, dass der
Auftrag darin besteht, irgendwie eine Menge von Programmen
auszuführen.
Also ein Auftrag kann aus einer Menge eventuell voneinander abhängigen
Teilaufträgen bestehen.
Und jeder Teilauftrag ist dann ein Auftrag oder Task und jeder Task
ist also eine Folge von Betriebssystemkommandos zusammen mit der zu
ihrer Ausführung nötigen Betriebsmittel.
Das heißt, ich muss wissen, was brauche ich, um diesen Task
tatsächlich auszuführen und die Art und Weise, wie das gemacht wird,
ist dann halt die Betriebsart, wie ein Task ausgeführt werden kann.
Ich habe also zunächst diesen größeren Auftrag, der wird aufgeteilt in
eine Menge von Einzeltasks, die dann aufwarten, dass sie ausgeführt
werden können auf meinen Betriebsmittel-CPU oder Prozessor und dass da
Sachen in Speicher gelegt werden, aus dem Speicher zurückkommen und so
weiter.
Und wie das geschieht, ist etwas, was mit der Betriebsart der
Rechenanlage zu tun hat.
Und da gibt es halt diese verschiedenen Möglichkeiten von
Doppelbetrieb bis runter zu kleinen Serverbetrieben, die jetzt im
Einzelnen vorstellen werden.
Was ist der Stapelbetrieb?
Das Einfache.
Ich nehme ein Programm.
Was ist ein Stapel?
Ein Stapel ist sowas.
Ich habe hier irgendeinen Tisch, da lege ich jetzt ein Stapel von
Aufträgen rauf.
Und dann sitzt hier jemand und der greift sich hier irgendeinen
Auftrag, bearbeitet ihn und legt ihn wieder hier irgendwo hin, dann
landet er da.
Also das ist Stapelverarbeitung.
Ganz einfach.
Ich habe einen Stapel von Aufträgen, die werden in irgendeiner
Reihenfolge ausgeführt.
Und wenn ich das so mache, dann kann derjenige, der hier sitzt, der
das macht, der sich hier ausführt, kann ständig aktiv sein.
Er hat ja ständig seine Aufträge, die ihm dahin gelegt werden und er
kann seine Arbeit so organisieren, dass er seine Zeit bestmöglich
nutzt.
Alle Komponenten werden bestmöglich ausgenutzt.
Das heißt aber auch, dass ein Auftrag, der gegeben wird, bei Übergabe
an den Rechner vollständig definiert sein muss.
Wenn Sie jetzt ein Programm schreiben und im Programm werden Sie als
Nutzer irgendwas gefragt, über irgendwelche Parameter für, meinetwegen
Optimierung, Sie haben irgendein Optimierungsproblem und jetzt wollen
Sie da irgendein Parameter einstellen.
Anzahl der Generationen bei volitionärer Optimierung zum Beispiel.
Dann heißt das, das Programm muss bei der Ausführung bei Ihnen nochmal
nachfragen.
Dann ist es nicht vollständig definiert.
Sie können also im Batch-Betrieb nur sowas durchführen, was
vollständig bekannt ist.
Meinetwegen, ich habe Rechnungen für alle Ihre Mitarbeiter, einmal am
Ende des Monats.
Ein Auftrag, steht alles drin, was gemacht werden muss und am Ende
kommen dann die ganzen Gehaltszettel.
Also ich habe eine zusammenhängende Übergabe so einer Beschreibung
eines Auftrags, kann nicht mehr eingreifen während der Bearbeitung
eines Auftrags.
Keine interaktiven.
Und in der Regel keine strengen Zeitanforderungen hinsichtlich
Beendigung der Aufträge.
Es kann sein, dass ich irgendwelche Deadlines habe, dann muss ich die
entsprechend rausgreifen.
Aber das ist dann etwas, was noch dazukommen könnte.
Zunächst mal habe ich da keine genauen Zeitanforderungen, kann das
optimieren bezüglich meines Rechens.
Typische Anwendungen sind halt so alle möglichen wissenschaftlich
-technischen Berechnungen, alles was nicht interaktiv laufen muss.
Hauptaufgabe ist eben hier, den Durchsatz des Rechners zu erhöhen,
möglichst viel Rechenleistung rauszuholen aus ihrem Rechner.
Dass wir wirklich mit voller Kraft arbeiten.
Das ist Batch-Betrieb, ist für viele Großrechner ein ganz wichtiger
Teil der Betriebsarbeit.
Dann kommt der Multiprogramm-Betrieb oder auch mehr Prozessbetrieb.
Das heißt, Sie haben hier mehrere Prozesse, die gleichzeitig im
Hauptspeicher resident sind.
Wenn das unser Hauptspeicher ist, haben Sie also hier mehrere
Programme, die gleichzeitig da drin sind.
Und hier haben Sie Ihre CPU, das Central Processing Unit und jetzt
wird entschieden, welches Programm tatsächlich ausgeführt wird.
Manchmal werden mehrere nebenläufig ausgeführt.
Das ist eine Frage.
Gustav asked nothing.
Hauptaufgabe hier ist, dass Sie Betriebsmitteln den Prozessen
zuteilen, dass Sie Prozesse aktivieren, blockieren.
Da gehen wir gleich darauf ein, was da passiert.
Und natürlich muss ich auch dafür sorgen, dass ein Programm, wenn das
hier ausgeführt wird, hier in meinem Rechner, dann greift das
natürlich auf den Speicher zu an verschiedenen Stellen.
Ich muss dafür sorgen, dass die Ausführung so eines Programms nicht
dazu führt, dass an anderen Stellen etwas verändert wird.
Also eine gegenseitige Isolation der Prozesse zur Vermeidung und der
Wünsch der gegenseitiger Beeinflussung beim Zugriff auf gemeinsam
benutzte Betriebssynchronisierung von Prozessen.
Wichtiges Thema, müsste ich eigentlich kurz darauf eingehen.
Jetzt haben wir noch eine solche Frage.
Naja.
Ich bin dankbar, dass in der Vergangenheit, wenn dieses Fragetool
benutzt wurde, überwiegend sehr gute Fragen gekommen sind.
Was wir heute haben, habe ich sonst eigentlich nie erlebt, dass da
leere Fragen stehen.
Vielleicht irgendein Fehler im Programm durch eine neue Person.
Weiß ich nicht.
Also, das zu diesen Dingen.
Vorteil des Modiprogrammbetriebs natürlich, dass ich den Rechner
besser ausnutzen kann.
Wenn ein Programm hier ausgeführt wird und es braucht ein
Betriebsmittel, dann ist es sinnvoll, dass ich sofort das nächste
Programm ausführen kann und dieses teilweise ausgeführte Programm
erstmal irgendwo im Wartezustand kommt und später wieder dran kommt.
Ich kann also meine Rechenanlage besser ausnutzen, wenn ich mehrere
gleichzeitig laufende Programme mache.
Und hier ist ein kurzes Beispiel, was da passieren kann.
Wenn ich also ein Programm betrachte, getrennt voneinander, dann habe
ich jeweils solche Programmausführungen.
Ich habe eine gewisse Vereinfachphase, in der ich Daten eingebe, dann
etwas verarbeite und dann etwas ausgebe.
Wenn ich also so einen typischen Stapelbetrieb hätte, wäre das ein
Programm.
Wäre was eingelesen, was ausgeführt, was ausgegeben.
Dann kommt das nächste Programm dran.
Besser wäre es, wenn ich mehrere Programme im Prinzip nebenläufig
ausführen kann.
Das heißt, die Betriebsmittel meines Rechners sind gleichzeitig für
mehrere Programme aktiv.
Hier angedeutet, wenn Sie sich den Teil anschauen, da sind mehrere
Aufträge aktiv.
Und hier wird einfach gesagt, sobald Auftrag A1 die Eingabe beendet
hat, kann ja Auftrag A2 auch die Eingabe schon mal machen.
Währenddessen wird Auftrag A1 ausgeführt.
Und wenn A2 fertig ist mit der Eingabe, macht A3 auch schon mal
Eingabe.
Währenddessen wird hier gerade A2 ausgeführt.
A2 wird gar nicht so lange ausgeführt.
Wenn A3 hier fertig ist, dann mit der Eingabe, wird ausgeführt.
Und ich habe insgesamt eine deutlich kürzere Zeit für die Ausführung
dieser drei Aufträge, als wenn ich die nacheinander gemacht hätte.
Im Prinzip ist es das gleiche Prinzip wie das Pipelining-Prinzip.
Ich habe also, wenn ich mir angebe, welche Zeiten ich hier brauche,
ich habe bei einer zeitlich nicht verzahnten Ausführung halt einfach
die Summe der jeweiligen Eingabe, Verarbeitungs- und Ausgabezeiten.
Und wenn ich das zeitlich verzahnt mache, dann ist das in etwa so das
Maximum für viele Aufträge, das Maximum dieser jeweiligen Zeiten.
Und also Summe der Eingabezeiten.
Die Summe der Ausführungszeiten, Summe irgendwann der Summe der
Eingabezeiten, Ausführungszeiten, Ausgabezeiten.
In etwa die Summe dieser Zeiten, das heißt also das Maximum der
Eingabe, Verarbeitungs- und Ausführungszeiten und nicht die Summe
aller diese.
Eine deutliche Verkürzung kann ich dadurch erreichen.
Das also Pipelining-Prinzip oder Multiprogramm-Betrieb kann meine
Ressourcen besser ausnutzen.
Dann gibt es natürlich auch Dialogbetrieb oder Timesharing-Mode.
Ich möchte gerne, dass jeder Teilnehmer zugreifen kann.
Also jeder, der hier an seinem Rechner sitzt, der möchte gerne
zugreifen auf meinen leistungsfähigen Rechner.
Ist die Frage, wie kann ich das denn erreichen, dass jeder Zugriff hat
und meint, er wäre der einzige Nutzer.
Das geht natürlich nur so, dass ich im Prinzip hier eine Zeitscheibe
habe und jeder einzelne Nutzer hat für einen kleinen Augenblick
Zugriff auf den Rechner und dann ist der nächste dran.
Und da kleiner Augenblick bei der Verarbeitungsfrequenz von
Rechenanlagen durchaus einiges an Verarbeitungskapazität bedeutet,
kann ich hier viele Rechner unterstützen oder viele Teilnehmer
unterstützen, gleichzeitig Aufträge ausführen und aber eben immer
stückweise, jeder hat ein kleines Stück Zeit zur Verfügung, um zu
kommunizieren mit dem Rechner.
Das ist also der Teilnehmerbetrieb im Timesharing-Mode.
Also Timesharing heißt, ich teile meine Zeit, meinetwegen eine Sekunde
auf, in entsprechende Abschnitte für die einzelnen Teilnehmer, die an
so einem Rechner dransitzen.
Der Teilhaberbetrieb ist was anderes, zum Beispiel bei der Buchung von
irgendwelchen Hotels oder ähnlichen kann es sein, dass viele
Reisebüros gleichzeitig auf eine Buchungssoftware zugreifen.
Da habe ich also viele Nutzer einer und derselben Software und auch
das gibt es.
Und ich habe dort eine Möglichkeit der Interaktion, anders als beim
Batchbetrieb, kann ich eben während der Ausführung dann mal kurz noch
interaktiv zugreifen.
Anwendungen sind hier kurz dargestellt.
Teilhaberbetrieb oder Teilnehmerbetrieb habe ich kurz schon erläutert.
Die Hauptaufgabe ist eben, dass man die Antwortzeit minimieren möchte
für den einzelnen Nutzer, während ich beim Batchbetrieb irgendwann
meinen Auftrag abgebe und dann am nächsten Tag schaue, ob das Ergebnis
da ist, kann ich beim Timesharing-Betrieb halt am Rechner sitzen und
direkten Programme ausführen.
Gleichzeitig mit vielen anderen bekomme ich immer ein kleines Stück
der Rechenzeit zur Verfügung.
Dann gibt es in technischen Anwendungen den Echtzeitbetrieb, Real-Time
-Mode, ganz wichtig, also Stichwort cyberphysische Systeme.
Wenn Sie technische Systeme steuern mit Informationsverarbeitung,
müssen Sie manchmal Zeit, also einfach Zeitschranken einhalten.
Wenn Sie zum Beispiel ein ABS-System haben und bremsen wollen, da muss
die Elektronik innerhalb vorgegebener Zeitschranken reagieren.
Das heißt, eventuelle Softwareprozesse müssen entsprechend ausgeführt
werden.
Wenn Sie Drive-by-Wire machen, lenken über Software, das muss
innerhalb von Zeitschranken ausgeführt werden.
Genauso bei Flugzeugsteuerung.
Echtzeitbetrieb ist eine ganz wesentliche Anforderung und das hat
große Auswirkungen auf die Art der Programmierung, auf die Art der
Betriebsmittel, die Sie einsetzen können.
Da können Sie also nicht mehr irgendwie Caches verwenden und
ähnliches, weil Sie dort zugesicherte Ausführungszeiten haben.
Sie brauchen dann geeignete Ein- und Ausgabegeräte dafür, Sensorik,
Steuerungsventile, Sensoren, Aktoren, die halt ihren Echtzeitbetrieb
umsetzen.
Sie brauchen Unterbrechungsmöglichkeiten, aber eben nur innerhalb von
Zeitschranken.
Wenn ein Programm ausgeführt wird, das eine feste Zeitschranke hat,
können Sie natürlich nicht unterbrechen, aber Sie müssen eben dafür
sorgen, dass Sie innerhalb der vorgegebenen Zeitschranken alle
Aufgaben erledigen können.
Hier ist also das Wesentliche, die größtmögliche und zeitgerechte
Verfügbarkeit der Betriebsmittel, so dass die technischen
Anwendungssysteme sinnvoll gesteuert werden.
Dann haben wir noch eine Art Client-Server-Betrieb.
Habe ich auch schon mal darauf hingewiesen, meine ich.
Das war beim Thema verteilte Systeme.
Ich kann viele Funktionen auf irgendwelche Server abschieben, z.B.
Dateiverwaltung, Mail-Server, WWW-Server, Druckersteuerung und
ähnliche und die einzelnen Clients brauchen sich darum nicht zu
kümmern, schicken Auftrag hin zum Server und erledigt die Aufgabe und
kann das dann machen.
Jeder Client kann also über Remote Procedure Calls oder auch Remote
Method Invocation in Java Funktionen auf so einem entfernten Rechner
ausführen und kann dann entsprechend die eigene Arbeit optimieren.
Auch hier eine sinnvolle Verteilung von Aufgaben auf verschiedene
Rechnerkomponenten, so dass man insgesamt eine möglichst effiziente
Ausführung der Informationsverarbeitung erreichen kann.
Was sind hier die Hauptaufgaben?
Hier ist natürlich so, dass ich insgesamt beim Client-Server
-Betriebssystem entfernte Prozessaufrufe möglichst effizient abwickeln
muss.
Dazu muss ich natürlich zunächst mal von meinem Client über eine
Kommunikationsschicht zugreifen können, oder andersrum, muss von hier
unten zugreifen können auf eine entsprechende Maklerschicht.
Dieser Makler muss mit den geeigneten Servers den geeigneten Server
liefern für die Ausführung einer bestimmten Aufgabe und die
Kommunikation mit dem Server geschieht über diese sogenannte
Kommunikationsschicht.
Das ist wieder so eine schichtenorientierte Aufteilung von Aufgaben,
sogenannte Multi-Tier-Architektur.
Tier ist halt ein Tier im biologischen Sinne, sondern eine Schicht.
Multi-Tier-Architektur heißt also eine Mehrschichten-Architektur und
sie haben halt hier, wie gesagt, die Service-Schicht, die
Maklerschicht und die Kommunikationsschicht.
Ein klassisches Beispiel zur Unterstützung solcher verteilten Client
-Server -Systeme ist der Common Object Request Broker, Torba, der ein
solches verteiltes System, der effizient zur Verfügung steht.
Genaues darüber hören Sie in anderen Vorlesungen.
Diese Dinge, die man in der Kommunikationsschicht und Maklerschicht
macht, also diese Sachen hier, werden manchmal aus Middleware
bezeichnet, weil ich hier halt die Kommunikation, spezielle Dienste
zur Kommunikation zwischen verschiedenen Rechnern mit
unterschiedlichen Aufgaben abwickeln kann.
Ganz modern sind Service-orientierte Architekturen.
Bei Service-orientierten Architekturen habe ich im Prinzip meine
Aufgaben aufgeteilt auf Dienste, auf Services.
Zum Beispiel im universitären Betrieb haben wir hier mal im Rahmen der
KIM -Architektur ein Service-Portal entwickelt für unterschiedliche
Dinge, also für Forschung und Entwicklung, Lehre, Studium,
Weiterbildung.
Kennen das Studierenden-Portal, wenn sie auf Studium, KIT.edu
zugreifen.
Das ist die oberste Schicht, ein Service-Portal für Aufgaben.
Da gibt es eine ganze Reihe Anwendungsdienste, die basieren auf
Basisdiensten, die dann wiederum auf technische Infrastruktur
zugreifen.
Und das Ganze wird intern unterstützt durch eine sogenannte Landkarte,
bei der Sie für jeden Service sehen, auf welche Ressourcen der
zugreift, welche Anforderungen er hat.
Und das ist also eine klassische Architektur, auch wie verschiedene
Schichten mit unterschiedlichen Aufgaben.
Einige Basisdienste, man hat Sicherheitsdienste und ähnliche Dinge
oder Zugriffsdienste auf Dateien.
Und dann Anwendungsdienste für spezielle Aufgabenbereiche im
universitären Bereich, halt Forschung, Entwicklung,
Informationsversorgung, Verwaltung oder Lehre, Studium und
Weiterbildung.
Das sind hier diese verschiedenen Themen, die hier eine Rolle spielen.
Wir haben hier das Studierendenportal realisiert auf diese Art und
Weise in einem größeren Projekt.
Wird gerade abgelöst, teilweise durch Campusmanagement.
Das erleben Sie ja täglich, was da alles so passiert.
Dann wird in der Regel nicht nur ein Betriebssystem ausgeführt,
sondern es gibt mehrere Betriebsarten nebeneinander.
Und dann muss ich sehen, dass das entsprechend dem Aufgabenprofil
unterschiedlich gemacht wird.
Deswegen kann ich nachts irgendwelchen Batchbetrieb laufen lassen.
Tagsüber habe ich hier den Timesharing-Betrieb.
Und hier ist also dann die Aufgabe so eines kombinierten Systems dafür
zu sorgen, dass ich jeweils für die richtigen Anforderungen die
richtige Betriebsart zur Verfügung stelle.
Deswegen habe ich dann Betriebsarten gleichzeitig nebeneinander.
Das ist auf Großrechner normalerweise der Fall.
Und jetzt kommen wir zu dem Punkt der Prozesse und Prozesszustände.
Also jeder Prozess... Entschuldigung, das war jetzt zu schnell.
Jeder Prozess, hatten wir gesagt, ist also ein Stück Code, der
ausgeführt werden soll.
Folge von Aktionen eines Rechners, die zu einer identifizierbaren
funktionellen Einheit zusammengefasst sind.
Und ich habe also meinen Programmcode.
Ich habe die Daten, die verwendet werden.
Ich weiß, in welchem Kontext das ausgeführt werden muss.
Was sind sonst noch für Dinge erforderlich?
Ich brauche oder in einem Prozess habe ich einen eigenen
Kontrollfluss, in der Regel einen eigenen Adressraum.
Und eine Variante sind halt die leichtgewichtigen Prozesse oder
Threads, bei denen eben nicht für jeden Thread einen getrennten
Adressraum habe, sondern einen gemeinsamen Adressraum.
Da muss man natürlich dafür sorgen, dass ich hier entsprechend keine
Probleme kriege und hier Werte überschreibe.
Das ist also ein Vorteil von Prozessen, dass sie Teilaufträge zeitlich
verzahnt ausführen können.
Und bei mehrfältigen Programmen können sie zwischen den Threads
einfacher umschalten, sind effizienter ausführbar als Prozesse mit
jeweils einem eigenen Adressraum.
Sie können einfacher kommunizieren, dadurch dass mehrere Prozesse auf
gleichen Bereiche zugreifen können, können sie auch indirekt
kommunizieren, weil ein Thread kann einen Wert setzen, der andere
Prozess Thread liest daraus.
Insofern haben sie dann Möglichkeiten, dass Threads kommunizieren über
ihren gemeinsamen Adressraum.
Und was ich Ihnen hier auf jeden Fall noch zeigen wollte, ist, dass
wir bei einem Prozess verschiedene Zustände haben können.
Können zunächst mal initiiert werden, dann sind sie irgendwann bereit
zur Ausführung, können aktiv sein, blockiert sein oder terminiert.
Und was das Einzelne heißt, wird hier dargestellt.
Das sind die verschiedenen Zustände.
Und jetzt geht es darum, initiiert ein Programm oder Prozess wird
angestoßen.
Ein Sortierprogramm soll ausgeführt werden oder irgendein anderes
Programm, hohen Gehaltsabrechnung.
Dann wird es häufig mehrere solche geben und ein Prozess wird bereit,
wenn hier im Prinzip alle Mittel verfügbar sind, damit er ausgeführt
werden kann.
Braucht also eine Betriebssystemkomponente, zunächst mal ein Prozess,
der initiiert wurde, mit dem Label versetzt oder belegt bereit.
Und dann müssen im Prinzip die Befehle dieses Programms ausgeführt
werden.
Es kann es sein, dass ein Prozess, der hier gelandet ist, bei dem die
bereit sind zur Ausführung, aktiv wird.
Dadurch, dass der Prozess-Scheduler sagt oder der Prozess-Umschalter
sagt, dass ein Programm tatsächlich ausgeführt werden soll in der CPU.
Dann geht also der Maschinenbefehlszyklus los.
Das wird aus den entsprechenden Orten in dem Programm, wenn die
Befehle rausgeholt werden, ausgeführt, bis es irgendwann wieder eine
Unterbrechung gibt, weil ich zugreife auf ein Betriebsmittel.
Ich muss vielleicht eine Eingabe machen oder ähnliches.
Also wenn ich aktiv gewesen bin, tatsächlich ausgeführt werde, kann es
sein, dass ich dann irgendwann unterbrochen werde, weil irgendein
anderer Prozess aktiv werden möchte.
Oder es kann sein, dass ich blockiert werde, weil oder nicht ich,
sondern der Prozess, weil er auf irgendein Ereignis warten muss.
Irgendein Ausgabe-Vorgang oder Verfügbarkeit eines Betriebsmittels und
dann wird er erstmal blockiert.
Ist also in diesem Zustand.
War aktiv, ist blockiert worden.
Sofern oder sobald dieses Betriebsmittel zur Verfügung steht, kann er
wiederum von blockiert auf den bereiten Zustand kommen.
Das heißt, wenn ein Ausgabe-Vorgang abgeschlossen ist, kann er weiter
ausgeführt werden.
Dann geht er wieder in die Schlange der bereiten Prozesse rein und
kann wieder ausgeführt werden.
Und irgendwann wird er natürlich fertig sein, dann kommt er in den
Zustand terminiert, da müssen eine ganze Reihe Dinge noch
abgeschlossen werden, Speicherplastik muss freigegeben werden und
ähnliche Dinge.
Und dann bin ich tatsächlich mit dem Prozess fertig.
Also wird ein Prozess normalerweise hier mehrfach durchlaufen,
beschleifen und irgendwann dann dort rauskommen.
Die Frage, wie man das macht, ist halt etwas der Zuteilung von
Rechenzeit.
Ich habe eine Warteschlange von Prozessen.
Mein Prozessor soll diese Prozesse ausführen.
Wie mache ich das am besten?
Also formal angegeben, ich habe meine bereiten Prozesse, die haben
alle Betriebsmittel im Prinzip zur Verfügung und sollen jetzt nur noch
den Prozessor kriegen.
Der Prozessor P wird frei, ist die Frage, welchem Prozess wird P als
nächstes zugeteilt.
Das einfachste wäre First Come, First Serve oder FIFO, First In, First
Out.
Ich habe eine einfache Warteschlange und muss halt so lange warten,
bis ich dort ausgeführt bin.
Nächste Möglichkeit wäre, dass meine Prozesse eine Priorität bekommen.
Sage meinetwegen, dass der Kürzeste Priorität hat oder einer, der mir
besonders gut gefällt, also irgendwelche Nummern bekommen, die werden
entsprechend der Reihenfolge ausgeführt.
Also ich kann das auch etwas komplizierter machen.
Ich kann sagen, meine bereiten Prozesse sind eingeteilt in
unterschiedliche Prioritätsebenen, L1 bis LK, jeder Prozess ist genau
einer solchen Ebene drin und die Prozesse haben also unterschiedliche
Prioritäten.
Diejenigen in, also P von P ist größer als P von Q, wenn das P in LI
ist und I kleiner J.
Also L1 wird als erstes ausgeführt.
Die Rechenzeit geht halt so, dass sich Rechenzeit zuteile an ein
Programm oder ein Auftrag in einer Menge LI von P, wenn alle anderen,
die eine kleinere Nummer haben, bereits leer sind.
Das heißt, alle mit den kleineren Werten werden zunächst ausgeführt
und innerhalb der Klasse mache ich das mit First Come, First Serve.
Das heißt zum Beispiel, wenn ich Betriebssystemprozesse habe, die
haben die höchste Priorität.
Sobald ein Betriebssystemprozess da ist, wird der ausgeführt.
Und irgendwann werden dann meine interaktiven Prozesse ausgeführt,
Übersetzungsprozesse, Editierprozesse und mit niedrigster Priorität
die Batchprozesse.
Die können irgendwo im Hintergrund ablaufen, die haben keine
Zeitschranken.
Während der Benutzer, der interaktiv was machen möchte, der sitzt am
Rechner und wartet darauf, dass sein Programm ausgeführt wird.
Eine andere Sache ist Shortest Job First, der mit der kürzesten, das
ist also eines der klassischen Verfahren, Shortest Job First, heißt
also die mit der kürzesten erwarteten Rechenzeit, wenn das erste ist
ausgeführt, hat so ein, wenn ich also hier eine Reihe von Programmen
habe, alle unterschiedliche Rechenzeiten haben.
Wenn ich mir die durchschnittliche Wartezeit anschaue, bei First Come,
First Serve und bei Shortest Job First, habe ich bei Shortest Job
First immer eine Verkürzung oder eine durchschnittlich kürzere,
mittlere Wartezeit, weil die kleinen hier müssten ja sehr lange
warten, bis ein langer ausgeführt wird.
Da kann ich lieber wenige lange, länger warten lassen, dafür viele
kleine schneller ausführen.
Ich kann auch Dinge kombinieren, ich kann sagen, ich kombiniere
Wartezeit und Laufzeit und habe dann eine Kombination dieser beiden
Dinge.
Wenn eine längere Zeit nicht ausgeführt wurde, weil er eine so lange
Laufzeit hat, dann wartet er so lange, bis er irgendwann eine
Priorität bekommt und trotzdem ausführt.
Das kann man alles kombinieren.
Ein Verfahren, das hier ein klassisches Verfahren ist, das sogenannte
Round Robin-Verfahren, Zeitscheibenverfahren, ähnlich wie das, was ich
Ihnen beim Timestaring schon angedeutet habe.
Mein Prozess bekommt jeweils eine maximale Prozessorzeit zugeteilt.
Also dieser Prozess PI soll jetzt Rechenzeit zugeteilt bekommen und
dann geht es also nach diesem Round Robin-Verfahren.
Jeder Scheibe ist hier ein Prozess zugeordnet und ich habe also hier
den Prozessor und dieser Prozessor da oben, der soll, wird also seine
Aufmerksamkeit immer für einen bestimmten Zeitabschnitt in einzelnen
Prozessen zuordnen.
So läuft die Scheibe darum.
Und dann kann ich entsprechend meine Programme ausführen.
Das heißt, wenn ich Befehle oder wenn ich Programme habe oder Prozesse
habe, die so wie hier angedeutet ausgeführt werden, sollten der eine
sehr lang, die anderen relativ kurz und ich habe deswegen zehn
Millisekunden zur Verfügung für die Ausführung.
Oder hier steht, ich habe 16 Millisekunden zur Verfügung.
Dann würde ich den ersten ausführen, der steht vorne in der Schlange,
16 Millisekunden lang.
Dann kommt der nächste dran, P2 braucht nur 12 Millisekunden.
Danach kommt sofort der dritte dran, der braucht wieder 12
Millisekunden.
Dann kommt P4 dran, der hat 32, die ersten 16 werden ausgeführt.
Und dann kommt, also jetzt der hier kommt da dran, dann wird wieder P1
ausgeführt mit dem nächsten Stück Rechenzeit, wieder 16 Sekunden.
Danach nochmal P4, dann ist P4 fertig und dann wird entsprechend P1
ausgeführt, sofern nicht schon wieder weitere Prozesse in dieser
Schlange hier angekommen sind.
Also das ist das Round Robin.
Und was hier anders ist bei den anderen Verfahren ist, dass ein
Programm ausgeführt wird, ein Prozess und dann unterbrochen wird und
irgendwann später wieder weiter ausgeführt wird.
Das heißt, ich muss hier einen Prozess unterbrechen.
Und das nennt man ein sogenanntes Preemptive Scheduling.
Das heißt, ein Prozess wird unterbrochen, vorzeitig, wird also
vorzeitig geleert, Preemptive, bevor er fertig ist, wird der Prozessor
geleert, kommt der nächste rein.
Beim Non-Preemptive Scheduling muss ich warten, bis er fertig ist und
selbst raus.
Wenn ich also Preemptive Scheduling mache, wie beim Round Robin, dann
unterbreche ich die, weil irgendein anderes Programm darauf zugreifen
möchte und danach wird halt entsprechend der nächste hier ausgeführt.
Das sind Zeitscheibenverfahren.
Dann müsste ich Ihnen eigentlich jetzt noch einiges erzählen über
Hauptspeicherverwaltung, Paging-Verfahren, was ich jetzt leider hier
nicht mehr schaffe in der heutigen Vorlesung.
Ich müsste Ihnen was erzählen darüber, wie man hier Seiten vom
Hintergrundspeicher in den Hauptspeicher hinein lädt über
Seitentabellen, die dafür sorgen, dass ich entsprechend eine
Seitenadresse in eine Seitenrahmenadresse im Hauptspeicher umsetzen
kann.
Ich müsste Ihnen hier darstellen, was genau passiert über die
Seitentabellen.
Und dann kommt noch das Thema der Synchronisation von Prozessen.
Bei denen man aufpassen muss, dass nicht auf die gemeinsamen
Ressourcen zugegriffen wird und dadurch Reihenfolgen verändert werden
und unterschiedliche Ergebnisse daraus ergeben, je nachdem, in welcher
Reihenfolge ich verschiedene Programme, Programmteile in meinem
Rechner ausführe.
Das ist die Synchronisation.
Dazu gibt es viele interessante Themen, die man durch den
wechselseitigen Ausschluss dann erledigen möchte, also das
Synchronisieren.
Und auch dazu gibt es hier einiges, was man da erzählen können.
Als schönes Abschlussbild möchte ich Ihnen das Philosophenproblem noch
mal kurz darstellen.
Da haben sie ein klassisches Problem, dass sie viele Ressourcen oder
viele Auftraggeber haben, die auf Ressourcen zugreifen wollen.
Die wollen etwas essen, diese ganzen Philosophen hier, brauchen dafür
zwei Gabeln.
Und wenn jeder auf zwei Gabeln zugreifen möchte, aber aus
Sparsamkeitsgründen, wie es halt so ist bei Philosophen, nur zwischen
zwei Tellern nur eine Gabel liegt, dann können eben nicht zwei
nebeneinanderliegende gleichzeitig essen, sondern die müssen sich
irgendwie absprechen.
Wenn sie Pech haben und alle machen immer genau das Gleiche, würden
sie alle gleichzeitig ihre rechte Gabel nehmen und brauchen auch noch
die linke Gabel.
Die hat aber schon der Nachbar und dann warten sie darauf, dass der
fertig wird.
Der wird nie fertig.
Wir haben auf einmal Probleme wie Deadlock, Starvation im wahrsten
Sinne des Wortes, weil die nicht essen können oder Fairness, dass
eigentlich jeder irgendwann mal essen können soll.
Was haben Philosophen mit Rechnern zu tun?
Im Rechner haben wir auch viele verschiedene aktive Komponenten, die
zugreifen wollen auf Ressourcen und sie müssen dafür sorgen, dass die
alle irgendwann fair auf ihre Ressourcen zugreifen können.
Es gibt noch viele weitere Themen, die ich hätte ansprechen können.
Es ist wirklich ärgerlich, dass ich anscheinend meine Zeit zu Beginn
etwas großzügiger verwendet habe und Ihnen dadurch etwas intensiver
erzählt habe, was es in dieser Vorlesung geht.
Die Inhalte liegen seit einiger Zeit auf dem Server.
Schauen Sie sich die restlichen Folien an.
Es tut mir leid, dass ich Ihnen die nicht mehr vorstellen kann.
Es ist auch noch das Kapitel 11 dabei, das jetzt für die Klausur am
Ende der Vorlesung eine Rolle spielt.
Es ist noch ein Kapitel 12 da drauf, Zusammenfassung, wo nochmal die
wesentlichen Punkte aufgeführt werden.
Und wie gesagt, es sind eine ganze Reihe wichtige Punkte hier in
dieser Vorlesung präsentiert worden.
Es ist eine ziemlich große Menge an Informationen.
Das weiß ich.
Großer Stoff, sehr umfangreicher Stoff.
Aber Wirtschaftsingenieure müssen nun mal in einer konzentrierten Form
die wesentlichen Punkte präsentiert bekommen.
Ich hoffe, dass es mir gelungen ist, bei Ihnen so ein bisschen
Interesse auch zu wecken an den Themen, die hier eine Rolle spielten.
Dass Sie einen Eindruck bekommen haben, welche Möglichkeiten, welche
Begrenzungen und welche Chancen es gibt durch Informationstechnologie.
Das war eigentlich der wesentliche Sinn dieser Vorlesung, Ihnen die
Grundlagen zu geben dafür, dass Sie anschließend in sinnvoller Art und
Weise weiter mit Informationsverarbeitung arbeiten können an
verschiedenen Stellen.
Kommen Sie in unsere Vorlesungen.
In meinem Bereich, wissen Sie, gibt es viel im Zusammenhang mit
selbstorganisierenden adaptiven Systemen, gerade im Bereich Energie,
aber auch in anderen Themen.
Effiziente Algorithmen spielen eine Rolle.
Bei meinen Kollegen sind eine ganze Reihe anderer Themen abgedeckt.
Schauen Sie sich den Abschnitt oder das Kapitel 12 Zusammenfassung an.
Da sind ein paar Hinweise gegeben, was für Themen bei uns noch
auftauchen.
Ich wünsche Ihnen viel Erfolg in der Klausur nächsten Montag.
Bereiten Sie sich vor, Sie sollten jetzt schon gut vorbereitet sein.
Nutzen Sie die Möglichkeiten der Tests diese Woche, jeden Tag über die
Apps können Sie das nutzen.
Und ansonsten haben Sie das Forum, wo Sie Fragen stellen können.
Unsere Doktoren, Mitarbeiter stehen für Sie bereit, dort zu antworten.
Also, ich hoffe, ich sehe Sie noch mal wieder.
Mir bringt es immer Spaß, Ihnen Stoff zu vermitteln.
Ich hätte nur gerne ein bisschen mehr Zeit dafür.
Also, vielen Dank!