Das Video kommt von YouTube: erst beim Abspielen verbindet sich die Seite mit YouTube (Google).
Vorlesung Betriebssysteme - 05 Synchronisation
Das Wichtigste aus dem Video
Tipp auf eine Zeit – das Video springt genau dorthin.
Transkriptautomatisch erstellt · 647 Zeilen
- Ja, willkommen zurück zu Betriebssysteme. Ähm, auch heute wieder ein paar Ankündigungen,
- zunächst mal etwas unerfreuliches. Ähm, wir haben ja letzte Woche die letzten Abgaben zu Aufgabe null bekommen und wir haben tatsächlich äh so ein paar
- mehrfachabgaben gefunden. Ja, also sprich ähm Gruppen, die scheinbar unabhängig voneinander, vielleicht auch nicht, ähm dieselben
- Abgaben getätigt haben. Ähm das können wir kann ich so nicht durchgehen lassen. Also wir werden mit den betreffenden Leuten mal sprechen.
- Also im Raum steht tatsächlich die Androhung äh dass diejenigen Leute die Studienleistung dieses Semester nicht mehr ja erbringen können. Ähm, also ich
- möchte Sie noch mal darauf hinweisen, noch mal explizit bitten, dass Sie die Aufgaben selbst lösen. Ja, also wenn Sie da irgendwie Schwierigkeiten haben, es
- gibt den Helptesk, ja, da weiß ich auch hier gleich in der nächsten Zeile noch mal explizit drauf hin. Ähm, wenn Sie Schwierigkeiten mit den Aufgaben haben,
- nutzen Sie den Helpdesk, dafür ist er da. Ja, und im Zweifelsfall, also wenn es wirklich irgendwie kurz vor der Abgabe Deadline ist und sie haben noch
- nicht die komplette Aufgabe gelöst, dann geben Sie sie teilweise ab. Ja, ich möchte keine Plagiate, keine mehrfachabgaben sehen und äh wir werden
- die angekündigten ja Konsequenzen auch durchziehen, wenn das äh wenn es notwendig ist. Ähm ja, auch
- heute gibt's wieder einen Live Q&A Termin oder äh Fragestunde äh 1515 bis 16:45. Ähm, ich freue mich auch, wenn Sie vorab
- wieder Fragen einreichen. Bis jetzt habe ich noch keine bekommen heute. Das ist entweder ein gutes Zeichen, weil sie alles verstanden haben, durchschauen,
- was es bei um was es bei Betriebssystem geht. Ähm vielleicht ist es auch kein gutes Zeichen, ja, dass ich sie alle abgehängt
- habe. Ich wäre wäre sehr dankbar, wenn ich da Feedback bekommen würde und vielleicht ein paar Fragen, die ich da besprechen kann. Ähm und wenn Sie das
- vorher einreichen, dann habe ich eben noch ein bisschen eine Chance, das vorzubereiten. Und jetzt mal noch was in äh
- Betriebssystem fremder Sache. Ähm es findet ab dieser Woche, ab Mittwochabend die Kiff die Kiff 48,0, das ist die äh die Konferenz der Informatik oder die
- Konferenz, doch die glaub die Konferenz der Informatikschaften. Ähm die sollte eigentlich endlich mal wieder in Dortmund stattfinden. Das
- passiert natürlich jetzt nicht, sondern sie findet im Neuland statt, um in der Formulierung der Veranstalter zu sprechen. Ähm geht am Mittwochabend los,
- soweit ich weiß, kann man auch durchaus noch ähm sich anmelden, teilnehmen und ähm ja, die freuen sich sicherlich über noch mehr äh noch mehr Besucher.
- Soweit zum organisatorischen kann man mich eigentlich verstehen. Also, der Vorlesungschatraum ist ganz ganz ruhig momentan.
- Akustisch, visuell, alles in Ordnung oder auch nicht? Da kommt was. Okay, anscheinend kann man mich verstehen. Das ist schon mal hilfreich.
- Okay. Ähm, soweit dazu. Letztes Mal haben wir Moment,
- viel zu viele Fenster offen. Letztes Mal haben wir über Scheduling gesprochen. Ich bin etwas leise. Okay, dann schaue
- ich mal, ob ich das hier noch hochdrehen kann. Das ist immer so ein bisschen so ein Warbon Spiel zwischen zu laut und übersteuert und zu leise.
- Man versteht es nicht. Jetzt habe ich mal den Mic Gay noch ein bisschen hochgedreht. Ja, letztes Mal haben wir über äh
- Scheduling gesprochen und zunächst mal haben wir da über äh ja Abfertigungszustände gesprochen und da haben wir unter anderem letztendlich
- diese Zustandsübergänge angeschaut. Ja, also es waren diese Einplanungsebenen ähm
- letztendlich kurzfristig, mittelfristig und langfristig. Ja, langfristig sind ist um diese Einplanung, wann werden überhaupt Prozesse gestartet? Wann
- werden sie beendet? Na, das sind Dinge, die z.B. regulär in so Rechenklustern stattfinden. Medium term oder mittelfristig sind so
- Dinge wie das Einlagern und Auslagern von Prozessen. Ja, also wenn festgestellt wird, dass nicht genug Hauptspeicher da ist, dann kann es eben
- kann das das Betriebssystem sich eben entscheiden oder der Scheduler sich entscheiden, einen Prozess auszulagern. Und das Short Term scheduling, das
- trifft eben so Entscheidungen zwischen lauffähig, also ready tatsächlich laufend, also der hat die CPU zugeteilt bekommen oder blockiert, wenn ein
- Prozess auf eine Ausgabe wartet, ne? Und diese vier hier rot äh oder mit mit Zahlen markierten Zustandsübergänge waren die vier ähm bei denen der
- Scheduler tatsächlich Entscheidungen trifft. Ja, und dann haben wir uns klassische CPU Zuteilungsstrategien angeschaut.
- Ähm, da will ich jetzt ein bisschen schneller drüber fliegen, weil das kann ich nicht noch mal alles wiederholen. Ähm, angefangen hatten wir letztendlich
- mit dem einfachsten Verfahren, dass man sich so wahrscheinlich vorstellen kann. erstmal äh das ist FCFS, First Come. First served
- ähm da haben wir unter anderem den sogenannten Konvoyeffekt gesehen. Ja, also wir hatten hier vier Prozesse, A, B, C, D, ähm, von denen zwei, nämlich B
- und D, sehr langlaufend waren. Sie haben eine sehr lange Bedienzeit, also belegen die haben einen sehr langen CPU-Soß und das sorgt dafür, dass alle Prozesse,
- die nach so einem Prozess mit lang mit langen CPU-stoß ähm dass die sehr lange warten müssen, bis sie dran kommen. Ja, und dieser Prozess hier, der hat äh
- letztendlich eine Ankunftszeit von 2, läuft aber erst los zum Zeitpunkt 101. Ja, und wenn man dann die normalisierte Durchlaufzeit anguckt, dann stellt man
- fest, dass die sehr sehr hoch ist. Ja, das ist was ähm was man insbesondere bei äh bei so interaktiven Prozessen interaktiven Systemen eigentlich nicht
- haben will. Und dann hatten wir uns in der Folge äh das Round Robin Verfahren angeguckt, dass das eben behebt oder versucht zu
- beheben, indem es Präion hinzufügt, ne? Also da haben wir eben die Zeitscheiben dazu bekommen, die dafür wo eben mit dem Timerbaustein und Unterbrechungen dafür
- gesorgt wird, dass einem Prozess die CPU entzogen wird, wenn er wenn er zu lange rechnet. Da haben wir dann auch festgestellt, die
- Prozesse, die viel ein Ausgabe machen, die also regelmäßig vor Ende ihrer Zeitscheibe die CPU abgeben, die werden hier benachteiligt, weil nämlich die
- Restzeit in ihrer Zeitscheibe verfällt. Und dann haben wir das Virtual Round Robin Verfahren uns angeschaut, VRR, was das wiederum behebt durch eine
- Vorzugsliste, ne? das Prozesse, die also vorzeitig die CPU abgegeben haben, ähm in eine Vorzugsliste kommen, die vor der normalen Bereitliste abgearbeitet wird,
- wo die Prozesse den die Restlaufzeit aus ihrer letzten Zeitscheibe äh zunächst mal noch äh fertig oder weiterrechnen können.
- Dann hatten wir uns noch ähm Shortest Process Next angeguckt, ähm wo letztendlich der Prozess geschul wird, der die kürzeste CPU Laaufzeit haben
- wird. Ja, und das hört man jetzt schon an der an der Beschreibung, äh dass man dadurch, also dafür braucht man letztendlich eine Vorhersage, muss also
- irgendwie die Zukunft vorgreifen können, um zu wissen, welcher welcher von den von den rechenbereiten Prozessen derjenige mit der kürzesten CPU, dem
- kürzesten CPU Stoß ist. Ähm und das haben wir letztendlich über z.B. über so eine exponentielle Glättung gemacht. Wir hatten noch zwei weitere ähm Verfahren
- uns angeguckt. SRTF und HP Response Ratio Next, die letztendlich auch vorhersagebasiert arbeiten. Und last but not least hatten wir uns noch das das
- Feedback Scheduling angeguckt, wo also Prozesse ähm hier von oben nach unten wandern, äh wenn sie wenn sie jedes Mal auf jeder Ebene wieder die Zeitscheibe
- aufbrauchen. Ja, das also sorgt dafür, dass also Prozesse mit langen CPUstößen immer weiter runter immer weiter runterwandern,
- möglicherweise mal durch so eine Anti-Aging Maßnahme wieder nach oben kommen können. Und ähm
- da haben wir uns im selben Kontext auch noch mal ganz kurz über Prioritäten unterhalten, ja, unterschieden zwischen statischen und dynamischen Prioritäten.
- Ähm bei den dynamischen Prioritäten ähm erfolgt eben die die Aktualisierung von den Prioritäten. ähm vor allem auch im Betriebssystem. Ja, und ähm diese
- Verfahren Shortest Process Next, STF, HRN und Feedback waren eben Spezialfälle, die eben äh zur Laufzeit äh die Prioritäten von Prozessen
- anpassen. Also bei Feedback z.B. wie eben die Prozesse, die sehr CPU lastig sind, die werden letztendlich kontinuierlich äh weiter in der
- Priorität abgesenkt und hatten uns dann am Schluss noch angeschaut Multilevel Scheduling ähm was äh ja das Ganze noch ein bisschen
- verallgemein hat. Das ist jetzt zunächst mal Multilevel Schedulings erstmal ein ein Verfahren, wo also jeder Prozess zunächst mal nach seiner nach seiner
- Sorte irgendwie äh einfach eine Priorität zugeordnet wird, ne? Also hier oben haben wir z.B. ist die Systemprozesse, die hier die höchste
- Priorität bekommen haben, die Studentenprozesse, die die niedrigste haben. Beim normalen Multilevel Scheduling ist zunächst mal kein
- Feedback vorgesehen, also spricht, dass ein Prozess seine Priorität wechselt. Ähm ja, das heißt also ein niederpriorer Studentenprozess bleibt auch
- niederprior, ein hochpriorer Systemprozess bleibt hochprior. Das kann man aber auch noch kombinieren und dann hat man multilevel Feedback. Ja, und das
- ist ein sehr allgemeines Verfahren, wo man letztendlich auch auf jeder Ebene entscheiden kann, nach welchem Scheduling Verfahren wird hier äh
- gescheduled und nach bestimmten Kriterien äh können Prozesse dann auch die Ebene wechseln, ne? Z.B. wenn sie sehr CPU lastig sind, weiter nach unten
- geschoben werden. Dann hatten wir kurz über äh Bewertungskriterien gesprochen, also welche
- Ziele äh bei einem bei einem äh Scheduling äh Verfahren ähm ja durchgesetzt werden sollen. Da haben wir Unterschieden zwischen benutzerentierten
- und systemorientierten Zielen oder Bewertungskriterien, die jeweils für völlig unterschiedliche Anwendungszwecke sinnvoll sind.
- Ähm, dann hatten wir uns noch ein Beispiel angeschaut, nämlich Unix. Skatering oder Unix. Das ist jetzt nur noch mal ein kleiner Auszug. Ähm, wo wir
- also gesehen haben, dass z.B. diese dieses äh diese diese äh diese exponentielle Glättung auch wirklich in der Realität eingesetzt wird. Und das
- ist so der Punkt, wo wir das letzte Mal stehene geblieben sind. Ähm, ich mache jetzt mal diesen Foliensatz noch fertig. Da gab es noch ein Beispiel zu Thema
- Windows und ähm dann gucken wir mal, ob es zum zu diesem Vorhinsatz noch Fragen gibt. Ähm
- ja, dazu sollte ich mir vielleicht auch mal die Frag jetzt Seite aufmachen nebenan Fragen auch bei mir ankommen. Da sind schon ein paar eingegangen. Ich äh
- gehe gleich drauf ein. Ja, Windows äh Art macht natürlich auch Scheduling. Ja, seit Windows int ähm gibt's Prioritätsklassen
- und ähm da werden letztendlich wird auch letztendlich Präumtion benutzt. Ja, also präumtive
- äh Prioritäts und zeitscheibenbasierte Einplanung passiert da. Ähm und Verdrängung, also Präion passiert auch dann, wenn es ein Thread ist, der im
- Kern ist. Ja, also selbst ein Betriebssystem eigener Thread, der wirklich Betriebssystem interne Dinge tut, kann
- verdrängt werden. Ja, bei Unix ist das klassischerweise nicht der Fall gewesen. Ähm, prioritätsbasiert, ja, das heiß, wir
- haben Prioritätsebenen, in dem Fall von 0 bis 31 und wenn wenn man zwei Prozesse oder zwei Threats hat, die auf derselben
- Prioritätsebene sind, dann wird Round Robin gemacht. Ja, die waren also rei um äh dran genommen und haben eine Zeitscheibe
- und ähm da gibt's jetzt eendlich verschiedene Prioritätsklassen. Ja, die Klasse Null ist reserviert für ein so so eine Hintergrund Aufgabe, die Wus
- regelmäßig durchführen muss. Ähm von 1 bis 5, das sind die sogenannten Variablen Prioritäten und von 16 bis 31 sind die sogenannten
- Echtzeitprioritäten. Ähm, so und dann gibt es bei Windows eben so
- äh ja, Eigenschaften, die sich sehr stark auf die Desktop Betriebssystem Natur von Windows beziehen. Ja, also z.B. die Art eines Threads ähm bestimmt,
- wie groß das Zeitquantum, also die Zeitscheibe äh eines Threads ist. Ja, also ob es ein Vordergrundthreat ist oder ein Hintergrundthread. Ja, also ist
- das ein tatsächlich ist es ein Fenster, das im Vordergrund ist oder ist das ein Fenster äh das im Hintergrund ist? Ja, und äh so eine Zeitscheibe, die kann
- zwischen 6 und 36 Einheiten groß sein und eine Einheit äh entspricht, ne, so einem Tick und so ein so so ein Tick kann je nachdem welche Version das von
- dem von Windows NT ist, kann z. 10 oder 15 Millisekunden z.B. sein. Ähm und ähm dieses Quantum, das verringert sich mit jedem Tick um 3 oder um eins. Ja, und
- das kommt jetzt wieder drauf an, wie sich dieser Thread in letzter Zeit verhalten hat. Ähm, wenn also nein, Moment, um 3 in der Regel und nur um
- eins, wenn der Thread äh ein IO Burst macht, also in den Wartezustand geht und die Zeitscheibenlänge, die sich die da daraus letztendlich resultiert, die
- kann jetzt variieren zwischen 20 Millisekunden, das war so ein Beispiel, dass ich das letzte Mal gebracht hatte, bis zu 180 Millisekunden. Ja, und das
- unterscheidet sich z.B. bei Server und Desktopversionen von Windows. Ja. Ähm was jetzt noch dazu kommt und das ist letztendlich so eine Heuristik, die
- die sich äh die sich die Firma Microsoft hier ausgedacht hat, ist ähm also zunächst mal sowas die die Basispriorität von einem Prozess, ja,
- also die die der Prozess so als Hülle für mehrere Theds, der hat erstmal eine Basispriorität, dann kann ein Thed innerhalb dieses Prozesses noch eine
- relative Priorität haben, plus minus irgendwie irgendeine Konstante und dann kommt noch ein sogenannter Boost dazu. Ja, der Boost
- ähm der kommt zum kommt zum Zuge, wenn man auf irgendeine Art und Weise mit dem Prozess z.B. interagiert. Ja, wenn bestimmte Ereignisse ähm auftreten, dann
- bekommt so ein Prozess kurzzeitig oder so ein Thread kurzzeitig eine höhere Priorität. Z.B. wenn ein Prozess eine Festplatte von äh eine Festplatten eine
- Ausgabe irgendwie fertig gestellt hat, also der hat eine ein aus ein Ibst gestartet, der ist fertig geworden. Ähm und es wird ein Block von der Festplatte
- geliefert, z.B. Ja, da wird der Prozess kurzzeitig in der Priorität angehoben. Ähm, noch viel witziger ist sowas wie die Mausbewegung oder Tastaturingaben.
- Ja, wenn Sie auf so ein Fenster drauf klicken unter Windows, dann wird die Priorität von dem von dem äh von dem Thread, der dahinter steckt, kurzzeitig
- um 6 angehoben. Ja, das heißt, also wenn Sie irgendwann mal ganz äh ganz dringend auf das Fertigstellen von irgendeiner
- rechtintensiven Arbeit von ihrem auf ihrem Windows Rechner gewartet haben und sie haben so nervös auf dem auf dem Fenster rum geklickt und das Gefühl
- gehabt, der wird jetzt schneller fertig, dann hatten sie vielleicht gar nicht so unrecht mit dem Gefühl. [räuspern] Ja, also Prozess wird tatsächlich dynamisch
- angehoben, wenn äh wenn er Eingaben vom Nutzer bekommt. Ja, und es gibt noch weitere Ereignisse, die die das die das also verursachen
- können, dass man so ein so einen Boost bekommt und dieser Boost, den man da also bekommt, also z. dieses + 6, das wird jetzt mit jedem Tick wieder
- reduziert. Ja, das heißt also z.B. nach sechs Ticks ist dann dieses dieser Boost, den man durch die Mausbewegung oder die Tastatureingabe bekommen hat,
- wieder weg. Und eine weitere Sache ähm macht Windows noch beim beim Scheduling, nämlich es liefert eine sogenannte
- Fortschrittsgarantie, ähm die also dafür sorgt, dass Threads nicht aushungern. Ja, also alle drei bis 4 Sekunden werden irgendwie bis zu zehn in letzter Zeit
- benachteiligte äh Threats für zwei Zeitscheiben noch ordentlich angehoben ähm auf die höchste variable Priorität, ja, aus diesen variablen Prioritäens
- Spektrum. Und ähm das führt dazu, dass äh das letztendlich das Aushungern verhindert wird.
- Ja, und man sieht bei Windows ähm da wurde letztendlich wahrscheinlich auch eine Menge äh Forschung betrieben mit Nutzern, die also beobachtet wurden,
- wie sich wie wie sie letztendlich auf das Systemverhalten so ansprechen und entsprechend wurde ähm wurde dieses Scheduling, diese Scheduling Horistik
- hier so angepasst, dass sich das System eben ein bisschen interaktiver anfühlt. zusammenfassend zum Thema Scheduling. Ja, also wir hatten diese drei diese
- drei äh ähm na ab diese drei Einplanungsebenen gesehen, also langfristig, mittelfristig, kurzfristig
- und die betrachteten Verfahren jetzt aus dieser Vorlesung, ja, also FCFS und Co, äh die gehören alle zum Shortterm Scheduling. Ja, wir haben verschiedene
- Benutzer und systemorientierte Kriterien gesehen, wie man diese Verfahren beurteilen kann und welches Verfahren letztendlich das Beste ist für ihren
- Anwendungsfall, für ihren Rechner, der möglicherweise irgendwie in eingebetteten System äh sitzt und irgendwie mit z.B. mit der mit der
- physikalischen Umgebung irgendwie interagieren muss. Ähm, das kommt letztendlich darauf an, was der Uscase genau ist. Ja, und das kann sich sehr
- stark dann unterscheiden, wie die wie die Leistung unterm Strich dann ist. So, jetzt schauen wir mal, ob es Fragen gibt.
- Ähm, wenn die Prozesse dem Scheduler mitteilen, wie lange ihre Bedienzeit sein soll, warum müssen wir noch die
- voraussichtliche Laufzeit vorhersagen? Der Prozess sagt uns ja schon, wie lange er laufen will bei einigen Scheduling Methoden. Also, das Prozesse dem
- Scheduler sagen, wie lange sie laufen wollen, das ist nicht der Regelfall. Ich wüsste jetzt da so für dafür noch nicht mal ein Beispiel.
- Ähm, das einzige Beispiel, wo wir eben nicht vorhersagen müssen, z.B. durch dieses äh durch diese
- exponentielle Glättung, ähm das ist eben im Bereich ähm ja Batchge Batchge Computing, also letztendlich so, ne, wenn man hier in dem Lido Cluster
- rechnen möchte, dann muss man bei seinem Rechenjob dazu sagen, wie lange der ungefähr laufen wird, ne? Läuft der 10 Minuten, läuft der eine Stunde, läuft
- der ein Tag? Das muss man muss man da ankündigen. Und nachdem dieses Zeitfenster dann abgelaufen ist, wird der Prozess auch wirklich weggeschossen
- von dem System. Ja, das ist aber, würde ich jetzt mal sagen, nicht der Regelfall. Im Regelfall ähm muss letztendlich der Scheduler eine
- Vorhersage treffen. Ja, man sieht es ein Prozess von außen erstmal so ohne weiteres nicht an wie die sich verhält, sondern man muss ihn halt beobachten und
- aus den Beobachtungen, aus den letzten Endbeobachtungen die nächste ähm Zeitscheibenlänge oder ne die letzte nächste CPU Burst Länge vorhersagen.
- Ich hoffe, das beantwortet diese Frage. Ähm, was bedeutet Variabel und Echtzeit bei den Prioritäten? Das sind zunächst mal nur Namen für die beiden Klassen.
- Ähm, na, nicht ganz beim Echtzeitscheduling äh die Echtzeit, diese Echtzeitklasse, also
- wir reden gerade noch mal von Windows NT von diesen von diesen Prioritäten von 0 bis 31 und bei den Klassen äh bei den Prioritäten 1 bis 15, bei diesen
- variablen Prioritäten, da wird Drown Robin gemacht. Ja, also da wird gibt's letztendlich einfach Zeitscheiben. Ähm bei den höheren Prioritäten, also bei
- den Echtzeitprioritäten ab 16 ähm laufen diese äh diese diese Threads, also wird wird keine Präsion gemacht, soweit ich mich düster erinnere. Das
- müsste ich vielleicht noch mal nachgucken, aber ich meine, dass das letztendlich FCFS entspricht. Ja, das heißt also, wenn da ein äh ein ein äh
- ein Thread seine die CPU nicht hergibt, dann kann er keine wirklich das System lahm legen. Das meine ich war der Unterschied. Gucke ich aber noch mal
- nach. Gibt es da denn jetzt ein besser oder schlechter, was das Scheduling in Unix
- und Windows angeht? ähm bestimmt. Also letztendlich ähm kommt es auf ihren
- Anwendungsfall an. Ja, also ich denke, dass Microsoft da schon sehr viel Zeit investiert hat, um dieses diese Geschichte z.B. mit diesem Dynamic
- Dynamic Boost ähm so lange zu optimieren, bis sich das eben für den Normalnutzer besonders fluffig anfühlt.
- Ähm ich kann mir aber sehr gut vorstellen, dass es auch Anwendungsfälle gibt, wo das eben nicht der Fall ist. Ja, also wo möglicherweise dann einfach
- so ein Unix, Linux, sonst wie Scheduler, ähm einfach bezüglich eines anderen äh Benutzer oder auch systemorientierten Kriteriums ja einfach besser aussieht.
- Ja, also wie wie so oft muss man hier sagen, kommt drauf an, was ihr Anwendungsfall ist.
- Woher weiß man eigentlich bei dem Lido System, also bei dem Klaster, wie lange das Programm läuft? kommt ja auch auf die Systemleistung, also
- Prozessorgeschwindigkeit, die Anzahl der Kerne und so weiter an. Ähm, na ja, letztendlich ist das eine Schätzung, die der Nutzer hier ähm hier
- angibt. Der schätzt der schätzt natürlich äh großzügig nach oben ab und ähm er weiß auch, wie die Maschinen beschaffen sind, auf den er arbeitet.
- Also in diesem Lidluster, da gibt es sehr, sehr viele gleichage Maschinen und da kann auch einfach mal ausprobieren und messen. Ja, aber letztendlich kann
- es natürlich immer passieren, dass man da einen Rechenjob laufen lassen möchte, der doch länger dauert, als man geschätzt hat. Ja, weil es sich die
- Laufzeit von so einem Prozess natürlich nicht nur darauf ankommt, wie schnell der Prozessor ist, äh wie sondern auch wie z.B. die Eingabedaten sind. Ja, je
- nachdem, wie die Eingabedaten sind, kann so ein Programm völlig unterschiedliche Laufzeiten haben. Ähm und dann hat man möglicherweise Pech gehabt. Ja, also im
- schlimmsten Fall muss man halt das diesen diesen Job noch mal starten mit einer höheren Schätzung. Ja, aber das ist natürlich ein Problem.
- Ja, wenn man überabschätzt, äh ja, überabschätzen ist mal die sicherere Sache, da wird der Prozess nicht
- vorzeitig weggeschossen. Hat aber den Nachteil, dass in der in der Regel langlaufende oder Prozesse, bei denen der Nutzer ankündigt, dass die
- langelaufen werden, äh dass die mit niederer niedriger Priorität geschulted werden. Ja, das heißt, also wenn man also einen Job in das System reinsteckt,
- wo man sagt, der läuft vier Tage durchgehend, ähm dann kann es sein, dass man auch durchaus mal ein z D Tage warten muss,
- bis der überhaupt losläuft. Ja, und wenn man Job ins System schiebt, wo man sagt, der wo man ankündigt, der läuft nur 10 Minuten, der kommt in der
- Regel sofort dran. Ja, also das ist ein Üste ist tatsächlich ein Problem. Letztendlich kann man nur messen und hoffen.
- Der Ton ist doppelt. Das ist natürlich in der Tat schlecht, wenn sie das ein bisschen Moment, jetzt schau ich mal, ob ich hier
- irgendeinen Unsinn gemacht habe mit meinem Ja, ich verstehe jetzt auch, was das Problem ist.
- So, jetzt hoffe ich mal, der Ton ist besser geworden. Schauen mal, ob der Turm besser geworden ist. Bei mir ist alles okay. Eigentlich
- nicht. Hier ist auch alles okay, nicht? Also ich ich spreche jetzt einfach mal bisschen weiter. Sie können ja mal kommentieren, ob der Ton sich auf
- irgendeine Art und Weise verändert hat. Ton ist jetzt okay. Okay, der Ton war vorher auch schon okay. Na gut, wir üben das noch.
- Ähm ja, so viel zum Thema Scheduling. Ähm heute machen wir weiter mit ähm ja letztendlich dem, was darauf
- folgt. Ja, wenn ich ähm ja, wenn ich wenn ich äh System mit mehreren konkurrenten Prozessen habe, dann kann es auch sein, dass ich die
- synchronisieren muss. Deswegen ist das Thema der heutigen Vorlesung Synchronisation.
- Ähm, was möchte ich mit ihm besprechen? Noch mal ganz kurz wiederholen. Da wollen wir schnell drüber springen. Ähm, dann werde ich versuchen erstmal das
- Problem noch ein bisschen klarer zu machen, ja, ein paar Begriffe ähm auf den Tisch legen. Ähm, und dann werden wir uns über verschiedene Lösungsansätze
- unterhalten, ähm wie man synchronisieren kann. Ja, also zunächst mal quasi die Athoklösung. Ja, wie würde man da jetzt rangehen, wenn man nicht Betriebssysteme
- gehört hat? Vielleicht was ist der Nachteil davon, wenn man das so tut. Ähm, dann w wir uns über Hardwareunterstützung zur
- Synchronisation unterhalten. Es ist wieder leise geworden. Weiß ich doch. Be Absicht. Ich mach's noch mal lauter. So.
- Ähm, Betriebssystem Unterstützung ähm für Synchronisation unterhalten und am Schluss über Sprachunterstützung. Ähm, ja, Prozesse sind Programme in
- Ausführung. Ja, also die Abstraktion für Kontrollflüsse äh in Rechnensystemen und zunächst mal sind die konzeptionell unabhängig voneinander. Ja, also
- konzeptionell sind es wirklich komplett nebenläufige Kontrollflüsse äh die so aus aus Nutzersicht auch wirklich gleichzeitig laufen, was sie natürlich
- technisch nicht machen. Ja, technisch findet ein Multiplexing der CPU statt. Es darf immer nur einer tatsächlich auf der CPU laufen und ähm das der
- Schedeling Verfahren entscheidet also wann wird ein Prozess verdrängt und ähm in welcher Reihenfolge kommen die die lauffähigen Prozesse dran. Ja, in
- welcher Reihenfolge werden die ausgeführt? Prozesse haben einen Adressraum. Ja, über logische und physische Adressen werden wir uns später
- noch unterhalten. Logische Adressen werden durch die Hardware let durch die durch die Memory Management Unit auf physische Speicheradressen abgebildet.
- Und ähm Prozesse können sich auch und das ist jetzt ein Problem, dass wir uns gleich mit dessen Folgen wir uns gleich anschauen werden, können sich auch Code
- und vor allem Datenbereiche teilen. Ja, also bei leicht und viertergewichtigen Prozessen hatten wir schon gesehen, die teilen sich denselben Adressraum, die
- können auf denselben Datenstrukturen arbeiten. Ähm und das Betriebssystem kann mit Hilfe der MMU, ja, das ist wieder auf
- der technischen Ebene, ähm einen Speicherbereich mehrere Adressräume einblenden. Ja, was dafür sorgt, dass wirklich zwei auch Prozesse, auch zwei
- schwägewichtige Prozesse ähm auf demselben Speicher, auf den selben Datenstrukturen arbeiten können. Ja, und auch im Betriebssystem selbst werden
- Daten, Strukturen, Daten geteilt. Ja, das gucken wir uns auch später noch mal an. Ähm, ja, was ist das Problem? Jetzt
- gucken wir uns mal eine einfache Datenstruktur an, die vielleicht im C auf den ersten Blick nicht so einfach aussieht für diejenigen, für die C neu
- ist. Ähm ist aber keine Raketenwissenschaft. Ich werde es mal kurz versuchen zu erklären. Ähm wir wollen eine verkettete Liste in C
- implementieren. Ja, also haben wir ein Struct element, wo also das das letztendlich die die Datastruktur für ein einzelnes Element
- in dieser Dataststruktur ist. Jedes Element hat eine Payload. Ja, in diesem Fall jetzt einfach ein Character, das könnte aber auch irgendwas komplexeres
- sein. Ja, es ist die Nutzlast, also das, was wir in dieser in dieser verketteten Liste verwalten wollen. Zeichen. So, und jedes Element hat, wie das halt
- so ist bei einer verketteten Liste, einen Nextzeiger auf das nächste Element ähm in unserer Liste. So, die eigentliche Liste ähm besteht
- letztendlich aus zwei Zeigern. Das eine ist ein, ne, wenn man liest ja immer von rechts nach links, ne? Head ist ein Zeiger auf ein Structement.
- Das ist also der Zeiger auf das erste Element in der Liste. Und dann wird hier so ein kleiner Kniff ähm gewählt. Also der dieser Tail
- Zeiger, das ist kein Zeiger auf das letzte Element, sondern das ist der Zeiger auf das auf den Nextiger im letzten Element. Ja, also Tail zeigt auf
- den Nextiger im letzten Element. So, warum das so ist, werden wir gleich an dem Beispiel bisschen sehen. Letztendlich sorgt dieser Kniff dafür,
- dass man bei diesen bei dieser bei diesem Einhängeschritt hier unten, der hier unten implementiert ist, äh dass man hier nicht unterscheiden muss, ist
- die Liste gerade leer oder ist sie nicht leer. Ja, wenn das hier einfach nur ein Zeiger auf das letzte Listenelement wäre, dann wäre diese Implementierung da
- unten ein bisschen komplexer. ist jetzt vielleicht äh für das eigentliche zu zeigende Problem
- gar nicht so relevant. Ähm, letztendlich ist aber das die Implementierung, die ich jetzt in diesem Beispiel hier zeigen werde.
- Ähm, so, das ist die also die Implementierung von der Funktion oder man könnte jetzt sagen, wenn man so objektorientiert das Ganze betrachtet,
- das ist die Methode NQ auf der Datenstruktur Liste. Ja, also die hängt ein neues, die hängt also hier in die in die Liste List ein neues
- Element Item ein. Ja, und dazu werden diese drei Schritte hier durchgeführt. Das gucken wir uns jetzt gleich mal in dem Beispiel an.
- Ähm, zuvor das vollständige Szenario. Wir haben zwei Fres, Faden 1, Faden 2, die laufen zum Nebenläufig vor sich hin und beide machen irgendwann mal in ihrer
- Laufzeit mal ein NQ auf dieselbe Liste. Ja, und da werden wir jetzt gleich sehen, dass ein Problem auftreten wird möglicherweise oder zumindest auftreten
- kann. Ja, also F 1 macht irgendwann mal ein NQ auf diese Liste hier von dem Element 1. Das ist dieses Ding hier unten links. Also letztendlich Thed 1
- möchte ein A in die Liste einhängen. Th 2 möchte ein B in die Liste einhängen. Okay. Und diese be diese Liste und auch diese
- ganzen Listenelementobjekte äh die liegen alle im in dem gemeinsamen Adressraum. Was man jetzt hier sieht, ist die leere
- Liste. Ja, und zwar ist das hier der Headpointer. Ja, wenn die wenn die Liste leer ist, zeigt der Headpointer auf einfach auf null, also auf nichts.
- Und der Tail Pointer zeigt auf das den eigenen Headpointer. Ja, das ist einfach der definierte Zustand für die leere Liste. Ja, das ist
- das, was ein Listenkonstruktor in der an der Stelle ähm so einrichten würde, dass die Liste so momentan inzahl so aussieht. Und diese beiden
- Elementobjekte hier ähm die haben hier schon mal in dem in diese diesen Payload jeweils ein ein Zeichen eingetragen bekommen und ihr jeweiligen Nextiger,
- der zeigt noch auf irgendwas. Ja, das ist erstmal nicht initialisiert. So, und jetzt gucken wir uns mal an, was passiert, wenn zunächst mal Faden 1 ein
- NQ macht auf das E1 und danach Faden 2 ein NQ auf das E2. Ja, und das sind jetzt einfach diese drei Schritte aus der NQ Funktion, die
- wir uns jetzt nacheinander angucken. Das ist also die Initial, der Initialzustand von unserer Liste L und das sind die beiden Elemente E1 und E2. So, jetzt
- wird zunächst mal dafür gesorgt, dass der Next Pointer von unserem Item, das ist in dem Fall hier das Element E1, ja, also das ist der Nextzeiger, der
- momentan noch nicht initialisiert ist, dass der mit Nalle initialisiert wird. Ja, das Ding wird jetzt mit Nalle initialisiert. Das tut diese Zuweisung
- hier. So, es gab eine Zwischenfrage, die wurde gleich wieder gelöscht. Ich vermute mal, ich habe es ja einfach
- mal schon beantwortet. Ähm, so was passiert jetzt hier in diesem zweiten Schritt? Also, hier wird
- zunächst mal die Liste genommen. Ja, das ist also dieses Objekt hier. Von dieser Liste wird das Tail Element, das ist das hier, das dieser Zeiger hier
- genommen. Und das Ganze wird dereferenziert. Ja, das heißt also das Dereferenzieren sorgt dafür, wir folgen dem Pointer dorthin, wo er hinzeigt. Ja,
- dorthin und dort wird Item zugewiesen. Das sorgt also dafür, dass dieser Zeiger jetzt ja, das ist also hier die erm noch mal ein Z
- ein dasselbe dasselbe, dass dieser Zeiger jetzt auf unser Item E1 zeigen soll. Ja, diese Zuweisung sorgt dafür, dass dieser Zeiger aufs nächste zeigt.
- So und im dritten Schritt wird wird noch mal hier das [räuspern] Listenelement die Liste genommen. Davon das der Tail Zeiger der hier und der
- soll jetzt [räuspern] zeigen auf den Next Zeiger von unserem Item. Ja, also der wird jetzt umgebogen von hier nach da.
- So und jetzt ist dieses erste NQ fertig. Und was wir jetzt hier sehen, ist also eine Liste, die ein Element beinhaltet. Ja, der Headpointer von der Liste zeigt
- auf das erste Element. Der Tailpointer zeigt auf den Next Pointer von dem letzten Element in unserer Liste, was gleichzeitig auch das
- erste Element ist. So, und jetzt kommt hier das äh das zweite NQ. Ja, das habe ich jetzt hier
- nicht noch durchanimiert. Letztendlich sorgt es dafür, dass der Headpointer nach wie vor auf das erste Element zeigt. Der Next Pointer zeigt auf das
- nächste und der Tail Pointer zeigt auf dem Nextinter vom letzten Element. Ja, so soll die Liste aussehen, wenn nacheinander E1 und dann E2 in die Liste
- enced wurden. So, das ist auch völlig richtig. So, so muss das aussehen, wenn das äh wenn das wenn das
- wenn die beiden fertig encute sind. Jetzt ist aber die Frage, was passiert, wenn diese beiden Threads ja ungünstig äh umgeschaltet werden? Ja,
- also wir hatten ja wir hatten ja gehört, das ist immer es kann immer nur ein Thread auf der CPU laufen und jetzt nehmen an, wir haben so
- irgendein PR prämitives Scheduling Verfahren, Robin z.B. und das sorgt dafür, dass an dieser Stelle hier ein Prozesswechsel stattfindet. Ja, also
- wird gesagt, die Zeitscheibe von diesem von dem Thread 1 ist jetzt abgelaufen. Jetzt darf Thread 2 mal dran kommen. Und jetzt macht dummerweise Thread 2 auch
- ein NQ. Ja, also wir haben jetzt den Fall Faden 1, Faden 2, überlapp Faden 1. Ja, also Faden 1 läuft aber nur bis hierhin, dann wird findet ein
- Prozesswechsel statt. Faden 2 macht hier sein NQue und dann findet wieder ein Prozesswechsel irgendwann statt. Wieder zurück zu Faden 1. Und jetzt gucken wir
- mal, was mit unserer Liste passiert. Also auch hier wird wieder erstmal dieser Nextiger. Moment, doch, nee, E1 wird eingehängt. Also der Nextiger hier
- wird auf Nah gesetzt. Ja, und der ähm na also List Tail zeigt hierhin und das wird gesetzt auf Item. Also es wird
- letztendlich dieser Zeiger hier ein eingefügt. Ist aber noch nicht ganz fertig, ne? Also um um das NQ komplett fertig zu machen, müsste man ja
- eigentlich diesen Schritt noch machen, der dafür sorgt, dass dieser Zeiger hier auf den Nextiger von E1 zeigt. Ja, und das äh fehlt noch. Wird jetzt
- halt vorzeitig unterbrochen. Ja, es kommt also passiert also Kontextwechsel zum Faden 2. Und was passiert jetzt? Ja, das ist also zunächst mal die Situation
- von oben kopiert. Was wird jetzt gemacht? Das wird also das Item den das Item ist in dem Fall das E2 wird hier also der dieser Nallzeiger erstmal
- eingetragen. Das ist noch völlig unkritisch, weil dieses Element, das sieht ja noch kein anderer Prozess oder kein anderer Th der der daneben daneben
- läuft. Was passiert jetzt? Ja, es wird wieder List Tail genommen, ne? List Tail ist dieser Zeiger hier. Den wird mit dem Sternchenoperator
- wird das wird dieser Zeiger dereferziert. Wir folgen also dem Zeiger dorthin, wo er hinzeigt. Und das wird gesetzt auf Item. Das heißt
- also dieser Zeiger hier wird übermalt durch einen Zeiger, der auch dieses Item hier zeigt. Und dann wird list tail, das ist dieser
- Zeiger hier wird gesetzt auf den Next Zeiger von dem Element 2. Ja, jetzt sind wir hier und wir sehen also das Zwischenergebnis
- ist jetzt momentan schon mal das, was der Fed 1 gemacht hat, wurde rückgängig gemacht. Ja, also momentan haben wir eine gültige Liste, aber in dieser Liste
- ist nur Element 2 eingehängt. Element 1 ist nicht mehr eingehängt. So, jetzt passiert wieder Prozesswechsel. Wir wechseln also zurück
- zu Thread 1. Und jetzt macht er natürlich hier noch seinen letzten Schritt. Ja, wenn er seinen letzten Schritt hier macht, dann wird ne nimmt
- nimmt er hier List Tail, das ist dieser Zeiger oder dieser Zeiger hier und der wird gesetzt auf in dem Fall wieder E1. Next. Heiß der Ted Zeiger zeig jetzt
- hier drauf. Ja. Und jetzt haben wir eine Listenatenstruktur, die völlig kaputt ist. Ja, also das ist keine gültige
- Liste mehr. Ja, die Liste ist so, wenn wir jetzt auf dieser Liste irgendwie weiterarbeiten, ähm dann werden wir je nachdem, was wir
- mit dieser Liste machen, ähm ja, einzelne Elemente nicht mehr drin sehen. Die sind da halt nicht mehr drin. Ja, also z.B. eine Operation, die versucht
- die Liste einfach komplett auszugeben. Die würde diesem Headpointer z.B. folgen, bis es hier bis es irgendwie in der Kette Nullpointer findet und dann
- bei diesem drüber iterieren würde dieses Element A hier nie auftauchen. Ja, und alle weiteren Elemente, die angehängt werden, die werden hier immer an den
- Tailpointer angehängt. Ja, die hängen also dann alle letztendlich direkt oder indirekt an diesen Zeiger hier dran und ähm
- die gehen verloren. Ja, also die sehen wir nicht mehr, wenn wir über die Liste eterieren. Ja, also kaputte Datenstruktur irgendwie schlecht.
- Ähm ja, was vielleicht noch dazu zu sagen ist, also das ist was jetzt hier irgendwie so aussieht, das wäre es ein Problem von C. Ähm ist es aber nicht.
- Ja, also das kann in Java passieren, das kann in jeder anderen Programmierprache passieren, wo sich nebenläufige Prozesse Datenstrukturen teilen können. Ja, das
- ist ein allgemeines Problem. Ja, wo gibt's das sonst noch? Ja, also gemeinsamer Speicher, also Shared Memory
- ähm z.B. Ne, da könnt da können wirklich schwergewichtige Prozesse sich Speicher teilen. Da kann man den selben Speicher
- in in zwei schwergewichtige Prozesse einblenden und dort drin gemeinsame Datenstrukturen ablegen. Ja, das Beispiel, das wir jetzt gerade
- gesehen haben, das würde jetzt am ehesten noch so einem leichtgewichtigen Prozess, also einem Thread entsprechen, ja, wo letztendlich einfach nebenläufig
- auf Variablen, auf dieselben Variablen zugegriffen wird. Ähm, im Betriebssystem selbst kann das passieren, ja, Betriebssystematen äh die
- gebraucht werden, um den Zugriff von Prozessen auf unteilbare Betriebsmittel zu koordinieren. Also um z.B. ähm Operation auf äh Dateisystemstrukturen
- zu machen. Ja, das Dateisystem ist eine geteilte Ressource, die letztendlich alle Prozesse sich teilen. Ja, Operation auf der Prozessstabelle,
- Speicherverwaltung, ja, oder auch das Nutzen von Geräten. Ja, also wenn ein Drucker z.B. nur exklusiv benutzt werden darf von einem Prozess, dann muss man
- den Zugriff darauf irgendwie koordinieren. Ja. Ähm, kleiner Vorg auf eine Masterveranstaltung, Betriebssystembau, ja, da kommt kommt
- man letztendlich auch auf ein ähnliches Problem, nämlich das der Unterbrechungssynchronisation. Ähm, wenn also eine Unterbrechung
- auftritt, dann kann das kann natürlich auch diese Unterbrechungsbehandlung auf Datenstrukturen arbeiten, äh auf den andere äh Kontrollfüße auch gerade
- arbeiten. Ja, wobei die Verfahren, die wir jetzt jetzt angucken, nicht notwendigerweise auch bei Unterbrechungen funktionieren. Ja, das
- nur so als Randnotiz. Ja, was sehen wir hier eigentlich? Das ist eine sogenannte Race Condition, ja,
- oder die halbgare Übersetzung Wettlaufsituation. Ja. Ähm, was ist eine Race condition? Das ist
- das ist noch nicht der Fall, wo wirklich ein Problem auftritt. Also eine Race Condition ist einfach nur eine Situation, wo mehrere Prozesse
- konkurrierend auf dieselben Daten zugreifen. Ja, und mindestens einer dieser dieser Prozesse manipuliert diese Daten auch.
- ähm und welchen Wert diese gemeinsamen Daten letztendlich haben, das kommt das das kommt bei einer Racing Condition darauf an, in welcher Reihenfolge die
- Prozesse zugreifen. Ja, welches Ergebnis letztendlich dabei rauskommt, ist im allgemeinen erstmal nicht vorhersagbar und kann, wie wir
- jetzt in dem Beispiel gerade gesehen haben, kann aber muss nicht, kann ähm bei überlappenden Zugriffen sogar inkorrekt sein. Ja, also in dem Beispiel
- von dieser Liste, da hatten wir ja letztendlich zwei Fälle jetzt betrachtet, ja, zwei Abläufe und in dem einen Ablauf ähm gab's gar keine
- Überlappung und letztendlich war das Ergebnis am Schluss korrekt. Ja, dem anderen Ablauf gab es eine Überlappung und die hat in dem Fall tatsächlich
- dafür gesorgt, dass die Datenstruktur ähm ja defekt war hinterher. So. Und was kann man tun, um Race Conditions zu vermeiden?
- Man muss synchronisieren. Ja, die konkurrentenprozesse müssen synchronisiert werden. So, was heißt synchronisieren?
- Synchronisation ist die Koordination der Kooperation und Konkurrenz zwischen Prozessen. Ja, das ist also die Definition aus äh aus so einem
- klassischen äh Nebenläufigkeitsbuch. Ja, Hertwig Hommel. Ähm, Synchronisation bringt die Aktivitäten
- verschiedener nebenläufiger Prozesse in eine Reihenfolge. Ja. Ähm, man sorgt also dafür, dass obwohl
- diese Prozesse konkurrierend nebenläufig auf diesen Datenstrukturen arbeiten wollen, sorgt man dafür, dass dass diese Operationen in eine definierte
- Reihenfolge gebracht werden. Und durch diese Synchronisation sorgt man also prozessübergreifend dafür, was innerhalb eines Prozesses einfach
- dadurch erledigt wird, äh dass bestimmte Datenstrukturzugriffe im weiteren Sinne Aktivitäten ohnehin sequenziell sind. Ja, also wenn man innerhalb eines eines
- Prozesses ist, dann hat man einfach eine Operation auf einer Datstruktur. NQ von Element 1 und danach NQ von Element 2. Ja, das ist einfach das ist sequentiell,
- da können keine Race conditions auftreten. Wenn man das aber Prozess übergreifend haben möchte, muss man synchronisieren.
- So, und jetzt kommt noch ein Begriff, der ein bisschen vorgreift. werden wir gleich sehen, was das konkret heißt, nämlich der des kritischen Abschnitts.
- Ähm bei Race Conditions streiten sich n Prozesse. Ja, also in unserem Beispiel waren es jetzt zwei, aber es können natürlich auch beliebig viele mehr sein,
- um den Zugriff auf gemeinsame Daten. Und ähm dieser Zugriff, der passiert durch der Regel nicht besonders lange Codefragmente, Codeabschnitte. Ja, also
- in dem in unserem Fall waren das ein paar wenige Zeilen in dieser NQ Funktion, ja, die letztendlich wirklich auf die
- Datenstruktur zugegriffen haben und deren Zugriffe dafür gesorgt haben, dass eine Race Condition auftritt und diese Codefragmente nennt man
- kritische Abschnitte. Ja, und was man letztendlich die Synchronisation sicherstellen möchte, ist, dass sich immer nur ein Prozess von
- unseren Prozessen, die sich hier streiten, äh in seinem kritischen Abschnitt oder in einem kritischen Abschnitt aufhält oder und aufhalten
- kann. Ja, man muss das erzwingen. So, und jetzt gucken wir uns mal an, wie geht das eigentlich?
- Ja. Ähm Lösungsansatz ist äh mit die ist ist das Einführen einer sogenannten Schlossvariablen.
- Ja, Schlossvariable hier Datentyp Log. Das ist einfach zunächst mal ein abstrakter Datentyp. Ja, da legen wir also hier auch eine Instanz davon an.
- Und dieser abstrakte Datentyp, der hat zwei Operationen, Acquire und Release. Ja, wie die letztendlich heißen, ist ist jetzt nicht so wichtig, aber
- letztendlich dieses dieses Aquired, das verschließt ein Schloss und das Release äh öffnet das Schloss wieder. Dieses Aquire hat noch eine zusätzliche
- Eigenschaft, nämlich, dass es den Prozess, der dieses Aquir aufruft, so lange verzögert, bis dieses bis das zugehörige Schloss frei ist. Ja, und
- wenn das Schloss frei ist, dann wird das Schloss von innen verschlossen. Ja, dann sind wir also in unserem kritischen Abschnitt und am Ende von dem kritischen
- Abschnitt wird dieses Schloss wieder freigegeben, ja, oder geöffnet, ohne dass irgendjemand weiterhin verzögert wird.
- wie dieses diese abstrakte Datentyp letztendlich implementiert ist. Ja, wie man wie man das tatsächlich umsetzt. Ähm das gucken wir uns jetzt gleich noch an.
- Da gibt's verschiedene Möglichkeiten. Allgemein nennt man solche Implementierungen dieses dieser dieser Schlossvariablen Schlossalgorithmen.
- Ja. So. Was hat Log denn für ein Basisdatentyp? Wie gesagt, das ist ein abstrakter Datentyp. Das das ist
- letztendlich die Basisklasse, würde ich jetzt mal sagen. Ja, also die Basisklasse aller Schlossalgorithmen heißt jetzt in unserem Kontext hier
- erstmal log. So, jetzt entferne ich mal wieder ein bisschen Dinge aus dem Frag jetzt.
- Okay. Ähm, wie kann man jetzt letztendlich so ein Schlossalgorithmus tatsächlich konkret bauen?
- Ähm, jetzt gucken wir uns erstmal den Adruck Lösungsansatz an oder sogar mehrere Adrucklösungsansätze äh wie man wie man sowas bauen könnte.
- Ja, das ist jetzt zunächst mal der naive Lösungsansatz. Ich versuche den mal kurz zu erklären. Wir sagen jetzt einfach, dass unser Logdatentyp, das ist einfach
- einsign. Ja, also in in C kann man mit Typed einen Datentyp neuen Datentyp anlegen und quasi den als Alias für einen
- anderen Datentyp anlegen. Letztendlich entspricht log einfach uns car, also einem vorzeichenlosen 8 Bit Integer vereinfacht gesagt.
- So und was macht das Aququire? Das Aquirer kriegt einen Pointer auf so einen Char übergeben. Ja, ich das Logobjekt übergeben.
- Und jetzt wird hier in einer Endlosschleife. Ja, wir sehen hier, der Schleifenrumpf ist leer, deswegen ist hier ein Semikron. Sie werden das in den
- Folgefolien direkt hinter diesem While hier finden. Ich habe das hier nur auf dieser ersten Folie mal in die nächste Zeile gezogen, damit es noch ein
- bisschen offensichtlicher wird, dass es also eine Schleife ist, die in ihrem Rumpf überhaupt nichts tut, sondern einfach nur wiederholt diese Bedingung
- hier überprüft. Ja, was ist was ist diese Bedingung? Diese Bedingung ist einfach der Wert von diesem Char, das hinter dem Lock steckt. Ja. und ein tar
- oder im weiteren Sinne eine Ganzzahl ist in C wahr, wenn sie ungleich 0 ist und sie ist falsch, wenn sie gleich 0 ist. Ja, das heißt also letztendlich diese
- Schleife hier dreht sich so lange im Kreis, wie dieses Lock hier z.B. 1 ist oder bis so sol so lange im Kreis wie dieses Log
- hier ungleich null ist. Ja. Oder anders gesagt, diese Schleife wartet so lange, bis log den Wert null hat.
- So, wenn Log den Wert null hat, dann gehen wir hier runter und setzen, ne, wir nehmen hier den diesen Logzeiger, diezen, in schreiben dort eine einzeln.
- Und damit haben wir das Log belegt. Ja, wenn jetzt ein anderer äh Prozess im weiteren Sinne kommt und auch das Aquir aufruft, dann wird der hier diese
- Funktion hier betreten. Hier in dieser While Schl feststellen, aha, Sternlog ist 1 und einfach hier end also endlos, na ja, sich so lange im Kreis drehen,
- bis das nicht mehr der Fall ist. Ja, und das klingt ja jetzt erstmal so, als ob man damit so ein so ein Schlossalgorithmus implementieren kann.
- Ja, das Release, das setzt letztendlich dieses Log wieder auf null. Das heißt, wenn ich also Aquire mache, schreibe ich da eine eins hin. Wenn ich
- das Release mache, schreibe ich da eine Null hin. Und der nächste, der während während ich in dieser ich mich in meinem kritischen Abschnitt befinde, versucht
- Acquirreed zu machen, der wird halt hier in dieser in dieser Warteschleife hier stehen bleiben. Ja, da wird solange nicht weiterkommen. So, jetzt steht aber
- schon mal ganz groß in rot über diese Folie falsch. Das ist die spannende Frage, warum steht da falsch ist.
- Ähm, so es gibt's ja schon ein paar Kommentare. Also zum einen, wie rechenintensiv ist die Schleife ohne Inhalt, wenn diese permanent
- weiterläuft? Ja, das ist auf jeden Fall Nachteil. Die ist die ist ziemlich rechenintensiv, ne? So, die die führt
- kontinuierlich CPU Instruktionen aus. Ja, da wird also wird keine Rechenzeit irgendwie an irgendjemand anders
- abgegeben. Also ist erstmal Rechenzeitverschwendung, könnte man sagen. Ähm,
- so und jetzt gibt's noch den noch einen weiteren Kommentar hier von Joachim, der vorschlägt, dass es vielleicht doch möglich sei, dass zwei Threads
- gleichzeitig Lock auf eins setzen. Das verstehe ich jetzt aber nicht. Also anders gesagt, ja, ich glaube schon, dass Sie recht haben, aber können
- Sie das mal konkretisieren? Also was muss denn passieren, dass zwei Threads das gleichzeitig tun? Wirklich gleichzeitig gibt's bei uns ja nicht.
- Ja, wir haben ein Uniprozessorsystem. Das heißt, also der Scheduler kann immer nur genau einen Prozess oder einen Thread ähm laufen lassen, die CPU
- zuteilen. Das läuft immer nur ein Thread auf unser einen CPU, aber trotzdem kann es passieren, dass zwei Threads dieses Log auf eins setzen.
- Ja, Vorschläge. [seufzt] Genau. Ja, hier kommt der Vorschlag. Wird nach der nach der
- Wildschleife unterbrochen. Ja, also wir haben diese Wildschleife. Wir stellen fest, aha, log ist null. Juhu,
- wir können das Log jetzt nehmen. Die While Schleife wird verlassen. Jetzt befinden wir uns hier und jetzt findet ein Prozesswechsel statt.
- Jetzt kommt ein anderer Prozess, ruft auch das Aququire auf. Stellt fest, aha, log ist null. Juhu, ich kann das Log nehmen. Geht hierhin,
- setzt log auf 1, betritt den kritischen Abschnitt, tut irgendwelche Dinge auf unserer geteilten Q und so weiter. Irgendwann passiert wieder ein
- Prozesswechsel zu dem Thread vorher. Der befand sich gerade direkt vor dieser Zuweisung 1. Ja, der weiß jetzt Lock, dass immer noch den Wert 1 hat von dem
- anderen Prozess. wieder die ein zu. Jetzt haben wir zweimal zwei Prozesse, die dem Log eine ein zugewiesen haben und beide gehen
- jetzt in ihren kritischen Abschnitt. Ja, das genau das, was wir verhindern wollten. So, also was ist das Problem?
- Der kritische Abschnitt, den wir sichern, also der kritische Abschnitt, den wir sichern wollen, äh ist ist kritisch, aber das Aquirer selbst ist
- auch kritisch, nämlich wenn wir genau hier zwischen diesen beiden Zeilen hier äh verdrängt werden. Ja, also genau der Moment nach dem Verlassen von der
- Mateschleife vor dem Setzen der Schlossvariablen äh ist das Problem. Ja, und das kann eben dazu führen, dass jetzt mindestens zwei, wenn ich sogar
- noch mehr Prozesse, diesen eigentlich durch Aquire oder letztendlich durch unser Log gesicherten kritischen Abschnitt äh überlappt ausführen. Ja,
- also genau das, was wir verhindern wollten. Und ähm die, sag mal, die klassische Lösung zunächst mal aus algorithmischer
- Sicht ist der sogenannte Bäckereialgorithmus. Ähm, wir werden auch gleich noch ganz kurzen Blick auf eine mögliche
- Implementierung werfen. Die den werde ich jetzt aber nicht vertiefen. Ähm, zunächst mal auf abstrakter Ebene ähm bekommt einen Prozess, der einen
- kritischen Abschnitt betreten will, eine Warteummer. Ja, der wie sich wie man sich das beim Amt vorstellen kann. Man zieht eine Nummer
- und dann erfordert erfolgt die Zulassung in den kritischen Abschnitt in der Reihenfolge der Nummern. Ja, so wie man sich das halte vom Amt
- vorstellt. Das heiß, wenn der kritische Abschnitt frei ist, dann darf der Prozess mit der niedrigsten Warenummer
- den kritischen Abschnitt betreten. Ja, und wenn der kritische Abschnitt wieder verlassen wird, dann wird die Wartenummer weggeschmissen. Ja, im
- Prinzip, wie man sich das vorstellt beim Amt oder in der Bäckerei. Ja, wobei das in deutschen Bäckereien jetzt vielleicht normalerweise so nicht funktioniert.
- Ähm n es gibt noch ein kleines Randproblem, was man bei der Implementierung auch sieht, wenn man sie sich genau anguckt, nämlich dass der
- Algorithmus, dieser Bäckereialgorithmus nicht garantiert, dass eine Wartennummer nur einmal vergeben wird. Ja, also es gibt so ein so so eine Stelle, wo es
- möglich ist, äh dass mehrere Prozesse dieselbe Warteummer kriegen, wo dann natürlich nach dem bisherigen Kriterium noch nicht klar ist, welcher als erstes
- dran darf. Und in diesem Fall entscheidet die Prozess ID, ja, also die äh z.B. für die niedrigst mögliche Prozesside ID unter denen mit derselben
- Warenummer darf als erstes. So, ich gehe gleich noch auf die Fragen ein.
- Jetzt mache ich aber erstmal den Bäckerealgorithmus noch fertig. Ähm, das ist jetzt so eine Pseudo Code Implementierung. Ja, also da ist das ist
- diese Logstruktur ein bisschen komplexer. Da gibt es also ein booli array choosing der Größe N. Ja, wir nehmen
- jetzt zunächst mal an, wir haben wir haben n Prozesse, die die hier konkurrieren. Ja, und dieses N wirkt sich auch direkt hier auf die
- Dimensionierung von diesem Choosing Array hier aus. Und wir haben ein Number Array, das auch die Größe N hat. Und diese beiden diese beiden Arrays werden
- letztendlich verwendet, um zu synchronisieren. Und das Aququire sieht so aus, dieses in dem I landet die aktuelle Prozess ID.
- Ja, und dann wird zunächst mal in dem Choosing Array hier die das eigene Feld auf True gesetzt. Ja, also jeder Prozess, der gerade der
- sich der gerade beim Warteummer ziehen ist, der schreibt hier erstmal ein T rein. Wenn er mit Warteummer ziehen fertig ist, schreibt er wieder ein False
- rein und dazwischen schreibt er in die eigene Number. Wir haben n ein Feld der Größe Numbers, ne? Das sind einfach die die Wartenummern aller beteiligten
- Prozesse und die Warteummer, die hier gezogen wird, ist das Maximum aus allen Wartenummern, die bis jetzt hier
- versammelt sind, + 1. Ja, das ist so die nächste Warteummer, die die hier äh die hier gezogen wird. Und das ist jetzt letztendlich auch die
- Zeile, wo es passieren kann, dass ähm dass mehrere Prozesse hier ähm in dieses Number Array dieselbe Warteummer eintragen. Ja, weil dieser Abschnitt
- hier eben nicht synchronisiert ist. Ja, also es kann passieren, dass hier mehrere Prozesse gleichzeitig diese dieses Maximum hier ausrechnen, eins
- drauf addieren und es kommt dann dieselbe äh dieselbe Wartenummer raus. Ähm, das löst sich aber gleich eben durch diese weitere Priorisierung durch
- die Prozess ID. Was wird hier unten gemacht? Hier wird letztendlich komm über das dieses komplette Choosing und das komplette Number Array gelaufen,
- also j = 0 bis N. Und das ist jetzt wieder so eine so eine so eine Wchleife mit einem leeren Rumpf. Ja, das den Semikolon hier dürfen Sie nicht
- übersehen. Ähm und jetzt wird erstmal gewartet, bis keiner mehr in keiner mehr gerade beim Auswählen einer Warenummer ist. beim Ziehen einer Warenummer.
- Ja, und dann wird letztendlich ähm Nein, Unsinn. Es wird jetzt erstmal geguckt, dass der der Prozess äh mit der
- Nummer J, dass der ähm nicht mehr wartet. Und dann wird geguckt, wartet äh hat der eine Warteummer. Ja, also wenn der wenn der hier eine Null
- eingetragen hat, dann wartet dieser Prozess gerade sowieso nicht. Wenn er wartet, dann wird geguckt, ist äh die Wartenummer von dem Konkurrenten kleiner
- als die eigene Warenummer. Ja, und hier unten ist noch diese Geschichte mit der Prozess ID Priorisierung äh codiert, aber letztendlich hier in dieser Zeile
- steckt drin, ich dieser Schlossalgorithmus äh di dieses dieser Bäckereialgorithmus, der wartet hier in einer in einer Whitechleife, die
- sonst nichts tut, solange dieser Konkurrent hier eine kleinere Warenummer hat. Ja, und wenn man sich das Verfahren
- genau anguckt, stellt man fest, also wenn diese Schleife hier durch ist, dann gibt es keinen mehr in der ähm in diesem Number Array, der also eine Wernummer
- gezogen hat, die kleiner ist als die eigene. Ja, und damit ist die eigene Warteummer die kleinste, die noch da ist. und dann darf man dementsprechend
- den äh den kritischen Abschnitt betreten. Ja, und bei dem Release wird jetzt endlich einfach in die in dieses in
- dieses Number Feld hier wieder eine Null eingetragen, ne? Das ist das das bildliche Wegwerfen von dem von dieser ähm
- von der Warteummer. So. Und dieser Algorithmus ähm der funktioniert. Ja, das kann man auch
- beweisen, dass das dass der korrekt ist, aber er hat ein paar Probleme. Ja, also zum einen weiß man in der Regel nicht vorher, wie viele Prozesse um den
- Eintritt in diesem kritischen Abschnitt konkurrieren werden. Also dieses groß n äh liegt in der Regel nicht fest. Ja, Prozess IDs liegen normalerweise auch
- nicht im Wertebereich von 0 bis n -1. Da könnte man wahrscheinlich noch irgendwie eine Abbildung finden. Das ist jetzt nicht das größte Problem, aber zumindest
- würde es dann nicht eins zu eins funktionieren. Ähm und was vor allem ein Problem ist, diese Funktion Aquire, die hat eine große Laufzeit. Ja, also auch
- diese auch hier haben wir wieder dieses aktive Warten, also in einer Schleife CPUZeit verbrennen ähm bis man also den bis man also den kritischen Abschnitt
- betreten darf. Und ähm diese Funktion Aquire hat äh auch eine eine Laufzeit, die linear mit der Anzahl der beteiligten Prozesse ist.
- Ja, also O von N, selbst wenn gerade überhaupt kein anderer Prozess im kritischen Abschnitt ist, selbst wenn, also der kritische Abschnitt gerade
- komplett frei ist. Okay, was man jetzt hier eigentlich haben wollen würde, wäre also ein Algorithmus,
- der nicht so kompliziert ist, ja, nicht so komplex ist in der Laufzeit. und aber gleichzeitig so einfach äh also der korrekt ist und aber auch so
- einfach wie der naive Ansatz, ja, das was wir vorhin gesehen haben, diese Schleife, die letztendlich selber kritisch war. Ja, das wäre eigentlich
- schön, wenn es das gäbe und da gucken wir uns gleich Lösungen dazu an. So, und jetzt gucke ich mir mal noch ein paar Fragen an hier.
- Ähm, ist durch dieses Problem dann bei Windows z.B., dass man z.B., wenn man eine PDF Datei aufhat, die dann nicht mit anderen Programmen editieren kann,
- heißt das Windows unterstützt das gar nicht. Also ähm bin jetzt kein regulärer Windows User äh
- wenn man eine Datei geöffnet hat mit einem Programm, dann kann es natürlich sein, dass dieses Programm dem Betriebssystem irgendwie signalisiert,
- ich habe jetzt hier meine meine Clown auf dieser Datei. Ja, und das andere Programme diese Datei deswegen dann gar nicht öffnen wollen. Ähm,
- prinzipiell könnten sich Programme genauso unter Windows natürlich äh beliebig synchronisieren und auch gleichzeitig z.B. auf dem auf derselben
- PDF-Datei arbeiten. Ja, das ist aber nicht so ohne weiteres äh immer möglich, ne? je nachdem, welche Operation auf dieser
- PDFdatei passieren sollen. So. Ähm, jetzt gibt's hier die Frage, ich verstehe noch nicht wirklich, warum
- eine Wild Schleife ohne Rumpf nicht in einer Endlusschleife endet. Ähm, na ja, nur weil eine Wild Schleife, gehen wir mal zurück zu der einfachen
- hier, warum warum diese Wchleife nicht in einer Endlosschleife endet. Ähm, na, diese Weschleife beendet endet
- genau dann, wenn das wenn die Bedingung hier in der Klammer den Wahrheitswert falsch bekommt. Ja, und ein char oder im weiteren Sinne ein int äh gilt
- in einer Bedingung als falsch, wenn es null ist. Ja, das heißt also sobald ein anderer Prozess herkommt und dieses Log auf null
- setzt, wird diese Bedingung hier falsch und die Schleife wird verlassen. Ja, das muss keine Schleife sein. Natürlich, wenn man jetzt nur lokal
- dieses Stück Code hier betrachtet, dann sieht das so aus, als ob die Schleife nie beendet werden kann. Ja, weil wenn die Schleife ein, also
- wenn diese Bedingung einmal wahr ist, dann gibt es erstmal keine keine ersichtliche Codstelle, die die äh die Wahrheit dieser Bedingung
- äh ändern könnte. Ja, dazu muss ein anderer Prozess kommen, der das Acquire äh der das Release aufruft, ja, der z. Gerade, also der sein kritischen
- Abschnitt verlässt, der ruft das Release auf und das Release, wie man hier unten sieht, setzt das Log auf null. Ja, und der andere Prozess, der hier in der
- Wchleife wartet, der kann dann der verlässt dann diese White Schleife, sobald er wieder äh rechnen darf.
- Warum benutzt man für Lock nicht einen Booli statt eines Chars? Äh täst genauso. Wäre letztendlich bei einem Maschinencode hinten rausfällt
- sogar dasselbe, vermute ich mal ganz stark. Ja, also auch ein Boolian ist klassischerweise in C ein 8 Bit Integer, aber
- das würde würde das Problem genauso lösen. Haben Sie völlig recht. Hat C eine Standard Logfunktion dafür? Äh nein, C
- hat zunächst mal kein Ja, stimmt nicht. C11 hat schon irgendwann das das auch das Konzept von Nebenläufigkeit. Äh letztendlich kommen da halt einfach
- Threads mit dazu und dann gibt's sowas wie Lock und Unlock und so weiter gibt's auch. Ähm ältere CSstandards haben überhaupt kein Konzept von
- Nebenläufigkeit. Ja, weil letztendlich muss man dann z.B. die Betriebssystemstellen dafür verwenden, die die für die
- Synchronisation da sind. Die gucken wir uns dann gleich an. Wie wahrscheinlich ist der Fall, dass zwei Prozesse Log auf ein setzen?
- Ja, das kommt auf an. Äh das kommt drauf an, wie lang z.B. für diese kritischen Abschnitte sind. Äh das kommt auff an, wie häufig diese kritischen Abschnitte
- überhaupt aufgerufen werden sollen oder wie oft das Aququire und äh letztendlich aufgerufen wird. Ja, also wenn sie zwei Prozesse haben, die einfach ständig in
- einer Endoschleife Acquire release Acquire release acquire release machen, dann wird's relativ wahrscheinlich. Ja, dann wird das innerhalb von ein paar
- Millisekunden tatsächlich auch passieren. Ähm, wenn Sie äh Prozesse haben, die nur sehr selten mal einen kritischen Abschnitt ausführen, dann
- wird das halt Größenordnungen unwahrscheinlicher. Das ist auch richtig gemein. Ja, dann haben sie also einen fiesen äh Synchronisationsfehler,
- Synchronisationsbug in ihrem Code, den sie aber bei durch Testen möglicherweise gar nicht finden, sondern das passiert, der tritt dann erst in 10
- 000 m Höhe beim Kunden auf. Ähm, deswegen ist das so wichtig, was ich heute erzähle. Ja, also je äh je unwahrscheinlicher diese dieser
- Fall eintritt, desto hässlicher ist dieser Bug eigentlich, weil irgendwann passiert doch mal und kann dann natürlich beliebige Resultate haben,
- wenn äh wenn das also z.B. ein ein Rechensystem ist, das Teil von einem Flugzeug ist oder so. Ja, wenn da irgendwie kaputte Datenstrukturen sind
- und das Flugzeug deswegen vom Himmel fällt, werden die Arrays dann beim Bäckereialgorithmus unendlich groß? Ähm
- ja, natürlich nicht. Also, erstes mal war natürlich AR grundsätzlich nicht unendlich groß werden können. Ähm und zum zweiten, weil man den in der Praxis
- auch nicht einsetzt. Ja, warum werden wir gleich noch mal ein bisschen genau besprechen. Was ist die maximale Array Größe in C? Ich weiß nicht, ob der
- C-Standard irgendwas vorschreibt. Äh letztendlich wird das in der Regel daran scheitern, äh wie groß ihr Hauptspeicher ist und oder der Stack
- ist, z.B. Ja, aber ich ich wäre jetzt ich wüsste jetzt keine keine Limitierung, die der CSstandard vorschreibt.
- Okay, Bäckereialgorithmus. Ähm, jetzt gucken wir uns mal andere Lösungen an. Ja, also aktives Warten ist
- ähm nachteiligt. Das woran es jetzt liegt, werden wir gleich noch sehen. Jetzt gucken wir jetzt erstmal Hardware Unterstützung für das Problem ist, dass
- wir jetzt gerade noch haben, nämlich sie erinnern sich an diesen an diesen kleinen Momenten nach der Wild Schleife vor dem Setzen von dem Lock. Ja, wenn
- genau dazwischen äh die Verdrängung passiert, dann haben wir wieder ein äh ja wieder eine Race Condition, wieder zwei Prozesse, die die in den kritischen
- Abschnitt reinkommen. Ja, und dafür gibt's Hardware Unterstützung. Ähm, die einfachste oder vermeintlich einfachste Möglichkeit ist einfach Unterbrechungen
- ausschalten, Unterbrechungen unterdrücken. Ja, allein der Unterbrechungsmechanismus in der CPU sorgt dafür, dass der CPU
- innerhalb des kritischen Abschnitts die CPU entzogen werden kann. Also z.B. wenn der Timerbaustein eine Interrupt auslöst und damit dem Scheduler signalisiert,
- die Zeitscheibe von dem gerade laufenden Prozess ist abgelaufen. Scheduler entscheidet jetzt, es kommt jetzt ein anderer Prozess dran und macht ein
- Kontextwechsel. Das ist die eigentliche der ist der einzige Grund, der dazu führen kann, dass also äh ja hinterher unsere
- Datruktur kaputt gehen kann durch eine Race Condition. Ähm und das kann ich zunächst mal ganz einfach verhindern, indem ich halt bei
- dem Aquir einfach sag, ne? Ich ich rufe diese CPU Instruktion CLI auf, die schaltet einfach die Interupts aus und das Release schaltet halt die
- Interrupts wieder ein. Ganz einfach. Oder me eine Idee, warum das vielleicht eine dumme Idee ist?
- Vorschläge, warum das warum man das vielleicht nicht machen sollte. gibt letztendlich zwei gute Gründe.
- Trudeln Vorschläge ein, wenn mein kritischer Abschnitt 2 Stunden dauert, das ist ganz schlecht, ganz genau. Ja, also wenn man zwei Stunden lang die
- Inupts abschaltet, dann wird zi Stunden lang das System keine vernünftigen Dinge mehr tun. Ja, also z.B. wird diesen Prozess die CPU zwei Stunden lang nicht
- mehr entzogen, zumindest nicht durch ein Timer Interrupt, der das Zeitscheibenende signalisiert. Ja, also plötzlich können Prozesse auch in dem
- System mit äh mit Prätion wieder die CPU monopolisieren. Das ist definitiv ein Problem. Ja, und auch können in diesen zwei zwei Stunden
- keine anderen Interrupts durchkommen. Also z.B. auch keine Tastatur äh Tasten keine Tastendrücke, Mausbewegungen, sonst wie. Ja, die werden alle nicht
- mehr ans Betriebssystem zugestellt, weil die alle letztendlich interupt getrieben arbeiten.
- Ähm, wenn das Programm selbst ein Interrupt aufruft, ja, Programme können kein Interrupt aufrufen in dem Sinn,
- sondern die lösen Bhaupt, dann lösen die Traps auf aus und die gehen immer noch. Ähm, genau. Wenn man den kritischen Abschnitt hat mit einer Endlosschleife,
- das geht natürlich in dieselbe Richtung wie die wie der zwei Stunden Vorschlag, ja, dann passiert halt endlos kein kein großer Fortschritt mehr in dem System.
- Nutzer Interaktion sind nicht mehr möglich. Genau. Tastatur Maus geht nicht mehr. Eingabe Ausgabegeräte tun nichts mehr.
- So und jetzt kommt natürlich auch noch ein weiterer Punkt. Ja, hat ein Programm überhaupt nötigen Rechte sowas zu machen? Nein, hat's nicht. Ja, also
- normales Anmeldungsprogramm darf diese beiden Instruktionen gar nicht ausführen. Ja, das ist wieder die Trennung zwischen äh zwischen
- Systemmodus oder Supervisor Modus und User Mode. Ja, im User Mode können diese beiden Instruktionen einfach nicht ausgeführt werden. Ja, das ist halt
- einfach nicht erlaubt. Genau aus dem Grund oder aus den gerade genannten Gründen. Ähm ja und letztendlich ähm wird eben
- letztendlich das komplette Betriebssystem, alle anderen Prozesse, Gerätetreiber, wird alles beeinträchtigt, wenn ich das mache. Ja,
- deswegen darf man es nicht. Aber es wäre zumindest für kurze kritische Abschnitte eine Möglichkeit, eine mögliche Lösung. Also man z.B. Ihr seht, wenn man gerade
- sowieso im Systemmodus ist, ähm also letzt nicht im Betriebssystem äh so eine Synchronisation machen möchte, ein kritischen Abschnitt sichern möchte,
- dann ist es eine Möglichkeit, die man nicht komplett vom Tisch wischen darf. Ja, das ist durchaus manchmal sinnvoll, das zu machen, wenn man sich sehr sicher
- ist, dass der kritische Abschnitt sehr kurz ist und definitiv auch endet. Ja, aber jetzt gucken wir uns noch mal das Problem an mit dieser Wild Schleife
- und dem Setzen auf eins in der Zeite darauf, wo wir eben dazwischen unterbrochen wurden. Und dafür gibt's auch ähm eine Hardwareösung, nämlich die
- sogenannten atomaren Operationen. Ja, viele CPUs unterstützen unteilbare Leseemodifikations und Schreibzyklen, mit denen sich ähm so so äh
- Schlossalgorithmen implementieren lassen. Ja, also z.B. Ähm hier Motorola ist das die Instruktion Test and Set. Ja, was macht
- diese Operation? Die testet in dem Fall hier Bit 7 von dem Zieloperanten Log, das ist also z.B. schon wieder ein 8 Bit Character.
- Ähm, die testet also dieses Bit und setzt dieses Bit und liefert den den vorherigen Zustand von diesem Bit in einem Condition Code.
- Ja, und abhängig abhängig von diesem Condition Code kann man jetzt hier springen oder nicht springen. Oder anders gesagt, wenn das Bit vorher schon
- eins war, dann wird einfach wieder zurückgesprungen zu dem Aquire. Ja, das heißt, wir haben also hier schon das hier ist es nichtich wiederum unsere
- Schleife. Ja, also die Instruktion, die Instruktion, die Instruktion, die die Instruktion. Wenn dieses Test und Set
- hier feststellt, dass dieses höchstwertige oder nee, wenn das höchstwertige Bit null war, dann setzt diese Instruktion dieses Bit
- und merkt sich den alten Zustand von diesem Bit wieder in so ein Condition Code und dann springt diese Sprunginstruktion eben nicht, ne? Und
- dann kann man ab hier in den kritischen Abschnitt gehen und weiß eben, dass das Bit vorher null war, durch mich jetzt auf eins gesetzt wurde. Ja, oder anders
- gesagt, diese Instruktion vereinigt eben das Testen und das Setzen. Ja, deswegen heißt sie Test and Set. Ja, die kann eben nicht noch noch
- weiter unterbrochen werden. Auf X86 gibt's eine ähnliche Instruktion, ne? kann man dafür letztendlich diese Exchange Instruktion
- verwenden. Von der Semantik her ziemlich ziemlich ähnlich wie in der Motorola Variante. Ja, auf Power PC gibt's was ähnliches. Letztendlich haben alle,
- Entschuldigung, letztendlich haben alle modernen Instruk moderne CPUs so eine so ein Lesemodifikationsschreibzyklusinstruktion.
- Ähm, so, aber die bis jetzt gezeigten Schlossalgorithme, die haben alle einen großen Nachteil, nämlich die warten
- aktiv. Ja, also schon die erste, dieser erste naive Vorschlag, der also einfach in der Wchleife arbeitet, der dieses dieses kleine Problem hatte, dass er
- halt in einem ungünstigen Zeitpunkt verdrängt werden kann, aber letztendlich wartete aktiv. Genauso der Bäckereialgorithmus. Ja, da
- waren letztendlich eine Menge von Wchleifen, die halt in ihrem Rumpf ein Semikolon stehen hatten, die aber halt einfach so lange äh sich im Kreis
- drehen, bis wieder irgendein Zustand erreicht ist. Ja, die warten aktiv. Ähm genauso diese atomanoperationen, ja, das sind auch letztendlich Schleifen,
- die ständig testen und setzen und testen und setzen und testen und setzen so lange, bis äh der getestete Wert null ist. Ja, also bis der bis das Log frei
- ist. und der ähm der kritische Abschnitt betreten werden kann. Ja, und dieses aktive Warten, das hat eine ganze Reihe Nachteile. Ähm zunächst mal der Prozess,
- der wartet, ja, der darauf wartet, dass Log z.B. null wird, der kann selbst überhaupt keine Änderung dieser Bedingung
- hervorrufen oder herbeiführen. Ähm, auf also Änderung der Bedingung herbeiführen, auf die er wartet. Ja, also der sorgt selbst nicht dafür, dass
- das Lock jemals null wird, sondern der wartet darauf, dass jemand anderes dieses Lock auf null setzt. Also ein anderer Prozess,
- ja? Trotzdem dreht er sich in der Schleife und rechnet. Mit anderen Worten, ähm z.B. Wenn ja, dieser Prozess läuft ja
- gerade der, nehmen wir mal an, wir sind irgendwie im im äh prämit Zeitscheiben Scheduling irgendwie, dann verbraucht er seine komplette Zeitscheibe mit
- iterieren und testen dieser Logariablen, also die kompletten 20 Millisekunden hindurch wird einfach nur in der Endlosschleife oder nicht in einer
- Endoschleife, in einer Testschleife äh ständig getestet, ob sich dieses Lock dann endlich mal auf null geändert hat. Ja, hat's aber natürlich nicht, weil
- dieser Prozess selbst das gar nicht machen kann auf dem und auf dem Uniprozessorsystem, der weil auch kein anderer Prozess dran kommen kann, der
- dieses, der vielleicht gerade im kritischen Abschnitt ist und dieses Log auf null setzen könnte. Ja, also er behindert ähm
- alle anderen Prozesse, weil er seine Zeitscheibe mit unnützen Schleifendrehen ähm äh ja verbrennt. Ja, also andere Prozesse, die sonstige
- sonstige sinnvolle Arbeit leisten können, die jetzt mit unserem kritischen Abschnitt überhaupt nichts zu tun haben und letztendlich schadet er auf sich
- selbst. Warum? Weil je länger dieser Prozess wartet, damit die CPU behält, ja, also auf dem auf dem Prozessor CPUZit verbrennt, desto länger muss er
- ja auch drauf warten, dass endlich ein anderer Prozess dran kommt, seinen kritischen Abschnitt beendet und Log auf null setzt.
- Ja, das heißt also das aktive Warten ist auf einem Uniprozessorsystem einfach nicht schlau. Es funktioniert,
- aber es ist nicht schlau. Nur bei dem Multiprozessorsystem wird es ein bisschen aufgeweicht, weil natürlich bei einem Multiprozessorsystem
- der andere Prozess, der das Log gerade hält in seinem kritischen Abschnitt ist, wirklich gleichzeitig auf eine anderen CPU weiterrechnen kann und das Log dann
- auf null setzen kann. Da kann das sinnvoll sein. Ja. Auf einem Uniprozessorsystem ist es nicht schlau aktiv zu warten
- und deswegen ist das was man eigentlich macht das sogenannte passive Warten. Ja, und dafür braucht man letztendlich Betriebssystem
- Unterstützung. Ja, und im Betriebssystem wird eine ähm primitive dafür verwendet. Das sind die Semmerapforen und die ermöglichen das und das gucken wir uns
- jetzt an. Ja, also passives Warten ist die das, wo wir hin wollen und da ist eben die Idee,
- während ein Prozess darauf wartet, dass eine Ereignis eintritt, ja, also z.B. das Log frei wird, gibt der Prozess einfach die Kontrolle über die CPU ab.
- Ja, das kennen wir schon von den IOBSTs, von den von den ein Ausgabestößen, ja, auch im Synchronisationsfall, also in dem Fall, dass ich auf ein Log warten
- möchte. blockiert sich der Prozess auf ein Ereignis, geht in den Zustand blockt. Ja, der Prozess Kontrollblock dieses
- Prozesses wird in eine Warteschlange eingereiht, die also auf dieses Ereignis in dem Fall die Freigabe dieses Logs warten. Und wenn das Ereignis eintritt,
- also das ein anderer Prozess dann dieses Lock freigibt, z.B., Dann wird ein darauf wartender Prozess deblockiert.
- Ja, das heißt also ein Prozess, der wartet, ist nicht mehr im Zustand running wie beim aktiven Warten, sondern im Zustand blocked.
- Ja, die Wartephase eines Prozesses auf das Freiwerden eines eines Logs wird als Blockadephase, ja, das was wir bis jetzt schon als ein Ausgabestoß kennen,
- ausgelegt. Also, wenn ich ein Log auf einem auf einer auf einem Lock, das gerade ein Aqu, das gerade gesperrt ist, dann wird
- der Ablauf Plan für die Prozesse aktualisiert. Also, es passiert wieder so eine Scheduling so ein Scheduling Schritt. Es wird ein anderer gerade
- schon gerade laufwähliger Prozess ganz normal plangemäß abgefertigt. Also der vorderste aus der aus der Ready Liste rausgenommen wird also dispatchted
- und wenn es gerade überhaupt keinen Laufwegen Prozess gibt, dann ähm läuft die CPU leer. Ja, das ist also so eine Eidelphase, wo die wo die wo das
- Betriebssystem einfach mal kurzzeitig gar nichts macht. Ja, kann sich z. Du kannst z.B. den Prozessor in niedrigeren ähm Energiemodus versetzen, so in so ein
- in so ein so ein leichten Schlafmodus, wo also das ganze System weniger Strom braucht, so lange bis wieder irgendeiner lauffähig wird.
- Ja, und wenn ähm so ein Prozess blockiert, also von Running auf blocked geht, weil er auf den Lock wartet, endet damit natürlich
- auch sein CPU-Soß. Ja, also man kann sich jetzt konzeptionell das Warten auf ein Log vorstellen wie das Starten eines ein Ausgabestoßes.
- Beginn eines ein Ausgabestoßes. Ja, genauso wie äh Einausgabe von der Festplatte oder von der Tastatur oder sowas.
- Ähm die Betriebssystemabstraktionen, die man dafür verwendet, sind die sogenannten Semmerapforen.
- Ja, was ist ein Semmeraphor? Das ist eine Betriebssystem Abstraktion, die in den 60ern von dem Herrn Dextstra beschrieben wurde, der bestimmt dem
- einen oder anderen ein Begriff ist. Ähm, das ist eine nicht negative ganze Zahl, ja, 0 1 2 3 4 5 und für diese nicht negativen ganzen Zahlen sind zwei
- unteilbare Operationen definiert. P das steht für niederländisch Prol. Das liegt einfach daran, dass der Herr
- Dextra halt ein Niederländer war. ist. Glaub den gibt's auch noch. Muss ich noch mal nachgucken. Ich glaube, den gibt's noch. Ähm, also pro lach oder
- erniedrige, ja, oder auch down oder weight. Und das äh hat folgende Semantik. Wenn diese dieser Semmervor den Wert null
- hat, dann wird der laufende Prozess blockiert. Ja, und sonst nichts. Und ansonsten wird
- der Semmerform 1 dekrementiert. Ja, also wenn der vorher den Wert 1 hatte, hatte hat er einfach hinterher den Wert null.
- Ja, und dieses P ist unteilbar. Also, es kann nicht passieren, dass man während der Ausführung eines Ps ähm verdrängt wird. Ja, das ist so die so die
- Grundidee und das V macht das gegenteilige. Ja, verhoch, erhöhe ab Signal, das sind so die verschiedenen Varianten. Ähm, wenn es einen auf diesen
- Semmervor wartenden Prozess gibt, also einen blockierten Prozess, der also darauf wartet, dass der dieser Semmervor frei wird, dann wird der bei einem bei
- einem Vlockiert und ansonsten wird der Semmer vor um eins inkrementiert. Ja, und dieses diese Semmerformen können
- eben verwendet werden, um zwischen nebenläufig arbeitenden Prozessen sogenannte Synchronisationssignale ähm auszutauschen.
- Was das ähm genau bedeutet, das werden wir gleich an dem Beispiel sehen. Ähm, das ist tatsächlich die
- Implementierung von Semmern in einem in dem OOstups, das objektorientierte Studentenbetriebssystem. Das ist so ein so ein System, dass man in der
- Masterveranstaltung Betriebssystem Bau äh hier selbst baut. Das ist die das ist so ein Teil von der Semmervorimplementierung und da sieht
- man das was das Weight halt genau tut. Ja, das Weight, also das P guckt ist der Counter null. Ja, der also der eingebaute die eingebaute nicht negative
- ganze Zahl. Ja, und wenn der Counter null ist, dann kann natürlich der Selmerform nicht mehr weiter dekrementiert werden und dann wird die W
- letztendlich diese Schritte hier durchgeführt, die letztendlich dafür sorgen, dass der Prozess, der dieses Weight aufgerufen hat, blockiert wird
- und im anderen Fall wird einfach der Counter erniedrigt. Ja, und beim Signal wird geguckt, gibt es einen einen Prozess, der auf diese Semphore wartet.
- Ja, wenn es einen gibt, dann wird der geweckt, ja, also wieder ready gesetzt und anderenfalls wird der Counter um eins erhöht.
- Ja, und letztendlich ist so ein Semmerfor abgeleitet von der Klasse Waiting Room. Waiting Room ist letztendlich einfach eine Liste von
- Prozesskontrollblöcken, ja, also eine eine Warteschlange von Prozessen. Der Scheduler, der lieitstellen, die halt hier die auch verwendet werden.
- Ja, das ist dieses Active, dieses Block. Und dieses Wakeup, ja, und dieses Active sagt letztendlich, welcher ist der gerade laufwä laufende Prozess. Block
- blockiert einen gerade laufenden Prozess und Wakeup setzt einen blockierten Prozess wieder auf die Ready Liste.
- So und so benutzt man Semmer und damit sind wir eigentlich fast wieder am Anfang der Vorlesung. Ja, also Semapfor ist einfach eine konkrete
- Log, also eine konkrete konkreter Datentyp, den man also als Schlossalgorithmus als Schloss äh Algorithmen Schlossviablen Datentyp
- verwenden kann. Also ein Semmer hat klassischerweise äh initial den Wert 1, z.B. Normalerweise muss man das einfach
- einfach zuweisen. Ähm und wenn man also wenn man ihn als äh als Schlossvariable in dem Sinne verwenden will, um gegenseitigen Ausschluss äh
- hinzubekommen, dann setzt man den eben in Zal auf eins. Und das ist jetzt wieder unser NQ aus dem Beispiel vom Anfang. Also se also mit dem Weight das
- Log diese dieser Semmer vor dekrementiert um ein, dann ist er null und dann plus wird er mit Signal wieder um ein erhöht, dann ist er wieder eins.
- Und wenn ein zweiter Prozess versucht gleichzeitig in diesen kritischen Abschnitt zu kommen, dann blockiert er hier. Ja, alle weiteren Prozesse, die
- versuchen hier reinzukommen, während das Log den Wert null hat, dieser Semmer vor den Wert null hat, blockieren hier und mit dem Signal wird einer dieser
- blockierenden wieder aufgeweckt. Ja, das sorgt eben dafür, dass immer nur ein Prozess in diesem kritischen Abschnitt drin sein kann.
- Ähm, Hauer hat nachgeguckt, Extra lebt nicht mehr. Okay, dann war es einer der anden der
- anderen alten Hasen, die es die erstaunlicherweise noch leben. Ähm, man kann selber mal vor, aber lass die
- auch noch andere Dinge benutzen, z.B. die sogenannte einseitige Synchronisation. Ja, das ist also ein Erzeugerverbraucherszenario z.B., ne?
- Wir haben hier eine äh wir haben hier eine Listendatenstruktur und die neben unter der Annahme, dass dieses NQ jetzt irgendwie auf irgende also das NQ und
- das DQ auf irgendwie auf andere Art und Weise synchronisiert ist, z.B. durch eine äh durch gegenseitigen Ausschluss. Dann kann man hier mit einer Semaphore,
- die die Anzahl der Elemente zählt, die gerade in der Liste drin stecken, einseitig synchronisieren, ne? Jedes Mal, wenn ein Element reingesteckt
- wurde, sagt man Signal. Das erhöht also dieses diesen Element Counter hier um eins und jedes Mal bevor man ein Element dqmen möchte sagt man wait. Ja, und das
- und wartet eben darauf, dass mindestens ein Element in der Q drin steckt. Das die sogenannte einseitige Synchronisation, die heißt einseitig,
- weil immer nur eine Seite we hier, aber hier nur eine Seite blockieren kann, nämlich die Consumer Seite natürlich. Ja, das Weight ist ist es ist von den
- beiden Operationen auf der Semaphore die einzige, die blockieren kann, ne? nur eine Seite kann blockieren, nämlich die Consumerseite.
- Ähm und dann gibt's noch die Betriebsmittelorientierte Synchronisation. Na, da wird die funktioniert letztendlich so ähnlich wie
- die ähm dies die Synchronisation mit gegenseitigem Ausschluss. Ja, also macht macht vor der Benutzung einer einer Ressource macht man Weight, nach der
- Benutzung macht man ein Signal und setzt diese Semfor letztendlich aber nicht auf den Wert 1, sondern auf irgendeinen Wert, der Größe 1 ist. Ja,
- also wenn von einem Betriebsmittel z.B. Zeh zur Verfügung stehen, dann initialisiert man das halt mit Zeh. Dann können sich bis zu zeh Prozesse in dem
- kritischen Abschnitt befinden, ne? Weil jeder dann eine von diesen zehn Ressourcen benutzen kann.
- So, man kann mit se vorne aber auch noch komplexere Dinge tun. Ja, ähm z.B. das erste Leserschreiberproblem. Das ist so ein klassisches Synchronisationsproblem.
- Ähm, da soll also ein kritischer Abschnitt geschützt werden, wo es als Arbeit zwei Klassen von beteiligten, konkurrierenden Prozessen gibt, nämlich
- die Schreiber und die Leser. Und die Schreiber, die wollen Daten ändern an dieser Datenstruktur und dementsprechend muss ein Schreiber exklusiven
- Zugriff auf diese Datastruktur bekommen. Ja, also es darf immer nur ein Schreiber ganz alleine auf dieser Datenstruktur arbeiten oder beliebig viele
- gleichzeitige Leser. Ja, lesen kann man ja problemlos gleichzeitig. Ähm, aber sobald einer schreiben möchte, darf eben dann nur der schreiben und
- auch keiner kann anderer gleichzeitig schreiben und kann anderer gleichzeitig lesen. Auch das kann man mit Semmerform und synchronisieren.
- Das ist jetzt aber schon ein bisschen komplizierter, ja? Ähm, letztendlich brauchen wir hier zwei Semaphoren. Eine, die wir hier Newtext
- nennen und ein die wir WD für WR nennen und ein Read Count, ne? Der Read Count soll zählen, wie viele Leser lesen gerade gleichzeitig. Das sind so die
- Initialwerte. Gerade z. liest keiner ähm Initial. Und was so ein Schreiber machen muss, der muss einfach diese right Semaphore von 1 auf null setzen
- oder dran blockieren, dann darf er schreiben, dann darf es wieder freigeben. Ja, das ist jetzt ganz normaler gegenseitiger Ausschluss. Immer
- nur ein Schreiber darf schreiben. Leser ist jetzt aber schon ein bisschen komplizierter. Ja, da wird es erstmal eine Mutex
- belegt. Dann wird der Read Count um eins erhöht, dann wird geguckt, bin ich der erste Leser, ja? Oder gibt's schon andere Leser? Wenn ich der erste Leser
- bin, muss ich hier mit dem Weight WD hierfür sorgen, dass kein Schreiber hier rein hier reinkommt. Ja, wenn ich der erste Leser
- bin, muss ich dafür sorgen, dass während ich lese und auch möglicherweise andere lesen kein Schreiber schreiben darf. Ja, so und dann wird hier tatsächlich
- gelesen auf der Datenstruktur und am Schluss wird wieder mit der Mutex gesichert. Hier dieser Read Count im eins erhöht. Dann wird geguckt, bin ich
- der letzte Leser, der fertig geworden ist mit lesen. Wenn ich der letzte bin, dann muss ich hier diese WRD Semaphore wieder freigeben, damit wenn hier ein
- Schreiber gerade blockiert und warte darauf, dass er schreiben darf, in diesen Schreibeabschnitt ähm hinein darf.
- Das ist schon so ein bisschen fancy. Da muss man schon ein bisschen drüber nachdenken, ob das wirklich in allen Fällen so richtig funktioniert. Ähm, das
- ist sogar nur das erste Leserschreiberproblem. Ja, die verschiedenen Leserschreiberprobleme unterscheiden
- sich dadurch, wer Priorität hat. Ja, die Schreiber oder die Leser. Wenn man sich diesen Code hier ein bisschen genauer anguckt, dann wird man feststellen, äh,
- dass hier die Leser Priorität haben. Ja, also sobald ein Schreiber ansteht da unten, dann darf der wirklich erst rein, wenn der letzte Leser fertig ist. Und es
- dürfen auch jederzeit noch weitere Leser kommen. Es gibt noch noch ein anderes Leserschreiberproblem, wo sobald ein Schreiber z.B. schreiben
- möchte, die Leser, also kein neuer Leser mehr zugelassen wird, ne? wird gewartet, bis alle Leser dann fertig sind, damit der Schreiber endlich drankommt. Je
- nachdem, wie wie die Priorisierung hier aussieht, ähm ist es das erste oder das zweite Leserschreiberproblem.
- Ähm, es gibt von Semfor noch ein paar Varianten ähm oder Erweiterungen. Ja, also
- Biennähre Semforen, das da haben wir jetzt gerade schon Beispiel gesehen, ja, Semfore, die einfach nur auf null oder auf eins steht, die man letztendlich
- verwendet, in der Regel verwendet, um einen kritischen Abschnitt immer einen kritischen Abschnitt für für gegenseitigen Ausschluss zu sorgen. Ähm,
- die nennt man auch Mutex, ja? Mutex für Mutual Exclusion, also gegenseitigen Ausschluss. Ähm von dem Weight oder dem P gibt es
- auch Varianten, die nicht blockieren, also wo dafür gesorgt wird, dass also wo man tatsächlich über einen Parameter noch steuern kann, was passieren soll,
- wenn das Ding gerade belegt ist. Ja, da kann man quasi so ein so das Belegend testen und wenn es aber nicht klappen würde, dann kann man einen anderen
- Gruppfahrt wählen, anstatt zu blockieren. Ähm, weight mit Timeout gibt es noch, also wo das ähm wo man das nicht
- einstellen kann. Ich möchte zwar blockieren, aber nur für 2 Sekunden. Wenn innerhalb von 2 Sekunden der der diese Semmerfor immer noch nicht frei
- wird, dann möchte ich nicht weiter blockieren. Oder es gibt Implementierungen, wo man nicht nur eine Semmerfor hat, sondern ein ganzes Array
- von Semmaforen. Ja, das klingt jetzt erstmal so, als wäre das keine Besonderheit. Die Besonderheit da ist äh dass auf diesem Array auch atomare
- Operationen durchgeführt werden können. Also z.B. erhöhe die erste Semaphore, erniedrige die zweite und teste die dritte auf einen Wert oder so. Und nur
- wenn wenn alle drei Operationen klappen, werden sie auch durchgeführt und das ganze Atom. Ja, das ist so der der die die POS 5 die System 5 AP z.B. für für
- SF und die kann das. Ähm und was es bei Semfor auch noch gibt, sind eben Fehlerquellen. Ja. Ähm zum einen
- entsteht die Gefahr von Verklemmungen. Was das ist, das gucken wir uns in der nächsten Vorlesung an. komplexere Synchronisationsmuster, wie
- wir das jetzt hier gesehen haben, äh die können durchaus schwierig werden. Na, also das Leserschreiberproblem ist nicht das einzige Synchronisationsproblem, was
- man in dem Kontext finden kann. Da gibt's noch ganz andere hässliche. Ähm, eine definitive Fehlerquelle ist, dass
- die kooperierenden Prozesse, die also alle auf derselben Datenstruktur arbeiten, dass die alle ein bestimmtes Synchronisationsprotokoll wirklich
- einhalten müssen. Ja, sobald ein Prozessor irgendwie durch einen Programmierfehler z.B. aus der Reihe tanzt, ähm können trotzdem Race Racing
- Conditions und möglicherweise Datenkorruption eintreten. Ja, also jeder muss die Protokolle exakt anhalten. Es gibt nichts, was das
- erzwingt, ähm weswegen man sich eigentlich sowas wünscht wie Unterstützung durch die Programmiersprachen.
- Und das gucken wir uns aber das nächste Mal an, ja, das Monitor Konzept. Okay. Ähm, an der Stelle möchte ich für heute einen Strich machen. Ich schaue
- jetzt noch mal ein bisschen Fragen an. Ähm, wie wird festgelegt, welcher der blockierten Prozesse freigegeben wird, wenn es z.B. drei blockierte Prozesse
- gibt? Ja, also wir haben ähm wir haben einen kritischen Abschnitt mit der mit der mit einer Mutex, also mit einer binären Semapore abgesichert. Ähm
- ein einer von diesen einer von den Prozessen geht in den kritischen Abschnitt. Die Semaphore ist jetzt null und jetzt kommen noch drei andere, die
- auch versuchen, die Semaphore zu dekrementieren mit dem Weight oder dem P. Und jetzt verlässt der, also und diese
- drei Prozesse, die blockieren jetzt alle. Und jetzt verlässt der erste Prozess wieder den kritischen Abschnitt und ruft das das Signal oder das V auf
- der Semapore auf. Welcher von den wartenden Prozessen wird jetzt denn deblockiert? Ähm, da werden wir auch in dem in dem Rest
- der Vorlesung noch mal ganz kurz drüber sprechen. Äh, also nächstes Mal ähm letztendlich kann man irgendeinen Verfahren wählen.
- Ja, also z.B. F Kampf. So, wer zuerst blockiert hat, darf als erstes wieder deblockiert werden. Normalerweise sollte man an der Stelle
- versuchen, äh den zu deblockieren, der auch nach der drüber liegenden Scheduling Strategie die höhere Priorität hat. Ja, also solche ein
- hochpriorer Prozess sollte als erstes deblockiert werden vor einem niederprierioren Prozess. Ja, das sind z.B. für Dinge, die man da entscheiden
- muss. Ähm, sonst kann es passieren, dass man Entscheidungen trifft, die der der gerade aktiven Schedulingstrategie zu
- Wiederlaufen. So, Dextra ist nicht der redanische Geheim, weiß ich nicht. Vielleicht
- gibt's auch einen redanischen Geheimnischef, der Dikstra heißt. Kenne ich nicht. Ist Dextra nicht ein Algorithmus, um den
- kürzesten Weg in einem Grafen zu finden? Nein, Digstra ist der Informatiker, der einen Algorithmus für einen kürzesten Weg in einem Grafen
- vorgeschlagen hat, den man auch deswegen den Digra Algorithmus nennt, aber ähm der hat noch andere Sachen gemacht, z.B. Semmer vorn erfunden.
- Okay, gibt's noch weitere Fragen zum Thema Synchronisation? Ansonsten freue ich mich auf vorab oder auch währenddessen eingereichte Fragen für
- die Q&A Sitzung in knapp einehalb Stunden, also 15:15 Uhr geht's los. Fragen auf den üblichen Kanälen im Channel auf frag jetzt per
- E-Mail, per Gumakkasten, wie Sie möchten. Einer tippt noch.
- Einer tippt noch. Einer hat wieder aufgehört zu tippen. Dann würde ich vorschlagen, wenn Sie die Frage doch noch haben, dann
- stellen Sie sie in der Kund a Sitzung in knapp einerhalb Stunden. Vielen Dank. M.
Zum Nachlesen
Nicht-blockierende SynchronisationNicht-blockierende Synchronisation (englisch non-blocking oder auch lock-free synchronization) ist eine Technik in der Informatik, um parallele Prozesse zu …
MutexEin kritischer Abschnitt (engl. critical section oder critical region) ist derjenige Teil im ausführbaren Code, in dem ein wegen des Mutex ungestörter …
ProzesssynchronisationGemeinsamer Zugriff auf Daten. Dabei muss verhindert werden, dass durch gleichzeitigen Zugriff Inkonsistenzen in den Daten entstehen. Dies wird durch Mutex- …
SpinlockEs ist eine Sperre (Lock) zum Schutz einer gemeinsam genutzten Ressource durch konkurrierende Prozesse bzw. Threads (siehe Kritischer Abschnitt) nach dem …