Moin Moin liebe Leute, weiter geht es im Kapitel Synchronisation von Unterbrechungen für die
Vorlesungsbetriebssysteme. In diesem Teil wollen wir uns kümmern um harte und weiche Synchronisation.
Fangen wir mit der harten Synchronisation an. Hat man im Prinzip auch schon ein paar
Mal jetzt nur noch sozusagen in das Konzept der Prioritätenebenen Modells eingepasst.
Also stellt euch vor, hier habt ihr diese typischen Produce-Consumengeschichten und wir
legen jetzt, das ist die Idee, den Buffer auf die in diesem Falle Ebene 1. Heißt, wir können
aus Ebene 1 heraus von der Produce-Methode aus direkt auf den Buffer zugreifen. Ist kein
Problem, weil ist in der Ebene. Aus der Ebene E0 können wir jetzt nicht direkt drauf zugreifen.
Wir müssen erst mit Kli die Interrupts ausschalten, damit wir auf der gleichen Ebene sind. Dann
dürfen wir zugreifen und mit die verlassen wir die Ebene wieder. Warum dürfen wir in
der Ebene E1 einfach drauf zugreifen? Wenn wir dann in der Ebene E1 sind, sowohl vom
Hauptprogramm aus, als auch vom Interrupt aus, dann wissen wir, Kontrollflüsse in
der Ebene E1 werden sequentialisiert. Heißt, erstmal läuft das Konsum komplett durch und
erst wenn das fertig ist, dann läuft das Produce komplett durch. Wenn beides komplett durch
läuft, ist irgendwie logisch. Da vermischt sich nichts von den Zugriffen, dann kann auch
nichts passieren. Gut, Vorteil ist klar, Konsistenz ist sichergestellt und ich sag mal, das ist
für Doofe. Also ein Kli und ein Sti außen rum zu schreiben, das kann jeder. Und das
werden wir noch sehen, das ist letztendlich auch unabhängig davon, was jetzt unser Compiler
aus unserem Code macht. Wir werden sehen, das ist nicht immer so. Also Idee einfach,
Kli Sti, oben den Zugriff außen rum. Dann kann man den Zustand auf die untere Ebene
legen, die höchstpriore Ebene, dann funktioniert die Sache. Jetzt kommt das große Aber. Das
große Aber lautet, erstens haben wir eine Breitbankwirkung, was soll das heißen? Heißt,
ich schalte ja alle Interrupts aus. Und zwar nicht nur, wie jetzt hier in unserem Beispiel
immer, den Tastatur Interrupt, der jetzt vielleicht was in diesen Buffer reinlegen will oder
davon rausnehmen will, sondern wir schalten ja auch die Interrupts von der Festplatte,
die Interrupts von der Ethernet-Karte und so weiter aus. Die wir ja eigentlich nun überhaupt
nicht ausschalten müssten, die stören uns an der Stelle ja überhaupt nicht. Nächster
Nachteil, Prioritätsverletzung. Ja, dadurch, dass wir mit unserem Hauptprogramm auf die
E1-Ebene wechseln, erhöhen wir sozusagen die Priorität von unserem Anwendungsprogramm.
Und dadurch werden gegebenenfalls ja wichtigere Sachen, nämlich andere Interrupts, gegebenenfalls
verzögert. Das will man eigentlich nicht. Ja, und das letzte, man kann das als prophylaktisches
Verfahren bezeichnen im folgenden Sinne. Gerade jetzt Tastatur Interrupt ist ein gutes Beispiel.
Wie viele Tasten kann man dann, sagen wir mal, pro Sekunde tippen? Aber ich weiß nicht,
wie schnell ihr tippt. Also ich würde sagen, ich schaffe so 1, 2, 3, 4 vielleicht Tasten
drücke pro Sekunde. Andersherum gefragt, wie oft könnte die CPU aus dem Tastatur-Buffer
abholen pro Sekunde? Ja, wahrscheinlich ein paar Millionen Mal. Das heißt, ich würde,
also ich blockiere die Interrupts, obwohl ich eigentlich nur vielleicht jedes millionstelmal
durch einen Interrupt unterbrochen werden würde. Also 99,9999% oder so trifft mich ja gar keine
Unterbrechung. Und trotzdem schalte ich die Interbrechung aus. Das ist eigentlich doof.
Jo, nutzt man das jetzt, das Verfahren oder nutzt man das jetzt besser nicht? Gut, das ist
natürlich immer die Frage, was sind die Vorteile, was sind die Nachteile? Die Vorteile,
dass man das Stroh doof anwenden kann, das ist toll. Nachteile ist jetzt die Frage,
wie schwerwiegend sind diese Nachteile? Ich brauche ja sagen, so eine Breitbandwirkung ist mir
doch wurscht, geht. Das ist mir das wichtigste. Ja, aber da gibt es ein Kriterium, dass man immer
im Hinterkopf haben müsste und das ist letztendlich die maximale Dauer, wie oft, wie lange verzögere
ich Interrupts. Es kann ja sein, stellt euch vor, was weiß ich, die Uhr tickt und die Uhr tickt und
die Uhr tickt. Die Ticks, die möchte ich mitkriegen, wenn ich jetzt aber über eine Länge bedauer,
die Interrupts ausschalte, dann kriege ich vielleicht mehrere Uhr und Ticks nicht mit.
Heißt letztendlich, meine Uhr geht falsch. Okay, dass die Uhr falsch geht, ist vielleicht nicht so
schlimm, aber wenn ich irgend so ein, na, von meinem Hammer, aber ein schöner Beispiel, wenn das Atomkraftwerk
sagt, mir wird warm und so ein Ding kriege ich nicht mit, das wäre eine dumme Idee. Also, was man
machen muss, ist abschätzen, ja wie lang sind die Interrupts maximal ausgeschaltet. Und das ist
sozusagen das Kriterium auf der einen Seite und auf der anderen Seite, ja wie oft kommen dann jetzt
von externen Interrupts, die dann eben potenziell verloren gehen können. Jetzt, wenn wir uns das
angucken für unser Beispiel, Zeichen in den Tastaturprofer legen bzw. da wieder rausholen,
ja was ist da die maximale Verzögerungsdauer? Ja, nicht viel, also das sind eine handvoll
Assembler Instruktionen, die reichen schon, um was in meinen Buffer reinzulegen bzw. da wieder
rauszuholen. Eine handvoll Assembler Instruktionen, da reden wir über ein paar Nanosekunden vielleicht,
das wäre sicherlich okay, jedenfalls in den allermeisten Fällen, Spezialfälle ist das
vielleicht auch schon zu lang. Also so ein bounded Buffer, das ist so ein Beispiel, da kann man sagen,
das könnte man mit cleave uns die regeln. Wir verzögern die Interrupts maximal nur ganz,
ganz, ganz, ganz wenig und das tolerieren wir. Anders wäre das, wenn wir jetzt nicht so eine
einfache Datenstruktur wie so einen bounded Buffer hätten, sondern keine Ahnung, irgendwas
Komplexeres, irgend so einen balancierten Baum, den wir vielleicht nach dem Einfügen eines Objekts
dann irgendwie noch umstrukturieren müssten. Wenn das dauert dann gegebenenfalls schon länger,
ja und dann muss man sagen, dann ist diese harte Synchronisation mit cleaves die nicht geeignet.
Das darf man dann einfach nicht benutzen. Jo, die Frage ist, was machen wir dann? Und da ist
der andere Ansatz die weiche Synchronisation. Gucken wir uns mal an, was damit gemeint ist und
gucken wir uns mal an, wie gut das dann wäre. Also die Idee ist jetzt, wir haben unseren Zustand
nicht mehr auf Ebene 1, sondern auf Ebene 0. Heißt, das Konsum braucht jetzt nichts mehr
besonderes tun, weil es ist in der gleichen Ebene, darf entsprechend auf die Daten zugreifen,
lesenschreibend. Aber jetzt sind wir mit unserer Produce Methode in der falschen Ebene. Die läuft
ja in der Intrapt Kontext ab und die Methode, die muss jetzt aufpassen, sie unterbricht das
Konsum ja theoretisch an einer beliebigen Stelle und muss jetzt aufpassen, dass es das Konsum nicht
durcheinander bringt beim Auslesen des Buffers. Und das ist jetzt die große Kunst, man schreibe
eine Consume bzw. eine Produce Methode, die so sind, dass das Konsum jederzeit durch das Produce
unterbrochen werden kann und beide Methoden laufen ein und frei durch. Wer gerade nichts zu tun hat,
der kann sich ja mal überlegen, ob er das schafft, diese zwei Methoden so zu schreiben. Das ist alles
andere als trivial, werden wir gleich sehen. Wir brauchen zwei Annahmen, die kennen wir schon.
Das Produce ist die Interrupt Funktion, das heißt, die unterbricht das Konsum, nicht umgekehrt,
alle anderen Kombinationen kommen nicht vor. Das Produce ist eine Interrupt Funktion, die hat also
von der Semantik her diese Rantokomplition Variante. Also das Produce wird immer durchlaufen,
von Anfang bis Ende, es kann das Konsum an beliebiger Stelle unterbrechen. Was wir jetzt
haben wollen, das werden wir noch merken, ist, das soll nachdem die beiden abgelaufen sind,
soll es so aussehen, als wenn entweder das Konsum komplett abgelaufen wäre, bevor das
Produce losgelaufen wäre. Oder umgekehrt, das Konsum läuft erst los, nachdem das Produce fertig
ist. Das ist eigentlich nicht so, das Konsum wird unterbrochen, irgendwo zwischenzeitlich,
aber es sieht aus Sicht des Anwendungsprogramms so aus, als wäre das Konsum erst nach dem Produce
losgelaufen. Der Effekt ist der gleiche. Ja, die Frage, wie kriegen wir das hin? Wir haben
nichts bei denken, das Beispiel hatten wir schon mal. Da haben wir gesehen, das hat nicht funktioniert.
Also woran lag das? Ja, das lag daran, dass wir hier dieses Occupied hatten, also die Anzahl
der in dem Buffer befindlichen Elemente, dass das eine Variable war, die an zwei Stellen geschrieben
wurde. Warum war das ein Problem? Ja, das Problem war, das Konsum hat irgendwie angefangen,
dann wurde es unterbrochen durch das Produce. Das Produce hat das Occupied geschrieben und das
Konsum ist dann weiter gelaufen und hat den vorher geänderten Wert einfach überschrieben und hat
damit die Änderung von Produce kaputt gemacht. Also da sieht man, wenn man Variablen hat, die von
zwei Seiten, von dem Produce und dem Konsum geschrieben wird, dann funktioniert es definitiv nicht.
Ja, jetzt kann man nachdenken, wie kann man das umbauen, dass dieses Problem nicht mehr auftritt.
Also im ersten Augenblick sieht es ja doch so aus, als bräuchten wir die Anzahl der Elemente in dem
Buffer. Wir brauchen es ja hier oben, um zu gucken, ob der Buffer schon voll ist, bzw. beim Konsum
brauchen wir diese Occupied, diesen Wert, um zu gucken, ob überhaupt was in dem Buffer drin ist.
Ja, das kann man aber im Intel anders machen, nämlich folgendermaßen, wir lassen das Occupied
einfach weg. Wir haben nur noch unsere zwei Schreiblesezeiger und aus den Schreiblesezeigern
heraus kann man auch ermitteln, ob noch Platz im Buffer ist, bzw. ob noch Elemente in dem Buffer
sind. Ja, und dann sieht es etwa folgendermaßen aus, ich gucke beim Produce, ob ich noch Platz
habe, wenn kann ich meinen Next-In-Zeiger um eins verschieben und ich treffe noch nicht auf die
auszulesenden Bites, dann könnte ich weitermachen, dann ist okay. Oder umgekehrt, wenn der Next-Out-Zeiger
dahin zeigt, wo der Next-In-Zeiger hin zeigt, das heißt da ist Next im Buffer und dann würde das
Konsum zurückkehren. Anhand dieser zwei Zeiger kann ich sehen, ob noch was im Buffer drin ist,
bzw. wie viel noch drin ist. Jo, was hilft mir das jetzt? Jetzt habe ich das Occupied weg.
Nichtsdestotrotz sind ja jetzt noch Variablen drin, also beispielsweise das Next-Out hier oben,
was von dem Produce gelesen wird, von dem Konsum geschrieben. Da muss man sich ja auch Gedanken
machen, ist das okay. Next-In wird hier oben geschrieben, Next-In wird hier beim Konsum gelesen.
Okay, aber es sollte jetzt jedem klar sein, das sind die Stellen, wo es kritisch ist. Wenn ich
hier das Next-Out lese, kann ich dann in irgendeinen inkonsistenten Zustand kommen, weil ich hier
irgendwas kaputtes lese. Oder hier unten bei dem Next-In oder wenn ich jetzt hier das Next-Out schreibe,
kann es sein, dass ich da mit dem Leser hier oben durcheinander bringe. Und das müsste man
sich jetzt im Detail angucken. Jo, wie machen wir das? Man muss jetzt gucken, von der Idee her,
das Konsum kann ja unterbrochen werden. Es kann vor dem If unterbrochen werden, es kann während
der Bedingungsberechnung hier unterbrochen werden, es kann vor dem Return unterbrochen werden,
es kann vor dem Anlegen der Variablen unterbrochen werden, usw. usw. Da muss man sich jetzt immer
überlegen, was ist dann jetzt, sagen wir mal, es wird noch vor dem If unterbrochen durch das
Produce. Ja, dann sollte jedem klar sein, das sieht dann so aus, als wäre das Produce erst
mal abgelaufen und dann fängt das Konsum ja erst richtig an. Das würde sicherlich funktionieren.
Wenn ich jetzt vor dem Lesen von dem Next-In unterbrochen werde, also ich habe das If angefangen,
ich habe das Next-Out schon gelesen und jetzt läuft das Produce. Das Next-In habe ich noch nicht,
wichtig. Jetzt läuft das Produce los. Ja, dann würde das Produce das Next-In ändern, bevor ich
das lese. Heißt, es sieht für mich so aus, als würde das Konsum nach dem Durchlauf vom Produce
erst starten. Es hat eigentlich schon früher angefangen, aber es hat ja noch keinen Effekt von
dem Produce gesehen. Erst wenn es das Next-In gelesen hat, merkt es die Auswirkungen von dem
Produce. Wenn es vorher unterbrochen wird, dann hat die halt noch nicht gemerkt. Heißt,
das Produce ist sozusagen komplett vorher durchgelaufen. Das ist die eine Variante. Hier
ersten Schritt. Ich gucke, was passiert, wenn ich das Next-In noch nicht gelesen habe. Eine andere
Variante wäre, es könnte ja sein, ich habe das Next-In schon gelesen und werde jetzt unterbrochen.
Wie schaut das jetzt aus? Ja, ich habe das Next-In schon gelesen und alles, was ich dann tue,
hat ja keine, da hat das Produce keine Auswirkungen mehr drauf. Das heißt, es sieht so aus, als würde
das Konsum komplett durchlaufen und wäre von dem Produce gar nicht geändert worden, gar nicht
beeinflusst gewesen. Heißt, das wäre der Fall. Es sieht so aus, aus der Sicht der Anwendung,
als ob das Konsum schon komplett durchgelaufen wäre, bevor das Produce überhaupt aufgerufen
worden wäre. Also, die zwei Fälle vor und nach dem Next-In wäre okay. Da wäre Unterbrechung kein
Problem. Jetzt haben wir hier noch die Änderung von dem Next-Out. Wie schaut das da aus? Also,
das mit dem vor dem Next-Out haben wir uns im Prinzip schon angeguckt. Das sähe dann so aus,
als wäre das Produce vor dem Konsum abgelaufen. Es war ja nach dem Next-In. So, wie wäre das jetzt,
wenn wir hier das Next-Out schon geschrieben hätten? Ja, das heißt ja praktisch, das Konsum wäre
schon komplett durch gewesen, bevor das Produce angelaufen gewesen wäre. Heißt, auch der Fall
wäre okay. Also, dann haben wir unsere vier Fälle, die wir hier überprüfen müssen. Vor dem Next-In,
nach dem Next-In, vor dem Next-Out, nach dem Next-Out. Und da sieht man, die Konsistenzbedingungen
ist in diesem Falle immer erfüllt, egal wann das Produce das Konsum unterbricht. Anderes Beispiel
dazu. Also, wie könnte man solche Probleme noch lösen? Gehen wir mal zurück zu unserem Zeitleseproblem.
Ihr erinnert euch, wir haben eine globale Variable, die wird vom Timer Tick jeweils
inkrementiert und wir haben eine Time-Funktion, die liest einfach diese globale Variable aus.
Allerdings, wir sind sagen wir mal nur auf einer 16-Bit-Architektur und wir lesen eine 32-Bit-Variable.
Ja, dann müssen wir das in zwei Häppchen machen. Und da haben wir uns mal angeguckt,
dass es halt doof, wenn wir den ersten Teil der Uhrzeit gelesen haben, dann unterbrochen werden
und dann erst den zweiten Teil lesen. Und das war das Problem, dass die Daten nicht automat
gelesen wurden. Gut, Lösungsidee wäre, wir lesen die Daten einfach und gehen davon aus,
dass wir nicht unterbrochen werden. Aber wir testen, ob wir unterbrochen wurden und wenn ja,
dann machen wir es halt noch einmal. Was können wir jetzt wieder als Annahmen treffen? Na so ähnlich
wie gerade eben. Wir sagen, die Time-Funktion, die wird vom Hauptprogramm aus aufgerufen,
das heißt, die kann unterbrochen werden durch den Timer Tick, umgekehrt nicht. Und weil der
Timerhändler eine Interrupt-Funktion ist, läuft die immer durch mit Ranto-Completion. Ja, und auch
jetzt, so wie gerade eben, müssen wir es irgendwie hinkriegen, dass es nach außen hin so aussieht,
als wenn die Time-Funktion vor Timerhändler, vor dem Tick ausgeführt worden wäre oder danach.
Also nicht während sozusagen, sondern vorher oder hinterher. Folgende Idee, wie kriegen wir raus,
ob wir unterbrochen worden sind? Ja, wir setzen einfach ein Interrupted-Flag auf Fault. Wir lesen
die Zeit und danach gucken wir dieses Interrupted-Flag an und wenn das in der Zwischenzeit auf 1
gesetzt wurde, dann ist in der Zwischenzeit der Timerhändler gelaufen. Seht ihr hier,
der Timerhändler, der zählt jetzt nicht nur die Zeit hoch, sondern der zeigt auch noch an,
dass er gegebenenfalls eine Funktion unterbrochen hat. Das stimmt jetzt nicht ganz perfekt. Ihr
könnt euch vorstellen, ich habe hier das Interrupted-Flag gerade auf Fault gesetzt,
werde dann unterbrochen, dann wird das Interrupted schon wieder gesetzt und die Zeit würde
incrementiert und danach würde ich die Zeit lesen. Das Zeitlesen wäre dann gar nicht unterbrochen,
aber trotz allem würde ich diesen Vorgang wiederholen. Genauso könnte es sein,
ich hätte die Zeit schon komplett gelesen, wäre eigentlich schon komplett fertig. Wenn
dann der Timerhändler läuft, dann setzt er mir dieses Interrupted-Flag trotzdem noch und ich
wiederhole mein eigentlich gutes Lesen noch einmal. Macht aber im Prinzip nichts, ich kriege auf jeden
Fall, wenn es einmal richtig durchläuft, einen konsistenten Zustand meiner Uhrzeit. Das ist
natürlich das, das wollen wir eigentlich. Jo, gucken wir uns mal an, welche Vor- und welche
Nachteile haben wir jetzt mit der Weichensynchronisation. Vorteil ist auch hier,
Konsistenz ist sehr geschält. Das funktioniert, so man es dann richtig macht. Zweiter Vorteil,
Priorität wird nie verletzt. Warum nicht? Die Interrupts, also die Abläufe im Rechner,
die höhere Priorität haben, die dürfen jederzeit laufen. Ihr habt gerade beim Timer Beispiel gesehen,
der Interrupt ist nie blockiert, er darf jederzeit kommen. Gut und in den meisten Fällen,
hatten wir ja auch beim Tastatur Interrupt, da haben wir gesagt, in allerallermeisten Fällen
werden wir ja gar nicht unterbrochen. Genauso hier beim Timer Tick, typischerweise werden wir
nicht unterbrochen. Dass die Uhrzeit genau in dem Moment tickt, wo wir sie abfragen,
habe ich gehabt, aber nur sehr selten. Und wenn wir nicht unterbrochen werden, dann kostet es ja
auch natürlich nichts. Eventuell kostet es gar nichts, im Beispiel Bound Buffer, die Produce
Funktion, die konnte einfach immer kostenlos durchlaufen und das Konsum genauso. Das ist
sozusagen der Best Case überhaupt. Und beim Timer auslesen haben wir gesehen, ja eigentlich
kostet es uns nichts. Es kostet eventuell was, wenn wir doch mal unterbrochen worden sind,
dann müssen wir es halt nochmal machen. Und ja, es kostet uns natürlich hier ein Mini-Ticklchen,
dieses Interrupted Flag auf 1 zu setzen, bzw. hier auf 0 zu setzen und abzufragen. Also ganz
kostenlos ist es nicht, aber ihr könnt euch vorstellen, das ist natürlich Mini-Minimal.
Ja, jetzt kommen wir allerdings leider zu den Nachteilen und die sind wieder groß. Also ein
großer Nachteil ist, dass die Lösung nicht trivial ist. Also ich weiß nicht, wer sich mal
hingesetzt hat und versucht hat, dieses Bound Buffer Problem mal selber zu lösen und ich weiß nicht,
wie viele Leute von denen, die es versucht haben, auf eine Lösung zu, wie viele Leute dann geschafft
haben, auch eine Lösung zu kommen. Das ist nicht einfach. Selbst wenn man mal in der Literatur sich
so eine Lösung anguckt, wenn man schon lange eventuell darüber nachdenkt, überhaupt zu verstehen,
warum die funktioniert und es dauert noch noch viel, viel länger, die gegebenenfalls auch noch
zu verifizieren diese Lösung. Also kann es in unter keinen Umständen passieren, dass da irgendwie
Interrupts verloren gehen, Buffer in inkonsistenten Zustand kommen und so weiter. Und jetzt noch das
größte Problem, das ist compilerabhängig das Problem. Ihr könnt euch vorstellen, ich gehe
noch mal zurück zu dem Beispiel hier. So habe ich es vielleicht in C oder C++ hingeschrieben,
aber das kann mir rein theoretisch passieren, dass ein compiler Sachen umsortiert, weil er der Meinung
ist, in anderer Reihenfolge werden die Instruktionen schneller ausgeführt. Dann könnte man denken,
dann kann er diese zwei Instruktionen hier vertauschen. Okay, in diesem Falle schlechtes
Beispiel, aber in diesem Falle darf er nicht, weil der Rollenteil steht. Aber ihr könnt euch beim
Bounded Buffer vorstellen, wenn er da Zeilen vertauscht, gänzlich oder nur vielleicht einzelne
Assembler Instruktionen von diesen Instruktionen, dann funktionieren die Lösungen eventuell schon
gar nicht mehr. Also man darf bei diesen Lösungen häufig gar nicht die C Variante oder C++ Variante
oder so angucken, sondern man muss den generierten Code angucken und gucken, ob der die Konsistenzgarantie
nicht zerstört. Das ist natürlich eine ganz hässliche Baustelle. Ich habe mein Betriebssystem
geschrieben, alles funktioniert wunderbar, ich habe verifiziert, super. Und dann kommt eine neue
compiler Variante raus und nichts geht mehr. Dann kann ich mein Code neu schreiben, das kann es
auch wenig sein. Ein letzter Punkt, der ist nicht ganz so schlimm, aber manchmal kneift es
ggf. auch. Wenn ich nur meine Zeit schnell auslese und das wiederholen muss im Falle,
dass mich ein Interrupt getroffen hat, dann ist das nicht so schlimm. Wenn ich aber, was weiß ich,
ein halbes Megabyte Daten ausgelesen habe, das kostet mich ggf. eine ganze Weile und merke dann,
oh in der Zwischenzeit bin ich unterbrochen worden, dann muss ich das halbe Megabyte noch
mal lesen. Das kneift mich eventuell dann. Also das geht eigentlich jetzt, diese weiche Synchronisation
geht nur für kleine Datenmengen typischerweise. Jo, kleines Fazit, weiche Synchronisation an sich
ist das klasse. Ich habe es gesehen an dem Baunet Buffer Beispiel, das Produce kann durchlaufen,
wird nicht beeinflusst vom Konsum und das Konsum kann durchlaufen, wird nicht beeinflusst durch
das Produce. Beide Funktionen können jederzeit ohne Beeinträchtigung der anderen durchlaufen. Super.
Muss man allerdings für jedes Problem, was man hat, jedes Mal eine neue Lösung sich ausdenken.
Es gibt keine Lösung, die allgemeingültig und immer funktioniert. Jo, und dementsprechend ist
das nicht, wenn es nicht allgemeingültig ist, ist es natürlich mühsam anzuwenden. Wenn ich eine
verkettete Liste habe, muss ich es komplett anders machen als bei einem Baunet Buffer, muss
ich es komplett anders machen als bei einem balancierten Baum, muss ich es komplett anders
machen für und so weiter und so weiter. Und sich jedes Mal eine neue Lösung auszudenken für jedes
neue Problem, was ich habe, jedes Mal verifizieren und jedes Mal verifizieren, wenn ich den Compiler
ändere, das kneift mich, das mag ich eigentlich nicht. Jo, ihr seht harte Synchronisation, weiche
Synchronisation, beide haben ihre gravierenden Nachteile, gravierenden Vorteile. Wir werden uns
im nächsten Video jetzt noch eine andere Lösung angucken, die ist in den meisten Fällen Mittel
der Wahl. Das ist so eine Art Kompromiss zwischen den beiden.
Presenters
Zugänglich über
Offener Zugang
Dauer
00:27:37 Min
Aufnahmedatum
2020-11-18
Hochgeladen am
2020-11-19 12:18:07
Sprache
de-DE
6. Kapitel der Vorlesung Betriebssysteme.