Sie sind auf Seite 1von 25

images/

2
1 Betriebssystementwicklung

1 Betriebssystementwicklung

1.1 Allgemein
• im fertigen System sind Fehler schwerer zu finden als in einem inkrementell entwickelten System

Virtuelle Maschine vs Emulator

Emulator emuliert z.B. eine x86-CPU, eine VM hingegen führt Maschinencode auf der vorhandenen CPU
aus.

1.2 Wozu brauche ich ein Betriebssystem?


• muss CPU multiplexen, wenn man mehr Fäden als Prozessorkerne hat
• muss den Speicher vor unerlaubtem Zugriff schützen
• Supervisor-Modus: Bei vielen aktuellen Prozessoren wird oft nur zwischen Supervisor-(alles ist er-
laubt) und User-Modus (eingeschränkte Zugriffe auf Speicherbereiche/Systemressourcen/CPU-Register)
unterschieden. Der Schutz des Arbeitsspeichers erfolgt über die Seitenverwaltungseinheit MMU der
CPU.
• BS fährt die Maschine erst richtig hoch und stellt dabei auch den Fäden jeweils einen Stack im
Speicher zur Verfügung

1.3 Wie debugge ich ein Betriebssystem?


Allgemein

Betriebssystementwicklung unterscheidet sich deutlich von gewöhnlicher Applikationsentwicklung:


• Bibliotheken fehlen
• die nackte Hardware bildet die Grundlage
• die ersten Schritte sind oft die schwersten
– Übersetzung
– Bootvorgang
– Systeminitialisierung
• komfortable Fehlersuche erfordert eine Infrastruktur
• Gerätetreiber für printf-Debugging
• STUB und Verbindung/Treiber für Remote Debugging
• Hardware Debugging-Unterstützung wie mit BDM
• Optimal: Hardware-Debugger wie Lauterbach

printf – Debugging

• printf muss selbst implementiert werden


• benötigt Bildschirm
• printf kann das Verhalten vom Debuggee verändern!
• Ausgaben können besser aufbereitet werden

3
Debuggen mit

• einer blinkende LED


• einer seriellen Schnittstelle

Software - Emulatoren

Emulatoren ahmen reale Hardware in Software nach.


Vorteile:
• i.d.R. kommunikativer als die reale HW ⇒ einfacheres Debugging, zB durch einen eingebauten De-
bugger (GDB-Stub)
• kürzere Entwicklungszyklen
Nachteile
• in Details können sich Emulator und reale HW unterscheiden!
• i.d.R. kommunikativer als die reale Hardware ⇒ einfacheres Debugging, da die Emulationssoftware

Debugger

Debugger dienen dem Auffinden von Softwarefehlern durch Ablaufverfolgung


• in Einzelschritten (single step mode)
• zwischen definierten Haltepunkten (breakpoints), z.B. bei
– Erreichen einer bestimmten Instruktion
– Zugriff auf ein bestimmtes Datenelement
• praktisch: Analyse von core dumps
Nachteile
• manchmal dauert die Fehlersuche mit einem Debugger länger als nötig!
• Einzelschritte kosten viel Zeit (vs. Nachdenken)
• kein Zurück bei versehentlichem Verpassen der interessanten Stelle
Funktionsweise
Praktisch alle CPUs unterstützen das Debugging, z.B. Intels x86 CPUs:
• die INT3 Instruktion löst breakpoint interrupt aus (ein TRAP)
– wird gezielt durch den Debugger im Code platziert
– der TRAP-Handler leitet den Kontrollfluss in den Debugger
• durch Setzen des Trap Flags (TF) im Statusregister (EFLAGS) wird nach jeder Instruktion ein
debug interrupt ausgelöst
– kann für die Implementierung des Einzelschrittmodus genutzt werden
– der TRAP-Handler wird nicht im Einzelschrittmodus ausgeführt
• mit Hilfe der Debug Register DR0-DR7 (ab i386) können bis zu vier Haltepunkte überwacht werden,
ohne den Code manipulieren zu müssen
– erheblicher Vorteil bei Code im ROM/FLASH oder nicht-schreibbaren Speichersegmenten
• besonders effektiv, wenn das Programm im Quelltext visualisiert wird (source-level debugging)

4
1 Betriebssystementwicklung

– erfordert Zugriff auf den Quellcode und Debug-Informationen


– muss durch den Übersetzer unterstützt werden (-g)

Remote Debugging

Remote Debugging bietet die Möglichkeit Programme auf Plattformen zu debuggen, die (noch) kein inter-
aktives Arbeiten erlauben. Der Zielrechner kann auch ein Emulator sein. Vorausetzungen
• Kommunikationsverbindung(seriell, Ethernet, ...)
• Gerätetreiber
Bestandteile
• Debugging-Komponente auf dem Zielsystem (stub)
– sollte möglichst einfach sein
• Kommunikationsprotokoll
– spiegelt die Anforderungen an den stub wieder
– basiert (z.B) auf der Übertragung von ASCII Zeichenketten

Debugging Deluxe

Viele Prozessorhersteller integrieren heute Hardwareunterstützung für Debugging auf ihren Chips (OCDS
– On Chip Debug System), z.B. JTAG. Vorteile
• der Debug-Monitor (z.B. gdb stub) belegt keinen Speicher
• Implementierung eines Debug Monitors entfällt
• Haltepunkte im ROM/FLASH durch Hardware-Breakpoints
• Nebenläufiger Zugriff auf Speicher und CPU Register
• mittels Zusatzhardware ist zum Teil auch das Aufzeichnen des Kontrollflusses zwecks nachträglicher
Analyse möglich

1.4 Ihr hattet als Vorlage startup Assembler code gegeben. Was hat der so
gemacht?

• bereitet alles für die Hochsprachenbefehle vor


• Umschaltung in Protected Mode
• initialisiert den Stack
• sorgt für die richtige Registerbelegung (Datensegmentregister, Stackpointer)
• initialisiert die IDT (lidt - Befehl), Gates, Int-Handler (guardian-Aufruf)
• initialisiert die GDT (lgdt)

Übersetzung – Probleme u. Lösungen

• kein dynamischer Binder vorhanden


– alle nötigen Bibliotheken statisch einbinden
• libstdc++ und libc benutzen Linux Systemaufrufe (insbesondere write)

5
– die normalen C/C++ Laufzeitbibliotheken können nicht benutzt werden. Andere haben wir
(meistens) nicht.
• generierte Adressen beziehen sich auf virtuellen Speicher!
– die Standardeinstellungen des Binders können nicht benutzt werden. Man benötigt eine eigene
Binderkonfiguration
• der Hochsprachencode stellt Anforderungen (Registerbelegung, Adressabbildung, Laufzeitum-
gebung, Stapel, ...)
– ein eigener Startup-Code (in Assembler erstellt) muss die Ausführung des Hochsprachencodes
vorbereiten

1.5 Was haben alle Geräte gemeinsam?


nauer aufschreiben

• Kontroller enthält Register über die mit ihm kommuniziert werden kann
• Brauchen Treiber im BS, die sich ihrer annehmen
• Müssen in der Interrupt-Vektor-Tabelle vom IO-Apic registriert werden, damit Interrupts überhaupt
im System ankommen (Plugbox)

1.6 Wie ist der Tastaturtreiber in MPStubs implementiert?

plugin

• ’Anstöpseln’ der Tastatur.


• Initialisierung der Tastatur und aktivieren der spezifischen Interruptbehandlung.
• Anmeldung des Keyboard Objektes bei der Plugbox (Vektor)
• Holt sich die Slot-Nummers
• Freischaltung des Interruptvektors beim IO-APIC; Zuordnung eines Slots zu einem Vektor

Prologue

• holen des aktuellen keys vom Keyboard-Kontroller


• reboot falls spezielle Tastenkombination
• falls key valid: abspeichern und Epilog anfordern, sonst nichts tun

Epilog

• sema.v()

getKey

• sema.p()
• return key
• key invalidate

6
1 Betriebssystementwicklung

1.7 Was ist zeitkritisch beim Betriebssystem?

(Das war die Überleitung auf das Thema Interrupts)

7
2 Interrupts

2.1 Allgemein

• Lost-Wakeup-Problem: Interrupts waehrend while-Schleife deaktivieren


• Lost-Update-Problem: Interrupts waehrend Indexbestimmung (keys–) sperren

2.2 Wie läuft ein Interrupt ab und was macht ein Interrupthandler so?

Hardware-Seite
• Geräte - Kontroller zieht Interrupt-Leitung hoch
• Signal kommt beim IO-APIC an, welcher sich eine geeignete CPU ausshct (nach Programmierung:
Feste Zuordnung, Zufall, CPU welche am wenigste Arbeit gerade hat usw.)
• IO-APIC leitet das Signal an diese CPU weiter über den LAPIC; CPU-Interrupt-Pin auf high.
• IO-APIC legt Interrupt-Geräte-Nr auf Bus
• LAPIC führt Auswahl und Priorisierung durch
Behandlung eines Interrupts
• Automatisch von der CPU:
– schaltet in Kernmodus
– sichert PC und EFLAGS auf dem Stack
– schaltet Interrupts lokal aus
– Geräte-Nr als Index für Adresse im Speicher wo der Interrupt-Handler liegt
– dieser wird angesprungen
– Unterbrechungsvektor: Teil d. Speichers, wo Liste mit allen Routinen ist
• Unterbrechungsroutine wird ausgeführt
– Sichern der flüchtigen Register auf dem Stack durch den Assembler-Code
– Sichern der nicht flüchtigen Register auf dem Stack durch den compilten Hochsprachen-Code
– Aufruf Prolog - Behandlung der Interruptquelle damit nicht direkt wieder ein Interrupt ausgelöst
wird
– Acknowledge an den Lapic
– Aufruf unter Umständen des Epiloges
– Wiederherstellen der nicht flüchtigen Register durch den compilten Hochsprachen-Code
– Wiederherstellen der flüchtigen Register durch den Assembler-Code
– iret: Rücksprung zum Programm das davor gelaufen ist

* Lesen des automatisch gesicherten Zustands von Supervisor-Stack


* Setzen des gesicherten Arbeitsmodus (Benutzer-/Systemmodus) und Sprung an die gesi-
cherte Adresse

8
2 Interrupts

push edx
push ecx //Interrupts sind per definition ausgeschaltet
push eax void guardian(uint32_t vector, cpu_context *context){
;; Pointer auf CPU
;; Kontext als 2tes Argument Gate *gate = plugbox.report((Plugbox::Vector)vector);
push esp
;; Int-Nr als 1tes Argument if( gate->prologue() ){
push %1 lapic.ackIRQ();
call guardian guard.relay(gate);
add esp,8 } else {
pop eax lapic.ackIRQ();
pop ecx }
pop edx }
iret
Listing 2: guardian function
Listing 1: IRQ-Entry

Abbildung 0.1: Interrupt Behandlung

9
2.3 Wie funktioniert das Ebenenmodell?

2.4 Wie ist Prolog/Epilog implementiert?


Prolog: Hardwareunterbrechung

• Läuft auf Hardwareunterbrechungsebene


– hat damit Priorität über Anwendungsebene und Epilogebene
• ist kurz, fasst wenig oder gar keinen Zustand an
– Üblicherweise wird nur der Hardware-Zustand gesichert und bestätigt
– Unterbrechungen bleiben nur kurz gesperrt (Latenzminimierung)
– kann bei Bedarf einen Epilog für die weitere Verarbeitung anfordern

Epilog: Softwareunterbrechung

• läuft auf Epilogebene E 21 (zusätzliche Kontrollflussebene)


– Ausführung erfolgt verzögert zum Prolog
– erledigt die eigentliche Arbeit (Latenzverbergung)
• hat Zugriff auf größten Teil des Zustands
– Zustand wird auf Epilogebene synchronisiert

10
2 Interrupts

Implementierung

• enter()
– Betreten der Epilogebene
– entspricht dem cli bei der harten Synchronisation
• leave()
– Verlassen der Epilogebene
– entspricht dem sti bei der harten Synchronisation
• relay()
– Anforderung eines Epiloges
– entspricht dem Hochziehen der IRQ-Leitung beim PIC
• queue
– Merken der Epiloge
– entspricht dem IRR (Interrupt-Request-Register) beim PIC
– Abarbeiten der gespeicherten Epiloge
– entspricht bei der harten Synchronisation dem Protokoll zwischen CPU und PIC

void enter(){
CPU::disable_int();
flag[system.getCPUID()] = true; void leave(){
CPU::enable_int(); Gate *gate;
bkl.lock(); CPU::disable_int();
}
while((gate =
void relay(Gate *gate){ queue[system.getCPUID()].
if(flag[system.getCPUID()]){ enqueue())){
if(gate->set_enqueued()){ CPU::enable_int();
queue[system.getCPUID()].insert(gate); gate->set_dequeued();
} gate->epilogue();
return; CPU::disable_int();
} }
flag[system.getCPUID()] = true;
CPU::enable_int(); bkl.unlock();
bkl.lock(); flags[system.getCPUID()] = false;
gate->epiloge(); CPU::enable_int();
leave(); }
}
Listing 4: leave function
Listing 3: enter/relay function

2.5 Da gibt es jetzt aber einige Synchronisationsprobleme oder?

• Synchronisation der Queues (Interrupts ausschalten)


• Synchronisation zwischen den Kernen - nur einer darf auf der Epilogebene sein (Big Kernel Lock);
da nützt Interrupts ausschalten auch nichts ⇒ Spinlock
• Nur ein Epilog des selben Typs insgesamt in den Warteschlangen (isenqued)

11
• Lost Update Problem, da Unterbrechungsbehandlung wieder Unterbrechbar: Flag nötig ob aktueller
Kern bereits das BKL hat (volatile bool, ints aus beim setzen)

2.6 Epilog macht das ganze jetzt langsamer. Warum macht man es dann?

Es geht darum die Zeit zu verringern, in der Interrupts komplett ausgeschaltet sind; Bei harter Synchroni-
sation ist dann zwar weniger Overhead, aber die Interruptsperre ist wesentlich länger (Breitbandwirkung).

Vorteile

• Konsistenz ist sichergestellt (durch Synchronisation auf Epilogebene)


• Programmiermodell entspricht dem (einfach verständlichen) Modell der harten Synchronisation
• Auch komplexer Zustand kann synchronisiert werden
– ohne das dabei Unterbrechungsanforderungen verloren gehen
– ermöglicht es, den gesamte BS-Kern auf Epilogebene zu schützen

Nachteile

• Zusätzliche Ebene führt zu zusätzlichem Overhead


– Epilogaktivierung könnte länger dauern als direkte Behandlung
– Komplexität für den BS-Entwickler wird erhöht
• Unterbrechungsperren lassen sich nicht vollständig vermeiden
– Gemeinsamer Zustand von Pro- und Epilog muss weiter hart oder weich synchronisiert werden

2.7 Warum sind im Prolog Interrupts gesperrt?

• dass ein Prolog sich nicht selbst unterbrechen kann, da sonst endlose Schachtelung
• run to completion

2.8 Funktioniert das Prolog/Epilogmodell mit mehreren Prioritaetsebenen?

• Bsp: E2 unterbricht E1 und haengt somit den Epilog frueher auf E05 ein

2.9 Wie würden wir denn das ohne Epilogebene machen?

• cli/sti ums Resume rumwickeln, oder lock-free-Liste oder anderweitig


• Irgendwie vermeiden, dass der aktive Thread in die readyqueue gehaengt wird, bevor seine Register
gesichert wurden (sonst racecondition mit anderer CPU)
• Kann man alles machen, tut aber hart weh
• Besser: Spinlock nehmen.

12
2 Interrupts

2.10 Können wir da keinen Semaphor nehmen?

• Nein, weil es ja kein Passives Warten in dem Kontext gibt.


• (Man könnte natürlich eine „Was für Arbeit kann ich tun während ich auf den Spinlock Warte“-Queue
führen aber das ist doch alles Gefrickel)
• Prioritätsverletzung wenn ich einfach mit einem Thread auf niedrigerer Ebene oder so weiter mache
(kann ja nicht in der Epilogebene arbeiten)
• außerdem arbeitet der bellringer auch auf epilogebene und eventuell wird der dann irgendwie schlafen
gelegt und dann ist schicht im schacht
• bzw wenn der timeraufruf in der epilogwarteschlange ist und ich leg mich schlafen in der epiloge-
bene... schlecht. immerhin ist der ja dann als enqueued gekennzeichnet und kommt auch nicht mehr
durch. auf keinem kern... zumindest nicht indirekt

2.11 Was ist der AST und wofuer koennte man ihn nutzen?

Ein AST(asynchronous system trap) ist eine Unterbrechung, die (nur) durch Software angefordert werden
kann
• z. B. durch Setzen eines Bits in einem bestimmten Register
• ansonsten technisch vergleichbar mit einer Hardware-Unterbrechung
• AST wird (im Gegensatz zu Traps/Exceptions)asynchron abgearbeitet
• AST läuft auf eigener Unterbrechungsebene zwischen der Anwendungsebene und den Hardware-UBs
(E 12 )
Gesetzmäßigkeiten des Ebenenmodels gelten
• (AST-Ausführung ist verzögerbar, wird automatisch aktiviert, ...)
• Sicherstellung der Epilogabarbeitung wird damit sehr einfach!
• Abarbeitung der Epiloge erfolgt im AST; und damit automatisch, bevor die CPU auf E0 zurückkehrt
• bleibt nur noch die Verwaltung der anhängigen Epiloge

2.12 Die Netzwerkkarte schickt jetzt viele Interrupts was macht man
dagegen?

interrupt storms

Hochfrequente Unterbrechungsanforderungen können einen Rechner lahm legen.


• es handelt sich entweder um unechte Unterbrechungen oder der Rechner ist mit der E/A Last über-
fordert
• kann leicht mit Seitenflattern (thrashing) verwechselt werden
Ursachen
• falsch konfigurierte oder fehlerhafte Hardware
• fehlerhaft implementierte Gerätetreiber
• Fehler im BS
Lösung
• Betriebssystem

13
– Unterbrechungsstürme erkennen
– das verursachende Gerät deaktivieren
– in den sogenannten polling mode schalten wenn die Anzahl der Interrupts einen bestimm-
ten Schwellwert überschreitet
• Geräte
– Interrupt rate limiting: das Gerät wartet einen programmierbaren Zeitabstand bevor es
wieder einen Interrupt generiert

2.13 Welche Interruptart gibt es noch, die man gar nicht haben will?

spurious interrupts

Ein technischer Mechanismus zur Unterbrechungsbehandlung birgt die Gefahr von fehlerhaften Unterbre-
chungsanforderungen.
Ursachen
• Hardwarefehler
• fehlerhaft programmierte Geräte
• elektrische Interferenzen (Übersprechen ?) auf einer Interrupt Leitung
Lösung
• Hardware- und Softwarefehler vermeiden
• Betriebssystem defensiv programmieren
– mit unechten Unterbrechungen rechnen
„Tejun’s patch also changes the way that the kernel responds to spurious interrupts - those which no driver
is interested in. Current kernels count the number of interrupts on each line for which no handler returned
IRQ_HANDLED; if 99,000 out of 100,000 interrupts are spurious, the kernel loses patience, disables the
interrupt line forevermore, and starts polling the line instead“

2.14 Was sind shared Interrupts?

Mehrere Geräte können sich einen Interrupteingang teilen. Die Behandlungsroutine für einen solchen Inter-
rupt muss dann alle Treiber, deren Geräte diesen Interrupt ausgelöst haben könnten, aufrufen (am IRQ kann
dies nicht festgestellt werden). Dabei kann es zu Problemen kommen, wenn einzelne Treiber zu lange aktiv
sind, und in der Zwischenzeit im Gerät, welches den Interrupt ursprünglich ausgelöst hat, beispielsweise
der Puffer voll wird und überläuft. Im schlimmsten Fall führt dies zu einem Datenverlust.
Bei modernen Peripheriegeräten vergeben der Computer und das Betriebssystem selbst die IRQ-Nummern
(PnP = Plug-and-Play-Geräte); während bei alten Steckkarten, beispielsweise bei ISA-Karten, die IRQ-
Eingänge von Hand eingestellt werden müssen oder fest auf den Karten verdrahtet sind.

2.15 Was braucht man für passives Warten?

• Fäden können auf ein Ereignis passiv warten


– von CPU-Zuteilung ausgeschlossen sein
– passiv warten = (Neuer) Fadenzustand: warten: (auf Ereignis)
• Eintreffen des Ereignisses bewirkt Verlassen des Wartezustands

14
2 Interrupts

– Faden wird in CPU-Zuteilung eingeschlossen


– Anschließender Fadenzustand: bereit

Erforderliche Abstraktionen

• Operationen: block(), wakeup()


– Betreten bzw. Verlassen des Wartezustands
• Warteobjekt: Waitingroom
– repräsentiert das Ereignis auf das gewartet wird
– enthält üblicherweise eine Warteschlange der wartenden Fäden
– Die Warteschlange sollte sinnvollerweise mit derselben Priorisierungsstrategie wie die Bereit-
liste des Schedulers verwaltet werden!

2.16 Wie funktioniert Zeitscheibenmultiplexing mit Timerinterrupts?


schreibst einen interrupthandler für den timer im prolog gibt der true zurück und auf epilog ebene ruft ein
scheduler.resume() auf und damit geschieht ein kontextswitch
• Interrupthandler für Timer gibt im Prolog true zurück
• Epilog ruft scheduler.resume() auf
• scheduler sucht nächsten Thread aus der Ready-List, wobei er darauf achtet dass das dying flag nicht
gesetzt ist
• ruft den dispatcher auf mit dispatch(new thread)
• der setzt den neuen CPU-Thread und ruft CPUThread.resume(new Thread) auf
• der ruft toc_switch auf und das macht den Kontextwechsel indem es die aktuellen Register in dem
dafür vorgesehenen Struct vom aktuellen Tread speichert und die aus dem neuen Thread lädt

2.17 Thread-Vorbereitung

Stack den toc_settle erzeugt:


• Adresse vom zu startenden Faden
• 0xcafebabe
• Adresse von kickoff

toc_settle bereitet den Stack nur vor. Eingetragen in das Stackpointerregister wird der dann erst in toc_go
und dann beim ret von toc_go springe ich in kickoff. Über der Adresse von kickoff liegt dann im Stack eine
Pseudo-Rücksprungadresse, die nie angesprungen werden darf - aber wir brauchen sie, damit der Stack
eben so aufgebaut ist, wie ein Stack eben immer aufgebaut ist, daher muss da halt ne Rücksprungadresse
liegen. Darüber liegt dann das Argument von kickoff und das ist dann der zu startende Faden.

15
3 Interprozesskommunikation

3.1 Lese/Schreiber-Problem

Abbildung 0.2: Leser-Schreiber Problem mit Semapho- Abbildung 0.3: Leser-Schreiber Problem mit Monitor
ren

3.2 Wie werden Monitore im Betriebssystem implementiert?

Abbildung 0.4: Monitore

16
3 Interprozesskommunikation

3.3 Wir haben da einmal shared memory und message passing kennen
gelernt. Was ist besser?

• beides ist gleich mächtig (das eine kann das andere emulieren)
• Shared Memory
– Umsetzung bei Paging durch Verwendung von gemeinsamen Seiten
– am schnellsten
– von zwei oder mehreren Prozessen wird ein bestimmter Speicherbereich gemeinsam benutzt
– das Kopieren zwischen Server und Clients ist nicht notwendig
– Synchronisation nötig, es sei denn bei atomaren Speicherzugriffen
– benötigt gemeinsamen Kern-Adressraum von isolierten Prozessen
• Message Passing
– Nachrichten werden von einem Prozess an eine Message Queue (verkettete Liste) geschickt
– dort kann die Nachricht von einem anderen Prozess abgeholt werden
– interaktion isolierter Prozesse
– einheitliches Paradigma für IPC mit lokalen und entfernten Prozessen
– ggf. Pufferung und Synchronisation
– Indirektion erlaubt transparente Protokollerweiterungen (Verschlüsselung, Fehlerkorrektur, ...)

3.4 Wie mache ich message passing, wenn ich shared memory habe?

Implementation einer synchronisierten Warteschlange

class Mailbox: public List{


Semaphore mutex;
Semaphore has_elem;
public:
Mailbox():mutex(1),has_elem(0){}

void send(Message *msg){


auf Basis von Semaphoren und gemeinsamem Spei-
mutex.p(); cher lässt sich leicht eine Mailbox-Abstraktion reali-
enqueue(msg); sieren:
mutex.v();
has_elem.v(); • Nachrichten werden nicht kopiert
}
Message *receive(){ – Sender sorgt für Speicher
has_elem.p();
mutex.p();
• receive blockiert ggf.
Message *msg = dequeue();
mutex.v();
• Mailbox-Abstraktion erlaubt M:N IPC
return meg;
}
};

Listing 5: mailbox

17
3.5 Wie mache ich shared memory wenn ich message passing habe?

Virtueller Gemeinsamer Speicher

Ermöglicht
• das Programmiermodell von Multiprozessoren auf Mehrrechnersystemen zu nutzen
• IPC über (virtuellen) gemeinsamen Speicher trotz getrennter Adressräume
Probleme
• Latenzen der Kommunikation und Trap-Behandlung: sehr langsam
• false sharing: Seitengröße entspricht nicht Objektgröße

18
4 Treibermodell

4 Treibermodell

4.1 Das Treibermodell umfasst detaillierte Vorgaben für die


Treiber-Entwicklung

• die Liste der erwarteten Treiber-Funktionen


• Festlegung optionaler und obligatorischer Funktionen
• die Funktionen, die ein Treiber nutzen darf
• Interaktionsprotokolle
• Synchronisationsschema und Funktionen
• Festlegung von Treiberklassen falls mehrere Schnittstellentypen unvermeidbar sind

4.2 Treiberinfrastruktur

• Ressourcen reservieren: Speicher, Ports, IRQ-Vektoren, DMA Kanäle


• Hardwarezugriff: Ports und Speicherblöcke lesen und schreiben
• Speicher dynamisch anfordern
• Blockieren und Wecken von Prozessen im Treiber: waitqueue
• Interrupt-Handler anbinden: low-level, Tasklets für länger dauernde Aktivitäten
• Spezielle APIs für verschiedene Treiberklassen: Zeichenorientierte Geräte, Blockgeräte, USB-Geräte,
Netzwerktreiber
• Einbindung in das proc oder sys Dateisystem

19
4.3 Was sind die Vorteile von einem einheitlichem Treibermodell?

Wäre jeder Treiber einzigartig würde dies eine Erweiterung des E/A Systems für jeden Treiber erfordern.
Dies wäre ein unrealistisch hoher Aufwand bei der heutigen Gerätevielfalt. Folgt die Schnittstellennorm
einem übergreifenden standardisierten Modell, so bietet sie dem Betriebssystem oder der Anwendungs-
software die Möglichkeit, auch Geräte eines gänzlich unterschiedlichen Typs geordnet anzusprechen;
beispielsweise könnte eine Software zum Abspielen eines Musikstücks dann nicht nur einen Soundkarten-
Treiber verwenden, sondern auch die Tondaten über einen Netzwerktreiber versenden, da dieser ebenfalls
einen Datenstrom annehmen kann. Eine spezielle Anpassung der Anwendung an dieses Szenario ist so
nicht mehr nötig. Das übergreifende Schnittstellenmodell erleichtert die Programmierung der Anwen-
dungssoftware und ermöglicht einen universelleren Einsatz, auch mit noch unbekannten, zukünftigen
Gerätetypen.

• ermöglichen ein (dynamisch) erweiterbares E/A System


• erlauben flexibles Stapeln von Gerätetreibern
– Filter
– virtuelle Geräte
• Beispiele: RAID, USB

4.4 Manche Treiber bieten auch mmap an. Was könnte das sein?

It is a method of memory-mapped file I/O. An mmap() implementation in a driver or a memory mapping


between user and kernel space is the fastest way to transfer data. This approach doesn’t incur a context
switch nor a memory buffer copying. Below is a sample code showing how a mmap() implementation for
a driver is done. Das Einblenden von Speicher (memory mapping) ist eines der interessantesten Merkmale
moderner Unix-Systeme. Bei Treibern kann diese Funktionalität eingesetzt werden, um Anwenderprogram-
men direkten Zugriff auf den Gerätespeicher zu geben. Das Einblenden eines Gerätes erfolgt dadurch. daß
ein Bereich von Adressen im User-Space mit dem Gerätespeicher verknüpft wird. Immer, wenn das Pro-
gramm in den zugewiesenen Adreßbereich schreibt oder daraus liest, greift es in Wirklichkeit auf das Gerät
zu. Im Beispiel mit dem X Server bekommt man durch die Verwendung von mmap schnellen und einfachen
Zugriff auf den Speicher der Grafikkarte. In einer Performance-kritischen Anwendung wie dieser macht der
direkte Zugriff einen großen Unterschied aus.

20
5 Betriebssystemarchitektur

5 Betriebssystemarchitektur

5.1 Monolithische Systeme

• Einführung eines Privilegiensystems


– Kernmodus ⇔ Anwendungsmodus
– Direkter Hardwarezugriff nur im Systemmodus; Gerätetreiber gehören zum System
• Programme laufen unter der Kontrolle des Betriebssystems
• gesamtes BS läuft als einziges Programm im Kernmodus
• Menge von Prozeduren zusammengefügt zu einem einzigen, ausführbaren binären Programm
• Jede Prozedur darf darf jede andere des Systems aufrufen
• sehr effizient, jedoch sehr komplex
• ein Fehler in einer Komponente kann zum Absturz des gesamten BS führen
• Unterbrechungsmechanismus: Bearbeitung im Kern (Epilog/Prolog)
• Interaktionsmechanismus: Funktionsaufrufe, Traps
• Isolationsmechanismus: Privilegebenen, Adressräume; Pro Anwendung ein Adressraum, Kern läuft
auf Systemebene
• Portabilität hoch: dank C
• Erweiterbarkeit mäßig
• Robustheit mäßig: Anwendungen isoliert, nicht jedoch BS-Komponenten (Treiber!)
• Leistung hoch: Nur Betreten / Verlassen des Kerns ist teuer

5.2 Allgemein

• Unterschied von Syscalls in Monolith und Mikrokern (Mikrokern langsamer, da zwei Adressraum-
wechsel in IPC: Anwendung → Kern und Kern → Serverprozess)

21
• Unterschied Usermodus/Systemmodus (Systemmodus hat privilegierte Operationen bspw. cli, Page-
tableeintraege in MMU aendern)

5.3 Warum hat man Monolithen entwickelt?

• Ziel: Mehrprogrammbetrieb auf „kleinen“ Computern

5.4 Welche Hardwareunterstützung hat man dafür gebraucht?

Da auf einem einzelnen Prozessor nur ENTWEDER das Betriebssystem ODER der Nutzerprozess laufen
kann, muss die Hardware des Computers in der Lage sein, die Operationen des Nutzerprozesses z.B. auf
die Verwendung gültiger Speicherbereiche zu überprüfen und notfalls das Betriebssystem zu aktivieren,
damit es dem Treiben ein Ende setzt. Dafür benötigt ein entsprechender Prozessor eine recht komplexe
Speicherverwaltungseinheit.
Für das Scheduling von Prozessen wird ebenfalls etwas Hardwareunterstützung in Form eines Timers benö-
tigt, der periodisch das Betriebssystem weckt, damit es gegebenenfalls einen Prozesswechsel durchführen
kann. Ohne so einen Timer kann man zwar auch ein Multiprozess-Betriebssystem realisieren, allerdings
müsste ein Prozess hier freiwillig den Prozessor freigeben. Das widerspräche dem Grundsatz, dass Prozes-
se sich nicht gegenseitig beeinflussen dürfen.
Der Ring, auch Domain genannt, bezeichnet (im Umfeld der Betriebssystem-Programmierung und des Mul-
titaskings) eine Privilegierungs- bzw. Sicherheitsstufe eines Prozesses. Diese schränkt den Prozess bezüg-
lich des auf der CPU nutzbaren Befehlssatzes und des verwendbaren Speicherbereichs gegebenenfalls ein.
Die Nutzung von Privilegierungsebenen ist sinnvoll, um die Hardware zu abstrahieren und um Prozesse
voneinander abzuschotten.
Im innersten Ring (höchste Berechtigungsstufe) läuft meist das Betriebssystem, evtl. sogar nur dessen Ker-
nel. Das Betriebssystem darf alles, insbesondere direkte Hardwarezugriffe und das Eingreifen in die RAM-
Bereiche anderer Prozesse. Anwendungsprogramme sind üblicherweise auf den äußersten Ring beschränkt
(niedrigste Berechtigungsstufe). Für Operationen, welche einen Hardwarezugriff erfordern, müssen An-
wendungsprogramme Betriebssystem-Dienste beauftragen. Der Befehlssatz wird für unprivilegierte Pro-
zesse („Userland“) derart eingeschränkt, dass sie nicht direkt auf die Hardware zugreifen können und sich
auch nicht aus ihrer Privilegierungsebene befreien können. Der Zugriff auf den Speicherbereich anderer
Prozesse wird meist durch Speichervirtualisierung verhindert. Somit wird gewährleistet, dass Prozesse z.
B. im Ring 3 in keinem Fall Prozesse im Ring 0 oder auch andere Prozesse im Ring 3 beeinflussen kön-
nen. Da die unprivilegierten Prozesse auf Hardware nicht direkt zugreifen können, existieren sogenannte
„Gates“ auf den darunterliegenden Ring, um auf der Programmierschnittstelle des Kernels die notwendigen
Aktionen anzufordern.
Die meisten Prozessor-Architekturen bieten nur zwei Ringe: Prozesse im Ring 0 befinden sich im Kernel-
Modus (kernel mode) – alle anderen im Benutzer-Modus (user mode). Die weitverbreitete x86-Architektur
bietet vier Ringe, von denen nur Ring 0 und Ring 3 verwendet werden. Hier werden Ring 0 und 1 dem
Betriebssystem zugerechnet, Ring 2 und 3 sind für Anwendungsprogramme vorgesehen. Jeder Wechsel von
einem Ring zum anderen erfordert in der CPU einen Kontextwechsel, der einige Rechenzeit in Anspruch
nimmt.

5.5 Mikrokerne

• Ziel: so wenig wie möglich im Kernmodus ausführen um hohe Ausfallsicherheit zu erreichen; Re-
duktion der Trusted Computing Base
– synchroner IPC, Kontextwechsel, CPU Scheduler, Adressräume
– no policies (abgesehen vom Scheduler)

22
5 Betriebssystemarchitektur

• Interaktion über Nachrichten (IPC, Inter Process Communication)


• Prinzip des geringsten Privilegs
– Systemkomponenten müssen nur so viele Privilegien besitzen, wie zur Ausführung ihrer Auf-
gabe erforderlich sind
– z.B. Treiber: Zugriff auf spezielle IO-Register, nicht auf die gesamte HW
– Nur der Mikrokern läuft im Systemmodus
– ein Fehlerhafter Treiber kann dann nicht das ganze BS lahm legen
– jeder Treiber und jedes Dienstprogramm hat genau die Berechtigungen die es braucht um seine
Aufgaben zu erledigen
• geringere Codegröße ermöglicht Ansätze zur formalen Verifikation des Mikrokerns
• Unterbrechungsmechanismus: IPC an Server-Prozess; Unterbrechungsbehandlung erfolgt durch
Faden im Benutzermodus
• Interaktionsmechanismus: IPC - Anwendungen und Systemkomponenten interagieren über Nach-
richten
• Isolationsmechanismus: Addressräume
– Ein Addressraum pro Anwendung,
– ein Addressraum pro Systemkomponente
• Portabilität mäßig: früher Assembler, aktuell in C
• Erweiterbarkeit sehr hoch: durch neue Server im Benutzermodus, auch zur Laufzeit
• Robustheit sehr hoch: durch strikte Isolierung
• Leistung mäßig - gut: IPC-Performance ist der kritische Faktor

5.6 Exokerne

• Ziel: Leistungsverbesserung durch Reduktion


– Entfernung von Abstraktionsebenen
– Implementierung von Strategien (z.B. Scheduling) in der Anwendung
• Extrem kleiner Kern, dieser Implementiert nur
– Schutz
– Multiplexing von Ressourcen (CPU, Speicher, Disk-Blöcke, ...)
• Trennung von Schutz und Verwaltung der Ressourcen!
– Keine Implementierung von IPC-Mechanismen (Mikrokerne) oder weiterer Abstraktionen (Mo-
nolithen)
– Anwendungen können die für sie idealen Abstraktionen, Komponenten und Strategien verwen-
den
• hohe Anforderungen an Anwendungsentwickler
• Definition von Exokern-Schnittstellen ist schwierig; Genaue Abwägung +zwischen Mächtigkeit, Mi-
nimalismus und ausreichendem Schutz
• Unterbrechungsmechanismus: nicht vorgegeben; Exokern verhindert nur die Monopolisierung der
CPU
• Interaktionsmechanismus: nicht vorgegeben; wird von der Anwendung bestimmt

23
• Isolationsmechanismus: Addressräume; Ein Addressraum pro Anwendung + von ihr gebrauchter
Systemkomponenten
• Portabilität sehr hoch: fExokerne sind sehr klein
• Erweiterbarkeit sehr hoch: aber auch erforderlich! – der Exokern stellt kaum Funktionalität bereit
• Robustheit gut: Schutz wird durch den Exokern bereitsgestellt
• Leistung sehr gut: Anwendungen operieren nahe an der Hardware, wenige Abstraktionsebenen

5.7 Virtuelle Maschinen

• Ziel: Isolation und Multiplexing unterhalb der Systemebene


• VM simuliert die gesamten Hardware-Ressourcen
• Multiplexen ganzer Rechner statt einzelner Betriebsmittel
• VMs laufen in Ring 3, Ringmodell durch Addressräume nachgebildet
• Unterbrechungsmechanismus: Weiterleitung an VM; VMM simuliert Unterbrechungen in den ein-
zelnen VMs
• Interaktionsmechanismus: nicht vorgesehen; Anwendungen in den VMS kommunizieren miteinan-
der über TCP/IP
• Isolationsmechanismus: VM, Paravirtualisierung; Jede Instanz bekommt einen eigenen Satz an
Hardwaregeräten
• Portabilität gering: sehr hardware-spezifisch, Paravirtualisierung ist aufwändig
• Erweiterbarkeit keine: in den üblichen VMMs nicht vorgesehen
• Robustheit gut: grobgranular auf der Ebene von VMs
• Leistung mäßig - gut: stark abhängig vom Einsatzszenario (CPU-lastig, IO-lastig, ...)

5.8 Bibliotheksbetriebssysteme

• Zusammenfassung von häufig benutzen Funktionen zur Ansteuerung von Geräten in Software-Bibliotheken
• Teure Hardware wird nicht optimal ausgelastet
– Hoher Zeitaufwand beim Wechseln der Anwendung
– Warten auf Ein-/Ausgabe verschwendet unnötig CPU-Zeit
• Organisatorische Abläufe sehr langwierig
– Stapelbetrieb, Warteschlangen
– von der Abgabe eines Programms bis zum Erhalt der Ergebnisse vergehen oft Tage
• Keine Interaktivität möglich
– Betrieb durch Operatoren, kein direkter Zugang zur Hardware
– Programmabläufe nicht zur Laufzeit parametrierbar
• Systemfunktionen als normale Subroutinen
• Unterbrechungsmechanismus: Polling
• Interaktionsmechanismus: Funktionsaufrufe
• Isolationsmechanismus: nicht nötig, da Anwendung = System

24
5 Betriebssystemarchitektur

• Portabilität gering: keine Standards, eigene Bibliotheken für jede Architektur


• Erweiterbarkeit mäßig: theoretisch gut, in der Praxis oft Spaghetti-Code
• Robustheit sehr hoch: single tasking, Kosten für Programmwechsel sehr hoch
• Leistung sehr hoch: direktes Operieren auf der Hardware, keine Privilegebenen

25

Das könnte Ihnen auch gefallen