====== Kleine Erklärung zu getkey() in OOStuBS ====== In BS kommt oft die Frage was passiert wenn ein Prozess in OOSTUBS ein read()/getkey() macht. Dazu habe ich mal eine Zusammenfassug geschrieben die hoffentlich auch anderen hilft. Garantie gibts keine. Vorbedingung: der Prozess ist im Zustand blockiert und wartet auf die Semaphore die das konsumierbare Betriebsmittel "Tastaturzeichen" verwaltet (dahingekommen ist der Prozesss durch die blockierende P()-Operation in getkey()), die die block()-Methode des Schedulers aufgerufen hat. P hat um sich ein Secureobjekt und macht ein leave() (um den krit. Abschnitt freizugeben) nachdem es block() augerufen hat. block() setzt den Prozess von der Bereitliste auf eine Warteliste die mit dem "Zeichen vorhanden Event" verknuepft ist, anschliessend findet eine Schedulingentscheidung statt. Ein anderer Prozess ist dann vom Scheduler ausgewaehhlt und vom Dispatcher eingelastet worden. 1. Taste wird gedrueckt 2. elektrisches Signal wird vom Tastaturcontroller an den APIC zugestellt und ueber den APIC-BUS an einen APIC zugestellt. 3. Interrupts vom Keyboardcontroller werden gesperrt 4. APIC nimmt eine Priorisierung vor und meldet den Interrupt sowie dessen Nummer zu gegebener Zeit an die CPU (Eintritt in Ebene 1, implizite Unterbrechung aller tieferen Kontrollfluesse) + Zustandsicherung (Statusregister) 5. Die Nummer dient als Index in die Interruptvektortabelle wo sich ein Zeiger auf die Behandlungsroutine befindet, nach dem Leeren der Pipeline und Herstellung eines Konsistenten Zustandes in der CPU wird diese Funktion angesprungen. In Oostubs ist ueberall eine ASM-Kopplungsroutine in diese Tabelle ingetragen, die fluechtige Register sichert, da die eigentlich Behandlung in Hochsprache stattfindet. 6. Man kommt im guardian() heraus und diese Funktion arbeitet den Prolog der Interruptabarbeitung ab, das gelesene Zeichen wird hier in einen Puffer kopiert (das kann auch erst im Epilog passieren, das macht den Prolog kuerzer aber man kann eher Zeichen verlieren) 7. Es wird ein Epilog relayed 8. Interrupts vom Tastaturkontroller werden wieder zugelassen, nachdem der Interrupt bestaetigt(rueckgesetzt) wurde (Ebene 1 wird verlassen (iret durch die asm-kopplungsroutine)) 9. Der anhaengige Epilog wird abgearbeitet (Ebene .5 wird betreten). Hier gibt es jetzt zwei Moeglichkeiten, abhaengig davon auf welcher Ebene der Prozess war, der auf das Zeichen warten sollte: a) Prozess war auf Ebene 0 und suspendiert und der guard (krit. Abschnitt) ist frei -> Die Semaphore die das Tastaturzeichen verwaltet macht einfach ein V() sobald ihr Epilog dran war. Der Epilog wird direkt ausgefuehrt und nicht erst in die queue gehaengt. b) Prozess war auf Ebene .5 (d.h. der guard war nicht available) -> der Prozess kommt jetzt wegen der Sequentialisierung nochmal dran und der P()-Syscall wird fertig ausgefuehrt. Die Vorbedingung wird also erst jetzt erfuellt, da zum Zeitpunkt des Tastendrucks der Syscall unterbrochen wurde und der Prozess zum Beispiel noch auf der Bereitliste steht. Der Epilog des Tastaturinterrupts wurde da der krit. Abschnitt belegt war in die Epilogqueue eingehaengt. Erst nachdem der Prozess den kritischen Abschnitt freigegeben hat wird in dem damit einhergehenden leave() irgendwann der Epilog der das V() macht ausgefuehrt. 10. im V() wird der Prozess durch den Scheduler vom Zustand blockiert in den Zustand bereit ueberfuehrt. 11. weitere Epiloge werden abgearbeitet (eventuell auch eine anstehende Schedulingentscheidung die den Prozess direkt einlastet). Hier bin ich mir nicht 100% sicher ob immer vor der Rueckkehr auf Ebene 0 eine Schedulingentscheidung getroffen werden sollte oder ob man einfach wartet bis die Zeitscheibe des gerade laufenden Prozesses ablaeuft und dann halt eine Schedulingentscheidung trifft. Wenn man jedesmal eine Schedulingentscheidung trifft hat man halt immer overhead, tut man es aber nie kann es passieren dass es zu einer Prioritaetsverletzung kommt (angenommen wir haetten im OOSTUBS Scheduler Prioritaeten :-)), weil ein Prozess mit einer hoeheren Prioritaet als der aktuell laufende deblockiert worden sein koennte und dann nicht sofort eingelastet wird. Ich denke ich wuerde in der Pruefung versuchen mich hier aus der Affaere zu ziehen indem ich sage ich fordere immer eine Schedulingentscheidung an, aber die prueft nur ob sich an der Bereitliste irgendwas geaendert hat und returned einfach sofort wenn dem nicht so ist. Die Aenderung an der Bereitliste kann man einfach dadurch erreichen das man im block()/wakeup() ein entsprechendes flag setzt. Dadurch hat man einen sehr geringen overhead, weil die Entscheidung nicht lange dauert wenn nichts passiert ist. Eine Prioritaetsverletzung hat man aber so ausgeschlossen. 12. Wechsel auf Ebene 0 durch resume auf den einzulastenden Prozess nach Abarbeitung aller Epiloge. Genauer ist man nach resume() noch im Kern, aber nach Ende der Epilogqueue erfolgt ein retne() das uns auf Ebene 0 zurueckbringt. 13. getkey() kehrt zurueck und returned das gelesene Zeichen --- //spjsschl//