Kapitel 4 - Kritischer Abschnitt
- Kritischer Abschnitt und Mutual Exclusion
- Dezentrale und Zentrale Steuerungen
- Semaphore und ihre Impementierung
- Verschiedenen Synchronisationsprobleme
|
Was ist ein Kritischer Abschnitt?
Ein Kritischer Abschnitt ist ein zeitlicher Bereich, in welchem mindestens
zwei Prozesse auf das gleiche Betriebsmittel zugreifen und mindestens eines davon
schreibt. (Zeitkritischer Ablauf)
Erklären sie am Bsp. Druckerspooler den kritischen Abschnitt!
- in Spooler ist der Platz 4 frei
- Prozess A will drucken und liest die Platznummer
- PUM aktiviert Prozess B, welcher ebenfalls diese Platznummer liest
- Prozess B schreibt den Auftrag nach Platz 4
- PUM schaltet zu A zurück und A überschreibt nun den Druckauftrag von B!
Deshalb notwendig: Wechselseitiger Ausschluss!
Warum werden keine Interrupt Unterbrechungen zur Synchronisation verwendet?
Ein Prozeß der auf gemeinsame Daten und Ressourcen zugreift, kann nicht
einfach alle Interrupts abschalten. Dies würde zu keinem optimalen Scheduling
führen, weil dann jeder Prozeß solange die Kontrolle behalten darf wie er will.
D.h. die wartenden Prozesse sind auf die Kooperation des gerade laufenden Prozesses
angewiesen (kooperatives Multitasking wie Win 3.11)
Was ist wechselseitiger Ausschluss?
Es muss gewährleistet werden, daß niemals sich mehr als ein Prozess in einem
kritischen Abschnitt befinden darf. Die Anwendung einer einfachen Sperrvariable ist
hier nicht möglich, da der Zugriff und das Setzen dieser, selbst einen kritischen
Abschnitt darstellt. Es müssen Algorithmen gefunden werden, welche die
Funktionalität gefahrlos implementieren.
Welche Anforderungen muss eine Steuerung für kritische Abschnitte erfüllen?
- exklusive Nutzung durch verschiedene Teilprozesse über mutual exclusion
- Ein Prozess darf einen anderen nur behindern, wenn beide im kritischen
Abschnitt
- Der Eintritt in einen kritischen Abschnitt darf nicht lang dauern
- Alle Prozesse müssen in endlicher Zeit ihre Betriebsmittel zugewiesen
bekommen
- globale Unabhängigkeit von allgemeinen Fortschreiten der Prozesse
Welche dezentralen Steuerungen kennen Sie? (aktives Warten)
- Test and Set Lock
- Ping-Pong (Striktes Alternieren)
- Decker Algorithmus
- Peterson Algorithmus
Wie arbeitet Striktes Alternieren? (Ping Pong)
|
Prozess A
|
Prozess B
|
While (1)
{
while (turn!=FALSE);
criticalsection();
turn = TRUE;
notcriticalstuff();
}
|
While (1)
{
while (turn!=TRUE);
criticalsection();
turn = FALSE;
notcriticalstuff();
}
|
|
Hier wird nur mit einer TURN Variable zwischen den Prozessen hin und her
geschalten. Nachteil ist das die Abläufe nur abwechselnd in die kritischen
Abschnitte eintreten können und das ein nicht im kritischen Abschnitt
arbeitender Prozess trotzdem warten muss, bis der andere diesen wieder
verlassen hat.
|
Wie schaut Peterson's Algorithmus aus?
CONST N = 2; //konkurrierende Prozesse
INT turn; //Container für aktiven Prozess
INT interested[N];
void P(int process) //Eintreten
{
int other = 1-process; //Gegenteil des Prozesses
interested[process] = TRUE; //Interesse bekunden
turn = process; //Flag setzen
while(( turn==process ) AND (interested[other]==TRUE));
}
void V(int process) //Welcher Prozess verläßt K.A.
{
interested[process]= FALSE; //Kein Interesse mehr
}
Das turn erzwingt ein Warten des interessierten Prozesses, solange ein andere Prozess im K.A.
ist. Der Hauptnachteil von dezentralen Steuerungen für kritische Abschnitte ist das Aktive Warten
(Spin Locks), was bedeutet, dass die Blockierzeit anderer Prozesse nicht genutzt werden kann.
Was sind Semaphore - zentrale Steuerung?
Ein Semaphor ist ein allgemeiner Mechanismus zur Synchronisation, ohne aktiv
warten zu müssen. Das Verfahren wurde von Dijkstra (1965) entwickelt und hat sich
durchgesetzt. Ein Semaphor arbeitet auf einer priviligierten Schicht des
Betriebssystems und ist somit nicht Teil der Prozesse.
Ein Binärer Semaphor kann einen Kritischen Abschnitt für zwei Prozesse verwalten.
Falls mehrere Prozesse gleichzeitig auf ein Betriebsmittel zugreifen dürfen, reicht
der binäre Semaphor nich aus. Der Semaphor wird dann als Zähler implementiert.
Falls er 0 ist, ist das Betriebsmittel voll ausgelastet. Ist er größer als 0, so
sind noch Ressourcen vorhanden, d.h. andere Prozesse dürfen in den Kritischen
Abschnitt eintreten, solange der Semaphor nicht Null ist. Dabei dekrementieren sie
diesen und blockieren ihn für andere Prozesse, falls der Semaphor nun Null ist.
Verläßt ein Prozess den Kritischen Abschnitt, inkrementiert der Prozess den
Semaphor wieder und signalisiert damit, dass er seine Arbeit getan hat und nun ein
anderer eintreten darf. Daher werden die beiden Operationen vor und nach dem
Kritischen Abschnitt oft als Down(sem) und Up(sem) definiert.
Wie funktioniert das Grundprinzip von Semaphoren?
Es wird nicht mehr eine Art Sperrvariable verwendet, sondern ein Zähler. Für
dieses Zähler gibt es die Operationen up(s) bzw. V und down(s) bzw. P zum Erhöhen
bzw. Dekrementieren des Zählers. Ist nach einem down(s) der Zähler null, wird
gewartet. Ist er irgendwann wieder größer als null werden die Anweisungen
ausgeführt. Der Semaphor ist nun nichts weiter als dieser Zähler, welcher nur durch
up und down ansprechbar ist! Die beiden Operationen P und V stellen natürlich auch
kleine kritische Abschnitte dar, welche über aktives Warten synchronisiert werden.
P(sem) //Probiere in kritischen Abschnitt einzutreten
{
if (sem == 0)
sleep(sem); //Prozess auf wartend setzen
sem--;
}
V(sem) //Verlasse kritischen Abschnitt
{
sem++; //Zugang wieder freigeben
wakeup(sem);
}
sleep und wakup sind Systembefehle, welche einen Prozess Schlafenlegen oder Aufwecken.
|
Prozess A
|
Prozess B
|
int sem = 1; //zwei Prozesse
|
While (1)
{
P(sem);
// kritischer Abschnitt
V(sem);
}
|
While (1)
{
P(sem);
// kritischer Abschnitt
V(sem);
}
|
|
Wie arbeiten Events bzw. Ereignisse?
Betriebssysteme wie z.B. Unix benutzen Events als Abstraktion von Semaphoren
zum Koordinieren von Prozessabläufen (im Gegensatz zu kritischen
Abschnitten). Ein Ereignis stellt nichts weiter als eine für Software erkennbare
Bedingung dar. Ein Prozess kann sich Schlafen legen, bis eine
bestimmte Bedingung eintreten.
- Ein Prozess setzt einen wait()-Aufruf auf ein Ereignis ab. Er wird dann an
dem Event-Deskriptor wartend gesetzt.
- Wenn an dem Event-Deskriptor das Ereignis signalisiert wird, so wird ein dort
wartender Prozess bereit gesetzt.
- Der wesentliche Unterschied zu den Semaphor-Operationen ergibt sich daraus,
dass die Signale nicht gespeichert werden, bei Semaphoren wird dagegen der Wert
bei signal() um 1 erhöht.
Semaphore: Wert 0. Kommt das signal() vor dem wait(): der
wait()-aufrufende Prozess wird nicht blockiert.
Event: Ein Wert ist nicht vorhanden, kein Prozess wartet. Das
Ereignis wird vor dem wait()-Aufruf signalisiert: der wait()-aufrufende Prozess
wird blockiert, bis ein weiteres Ereignis eintrifft. Das erste Ereignis wird nicht
gemerkt.
Was versteht man unter einem Monitor und wie funktioniert er?
Ein Monitor besteht aus einigen Prozeduren, Variablen und Datenstrukturen die
zu einer besonderen Art Modul zusammengefasst werden. Dieses Monitormodul
verkapselt Semaphore und die Operationen auf diesen Semaphoren. Nützlichste
Eigenschaft eines Monitors ist, daß jeweils nur ein Prozeß zu einer Zeit einen
Monitor benutzen kann. Ein Monitor ist zwar vielen Prozessen zugänglich, aber nur
jeweils ein Prozeß kann den Monitor zu einem gegebenen Zeitpunkt benutzen. Monitore
werden häufig zur Verwaltung von Puffern und Geräten eingesetzt.
- Es gibt dezentrale und zentrale Steuerungen für kritische Abschnitte
- Dezentrale Steuerungen ist das aktives Warten
- Zentrale Steuerungen sind Semaphore (passives Warten)
- Beim passiven Warten werden Prozesse schlafengelegt
- Ereignisse dienen der Koordination
Merktabelle: Synchronisationsmöglichkeiten
Was ist das Erzeuger-Verbraucher-Problem?
Zwei Prozesse nutzen einen Puffer mit N Plätzen zum Datenaustausch. Während
Prozess A Daten sequentiell in den Puffer schreibt, entnimmt Prozess B ein Datum
aus dem Puffer.
Nun stellen sich die Fragen
- Wie soll man das gleichzeitige Einfügen und Entnehmen synchronisieren?
- Ein Datum darf nur eingefügt werden wenn der Puffer nicht voll ist.
- Ein Datum darf nur eingefügt werden, wenn noch Platz im Buffer ist.
Gelöst werden, kann das Problem mit mehreren Semaphoren.
- Ein Semaphor für die Kontrolle des kritischen Abschnittes
- Ein Semaphor, welcher die freien Plätze zählt
- Ein Semaphor, welcher die vergebenen Plätze zählt
const N 100 //buffer size
int mutex = 1 //Für Kritischen Abschnitt
int empty = N //Anfangs leerer Buffer
int full = 0 //noch keine Daten im Buffer
void ErzeugerProzess()
{
int item;
while(1)
{
iNode = CreateItem();
P(empty);
P(mutex);
Insert(item) //Critical Section
V(mutex); //Mutex wieder erhöhen
V(full); //full=full+1
}
}
void VerbraucherProzess()
{
int item;
while(1)
{
P(full);
P(mutex);
Item = RemoveItem(); //Critical Section
V(mutex);
V(empty);
MakeSomething(Item);
}
}
Der Semaphor mutex verhindert, daß gleichzeitig Verbraucher und Erzeuger im kritischen
Abschnitt sind. Denn falls der Erzeuger gerade eingetreten ist, ist der Semaphore mutex null.
Versucht nun der Verbraucher in den kritischen Abschnitt einzutreten, muss er bei P(mutex) warten,
da der mutex noch null ist. Um nun zu Verhindern das aus einer leeren Liste gelesen oder in eine
volle geschrieben wird, werden hier elegant zwei weitere Semaphoren verwendet, welche das
Vorhandensein von freien Plätzen (empty) und belegten P lätzen (full) zählen.
Was ist das Philosophenproblem?
Beim Philosophenproblem geht es nicht um Kommunikation sondern um
Synchronisation. Abstrakt gesehen, gibt es N Prozesse und N Betriebsmittel. Ein
Prozess benötigt mindestens zwei der Betriebsmittel um fortschreiten zu können.
Ohne Synchronisation, würden im schlimmsten Fall alle 5 Prozesse je ein
Betriebsmittel bekommen und dann unendlich darauf warten, daß ein anderer Prozess
eins freigibt.
Es sitzen fünf Personen an einem runden Tisch. Sie können entweder Denken oder
Essen. Es gibt zwar für jeden einen Teller, aber nur fünf Stäbchen. Um Essen zu
können braucht man aber bekanntermaßen zwei.
Wie kann das Philosophenproblem gelöst werden?
Natürlich denkt man zuerst an Semaphoren. Nur funktioniert die triviale Lösung
mit einem Semaphor pro Stäbchen nicht. Es können immer noch alle Philosophen je ein
Stäbchen gleichzeitig aufnehmen und die Prozesse wären allesamt verklemmt. Es muss
also irgendwie möglich sein, mehr als ein Stäbchen gleichzeitig aufzunehmen. Bei
den verschiedenen Implementationen ist vor allem darauf zu achten, daß dieses
Aufnehmen der beiden Stäbe eine Atomare Funktion ist.
Wie funktionieren Unix-Signale?
Ein Ereignis wird hier als Signal bezeichnet. Unter Unix gibt es vorgegebene
durchnummerierte Signale. Unter Unix kann mit kill(Prozessnummer,
Signalnummer) einem Prozess ein Ereignis signalisiert werden.
Die Reaktion eines Prozesses auf ein Signal kann verschieden ausfallen:
- Aktivierung einer Prozessfunktion, die der Prozess vorher für dieses Signal
mit der Funktion signal( Signalnummer, Funktionsadresse)
angemeldet hat
- Er kann das Signal mit signal(Signalnummer, SIG_IGN);
ignorieren
- Der Prozess kann beendet werden.
- Hat der Prozess für eine Signalnummer nichts festgelegt, dann gibt es
Voreinstellungen:
|
|
#define SIGFPE
|
8
|
/* Floating point trap */
|
|
|
#define SIGILL
|
4
|
/* Illegal instruction */
|
|
|
#define SIGINT
|
2
|
/* voreingestelle Interrupttaste z.B. Ctl -c*/
|
|
|
#define SIGSEGV
|
11
|
/* Memory access violation */
|
|
|
#define SIGTERM
|
15
|
/* Kill -Kommando ohne Angabe*/
|
|
|
#define SIGKILL
|
9
|
/* unbedingter Prozessabbruch*/
|
Was unterscheidet Pipes und Named Pipes?
Pipes sind nur innerhalb von Prozessgruppen (UNIX) oder zwischen Threads eines
Prozesses verwendbar, da diese nur dort bekannt ist (und eindeutig
identifizierbar). Diese Einschränkung kann mit der Named Pipe
umgangen werden. Eine Named Pipe ist ein mit einem eindeutigen Namen
versehenes Objekt des Betriebssystems. Ein Prozess kann die Pipe
erstellen, ein anderer kann eine Verbindung zu der Pipe herstellen. Im UNIX erhält
eine Named Pipe einen Pfadnamen, wie eine Datei.

Was sind Sockets?
Sockets sind im Grunde Pipe Erweiterungen für Internet und andere Netzdienste.
Dabei werden ein SocketA und ein SocketB zu einer bidirektionalen Pipe verbunden.
Identifiziert wird ein Socket über Rechneradresse und Portnummer. So ermöglichen
Sie eine Client-Server-Kommunikation:
Ein Serverprozess erstellt einen Socket mit create und wartet dann auf
Verbindungswünsche (listen).
Welche Möglichkeiten zur Kommunikation und Synchronistation kennen Sie?
Kommunikation
- Pipes (FIFO-Puffer - Senden (Schreiben) ist asynchron, Empfangen (Lesen)
blockierend)
- Named Pipes
- Mailboxen (Tool zum Senden und Empfangen von Nachrichten)
- Send - Receive (BS Unterstützung für Mailboxen - synchron oder asynchron)
- Queues
- Sockets
- RPC
- Shared Memory
Synchronisation
- Monitore
- Events
- Semaphore
- Barierren (oft zur Threadsynchronisation verwendet)
|
|
|
|
|
|
Kapitel 1
|
Einleitung
|
|
Ebenen- oder Schichtenmodell, Aufgaben...
|
|
Kapitel 2
|
Modelle
|
|
Schichtenmodell, Betriebsmittel, Instanzen...
|
|
Kapitel 3
|
Aktivitäten
|
|
Token, Multitasking, Prozesse,Timesharing...
|
|
Kapitel 4
|
Kritischer Abschnitt
|
|
Gegenseitiger Ausschluss,Peterson,Semaphore...
|
|
Kapitel 5
|
Scheduling
|
|
Prozessorzuteilung, Prioritätsscheduling, RR...
|
|
Kapitel 6
|
Speichermanagment
|
|
Freispeicherverwaltung, Segmentierung, Paging...
|
|
Kapitel 7
|
Seitenersetzung
|
|
NRU, FiFo, Second Chance, LRU, NFU, Aging...
|
|
Kapitel 8
|
Deadlocks
|
|
Bedingungen, Erkennung und Auflösung...
|
|
Kapitel 9
|
Dateisysteme
|
|
Freispeicherverwaltung, Festplattenscheduling...
|
|
|
|
|
|
|
Quellen:
|
Andrew S. Tanenbaum
Computerarchitektur
|
Andrew S. Tanenbaum
Moderne Betriebssysteme
|
Petterson
Computer Architectur & Design
|
Christian Märtin
Rechnerarchitekturen
|
Kalfa
Skript und Vorlesung
|
Word Wide Web
Verschiedenste Seiten
|
|
|
|
|