Systemnahe Programmierung
Systemnahe Programmierung
Systemnahe Programmierung
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Systemnahe</strong> <strong>Programmierung</strong><br />
Johann Schlichter<br />
Institut für Informatik<br />
TU München, Munich, Germany<br />
Oktober 2003<br />
Vorlesungsunterlagen<br />
(Student Script 1 )<br />
1 Script generated by Targeteam; Not for general Distribution
Inhaltsverzeichnis<br />
1 Übersicht 2<br />
1.1 Ziel der Vorlesung . . . . . . . . . . . . . . . . . . . . . . . . . . 2<br />
1.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3<br />
1.2.1 Anforderungen an Rechensysteme . . . . . . . . . . . . . 3<br />
1.2.2 Struktur eines Rechensystems . . . . . . . . . . . . . . . 6<br />
1.3 Themen der Vorlesung . . . . . . . . . . . . . . . . . . . . . . . 8<br />
1.3.1 Laufzeitmodell . . . . . . . . . . . . . . . . . . . . . . . 8<br />
1.3.2 Inhaltsübersicht . . . . . . . . . . . . . . . . . . . . . . . 9<br />
1.4 Literaturübersicht . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
1.4.1 Begleitend zur Vorlesung . . . . . . . . . . . . . . . . . . 10<br />
1.4.2 Begleitend zur Übung . . . . . . . . . . . . . . . . . . . 10<br />
1.4.3 Weiterführende Literatur . . . . . . . . . . . . . . . . . . 10<br />
2 Rechner und hardwarenahe Programme 12<br />
2.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />
2.2 von Neumann Rechner . . . . . . . . . . . . . . . . . . . . . . . 12<br />
2.3 Architektur sequentieller Rechner . . . . . . . . . . . . . . . . . 14<br />
2.3.1 Blockdiagramm für eine Architektur . . . . . . . . . . . . 14<br />
2.3.2 Befehlsgrundzyklus . . . . . . . . . . . . . . . . . . . . . 17<br />
2.3.3 E/A Architekturvarianten . . . . . . . . . . . . . . . . . . 18<br />
2.3.4 Architektur der MI . . . . . . . . . . . . . . . . . . . . . 19<br />
2.4 Befehlsvorrat eines Rechners . . . . . . . . . . . . . . . . . . . . 20<br />
2.4.1 Charakterisierung der Befehle . . . . . . . . . . . . . . . 20<br />
2.4.2 Aufbau Maschinenbefehle . . . . . . . . . . . . . . . . . 22<br />
i
Schlichter, TU München INHALTSVERZEICHNIS<br />
2.4.3 Befehle der MI . . . . . . . . . . . . . . . . . . . . . . . 30<br />
2.5 Hardwarenahe Programme . . . . . . . . . . . . . . . . . . . . . 34<br />
2.5.1 Definitionen . . . . . . . . . . . . . . . . . . . . . . . . 34<br />
2.5.2 Programmaufbereitung . . . . . . . . . . . . . . . . . . . 35<br />
2.5.3 MI Assemblerprogramm . . . . . . . . . . . . . . . . . . 36<br />
2.5.4 Assembler Grundfunktionen . . . . . . . . . . . . . . . . 38<br />
2.5.5 Assemblerläufe . . . . . . . . . . . . . . . . . . . . . . . 40<br />
2.5.6 Binder und Lader . . . . . . . . . . . . . . . . . . . . . . 44<br />
2.6 Hardwarenahe Datenstrukturen . . . . . . . . . . . . . . . . . . . 49<br />
2.6.1 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49<br />
2.6.2 Programme im Arbeitsspeicher . . . . . . . . . . . . . . . 53<br />
3 Parallele Systeme 59<br />
3.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />
3.2 Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />
3.2.1 Begriffsdefinitionen . . . . . . . . . . . . . . . . . . . . 61<br />
3.2.2 Beschreibungskonzepte . . . . . . . . . . . . . . . . . . . 62<br />
3.3 Modellierung paralleler Systeme . . . . . . . . . . . . . . . . . . 63<br />
3.3.1 Modellierungsziele . . . . . . . . . . . . . . . . . . . . . 64<br />
3.3.2 Verhaltensbeschreibung . . . . . . . . . . . . . . . . . . 65<br />
3.3.3 Ereignisse und Aktionsstrukturen . . . . . . . . . . . . . 66<br />
3.3.4 Aktionen als Zustandsübergänge . . . . . . . . . . . . . . 72<br />
3.3.5 Petri-Netze . . . . . . . . . . . . . . . . . . . . . . . . . 77<br />
3.4 Thread-Konzept . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />
3.4.1 Charakterisierung von Threads . . . . . . . . . . . . . . . 90<br />
3.4.2 Threads in Java . . . . . . . . . . . . . . . . . . . . . . . 93<br />
3.5 Synchronisation . . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />
3.5.1 Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . 96<br />
3.5.2 Definition: Wechselseitiger Ausschluss . . . . . . . . . . 98<br />
3.5.3 Modellierung . . . . . . . . . . . . . . . . . . . . . . . . 99<br />
3.5.4 Synchronisierungskonzepte . . . . . . . . . . . . . . . . 100<br />
3.5.5 Semaphore . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />
3.5.6 Synchronisierung von Java Threads . . . . . . . . . . . . 111<br />
ii
Schlichter, TU München INHALTSVERZEICHNIS<br />
3.6 Verklemmungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />
3.6.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . 113<br />
3.6.2 Belegungs-Anforderungsgraph . . . . . . . . . . . . . . . 113<br />
3.6.3 Verklemmungs-Erkennung . . . . . . . . . . . . . . . . . 114<br />
3.6.4 Verklemmungs-Vermeidung . . . . . . . . . . . . . . . . 114<br />
3.6.5 Verklemmungs-Verhinderung . . . . . . . . . . . . . . . 115<br />
4 Prozess- und Prozessorverwaltung 118<br />
4.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 118<br />
4.2 Betriebssystem - Überblick . . . . . . . . . . . . . . . . . . . . . 119<br />
4.2.1 BS-Hauptaufgaben . . . . . . . . . . . . . . . . . . . . . 119<br />
4.2.2 Systemprogrammierung . . . . . . . . . . . . . . . . . . 120<br />
4.2.3 Betriebssystem-Architektur . . . . . . . . . . . . . . . . 121<br />
4.2.4 Betriebsarten . . . . . . . . . . . . . . . . . . . . . . . . 126<br />
4.3 Prozessverwaltung . . . . . . . . . . . . . . . . . . . . . . . . . 127<br />
4.3.1 Prozesskonzept . . . . . . . . . . . . . . . . . . . . . . . 127<br />
4.3.2 Dispatcher . . . . . . . . . . . . . . . . . . . . . . . . . 131<br />
4.3.3 Arbeitsmodi . . . . . . . . . . . . . . . . . . . . . . . . 132<br />
4.3.4 Systemaufrufe . . . . . . . . . . . . . . . . . . . . . . . 133<br />
4.3.5 Realisierung von Threads . . . . . . . . . . . . . . . . . 137<br />
4.4 Prozessorverwaltung . . . . . . . . . . . . . . . . . . . . . . . . 140<br />
4.4.1 Kriterien . . . . . . . . . . . . . . . . . . . . . . . . . . 140<br />
4.4.2 Scheduling-Strategien . . . . . . . . . . . . . . . . . . . 141<br />
4.4.3 Beispiel Unix Scheduling . . . . . . . . . . . . . . . . . 144<br />
4.4.4 Thread Scheduling . . . . . . . . . . . . . . . . . . . . . 145<br />
4.4.5 Mehrschichtiges Scheduling . . . . . . . . . . . . . . . . 147<br />
4.4.6 Echtzeit Scheduling . . . . . . . . . . . . . . . . . . . . 148<br />
4.5 Unterbrechungskonzept . . . . . . . . . . . . . . . . . . . . . . . 150<br />
4.5.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />
4.5.2 Unterbrechungsarten . . . . . . . . . . . . . . . . . . . . 151<br />
4.5.3 Behandlung externer Unterbrechungen . . . . . . . . . . 153<br />
4.5.4 Konflikte . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />
iii
Schlichter, TU München INHALTSVERZEICHNIS<br />
5 Speicherverwaltung 158<br />
5.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 158<br />
5.2 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158<br />
5.2.1 Adressräume . . . . . . . . . . . . . . . . . . . . . . . . 159<br />
5.2.2 Organisation von Adressräumen . . . . . . . . . . . . . . 160<br />
5.2.3 Fragmentierung . . . . . . . . . . . . . . . . . . . . . . . 162<br />
5.2.4 Forderungen an Adressraumrealisierung . . . . . . . . . . 164<br />
5.3 Speicherabbildungen . . . . . . . . . . . . . . . . . . . . . . . . 165<br />
5.3.1 Direkte Adressierung . . . . . . . . . . . . . . . . . . . . 165<br />
5.3.2 Basisadressierung . . . . . . . . . . . . . . . . . . . . . . 167<br />
5.4 Virtueller Speicher . . . . . . . . . . . . . . . . . . . . . . . . . 168<br />
5.4.1 Seitenadressierung . . . . . . . . . . . . . . . . . . . . . 168<br />
5.4.2 Segment-Seitenadressierung . . . . . . . . . . . . . . . . 179<br />
5.4.3 Speicherverwaltung der MI . . . . . . . . . . . . . . . . . 180<br />
6 Dateisysteme 182<br />
6.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 182<br />
6.2 Charakteristika von Dateisystemen . . . . . . . . . . . . . . . . . 183<br />
6.3 Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184<br />
6.4 Memory-Mapped Dateien . . . . . . . . . . . . . . . . . . . . . . 187<br />
6.5 Verzeichnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187<br />
6.6 Schichtenmodell . . . . . . . . . . . . . . . . . . . . . . . . . . 188<br />
6.6.1 Datenträgerorganisation . . . . . . . . . . . . . . . . . . 188<br />
6.6.2 Blockorientiertes Dateisystem . . . . . . . . . . . . . . . 189<br />
6.6.3 Dateiverwaltung . . . . . . . . . . . . . . . . . . . . . . 189<br />
6.7 Einbettung der E/A . . . . . . . . . . . . . . . . . . . . . . . . . 190<br />
7 Prozesskommunikation 191<br />
7.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 191<br />
7.2 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />
7.2.1 Kommunikationsarten . . . . . . . . . . . . . . . . . . . 192<br />
7.2.2 Verteilte Systeme . . . . . . . . . . . . . . . . . . . . . . 196<br />
7.3 Nachrichtenbasierte Kommunikation . . . . . . . . . . . . . . . . 198<br />
iv
Schlichter, TU München INHALTSVERZEICHNIS<br />
7.3.1 Elementare Kommunikationsmodelle . . . . . . . . . . . 198<br />
7.3.2 Erzeuger-Verbraucher Problem . . . . . . . . . . . . . . . 204<br />
7.3.3 Modellierung durch ein Petrinetz . . . . . . . . . . . . . . 204<br />
7.3.4 Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205<br />
7.3.5 Kanäle . . . . . . . . . . . . . . . . . . . . . . . . . . . 207<br />
7.3.6 Ströme . . . . . . . . . . . . . . . . . . . . . . . . . . . 207<br />
7.4 Client-Server-Modell . . . . . . . . . . . . . . . . . . . . . . . . 208<br />
7.5 Netzwerkprogrammierung . . . . . . . . . . . . . . . . . . . . . 210<br />
7.5.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />
7.5.2 Server Protokoll . . . . . . . . . . . . . . . . . . . . . . 212<br />
7.5.3 Client Protokoll . . . . . . . . . . . . . . . . . . . . . . . 213<br />
7.5.4 Bidirektionale Stromverbindung . . . . . . . . . . . . . . 214<br />
7.5.5 Java Socket Class . . . . . . . . . . . . . . . . . . . . . . 214<br />
7.6 Remote Procedure Call . . . . . . . . . . . . . . . . . . . . . . . 216<br />
8 Sicherheit in Rechensystemen 223<br />
8.1 Fragestellungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 223<br />
8.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223<br />
8.3 Schutzmechanismen . . . . . . . . . . . . . . . . . . . . . . . . 228<br />
8.3.1 Anforderungen . . . . . . . . . . . . . . . . . . . . . . . 228<br />
8.3.2 Ebenen des Zugriffschutzes . . . . . . . . . . . . . . . . 229<br />
8.3.3 Schutzmatrix . . . . . . . . . . . . . . . . . . . . . . . . 229<br />
8.4 Sicherheit in verteilten Systemen . . . . . . . . . . . . . . . . . . 234<br />
8.4.1 Unsicherheitsfaktoren in verteilten Systemen . . . . . . . 234<br />
8.4.2 Sicherheitsanforderungen . . . . . . . . . . . . . . . . . 235<br />
8.4.3 Kryptographie . . . . . . . . . . . . . . . . . . . . . . . 236<br />
8.4.4 Authentifizierungsdienst Kerberos . . . . . . . . . . . . . 240<br />
8.4.5 Mobiler Code . . . . . . . . . . . . . . . . . . . . . . . . 245<br />
9 Zusammenfassung 248<br />
v
Schlichter, TU München INHALTSVERZEICHNIS<br />
¯ Prof. J. Schlichter<br />
– Lehrstuhl für Angewandte Informatik / Kooperative Systeme, Fakultät für<br />
Informatik, TU München<br />
– Boltzmannstr. 3, 85748 Garching<br />
Email: schlichter@in.tum.de (URL: mailto:schlichter@in.tum.de)<br />
Tel.: 089-289 18654<br />
URL: http://www11.informatik.tu-muenchen.de/<br />
1
Kapitel 1<br />
Übersicht<br />
1.1 Ziel der Vorlesung<br />
Diese Vorlesung beschäftigt sich mit den technischen Aspekten von Rechensystemen<br />
und der Informationsverarbeitung, insbesondere der systemnahen <strong>Programmierung</strong>.<br />
Dabei werden sowohl nicht verteilte als auch verteilte Systeme betrachtet.<br />
¯ Der erste Teil der Vorlesung behandelt hardwarenahe Programm- und Datenstrukturen.<br />
Deshalb wird zunächst die Modellmaschine MI (basierend auf einer<br />
VAX Architektur von vormals Digital Equipment) vorgestellt. Diese Maschine<br />
dient als Basis zur Diskussion des Aufbaus von typischen Maschinenund<br />
Assemblerbefehlen. Anhand dieser Modellmaschine wird die Entwicklung<br />
von Programmen unter Berücksichtigung der jeweiligen Komponenten einer<br />
Hardwarekonfiguration aufgezeigt. Die einfachen Programme, die zunächst<br />
entwickelt werden, werden dann zu den Programm- und Datenstrukturen verallgemeinert,<br />
die heute für hardwarenahe Systemebenen verwendet werden.<br />
¯ Im nächsten Teil der Vorlesung erfolgt eine wichtige Verallgemeinerung,<br />
nämlich der Übergang von sequentiellen zu parallelen Systemen und die<br />
Entwicklung von parallelen Systemen. Zu den Themen, die behandelt werden,<br />
gehören die Grundlagenprobleme paralleler Systeme und die Verfahren, mit<br />
denen diese gelöst werden können: Modellierung des Systemverhaltens,<br />
Synchronisation, Verklemmungen.<br />
¯ Im Anschluss daran werden Konzepte und Verfahren sowie der Aufbau von<br />
Betriebssystemen, die der wesentliche Teil der Konkretisierung paralleler Systeme<br />
sind, behandelt. Insbesondere geht es um die Arbeitsspeicherverwaltung<br />
2
Schlichter, TU München 1.2. MOTIVATION<br />
(Hauptspeicherverwaltung, "main memory management"), die Prozessverwaltung<br />
und die Prozessorzuteilung sowie Mechanismen zur Kontrolle der Nebenläufigkeit.<br />
¯ Parallele Systeme können auf zentralen Hardwarekonfigurationen realisiert<br />
werden. Als Basis können aber auch verteilte, vernetzte Hardwarekonfigurationen<br />
dienen, so dass verteilte Rechensysteme entstehen, die heute durchweg<br />
im Einsatz sind. Es werden deshalb die grundlegenden Aspekte von verteilten<br />
Systemen behandelt, insbesondere die nachrichtenbasierte Prozesskommunikation,<br />
das Client-Server Modell und die Netzwerkprogrammierung.<br />
¯ Mit der rasanten Verbreitung des Internet und dessen Nutzung für private und<br />
geschäftliche Transaktionen (E-Commerce und E-Business) steigt der Bedarf<br />
an sicheren IT-Systemen. Der Abschnitt behandelt nach einigen verbreiteten<br />
Sicherheitslücken verschiedene Schutzmechanismen, wie Schutzmatrizen,<br />
Kryptosysteme und Authentifizierungsmechanismen.<br />
1.2 Motivation<br />
Aufgabe der Informatik ist es, Rechensysteme zu entwickeln und diese Anwendern<br />
als leistungsfähige Hilfsmittel für Lösungen ihrer Informationsverarbeitungsprobleme<br />
zur Verfügung zu stellen. Diese Aufgabe ist vielgestaltig,<br />
weitreichend, kompliziert und komplex; sie führt im Zuge der Weiterentwicklung<br />
von Rechensystemen und im Zuge der wachsenden Nachfrage der Gesellschaft<br />
nach Information fortwährend auf neue Fragestellungen, für die nach Antworten<br />
gesucht werden muss. Sie hat zudem dazu geführt, dass sich große Bereiche der<br />
Industrie und der Wirtschaft mit dieser Aufgabe befassen. Im folgenden werden<br />
zunächst die wichtigsten Anforderungen, die mit der Entwicklung von Rechensystemen<br />
erfüllt werden sollen, in Kürze genauer erklärt.<br />
1.2.1 Anforderungen an Rechensysteme<br />
Rechensysteme sind offene, dynamische, technische Systeme mit Fähigkeiten zur<br />
Speicherung und zur Verarbeitung von Information, die für Anwendungen und<br />
Anwender nutzbar zur Verfügung gestellt werden sollen.<br />
Offenes System<br />
Ein Rechensystem R ist ein offenes System sagt zweierlei:<br />
3
Schlichter, TU München 1.2. MOTIVATION<br />
¯ R ist als System eine durch Zusammenfassung gebildete, abgegrenzte Einheit.<br />
Innen ist das, was zu R gehört, und außen ist das, was nicht zu R und damit zur<br />
Umgebung U(R) gehört.<br />
¯ R hat eine (offene) Schnittstelle, mit der Einwirkungen von U(R) auf R und<br />
Einwirkungen von R auf U(R) möglich sind.<br />
¯ Schnittstelle eines Rechensystems<br />
Umgebung<br />
U(R)<br />
Schnittstelle R - U(R)<br />
Rechensystem R<br />
¯ Sichten eines Rechensystems<br />
Es existieren verschiedene Sichten auf das Rechensystems R<br />
– Außensicht vs. Innensicht. Die Außensicht, die von außen die Eigenschaften<br />
der R-U(R) Schnittstelle zeigt, und die Innensicht, welche die inneren<br />
Eigenschaften von R zeigt.<br />
– Black-box Sicht vs. White-box Sicht. Das System ist ein schwarzer<br />
Kasten (black box): Das System wird als einzelner Gegenstand aufgefasst.<br />
Das System ist ein weißer Kasten (white box oder auch glass box): Für<br />
das Verständnis des Systems ist die Zusammensetzung in Subsysteme und<br />
Komponenten wichtig.<br />
– Sichten sind methodische Hilfsmittel für Systemanalysen:<br />
£ Komponenten haben Eigenschaften, die denen von Systemen entsprechen.<br />
Dies bedeutet, Komponenten können für sich als Systeme betrachtet<br />
werden.<br />
4
Schlichter, TU München 1.2. MOTIVATION<br />
£ Verbindungen zwischen Komponenten beschreiben Abhängigkeiten zwischen<br />
Komponenten.<br />
£ Aufteilungen liefern verfeinerte White-box Sichten.<br />
£ Zusammenfassungen und Abgrenzungen liefern Einheiten, für die Blackbox<br />
Sichten möglich sind.<br />
£ Rekursion bei der Entwicklung und Analyse von Rechensystemen. Die<br />
umrissene Vorgehensweise unter Anwendung der erklärten Systembegriffe<br />
ist für Analysen großer Systeme geeignet, wenn sie iterativ (rekursiv)<br />
angewandt wird; sie ist insbesondere dann geeignet, wenn sie induktiv<br />
angewandt werden kann. Die Möglichkeiten hierfür ergeben sich aus den<br />
Eigenschaften, die ein gegebenes System hat. Für ein Rechensystem R<br />
wird über diese Möglichkeiten bei der Entwicklung von R entschieden.<br />
Die Vorgehensweise bei der Entwicklung von R und die Vorgehensweise<br />
bei Analysen von R stehen also in engem Zusammenhang.<br />
Dynamisches System<br />
Eigenschaften des Rechensystems R ändern sich mit der Zeit<br />
Beschreibung des Verhaltens von R.<br />
In diesem Zusammenhang werden wir Beschreibungsmöglichkeiten kennenlernen,<br />
insbesondere Petrinetze. Diese dienen dazu das Verhalten von Prozessen und<br />
deren Interaktion zu spezifizieren. Ein Rechensystem erhält seine Fähigkeiten<br />
zu selbsttätigen Veränderungen seiner Eigenschaften mit entsprechenden Komponenten;<br />
demnach sind für R aktive und passive Komponenten zu unterscheiden:<br />
Mit seinen aktiven Komponenten führt R Aktionen aus, die Veränderungen der<br />
Eigenschaften von R bewirken; die passiven Komponenten sind Hilfsmittel für<br />
diese Aktionen.<br />
Technisches System<br />
Rechensystem ist mit hardware- und softwaretechnischen Mitteln realisiert. R<br />
ist entweder ein mit technischen Mitteln realisiertes, reales System oder ein<br />
künstliches, artifizielles System, das die Eigenschaften hat, die es mit seiner<br />
Entwicklung und mit seinen Weiterentwicklungen erhält.<br />
Informationsspeicherung und -verarbeitung<br />
5
Schlichter, TU München 1.2. MOTIVATION<br />
Information<br />
Daten<br />
Nachricht<br />
Wissen<br />
Information<br />
Repräsentation Interpretation<br />
Daten<br />
Daten sind elementare Fakten, Aussagen und Sachverhalte. Sie sind leicht<br />
zu strukturieren, leicht maschinell zu erfassen und leicht zu übertragen. Im<br />
Zusammenhang mit der Übertragung spricht man auch gerne von Nachrichten.<br />
Information sind Daten mit einer Bedeutung und einem Zweck; sie erfordert<br />
Analyse, Konsens bzgl. Semantik und Interpretation. Wissen ist Information<br />
in einem bestimmten, für den Menschen relevanten Kontext; es ist schwierig,<br />
Wissen zu strukturieren, schwierig maschinell zu erfassen und zu verarbeiten.<br />
Weiterhin ist es schwierig Wissen zu übertragen, da es oft nur implizit existiert<br />
(siehe auch das neue Forschungsgebiet Wissensmanagement bzw. "Knowledge<br />
Management").<br />
1.2.2 Struktur eines Rechensystems<br />
6
Schlichter, TU München 1.2. MOTIVATION<br />
Datenbank World Wide Web Email<br />
Shell Übersetzer Dateisystem<br />
Betriebssystem<br />
Maschinensprache<br />
Mikroprogramme / festverdrahtete Programme<br />
physische Komponenten und Geräte<br />
Anwendungsprogramme<br />
Systemprogramme<br />
Hardware<br />
In dieser Vorlesung werden wir uns besonders mit Aspekten der technischen Informatik<br />
beschäftigen, und zwar mit den Bereichen Hardware (Architektur, Maschinensprache)<br />
und Systemprogramme (Betriebssysteme, Assembler, Kommunikation<br />
in verteilten Systemen). Die Vorlesung ist als eine Einführung in diese Bereiche<br />
zu interpretieren; eine detailliertere Behandlung von Hardware, Systemprogramme<br />
und verteilte Systeme erfolgt in weiterführenden Vorlesungen des Hauptstudiums.<br />
Mikroprogramme dienen zur Realisierung der Maschinensprache; bei<br />
RISC-Rechnern (z.B. Sun Workstation) sind die Mikroprogramme oft festverdrahtet.<br />
¯ Darstellung von Programmen in maschinennaher Form für bestimmte Anwendungen<br />
auch heute noch unerläßlich, beispielsweise für den Übersetzerbau,<br />
eingebettete Systeme oder für systemnahe <strong>Programmierung</strong> in Teilen des Betriebssystems.<br />
Beispiele dafür sind Echtzeitanwendungen, aber auch Teile<br />
des Betriebssystems wie Nachrichtenaustausch, low-level Speicherverwaltung.<br />
Eingebettete Systeme sind in Autos oder Handies zu finden. Beispielsweise<br />
ist in modernen Autos bereits eine zwei- oder dreistelligen Anzahl von Prozessoren<br />
zu finden, die über einen internen Bus miteinander verbunden sind.<br />
¯ Thema der Vorlesung ist also maschinennahe <strong>Programmierung</strong>, systemnahe<br />
Programmentwicklung; nebenläufige ("concurrent") Ausführung von mehreren<br />
7
Schlichter, TU München 1.3. THEMEN DER VORLESUNG<br />
Teilabläufen Nichtdeterminismus. Dabei ergibt sich von selbst eine neue<br />
Qualität der <strong>Programmierung</strong>: Nebenläufigkeit, d.h. zwei oder mehrere<br />
Teilabläufe finden gleichzeitig statt, beispielsweise CPU (bzw. RK) und<br />
Drucker. Neue Qualität insofern, als Nebenläufigkeit auch Nichtdeterminismus<br />
enthält. Unter Nichtdeterminismus verstehen wir das willkürliche Auftreten<br />
von Abläufen und Ereignissen (bedingt durch Einflüsse wie Last, Interaktion<br />
mit Benutzer).<br />
1.3 Themen der Vorlesung<br />
Diese Vorlesung beschäftigt sich mit den technischen Aspekten von Rechensystemen<br />
und der Informationsverarbeitung, insbesondere der systemnahen <strong>Programmierung</strong>.<br />
Dabei werden sowohl nicht verteilte als auch verteilte Systeme betrachtet.<br />
1.3.1 Laufzeitmodell<br />
Bereitstellung eines indirekten Zugangs zur Rechnerhardware über eine Dienstschicht.<br />
Ziel dieser Schicht ist die Realisierung einer virtuellen Maschine. Virtualisierung<br />
kann sowohl zur Fehlervermeidung als auch zur Reduktion der Komplexität<br />
eingesetzt werden.<br />
¯ Die in den Diensten bereitgestellten Abstraktionen stellen einen Rahmen für<br />
die Organisation von Anwendungen zur Laufzeit dar, d.h. ein Laufzeitmodell.<br />
¯ Dienste des Laufzeitmodells: Systembedienung, Prozessmanagement, Prozessinteraktion,<br />
Datenhaltung, Gerätemanagement. Wichtige Fragestellungen der<br />
systemnahen <strong>Programmierung</strong> sind:<br />
a) welche Dienste gehören zu einem Laufzeitmodell?<br />
b) gibt es einen inneren Zusammenhang unter den Diensten?<br />
c) welche Dienstmenge ist unverzichtbar?<br />
d) wo werden die Dienste realisiert?<br />
Beispielsweise werden die Dienste im Betriebssystemkern oder als Systemprozesse<br />
realisiert, die im Benutzermodus ("user mode") ablaufen.<br />
¯ elementare Abstraktionen eines Laufzeitmodells. Prozesse sind dynamische<br />
Objekte, die Aktivitäten in einem Rechensystem repräsentieren. Jeder<br />
Prozess ist definiert durch einen Adressraum A, eine darin gespeicherte<br />
Handlungsvorschrift H in Form eines sequentiellen Programms und einen<br />
8
Schlichter, TU München 1.3. THEMEN DER VORLESUNG<br />
Aktivitätsträger T, der mit der Handlungsvorschrift verknüpft ist und sie<br />
ausführt. Der Aktivitätsträger T wird oft auch als "Thread" bezeichnet. Das<br />
Tripel (A, H, T) repräsentiert einen sequentiellen Prozess.<br />
Elemente des Laufzeitmodells<br />
– Adressräume = Abstraktion eines physischen Speichers. Unter einem<br />
Adressraum versteht man einen von der Speichertechnologie und den<br />
beschränkten Ausbaumöglichkeiten physischer Speicher unabhängigen<br />
virtuellen Speicher. Adressräume verschiedener Prozesse sind gegeneinander<br />
abgeschottet.<br />
Threads ablaufen.<br />
Innerhalb eines Adressraums können ein oder mehrere<br />
– Threads = Abstraktion eines physischen Prozessors. Ein Thread ist der<br />
Träger einer sequentiellen Aktivität, die durch die Ausführung eines dem<br />
Thread zugeordneten Programms (Handlungsvorschrift) bestimmt ist.<br />
– Prozessinteraktion. Prozessinteraktion kann entweder speicherbasiert,<br />
d.h. es gibt gemeinsame Speicherbereiche, auf die die Prozesse<br />
(Threads) zugreifen, oder nachrichtenbasiert ablaufen. Im ersten Fall<br />
sind Mechanismen zur Synchronisation der Zugriffe auf den gemeinsamen<br />
Speicherbereich notwendig, um Inkonsistenzen zu vermeiden. Im letzten Fall<br />
haben die Prozesse getrennte Adressräume. Sie tauschen Informationen über<br />
Nachrichten aus. Dieser Ansatz wird vor allem in verteilten Rechensystemen<br />
verwendet, wo Prozesse über ein Rechnernetz miteinander kommunizieren.<br />
1.3.2 Inhaltsübersicht<br />
Im einzelnen werden in der Vorlesung die folgenden Themen behandelt:<br />
1. hardwarenahe Programm-/Datenstrukturen anhand der Modellmaschine MI.<br />
Neben der MI Rechnerarchitektur werden einige Maschinenbefehle sehr kurz<br />
behandelt. Auch wird die Programmaufbereitung mit Assembler, Binder und<br />
Lader kurz andiskutiert. Ein Aspekt ist auch noch die Darstellung von ein-<br />
/mehrdimensionalen Felder im Arbeitsspeicher.<br />
2. Übergang von sequentiellen Systemen zu parallelen Systemen: Verhaltensbeschreibungen<br />
mit Hilfe von Spuren und Petrinetzen, Synchronisationskonzepte.<br />
Hier steht vor allem die speicherbasierte Prozessinteraktion im<br />
Vordergrund, d.h. das gemeinsame Nutzen von Daten. Hier kommen<br />
Semaphore und Monitore zum Einsatz. Ein wichtiger Aspekt ist die Behandlung<br />
von Verklemmungen, d.h. Prozesse blockieren sich gegenseitig.<br />
9
Schlichter, TU München 1.4. LITERATURÜBERSICHT<br />
3. grundlegende Betriebssystemkonzepte: Prozessverwaltung,<br />
Prozessorzuteilung, Arbeitsspeicherverwaltung, Dateisysteme. Bei der<br />
Arbeitsspeicherverwaltung steht vor allem der virtuelle Speicher in Form der<br />
Seitenadressierung im Vordergrund. Die Modellmaschine MI unterstützt auch<br />
die virtuelle Adressierung auf der Basis von Seiten und Kacheln. Bei Dateisystemen<br />
wird neben allgemeinen Anforderungen auch ein Schichtenmodell kurz<br />
vorgestellt.<br />
4. Kommunikation in verteilten Systemen: Client-Server-Paradigma, RPC,<br />
Netzwerkprogrammierung. In diesem Abschnitt steht die nachrichtenbasierte<br />
Prozessinteraktion im Vordergrund, und zwar die Kommunikation über Ports<br />
und Sockets.<br />
5. Sicherheit in Rechensystemen: Schutzmechanismen, Zugriffskontrolllisten,<br />
Capability-Listen, Kryptographie, Authentifizierungsdienst Kerberos. In<br />
diesem Abschnitt steht die Sicherheit in Rechensystemen im Vordergrund, und<br />
zwar sowohl in lokalen als auch in verteilten Systemen.<br />
1.4 Literaturübersicht<br />
Literatur, die als Basis für die Vorlesung verwendet wird.<br />
1.4.1 Begleitend zur Vorlesung<br />
¯ Jürgen Nehmer, Peter Sturm, "Systemsoftware - Grundlagen moderner<br />
Betriebssysteme", dpunkt.verlag, 2001<br />
¯ Andrew S. Tanenbaum, "Modern Operating Systems", Prentice Hall, 2001<br />
1.4.2 Begleitend zur Übung<br />
U. Borghoff, T. Gasteiger, A. Schmalz, P. Weigele, H.J. Siegert, "MI - Eine<br />
Maschine für die Informatikausbildung", TU Bericht 1987<br />
1.4.3 Weiterführende Literatur<br />
¯ Manfred Broy, "Informatik - Eine grundlegende Einführung", Springer-Verlag,<br />
1998<br />
10
Schlichter, TU München 1.4. LITERATURÜBERSICHT<br />
¯ George Coulouris, Jean Dollimore, Tim Kindberg, "Distributed Systems -<br />
Concepts and Design", Addison-Wesley, 2001<br />
¯ Claudia Eckert, "IT-Sicherheit: Konzepte - Verfahren - Protokolle", Oldenbourg<br />
Verlag, 2003<br />
¯ Elliotte Rusty Harold, "Java Network Programming", O'Reilly, 2000<br />
¯ Ralph Morelli, "Java, Java, Java - Object-Oriented Problem Solving", Prentice<br />
Hall, 2000<br />
¯ Andrew S. Tanenbaum, "Computernetzwerke", Prentice Hall, 2000<br />
¯ Andrew S. Tanenbaum, Marten van Steen, "Verteilte Systeme - Grundlagen und<br />
Paradigmen", Pearson Studium, 2003<br />
11
Kapitel 2<br />
Rechner und hardwarenahe<br />
Programme<br />
In diesem Abschnitt werden hardwarenahe Programme und Datenstrukturen<br />
für einfache Rechensysteme behandelt. Die Systeme sind zentralisiert, führen<br />
sequentielle Berechnungen aus und bestehen aus wenigen Komponenten.<br />
2.1 Fragestellungen<br />
Dieser Abschnitt wird relativ kurz gehalten, da einige Themen bereits in<br />
einführenden Vorlesungen zu Informatik angesprochen wurden. Insbesondere<br />
werden nachfolgende Themen angesprochen.<br />
¯ prinzipieller Aufbau eines einfachen sequentiellen Rechners.<br />
¯ Basisfähigkeiten sequentieller Rechner. Beispiele für Basisfähigkeiten sind der<br />
Befehlsgrundzyklus sowie der vorhandene Befehlsvorrat.<br />
¯ kurze Einführung in die Modellmaschine MI.<br />
¯ Programmaufbereitung in die Modellmaschine MI.<br />
¯ hardwarenahe Datenstrukturen (z.B. Linearisierung von Feldern).<br />
2.2 von Neumann Rechner<br />
Die Entwicklungsgeschichte von Rechensystemen wurde durch den sogenannten<br />
von Neumann-Rechner geprägt.Dieses Konzept dient auch weiterhin als Basis für<br />
12
Schlichter, TU München 2.2. VON NEUMANN RECHNER<br />
die meisten Rechnerarchitekturen, wobei es jedoch einige Variationen gibt, um<br />
gewisse Schwächen zu umgehen (z.B. mehrere Bussysteme).<br />
Arbeitsspeicher<br />
Eingabewerk Steuerwerk<br />
Ausgabewerk<br />
¯ Komponenten<br />
Rechenwerk<br />
Prozessor<br />
(CPU)<br />
– Arbeitsspeicher (ASP, oft auch als Hauptspeicher bezeichnet): Speicherung<br />
der Programme und ihrer Daten. ASP besteht aus einem Feld von adressierbaren<br />
Speicherzellen. Problem: Variable höherer Programmiersprachen sind<br />
typisiert; Arbeitsspeicher ist jedoch nicht typisiert, sondern nur Folge von<br />
Bits. Es gibt einige Rechnerarchitekturen, die einen typisierten Arbeitsspeicher<br />
(eine sogenannten "tagged memory") unterstützen, z.B. Burroughs<br />
Rechner.<br />
– Steuerwerk: Steuerung des Ablaufs der Befehle eines Programms. Jeder<br />
Maschinenbefehl ist ein Paar (Opcode, Operanden). Die Aktionen, die das<br />
Steuerwerk ausführt werden mit Hilfe von Maschinenbefehlen spezifiziert.<br />
Ein Maschinenbefehl besteht aus dem OpCode und den Operanden. Der<br />
OpCode spezifiziert die Operation (abgesehen von Sprungbefehlen), die<br />
dann im Rechenwerk ausgeführt werden soll. Das Steuerwerk enthält<br />
Informationen über den Status des Programmablaufs (z.B. Befehlszähler)<br />
und Einrichtungen für die Erzeugung der Steuersignale zur Ausführung der<br />
einzelnen Befehle (Steuersignale für den Steuerbus beim Zugriff auf Ein-/<br />
Ausgabewerk bzw. ASP).<br />
Aufgaben des Steuerwerks<br />
1. Holen von Maschinenbefehlen aus dem Arbeitsspeicher.<br />
13
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
2. Dekodieren der Befehle.<br />
3. Holen der Operanden der Befehle aus dem Arbeitsspeicher. Dies erfordert<br />
je nach Situation die geeignete Adressrechnung zur Bestimmung der<br />
relevanten Speicherzellen im Arbeitsspeicher. Sowohl das Steuerwerk<br />
als auch das Rechenwerk führen Maschinenbefehle aus. Letzteres<br />
die Rechenoperationen, während das Steuerwerk Maschinenbefehle zur<br />
Steuerung des Programmablaufes ausführt (z.B. Sprungbefehle).<br />
– Rechenwerk: Ausführung von Rechenoperationen, die während des Ablaufs<br />
durchgeführt werden müssen.<br />
– Eingabewerk: Einlesen des Programms und der Daten in den Arbeitsspeicher.<br />
– Ausgabewerk: Ausgabe von Daten aus dem Arbeitsspeicher. Ein-<br />
/ Ausgabewerk werden bei der MI zu einem EA-Prozessor (EAP)<br />
zusammengefasst. Ein MI-Rechner kann mehrere EA-Prozessoren besitzen.<br />
¯ Ein Prozessor (Rechnerkern) ist die Zusammenfassung des Steuer- und<br />
Rechenwerks. Register dienen zur Speicherung von Zwischenergebnissen.<br />
Daneben gibt es in Rechnerimplementierungen ein Bussystem, das die<br />
einzelnen Komponenten untereinander verbindet. Ein MI-Rechner kann bis zu<br />
vier Rechnerkern besitzen; sie werden mit RK0 bis RK3 bezeichnet.<br />
2.3 Architektur sequentieller Rechner<br />
Der von Neumann Rechner ist ein logisches Modell für einen Rechner. Mit Hilfe<br />
von physischen Komponenten wird dieses Modell implementiert.<br />
2.3.1 Blockdiagramm für eine Architektur<br />
14
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
Prozessor<br />
(Register)<br />
Adressbus<br />
Datenbus<br />
Steuerbus<br />
Arbeitsspeicher<br />
Monitor Tastatur Festplatte<br />
Graphik<br />
Controller<br />
Keyboard<br />
Controller<br />
Festplatten<br />
Controller<br />
¯ Der Bus verbindet die einzelnen Rechnerkomponenten. Er wird exklusiv<br />
genutzt. Allgemeines Problem: Synchronisation konkurrierender Zugriffe<br />
verschiedener Komponenten auf den exklusiv nutzbaren Bus. Der Bus besteht<br />
aus einer Menge von Verbindungsleitungen.<br />
– Adressbus (16, 32 oder 64 bit): Übergabe von Adressen. Der Adressbus<br />
addressiert einzelne Speicherzellen des Arbeitsspeichers (gegebenenfalls<br />
auch Speicherzellen in den Ein-/Ausgabewerken). Die Breite des Adressbus<br />
legt die Menge der maximal adressierbaren Speicherzellen fest, z.B. können<br />
mit einem 32 bit breiten Adressbus maximal 2 32 Zellen angesprochen werden<br />
(insgesamt 4 GByte).<br />
– Datenbus (32 oder 64 bit): Übermittlung von Daten.<br />
– Steuerbus: Menge von Steuerleitungen, z.B. Lese- oder Schreibbefehl an<br />
ASP. Der Steuerbus dient zur Koordination der Lese- und Schreibzyklen<br />
zwischen Prozessor und den anderen Komponenten.<br />
¯ Kommunikation zwischen Komponenten<br />
Nachfolgendes Beispiel zeigt die Aktionen, die ein Prozessor (Rechnerkern<br />
RK) und der Arbeitsspeicher ausführen, um Daten vom Arbeitsspeicher über<br />
den Bus zum Prozessor zu übertragen.<br />
Arbeitsspeicher<br />
Daten<br />
15<br />
Prozessor<br />
(RK)
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
– Ablauf der Kommunikation<br />
Die Komponenten arbeiten nicht mit einem gemeinsamen Takt, sondern<br />
unabhängig voneinander.<br />
1. RK legt die ASP-Adresse der gewünschten Dateneinheit (16, 32 oder 64<br />
bit Umfang) auf den Adressbus.<br />
2. RK legt das Signal "Lesen" auf eine bestimmte Leitung im Steuerbus.<br />
3. RK legt das Signal "Adresse gültig" auf eine bestimmte Leitung im<br />
Steuerbus. Damit wird erreicht, dass die Adresse erst gelesen wird, wenn<br />
alle Adressleitungen und Signale gültig sind (d.h. stehen).<br />
4. ASP ist passiv, "hört" aber auf Adress- und Steuerbus mit.<br />
5. ASP erkennt, dass eine Adresse gültig ist und prüft, ob er mit dieser<br />
Adresse gemeint ist. Falls ja, führe Schritte 6 - 12 aus. Dies sieht er<br />
anhand von der spezifizierten Adresse; später werden die Aufteilung der<br />
Adressbereiche noch gesondert behandelt. Wenn ASP durch die Adresse<br />
nicht angesprochen ist, erfolgt keine weitere Aktion.<br />
6. ASP prüft das Transportrichtungssignal im Steuerbus (es erkennt das<br />
Signal "Lesen").<br />
7. ASP legt die (adressierten) Daten auf den Datenbus.<br />
8. ASP legt das Signal "Daten bereit" auf eine bestimmte Leitung im<br />
Steuerbus.<br />
9. RK wartet, bis das Signal "Daten bereit" erscheint. Er weiß dann, dass die<br />
Daten auf dem Datenbus gültig sind und übernimmt diese.<br />
10. RK legt das Signal "Daten übernommen" auf eine bestimmte Leitung im<br />
Steuerbus.<br />
11. ASP deaktiviert daraufhin den Datenbus und das Signal "Daten bereit" .<br />
12. Sobald das Signal "Daten bereit" vom ASP weggenommen wird,<br />
deaktiviert der RK die von ihm verwendeten Adress- und Steuerleitungen.<br />
Der Bus ist wieder im Grundzustand und kann damit von einem anderen<br />
Prozessor verwendet werden.<br />
– Protokoll<br />
Zwischen den beteiligten Kommunikationspartnern existiert ein vereinbartes<br />
Verhaltensprotokoll.<br />
£ Verhaltensprotokolle in der Praxis: in der Vorlesung (Finger heben),<br />
Straßenverkehrsordnung, etc.<br />
£ Definition<br />
Ein Ablauf aus Aktions- und Kommunikationsschritten zwischen Kommunikationspartnern<br />
heißt ein Protokoll. Das Protokoll definiert auch<br />
16
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
Syntax, Semantik und die Reihenfolge der auszutauschenden Information.<br />
Protokolle dienen zur Festlegung der Kommunikation zwischen Komponenten.<br />
Sie spielen eine wichtige Rolle für effiziente Kommunikation<br />
und Kooperation. Es besteht eine hoher Standardisierungsbedarf, da die<br />
beteiligten Komponenten dasselbe Protokolle verwenden müssen. Protokolle<br />
spielen im Bereich der Rechnernetze eine sehr wichtige Rolle.<br />
£ Der oben geschilderte Ablauf ist ein Busprotokoll. Das obige Busprotokoll<br />
macht keine Annahmen über die Geschwindigkeit der Arbeit bei<br />
den beteiligten Partnern (d.h. es besteht asynchrones Arbeiten ohne<br />
Zeitgrenzen). Deshalb sind immer wieder Quittungen eines Partners<br />
erforderlich, in denen er mitteilt, dass er einen Schritt abgeschlossen hat.<br />
2.3.2 Befehlsgrundzyklus<br />
Die Reihenfolge der Ausführung der Maschinenbefehle wird durch den<br />
Befehlsgrundzyklus des Prozessors festgelegt.<br />
ÛÐ ØÖÙ ß<br />
ÓÐ Ò Ò ×ØÒ Å× ÒÒÐ Ö×× ×ØÑÑØ ÙÖ <br />
Ð×ÞÐÖ È<br />
×ØÑÑ Ò ÇÔÓ × Ð×<br />
×ØÑÑ ÙÒ Ð ÇÔÖÒÒ × Ð× Ò ÚÓÒ<br />
Ö Ö××ÖÙÒ×ÖØ<br />
Ö Ò Ð Ù× Ñ×Ø ÙÖ × Ê ÒÛÖ<br />
× ÐØ Ò Ð×ÞÐÖ ÓÖØ ÞÙÑ Æ ÓÐÖÐ<br />
<br />
Der Fortschaltung und damit der Wert des neuen Befehlszählers ist abhängig von<br />
der Länge des aktuellen Befehls (Operanden) bzw. von dem Sprungziel.<br />
¯ Ein Berechnungsschritt besteht aus den beiden Phasen: Befehlsholphase und<br />
Befehlsausführungsphase. Damit ergibt sich die Pipeline<br />
hole<br />
Befehl<br />
dekodiere<br />
Befehl<br />
führe<br />
Befehl aus<br />
In modernen Prozessoren werden die einzelnen Phasen der Pipeline durch<br />
getrennte Hardwarekomponenten durchgeführt. Dadurch ist eine verschränkte<br />
Ausführung von mehreren Befehlen möglich. Verschiedene Befehle können<br />
sich gerade in unterschiedlichen Phasen befinden, z.B. Befehl 1 wird<br />
gerade ausgeführt, während Befehl 2 dekodiert wird und während die<br />
17
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
Befehlsholeinheit gerade den Befehl 3 aus dem Arbeitsspeicher holt. Dadurch<br />
ist eine Beschleunigung des Ablaufes möglich. Besondere Aufmerksamkeit<br />
benötigen Sprünge, da sie die serielle Ausführung von Maschinenbefehlen<br />
unterbrechen.<br />
Animation Pipelining<br />
siehe online Version<br />
¯ Die Ausführung des Befehls erfolgt auf einer niedrigeren Abstraktionsebene,<br />
der Mikroebene.<br />
¯ Der Grundzyklus hat einen festen Zeittakt, der die Dauer eines Zyklus festlegt.<br />
2.3.3 E/A Architekturvarianten<br />
Die Ein-/Ausgabewerke können durch unterschiedliche Architekturvarianten<br />
realisiert werden. Dabei muss insbesondere das unterschiedliche Zeitverhalten<br />
der peripheren Geräte und der Komponenten wie Prozessor und Arbeitsspeicher<br />
berücksichtigt werden. Periphere Geräte sind um ein Vielfaches langsamer.<br />
E/A-Controller können entweder direkt an den Prozessorbus oder über einen<br />
getrennten Gerätebus an den Prozessorbus angeschlossen werden.<br />
¯ Speicherbasierte E/A<br />
Bei dieser Variante entsprechen E/A-Controller in ihrer Ansteuerung herkömmlichen<br />
Speicherbausteinen. Der Prozessor kann Register der E/A-Controller<br />
nicht von normalen Speicherzellen unterscheiden.<br />
Prozessor<br />
Arbeitsspeicher<br />
E/A Controller E/A Controller<br />
Durch normale Lese- und Schreiboperationen und ohne Einschränkungen<br />
bei den verwendeten Adressierungsarten kann auf die Register der E/A-<br />
Controller zugegriffen werden. Diese E/A-Architekturvariante wird bei der<br />
Modellmaschine MI verwendet.<br />
¯ E/A-Bus Controller<br />
Bei dieser Variante werden die peripheren Geräte nicht direkt an den schnellen<br />
Prozessorbus angeschlossen, sondern über einen separaten Gerätebus.<br />
18<br />
Bus
Schlichter, TU München 2.3. ARCHITEKTUR SEQUENTIELLER RECHNER<br />
Prozessor<br />
E/A Controller E/A Controller E/A Controller<br />
Arbeitsspeicher<br />
E/A-Bus<br />
Controller<br />
Prozessorbus<br />
Gerätebus<br />
Bekannte Gerätebussysteme sind PCI, ISA und IEEE 1394 (FireWire) im PC-<br />
Bereich. Der Bus-Controller kann eine Reihe von Grundfunktionen, z.B.<br />
die Bearbeitung von Interrupts und DMA-Aufträgen, für alle angeschlossenen<br />
Geräte-Controller übernehmen.<br />
2.3.4 Architektur der MI<br />
Die Modellmaschine MI basiert auf der real-existierenden VAX-Architektur (von<br />
Digital Equipment). Sie umfasst bis zu 4 Prozessoren; Adressbus und Datenbus<br />
haben jeweils 32 bit.<br />
Prozessor<br />
RK0<br />
Adressbus<br />
Datenbus<br />
Steuerbus<br />
Prozessor<br />
RK3<br />
Plattenspeicher 1<br />
Arbeitsspeicher<br />
Plattenspeicher 2<br />
E/A<br />
Controller1<br />
Terminal<br />
Drucker<br />
E/A<br />
Controller2<br />
E/A-Controller werden bei der MI auch als E/A-Prozessoren bezeichnet. Bei der<br />
MI hat jeder Prozessor eine Nummer 0 - 3. Der Bus kann jeweils nur von einem<br />
Rechnerkern betrieben werden. Die MI-Architektur ist eine Multiprozessor-<br />
19
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Architektur.<br />
¯ Die Prozessoren der MI (RK0 - RK3) haben jeweils 16 Register R0 -<br />
R15 mit jeweils 32 Bit. Das Register R15 ist der Befehlszähler PC. Das<br />
Register R14 ist der Kellerpegel SP. Der Kellerpegel dient zur Verwaltung des<br />
Kellerbereichs im Arbeitsspeicher bei Unterprogrammaufrufen. Daneben gibt<br />
es noch einige Sonderregister, z.B. für die Ansteuerung der aktuellen Seiten-<br />
Kacheltabelle. Moderne Prozessoren umfassen sehr viel mehr Register. Ziel ist<br />
die Reduzierung von Arbeitsspeicherzugriffen, um den Bus als Flaschenhals zu<br />
umgehen.<br />
¯ R14 und R15 sind vom Benutzer nicht frei verwendbar. Die übrigen Register<br />
können aus Sicht der MI frei verwendet werden.<br />
¯ R0 - R15 sind symbolische Adressen.<br />
2.4 Befehlsvorrat eines Rechners<br />
Das Verhalten eines Rechensystems R wird von den Aktionen bestimmt, die<br />
der Prozessor P von R ausführt, und dieses Verhalten wird gestaltet, indem die<br />
Befehle, die P ausführen soll, festgelegt und zur Ausführung bereitgestellt werden.<br />
In jedem Grundzyklus führt P eine atomare Aktion aus, die mit einem Befehl<br />
spezifiziert ist, und die eine Veränderung des Zustands von R bewirkt.<br />
2.4.1 Charakterisierung der Befehle<br />
Ein Befehl ist eine elementare Verarbeitungs- oder Berechnungsvorschrift. Als<br />
solche legt ein Befehl fest, was getan werden soll, und womit es getan werden<br />
soll; zudem legt ein Befehl, wie schon erklärt wurde, seinen Nachfolgerbefehl<br />
fest. Ein Befehl besteht aus einer Operations- und einer Operandenspezifikation.<br />
¯ Ein Befehlswort ist die binäre Darstellung eines Befehls in einem Rechner. Ein<br />
Befehlswort ist ein Paar<br />
– Operationsteil: was soll getan werden?<br />
– Operandenteil: womit soll es getan werden? Der Operandenteil wird oft<br />
auch als Adressteil bezeichnet, weil Operanden überwiegend mit Adressen<br />
spezifiziert werden.<br />
20
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
¯ Befehlsvorrat<br />
Der Befehlsvorrat (Instruktionssatz) eines Rechners ist die Menge der<br />
Befehle, die der Prozessor des Rechners ausführen kann. Aus der<br />
angegebenen Struktur von Befehlen ergibt sich, dass der Befehlsvorrat mit den<br />
jeweils zugelassenen Operationsspezifikationen und den ihnen zugeordneten<br />
Operandenspezifikationen festgelegt ist; dabei bedeutet zugelassen, dass der<br />
Prozessor die entsprechende Repräsentation als Befehlswort interpretieren und<br />
den Befehl ausführen kann.<br />
¯ Befehlsklassen<br />
Befehle des Befehlsvorrats lassen sich in 3 Hauptgruppen einteilen:<br />
1. Transportbefehle: dienen dem Austausch von Daten (Binärwörtern)<br />
zwischen Registern, Arbeitsspeicher und E/A-Geräten.<br />
2. Rechenbefehle: dienen zur Verknüpfung von Binärwörtern, z.B. Arithmetik-,<br />
Logik- und Schiebeoperationen.<br />
3. Steuerbefehle: dienen zur Steuerung der Reihenfolge der Befehlsausführungen,<br />
z.B. unbedingte und bedingte Sprünge.<br />
¯ Getrieben von den hardware- und softwaretechnischen Fortschritten und von<br />
Erweiterungen ihrer Einsatzgebiete entstanden in der Folgezeit Rechner mit<br />
wachsenden Befehlsvorräten und mit komplizierteren Befehlen. Dieser Trend<br />
verstärkte sich, als in den 60-er Jahren die Rechner für wissenschaftliche und<br />
für kommerzielle Anwendungen, die bis dahin nebeneinander entwickelt wurden,<br />
zu sog. Rechnerfamilien (Rechner mit unterschiedlichen Leistungsmerkmalen<br />
aber einem allen Familienmitgliedern gemeinsamen Befehlsvorrat)<br />
zusammengeführt wurden. Dieser Trend setzte sich allgemein bis in die<br />
80-er Jahre und darüber hinaus bis heute fort. Diese Rechner werden mit<br />
CISC (für Complex Instruction Set Computer) charakterisiert. Sie haben Befehlssätze<br />
mit 250-350 Befehlen (Operationen) und vielen, zum Teil komplizierten<br />
Adressierungsarten.<br />
In den 80-er Jahren begann die Entwicklung von Rechnern mit bewusst<br />
klein gewählten Befehlsvorräten, etwa 100 Befehlen, und wenigen, einfachen<br />
Adressierungsarten, die mit RISC (für Reduced Instruction Set Computer)<br />
charakterisiert werden. Zur Begründung für RISC wurden CISC-<br />
Befehlsstatistiken ausgewertet. Sie zeigten, dass 95% der ausgeführten Befehle<br />
lediglich 25% der Befehle des Befehlsvorrats nutzen. Dieses Ergebnis ist<br />
insbesondere darauf zurückzuführen, dass nur ein kleiner Teil der Maschinenprogramme,<br />
die ausgeführt werden, von Hand als hardwarenahe Programme<br />
entwickelt werden; sie sind vielmehr die Ergebnisse von Transformationen von<br />
Programmen, die in höheren Programmiersprachen entwickelt werden.<br />
21
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Diese Argumente, die damals zur Begründung für RISC benutzt wurden, gelten<br />
heute verstärkt.<br />
– CISC = Complex Instruction Set Computer; ca. 250 - 350 Operation; z.B.<br />
Intel Pentium, Modellmaschine MI<br />
– RISC = Reduced Instruction Set Computer; ca. 100 Operation; z.B. Sun<br />
Sparc<br />
– Während CISC komplexe Befehle unterstützt, werden diese bei RISC durch<br />
eine Menge einfacher Befehle ersetzt;<br />
ÁË Ð<br />
<br />
ÊÁË Ð<br />
ÄÇ Ê<br />
ÄÇ Ê<br />
Ê Ê<br />
ËÌÇÊ Ê <br />
Die Wirkung ist S[a3] = S[a1] + S[a2]. Dabei bezeichnet S[a3] die<br />
Speicherzelle, deren Adresse a3 ist. Der Inhalt der mit a1 und a2<br />
adressierten Speicherzellen wird addiert und der Speicherzelle mit Adresse<br />
a3 zugewiesen. Man spricht im Zusammenhang mit RISC oft auch von einer<br />
Load und Store Architektur.<br />
2.4.2 Aufbau Maschinenbefehle<br />
Ein Maschinenbefehl gliedert sich in einen Operations- und einen Operandenteil<br />
(am Beispiel der MI); der Operandenteil kann u.U. fehlen;<br />
mnemonische Zeichen<br />
Datentyp/Kennung<br />
(Operationsteil, Operandenspezifikation)<br />
1-4 teilig mit<br />
Ausdrücken<br />
Å× ÒÒÐ ÇÔÖØÓÒ×ØÐ <br />
ÇÔÖØÓÒ×ØÐ ßÇÔÖÒÒ×ÔÞ <br />
¯ Der Operationsteil spezifiziert die Operation, die Kennung, und die Anzahl<br />
der Operanden. Letztere Information wird vom Assembler automatisch<br />
hinzugefügt; sie muss nicht vom Programmierer angegeben werden. Die<br />
Kennung ist wichtig bei den unterschiedlichen Adressierungsarten. Die<br />
Kennung definiert den Typ und die Länge der Operanden (LOP) in Bytes:<br />
22
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
B Byte: 1<br />
H Halbwort: 2<br />
W Wort: 4<br />
F 32 bit Gleitpunktzahl: 4<br />
D 64 bit Gleitpunktzahl: 8<br />
Bei den Kennungen wird zwischen W und F unterschieden, weil bei bestimmten<br />
Operationen die Kennung nicht nur eine Angabe über die Länge der Operanden,<br />
sondern auch über die Art des Operanden enthalten muss. Beispielsweise ist<br />
für das Kopieren von Daten die Angabe der Länge ausreichend, aber bei der<br />
Addition muß auch noch die Angabe, ob ganze Zahlen oder Gleitpunktzahlen<br />
addiert werden sollen, dazukommen.<br />
¯ Die Operanden werden entweder direkt spezifiziert oder durch eine Vorschrift<br />
zur Bestimmung (Adressrechnung) angegeben. Bei der Modellmaschine MI<br />
lautet die Reihenfolge der Operanden stets so, dass das Ziel als letztes<br />
angegeben wird.<br />
¯ Maschinenadressen<br />
Der Arbeitsspeicher besteht aus einer Folge von fortlaufend nummerierten<br />
Bytezellen, denen jeweils Adressen zugeordnet sind. Man spricht hier von<br />
Maschinenadressen.<br />
Speicher<br />
0 7 0 7<br />
Adressraum a a+1<br />
Die Nummerierung beginnt bei 0; die höchste Nummer hängt vom Ausbau des<br />
physikalischen Speichers ab. Die Nummer der Bytes entsprechen jeweils den<br />
Maschinenadressen. Falls bei der MI eine Dateneinheit angesprochen wird,<br />
die mehr als ein Byte umfasst, dient immer die Adresse des ersten Bytes als<br />
Adresse der Dateneinheit. Die nachfolgenden Bytes der Dateneinheit haben<br />
immer höhere Adressen; sie werden jedoch nicht explizit spezifiziert. Eine<br />
Speicherzelle umfasst jeweils ein Byte; die Bits einer Speicherzelle b werden<br />
mit b 0 bis b 7 bezeichnet (von links nach rechts betrachtet).<br />
– Die Menge der möglichen Maschinenadressen ist der Maschinenadressraum.<br />
Prozesse sind ja Instanzen eines ablaufenden Programms. Prozesse<br />
haben ihre eigenen Adressen mit sogenannten Prozess- bzw. Programmadressen.<br />
Zu einem späteren Zeitpunkt wird eine Abbildung zwischen Pro-<br />
23
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
grammadressen und Maschinenadressen vorgestellt, die Seitenadressierung.<br />
Sie ist ein wichtiger Teil des Betriebssystems.<br />
– S[a] ist der Wert der durch a adressierten Speicherzelle.<br />
– Über den Adressbus des Rechners werden Maschinenadressen übertragen.<br />
– Bei der Modellmaschine MI ist eine gültige Maschinenadresse<br />
£ einem Arbeitsspeicherbereich, oder<br />
£ einem Register eines EA-Prozessors (EA-Register) zugeordnet. Falls<br />
beides nicht zutrifft, ist die Maschinenadresse ungültig. Die Verwendung<br />
einer ungültigen Adresse führt zu einem Speicherschutzalarm, und damit<br />
zu einer Unterbrechung des in Ausführung befindlichen Prozesses. Die<br />
normalen MI-Maschinenbefehle können sowohl zur Adressierung des<br />
Arbeitsspeichers als auch zur Adressierung der EA-Register verwendet<br />
werden.<br />
¯ Aufbau des Maschinenadressraums der MI<br />
24
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
¯ Operandenspezifikation<br />
H'0000 0000'<br />
H'000F FFFF'<br />
H'0010 0000'<br />
H'1FFF FFFF'<br />
H'2000 0000'<br />
H'3FFF FFFF'<br />
H'4000 0000'<br />
H'FFFF FFFF'<br />
1 MB Arbeitsspeicher<br />
Ausbau<br />
Arbeitsspeicher<br />
Erweiterungen<br />
(1 MB - 512 MB)<br />
Bereich, in dem über<br />
festgelegte Adressen<br />
die Register der EA-<br />
Prozessoren<br />
zugänglich sind<br />
keine Zuordnung<br />
ÇÔÖÒÒ×ÔÞ <br />
ÖØ Ö××ÖÙÒ <br />
ÙÒÑØØÐÖÖ ÇÔÖÒ <br />
Ê×ØÖÖ××ÖÙÒ <br />
ÖÐØÚ Ö××ÖÙÒ <br />
ÒÖØ Ö××ÖÙÒ <br />
ÒÞÖØ ÖÐØÚ Ö××ÖÙÒ <br />
ÒÞÖØ ÒÖØ Ö××ÖÙÒ <br />
ÃÐÐÖÖ××ÖÙÒ<br />
Im weiteren wird die Notation der Modellmaschine MI verwendet.<br />
– Direkte Adressierung<br />
Man spricht in diesem Zusammenhang auch von einer absoluten Adresse.<br />
Der Operand zeigt auf eine Stelle im Speicher, wo der Wert des Operanden<br />
25
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
gespeichert ist.<br />
ÖØ Ö××ÖÙÒ <br />
Ù×ÖÙ Ö ÒÞ Ð <br />
ÁÒØØÓÖ ß ßÚÓÖÞ ÒÐÓ× ÒÞ Ð<br />
Ò <br />
Der Ort des Operanden wird durch seine Adresse angegeben; dabei sind<br />
symbolische Adressen (Identifikatoren) zugelassen. Symbolische Adressen<br />
sind frei wählbare Bezeichner für Speicherzellen. Der Definitionsbereich<br />
einer symbolischen Adresse ist das Programm, in dem es definierend<br />
auftritt. Ein Bezeichner tritt in einer Operandenspezifikation eines Befehls<br />
anwendend auf.<br />
£ Beispiele<br />
MOVE H 107, a + 315 Wirkung: S[a+315] = S[107]<br />
MOVE H 205 + 7, H'F1' Wirkung: S[241] = S[212]<br />
Es werden jeweils Halbwörter betrachtet, d.h. es werden jeweils 2 Byte<br />
transportiert.<br />
ADD W 101, 35, b Wirkung: S[b] = S[101] + S[35]<br />
Hier werden die Operanden als Wörter, d.h. mit 4 Byte betrachtet<br />
– Unmittelbarer Operand<br />
Ein unmittelbarer Operand ("immediate operand") steht im Befehl selbst.<br />
ÙÒÑØØÐÖÖ ÇÔÖÒ <br />
Á ßÐØÔÙÒØÞÐ Ù×ÖÙ Ö ÒÞ Ð<br />
Für unmittelbare Operanden ist keine Adresse im Arbeitsspeicher oder die<br />
Adresse eines Registers definiert.<br />
£ Beispiele<br />
MOVE B I 2, a2 Wirkung: S[a2] = 2 als Byte<br />
ADD W I H'FFOO', a2 , a3 Wirkung: S[a3] = H'FFOO' + S [a2]<br />
– Registeradressierung<br />
Die Register des Prozessors sind ausgezeichnete Speicherplätze. Operanden<br />
können aus Registern kommen und Ergebnisse können in Registern abgelegt<br />
werden. Da Zugriffe auf Register wesentlich schneller (es ist kein Transfer<br />
über den Bus notwendig) erfolgen als Zugriffe auf den Arbeitsspeicher,<br />
werden Zwischenergebnisse oder häufiger benötigte Werte tunlichst in<br />
Registern gehalten. Die Register werden durch einen symbolischen<br />
Registernamen bezeichnet. Moderne Architekturen haben sehr viel mehr<br />
Register als die Modellmaschine MI. Der Bus wird gerne als der Flaschenhals<br />
der von Neumann Architektur bezeichnet. Um dieses Problem weiter<br />
zu reduzieren, werden in heutigen Architekturen neben einer großen<br />
Registermenge zunehmend auch Prozessor-Caches verwendet.<br />
26
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Ê×ØÖÖ××ÖÙÒ ÊÜ<br />
ÊÜ Ê Ê Ê Ê Ê ËÈÈ<br />
Es wird der Wert des angesprochenen Registers verwendet.<br />
£ Es gilt: SP = R14 und PC = R15.<br />
£ Register werden auch im Zusammenhang mit der relativen, der indirekten<br />
und der indizierten Adressierung benutzt.<br />
– Relative Adressierung<br />
ÖÐØÚ Ö××ÖÙÒ <br />
ßÙ×ÖÙ Ö ÒÞ Ð ÊÜ<br />
ÒÞÖØ ÖÐØÚ Ö××ÖÙÒ <br />
ÖÐØÚ Ö××ÖÙÒÁÒÜÒ<br />
ÁÒÜÒ ÊÜ<br />
Die Funktion !adr entspricht einem Inhaltsoperator. Die Relativadressierung<br />
ist mit der Basis Rx und der angegebenen ganzen Zahl als<br />
Relativadresse zu verstehen.<br />
£ Steht auf Operandenstelle der Ausdruck "z+!Rx", so wird die Zahl<br />
z zum Wert des Registers Rx addiert und das Ergebnis als Adresse<br />
interpretiert, d.h. Operand = S[z + Wert von Rx].<br />
£ Steht auf Operandenstelle der Ausdruck "z+!Rx/Ry/", so werden<br />
die Zahl z, der Wert des Registers Rx und LOP * Wert des Registers<br />
Ry addiert und das Ergebnis als Adresse interpretiert. LOP ist die<br />
Länge des Operanden, z.B. 1 für B, 2 für H und 4 für W.<br />
£ Die relative Adressierung eignet sich sehr gut für den Zugriff von Feldern.<br />
£ Beispiel<br />
Es sei eine Feld a mit den Elementen a[1], a[2], ..., a[10] in<br />
aufeinanderfolgenden Speicherzellen abgelegt. Die Elemente sind von<br />
der Kennung W, d.h. 4 Byte lang. Das Element a[j] soll in Register R5<br />
gebracht werden. Die Adresse des ersten Elements a[1] stehe bereits in<br />
Register R0 und der Index j des gewünschten Elements in R1. Zur Lösung<br />
wird ein Befehl benötigt:<br />
ÅÇÎ Ï Ê Ê Ê<br />
-4 muß subtrahiert werden, da das Feld ab 1 durchgezählt ist; der Ausdruck<br />
R0 /R1/ verweist auf das Element j+1 und nicht auf das Element j.<br />
27
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Speicher<br />
R0<br />
R1<br />
a[1] a[2] a[j] a[10]<br />
– Indirekte Adressierung<br />
Bei der indirekten Adressierung (auch Adresssubstitution) entsteht in einem<br />
ersten Schritt die Adresse einer Speicherzelle. In dieser Speicherzelle steht<br />
nicht direkt der gewünschte Wert, sondern die Adresse des gewünschten<br />
Wertes. Man substituiert also beim Holen des Operanden zweimal.<br />
ÒÖØ Ö××ÖÙÒ <br />
ÖÐØÚ Ö×× ÊÜ<br />
ÒÞÖØ ÒÖØ Ö××ÖÙÒ <br />
ÒÖØ Ö××ÖÙÒÁÒÜÒ<br />
ÁÒÜÒ ÊÜ<br />
Die indirekte Adressierung wurde erstmals in der PERM (Programmgesteuerte<br />
Elektronische Rechenanlage München) verwirklicht (Erfinder:<br />
Schecher). !!Rx ist als Abkürzung für !(!Rx) zu sehen.<br />
£ Steht auf Operandenstelle der Ausdruck "!(z+!Rx)", so gilt:<br />
S[ S[z + Wert von Rx]]<br />
Die Zahl z wird zum Wert des Registers Rx addiert und das<br />
Ergebnis als Adresse a1 interpretiert. Der Inhalt der mit a1 adressierten<br />
Speicherzelle wird als Adresse einer Speicherzelle interpretiert, deren<br />
Wert der gesuchte Operand ist.<br />
£ Steht auf Operandenstelle der Ausdruck "!(z+!Rx)/Ry/", so gilt:<br />
S[ S[z + Wert von Rx] + LOP*Wert von Ry]<br />
LOP ist wieder die Länge des Operanden, z.B. 1 für B, 2 für H und 4 für<br />
W.<br />
£ Beispiel<br />
ÅÇÎ Ï Þ Ê Ê Ê<br />
Schritte für die Adressrechnung<br />
1. Relativadresse: a1:= z + (Wert in R3);<br />
2. dann indirekte Adresse: a2 := S[a1];<br />
3. dann Indizierung: a3:= a2 + (Wert in R4) * LOP;<br />
4. dann ab Operandenadresse a3 den (Wort-)Operand w holen: w := S[a3];<br />
anschließend wird w in Register R5 gespeichert.<br />
28<br />
R5
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
a1<br />
a2<br />
a3<br />
a2<br />
w<br />
Wert von<br />
R4*LOP<br />
– Kelleradressierung<br />
Die Kelleradressierung dient zur Speicherung von Daten vor allem bei<br />
Unterprogrammaufrufen und der Blockstrukturen von Programmen.<br />
ÃÐÐÖÖ××ÖÙÒ ÊÜ ÊÜ<br />
Diese Adressierungsart ist vorwiegend für den Zugriff auf kellerartig<br />
angeordnete Elemente gedacht. Der Keller wächst von größeren Adressen<br />
zu kleineren Adressen. Der Kellerpegel zeigt auf die Adresse des letzten<br />
belegten Bytes im Keller, dies ist auch die Adresse des letzten Elementes<br />
im Keller. Der aktuelle Kellerpegel befindet sich in dem angegebenen<br />
Register (meist wird SP als Register verwendet). Das Register wird entweder<br />
vor der Adressberechnung dekrementiert oder nach der Adressberechnung<br />
inkrementiert, und zwar um den Wert LOP gemäß Kennung im Befehl.<br />
29
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Adresse<br />
kleiner<br />
größer<br />
wachsender<br />
Keller<br />
belegter Keller<br />
-!Rx entspricht der Kelleroperation push<br />
!Rx+ entspricht der Kelleroperation pop<br />
2.4.3 Befehle der MI<br />
-!<br />
!+<br />
Nachfolgend werden exemplarisch einige MI Befehle kurz vorgestellt. Eine<br />
detaillierte Darstellung der verfügbaren Maschinenbefehle befindet sich im MI<br />
Manual.<br />
Transportbefehle<br />
ÅÇÎ ßÀÏ ÏÖÙÒ Ë ℄ Ë ℄<br />
ÅÇÎÆ ßÀÏ ÏÖÙÒ Ë ℄ Ë ℄<br />
ÄÊ ßÀÏ ÏÖÙÒ Ë ℄ <br />
Der Befehl MOVE B R1, 201 transportiert das letzte Byte des Registers R1<br />
nach der Speicherzelle 201. Bei MOVEN wird eine negative Zahl gebildet;<br />
hier ist sofort ersichtlich, daß W und F unterschieden werden müssen, weil -<br />
x für eine ganze Zahl x anders auszuführen ist als für eine Gleitpunktzahl x.<br />
Weitere Transportbefehle sind PUSHR, der die Register R0 bis R14 der MI in den<br />
Keller speichert (wobei der Kellerpegel, d.h. SP, jeweils erhöht wird), sowie der<br />
entsprechende Befehl POPR, der die Werte vom Keller entfernt und in die Register<br />
lädt (der Kellerpegel wird erniedrigt). Die Register werden in der Reihenfolge<br />
R14 - R0 im Keller abgelegt, und in umgekehrter Reihenfolge aus dem Keller<br />
entfernt.<br />
30
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
Rechenbefehle<br />
ÇÊ ßÀÏ ß <br />
ÏÖÙÒ Ë ℄ Ë ℄ ÓÖ Ë ℄ ØÛ× ÐÓ× <br />
ÇÔÖØÓÒ<br />
ßÀÏ ß <br />
ÏÖÙÒ Ë ℄ Ë ℄ Ë ℄<br />
Logische Operationen wie z.B. OR oder ANDNOT werden gerne verwendet, um<br />
einzelne Bits zu setzen oder mit Hilfe einer Maske einzelne Bits auszublenden.<br />
¯ Shiftbefehl<br />
ËÀ ÏÖÙÒ Ë ℄ Ë ℄ ÚÖ× ÓÒ<br />
ÙÑ Ë ℄ ÇÔÖÒÒ Ñ Ï ÓÖÑØ<br />
– S[a1] 0<br />
Verschiebung nach links um S[a1] Stellen. Von rechts werden 0-Bits<br />
nachgezogen. Nach links hinausgeschobene Bits gehen verloren.<br />
£ Arithmetische Interpretation: Multiplikation von S[a2] mit 2 S[a1]<br />
(Überlauf möglich).<br />
– S[a1] 0<br />
Verschiebung nach rechts um |S[a1]| Stellen. Von links werden vorzeichengleiche<br />
Bits nachgezogen (d.h. der Wert von b bestimmt, welche Bitwerte<br />
0<br />
von links nachgezogen werden). Nach rechts hinausgeschobene Bits gehen<br />
verloren.<br />
£ Arithmetische Interpretation: Division von S[a2] durch 2 |S[a1]|<br />
– S[a1] = 0<br />
keine Verschiebung; Wirkung wie MOVE W a2, a3<br />
¯ Rotationsbefehl<br />
ÊÇÌ ÏÖÙÒ Ë ℄ Ë ℄ ÖÓØÖØ ÙÑ<br />
Ë ℄ ÇÔÖÒÒ Ñ Ï ÓÖÑØ<br />
Während man SH auch als arithmetischen Shift bezeichnet, nennt man ROT<br />
einen logischen Schiebebefehl.<br />
– S[a1] 0<br />
Verschiebung nach links um S[a1] Stellen. Nach links hinausgeschobene Bits<br />
werden von rechts nachgezogen.<br />
31
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
– S[a1] 0<br />
Verschiebung nach rechts um |S[a1] |Stellen. Nach rechts hinausgeschobene<br />
Bits werden von links nachgezogen.<br />
– S[a1] = 0<br />
keine Verschiebung; Wirkung wie MOVE W a2, a3<br />
¯ Anwendungsbeispiel 1<br />
Aufgabe: prüfe, ob eine ganze Zahl x im W Format gerade ist. Prüfung, ob<br />
x gerade ist, durch Prüfung des Bit x[b ] = 0; Ungerade bedeutet, dass das<br />
31<br />
letzte Bit gleich 1 ist. Eine Möglichkeit ist z.B. das Ausblenden aller Bits bis<br />
auf b ; dann Abfrage mit JGE (Jump Greater Equal), d.h. ist die Bedingung<br />
31<br />
größer/gleich 0.<br />
ÊÇÌ Á Ü Ê ÙÑ Ò Ö Ø× Ñ ÃÖ× ÛÖ ÞÙÑ<br />
ÎÓÖÞ Ò<br />
 ×ÔÖÒ ÐÐ× Ð ÔÓ×ØÚ ×Ø ÁÒ<br />
×Ñ ÐÐ ×Ø Ð Ö<br />
¯ Anwendungsbeispiel 2<br />
Aufgabe: In Abhängigkeit eines Zeichenausschnitts im Zeichen z sollen<br />
entsprechende Operationen ausgeführt werden. Das entspricht einer Fallunterscheidung.<br />
Ein Beispiel dafür sind in höheren Programmiersprachen die sogenannten<br />
case-Anweisungen.<br />
Zeichen z<br />
b 0<br />
b 1<br />
b 2<br />
b 3<br />
b 4<br />
k sei der betrachtete Ausschnitt mit k = 0,...,7<br />
Die zugehörigen Bearbeitungssequenzen stehen ab den Adressen: f0, f1, ...,<br />
f7, d.h. in Abhängigkeit des Wertes von k wird an die entsprechende Stelle f i<br />
gesprungen. Realisierung mit Hilfe einer Sprungkaskade.<br />
– Vereinbarung der Sprungziele<br />
Ï Ö×× Ö ÖØÙÒ××ÕÙÒÞ <br />
Ï Ö×× Ö ÖØÙÒ××ÕÙÒÞ <br />
Ï Ö×× Ö ÖØÙÒ××ÕÙÒÞ <br />
32<br />
b 5<br />
b 6<br />
b 7
Schlichter, TU München 2.4. BEFEHLSVORRAT EINES RECHNERS<br />
– Programmausschnitt<br />
ÄÊ Ï Ê ×ØÞ Ê×ØÖ Ê ÞÙÖ <br />
ÅÇÎ Þ Ê Ê ℄ Ê <br />
℄ Þ<br />
ËÀ Á Ê Ê ÖØÑØ× Ö ËØ Ò Ö Ø×<br />
ÙÑ Þ Ù ÈÓ×ØÓÒ ÞÙ<br />
ÔÐØÞÖÒ<br />
ËÀ Á Ê Ê ËØ Ò ÐÒ× ÙÑ ÛÐ× <br />
ÏÓÖØÖÒÞ ÞÙ ÖÐØÒ ×Ø<br />
ÙÑ ÄÇÈ <br />
ÅÇÎ Ê ×× Ö ËÔÖÙÒÞÐÖ××Ò Ò <br />
Ê<br />
Ï Ê Ê Ö×× Ö ÖØÙÒ××ÕÙÒÞ Ò<br />
Ê<br />
ÂÍÅÈ Ê ×ÔÖÒ ÞÙÖ ÖØÙÒ××ÕÙÒÞ<br />
Steuerbefehle<br />
I.a. werden Befehle in der Reihenfolge ihrer Programmaufschreibung ausgeführt.<br />
Sprünge spezifizieren einen Nachfolgebefehl, der i.a. in der Programmaufschreibung<br />
nicht unmittelbar auf den Sprungbefehl folgt. Es wird zwischen unbedingten<br />
und bedingten Sprüngen unterschieden.<br />
¯ ÂÍÅÈ unbedingter Sprung an die Adresse a1; der Befehlszähler PC wird mit<br />
a1 besetzt<br />
¯ Bedingungen<br />
Die Bedingungen von bedingten Sprüngen sind durch die jeweiligen Werte von<br />
vier speziellen 1-Bit-Registern bestimmt.<br />
N negativ<br />
Z zero (0)<br />
V Überlauf (overflow)<br />
C Übertrag (carry)<br />
Die Register können zu dem Spezialregister CC = (N, Z, V, C) zusammengefasst<br />
werden.<br />
– Die Ausführung des Befehls führt mit x = S[a1]+S[a2] zu<br />
folgender Besetzung von CC<br />
CC = (x 0, x == 0, ?, ?) wobei sich die Werte für ? gemäß der<br />
Arithmetik ergeben.<br />
33
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Es gilt dabei S[a3] = x. Wenn x 0 ist, ergibt sich N = 1, sonst gilt N=0.<br />
Falls x gleich 0 ist, wird Z mit 1 besetzt.<br />
– Die Ausführung des Befehls ÅÇÎ Ï <br />
Besetzung von CC<br />
führt mit x = S[a1] zu folgender<br />
CC=(x0, x == 0, 0, C), d.h. der Wert von C bleibt unverändert.<br />
– Die Ausführung des Befehls ÅÈ Ï führt mit x = S[a1] und y = S[a2]<br />
zu folgender Besetzung von CC. ÅÈ ist ein Vergleichsbefehl, der die Werte<br />
der mit a1 und a2 bezeichneten Operanden vergleicht.<br />
CC = (x y, x == y, V, C), d.h.<br />
unverändert.<br />
die Werte von V und C bleiben<br />
¯ bedingte Sprünge<br />
Die Bedingungen bzgl. des Sprungs beziehen sich auf die Werte im<br />
Spezialregister CC. Die nachfolgende Liste von Sprüngen ist nur eine Auswahl<br />
der in der MI verfügbaren Sprünge (siehe MI Manual). Bei den 1-Bit-Registern<br />
geht es insbesondere darum, ob das entsprechende Bit gesetzt ist oder nicht.<br />
– Beispiele von Sprungbefehlen<br />
ÂÉ ÒÙÒ <br />
ÂÆ ÒÙÒ <br />
 ÒÙÒ Æ ÓÖ <br />
ÂÎ ÒÙÒ Î<br />
 ÒÙÒ <br />
Animation MI Programm<br />
siehe online Version<br />
2.5 Hardwarenahe Programme<br />
Zur Erleichterung werden hardwarenahe Programme für ein Rechensystem nicht<br />
als ausführbare Maschinenprogramme (Folge von Befehlswörtern) realisiert,<br />
sondern mit Hilfe von Assemblerprogrammen.<br />
2.5.1 Definitionen<br />
Maschinenschnittstelle<br />
Als Maschinenschnittstelle bezeichnet man die Gesamtheit aller Datenobjekte und<br />
Operationen der reinen Hardwarearchitektur (auch Programmierschnittstellen der<br />
Maschine).<br />
34
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
¯ Folge von Maschinenbefehlen ist auf dieser Ebene eine Folge von Binärzeichen.<br />
Auf dieser Ebene müsste man insbesondere die Befehle der Maschine als reine<br />
Folge von Binärzeichen (Befehlswörter) schreiben; diese Schnittstelle ist sehr<br />
programmier-unfreundlich.<br />
Assemblerschnittstelle<br />
Die Assemblerschnittstelle ist die eigentliche maschinennahe (konkrete) Programmierschnittstelle.<br />
Sie erlaubt, alle Befehle, Adressen und Datenobjekte der reinen<br />
Hardware darzustellen.<br />
¯ Verwendung von Namen für Adressen und Operationen. Assemblersprachen<br />
sind eine geringfügige Erweiterung von reinen Maschinensprachen. Zur<br />
bequemeren Nutzung werden u.a. symbolische Adressen (z.B. Identifikatoren)<br />
und symbolische Namen für Operationen (z.B. ADD für Addition) verwendet.<br />
Für die Beschreibung der MI (insbesondere der MI Befehle) haben wir bereits<br />
diese Schnittstelle zugrunde gelegt.<br />
Assembler<br />
Ein Assembler ist ein Programm, das die Aufgabe hat,<br />
1. Assemblerbefehle in Maschinencode zu transformieren,<br />
2. symbolischen Namen Maschinenadressen zu zuweisen, sowie<br />
3. ein Objektprogramm zu erzeugen.<br />
2.5.2 Programmaufbereitung<br />
Hier beschäftigen wir uns mit dem Programmieren auf der Assemblerschnittstelle.<br />
Ausgangspunkt sind Assembler-Programme (z.B. MI-Programme), und wir<br />
versuchen zu klären, was ist noch zu tun, um ein ausführfähiges Programm<br />
im Arbeitsspeicher zu haben. Wir wollen eine grobe Vorstellung der<br />
Funktion eines Assemblers, Binders und Laders vermitteln. Binder/Lader<br />
sind i.d.R. Bestandteil des Betriebssystems. Hier steht nicht die Konstruktion<br />
solcher Komponenten (Systemprogrammierung), sondern deren Aufgaben und<br />
Funktionen im Vordergrund.<br />
35
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Programm<br />
z.B. Java Programm<br />
Assembler<br />
Programm<br />
Compiler<br />
Assembler<br />
Bindemodul Bindemodul<br />
Lademodul<br />
Maschinenbefehle mit<br />
relativen Adressen<br />
Maschinenprogramm im<br />
Arbeitsspeicher<br />
Binder<br />
Lader<br />
symbolische Bezüge nach<br />
außen<br />
offene Referenzen<br />
In der Vorlesung werden nur allgemeine Assembler-Funktionen behandelt. Für<br />
den MI-Assembler gilt, dass er die Aufgaben Assemblieren und Binden in sich<br />
vereinigt, also ein ausführbares Ladeobjekt erstellt. (vgl. MI-Handbuch, Kapitel<br />
4).<br />
2.5.3 MI Assemblerprogramm<br />
Ein Assemblerprogramm besteht aus einer Menge von Segmenten, die jeweils<br />
durch ein Trennzeichen (z.B. Neuzeile) voneinander getrennt sind (siehe MI<br />
Manual für die Grammatik des MI Assemblers).<br />
36
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
×ÑÒØ ×ÑÒØÒÑ Ë ß<br />
ÐÖ×× ÌÖÒÒÞ<br />
ßßÅÖ ÒÛ×ÙÒÌÖÒÒÞ<br />
ÐÖ×× ÚÓÖÞ ÒÐÓ× ÒÞ Ð<br />
ÅÖ ÆÑ ßÌÖÒÒÞ<br />
ÒÛ×ÙÒ ××ÑÐÖ×ØÙÖÙÒ <br />
Å× ÒÒÐ ØÒÒØÓÒ<br />
ÌÖÒÒÞ ßÃÓÑÑÒØÖÆÙÞÐ<br />
Die Assemblersteuerung enthält Anweisungen an den Assembler, z.B. die<br />
Bekanntmachung von Variablen in anderen Segmenten durch die Importund<br />
Exportanweisungen. Ablageadressen sind hier als Programmadressen zu<br />
verstehen. Anwenderprogramme arbeiten mit virtuellen Speicheradressen, die<br />
bei der Ausführung des Programms in Maschinenadressen umgewandelt werden,<br />
d.h. der virtuelle Arbeitsspeicher wird auf den realen Arbeitsspeicher abgebildet.<br />
Die Segmentierung dient der Einteilung eines Programms in Einheiten (genannt<br />
Segmente) aus Benutzersicht (z.B. für gleichartige Zugriffsrechte!).<br />
¯ Komponenten eines systemnahen Programms<br />
Ein systemnahes Programm besteht aus drei Komponenten: dem Anweisungsteil,<br />
dem Datenteil und dem Registerteil.<br />
im Arbeitsspeicher<br />
im Prozessor<br />
Datenteil<br />
Anweisungsteil<br />
Registerteil<br />
Variable<br />
Programmcode<br />
invariant<br />
Hilfsvariable<br />
Der Anweisungs- und Datenteil sind explizit definiert, während der Registerteil<br />
implizit für jedes Programm existiert. Die implizite Definition bedeutet, dass<br />
die Registerwerte (mit Ausnahme des PC) bei Ablaufbeginn undefiniert sind.<br />
Die Anfangswerte des Datenteils werden i.a. durch das Programm vorgegeben.<br />
Der Anweisungs- und Datenteil werden je nach Art der Daten im Keller bzw.<br />
auf der Halde gespeichert.<br />
37
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
2.5.4 Assembler Grundfunktionen<br />
Ein Assembler muss aus den Assemblerbefehlen den entsprechenden Maschinencode<br />
erzeugen, und anschließend das Objektprogramm generieren, wobei symbolische<br />
Namen auf Maschinenadressen abgebildet werden.<br />
Erzeugen von Maschinencode<br />
Beispiel<br />
Ö×× ÅÖ Ð<br />
Ï ÁÒÜ Ê<br />
ÁÒÜ <br />
¯ Aktionen bei der Erzeugung<br />
– Maschinencode für Operationsteil, z.B. in MI: Assemblerbefehl ADD W<br />
wird codiert als H'C1', falls nur 2 Operandenspezifikationen folgen.<br />
– Maschinencode für Operandenspezifikationen, z.B. in MI: R6 codiert als<br />
H'56'<br />
– Konvertieren von symbolischen Namen in Maschinenadressen, z.B. symbolischer<br />
Name Index auf Adresse 1033 abbilden<br />
– Konstruktion eines Maschinenbefehls im korrekten Format<br />
– Konvertieren von Konstanten in interne Maschinen-Repräsentationen z.B.<br />
EOF in H'454F46'. Hier ist die Zeichenkette "EOF" gedacht, und nicht endof-file.<br />
– Assembler-Steuerungsanweisung: Assembler Direktiven, z.B. bei MI RES,<br />
werden vom Assembler nicht transformiert, sondern interpretiert, z.B. als<br />
Auftrag, Speicherplatz zu reservieren.<br />
¯ Problem<br />
Abarbeiten des Quellprogramms Zeile für Zeile. Befehle können jedoch<br />
Vorwärts-Referenzen enthalten, z.B. im Beispiel Definition des symbolischen<br />
Namens Index erst nach Nutzung. Die korrekte Adresse der Vorwärts-Referenz<br />
ist bei dieser Vorgehensweise beim ersten Auftreten der Referenz noch nicht<br />
bekannt und es kann noch kein korrekter Code erzeugt werden.<br />
– Lösung<br />
Assembler macht 2 (oder mehr) Läufe (siehe Seite 40) (engl. "pass") über<br />
das Assemblerprogramm.<br />
38
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
£ 1-ter Lauf: Zuordnen von Maschinenadressen<br />
£ 2-ter Lauf: Codierung, d.h. Erzeugung der Maschinenbefehle<br />
Erzeugen des Objektprogramms<br />
Der Assembler schreibt den generierten Maschinencode in ein Objekt-<br />
Programm/Ladeobjekt (z.B. ".o" oder ".obj" Datei), das dann in den Speicher<br />
geladen und ausgeführt werden kann. Ein einfaches Objekt-Programm enthält unterschiedliche<br />
Klassen von Einträgen (z.B. gekennzeichnet mit einem speziellen<br />
Buchstaben), die unterschiedliche Informationen für den Binder/Lader zur Verfügung<br />
stellen.<br />
¯ Header Informationen<br />
Name des Programms, Startadresse (Hex), Länge des Programms (Hex),<br />
Einträge z.B. mit H gekennzeichnet. Bei MI enthält die erste Zeile des<br />
Objektprogramms: Startadresse, Anzahl der Zeichen in Segmentnamen,<br />
Segmentname<br />
¯ Text<br />
Maschinencode und Daten, Einträge z.B. mit T gekennzeichnet. Bei MI: keine<br />
extra Kennung und Code wird zeilenweise im Ladeprogramm gespeichert.<br />
¯ Ende<br />
Ende des Programms und Adresse der ersten auszuführenden Instruktion (Hex),<br />
Einträge z.B. mit E gekennzeichnet. Bei MI: letzte Zeile mit einem Punkt in<br />
erster Spalte.<br />
¯ Beispiel eines Ladeobjekts<br />
××ÑÐÖ Ó<br />
Ì×Ø Ë<br />
ÅÇÎ Ï Á À ËÈ<br />
ÂÍÅÈ ×ØÖØ<br />
ÞÙÖ× ÄÓØ<br />
Ì×Ø<br />
<br />
<br />
ÛØÖÖ Å× ÒÒ Ó<br />
Die erste Zeile spezifiziert die Startadresse H'0, die Länge 4 des Segmentnamens<br />
sowie den Segmentnamen selbst. Die 2. Zeile repräsentiert den Befehl:<br />
39
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
MOVE W I H'10000, SP. Die 3. Zeile entspricht JUMP start; die Adresse von<br />
start ergibt sich dabei durch AF19.<br />
Datenstrukturen des Assemblers<br />
Für die Transformation benötigt der Assembler im wesentlichen 2 Tabellen: eine<br />
Objekt-Code Tabelle und eine Symboltabelle. Beide Tabellen (OCT u. SYMT)<br />
werden normalerweise als Hashtabellen realisiert, auf OCT wird dann z.B. mit<br />
dem Befehl als Such-Schlüssel zugegriffen.<br />
¯ Objekt-Code Tabelle (OCT)<br />
Die Objekt-Code Tabelle enthält eine Zuordnung zwischen Maschinencode und<br />
Assemblerbefehl; diese Tabelle ist meist statisch fest. OCT kann auch weitere<br />
Informationen enthalten: z.B. Befehlslänge, Operandenzahl, Format.<br />
– MI: ÅÇÎ Ï entspricht H'A0' oder ÂÍÅÈ entspricht H'F1'<br />
¯ Symboltabelle (SYMT)<br />
Die Symboltabelle enthält die Zuordnung zwischen symbolischen Namen und<br />
Maschinenadressen; diese Tabelle kann auch weitere Informationen enthalten:<br />
z.B. Typ, Länge.<br />
– Der Assembler erzeugt die Symboltabelleneinträge während des ersten<br />
Laufs. Während des zweiten Laufs werden dann alle Namen, die in Operandenspezifikationen<br />
auftreten, durch die in der Symboltabelle eingetragene<br />
Adresse ersetzt.In die Symboltabelle werden auch die benutzerdefinierten<br />
Symbole (z.B. bei der MI durch EQU Anweisungen festgelegt) aufgenommen,<br />
zusammen mit dem Wert, der durch eine solche Anweisung für den<br />
symbolischen Namen festgelegt wird.<br />
2.5.5 Assemblerläufe<br />
Ein Assembler macht 2 (oder mehr) Läufe über das Assemblerprogramm.<br />
Nachfolgend werden kurz die Schritte eines einfachen Assemblers vorgestellt.<br />
Lauf 1<br />
Der Lauf 1 erzeugt ein Programm in einem Zwischenformat, das als Eingabe für<br />
Lauf 2 dient, insbesondere erfolgt eine Zuordnung von symbolischen Namen zu<br />
Maschinenadressen.<br />
40
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
1. Initialisiere Location Counter (Zähler) mit Startadresse, falls angegeben, sonst<br />
mit 0.<br />
2. Ersetze Operationsteil durch Maschinencode.<br />
3. Auftreten eines symbolischen Namens N in einem Befehl: Trage N in<br />
Symboltabelle ein, falls noch nicht in Tabelle.<br />
4. Falls eine Marke definierend auftritt:<br />
¯ Falls Name noch nicht in SYMT: Erzeuge SYMT-Eintrag mit Zählerstand<br />
¯ Falls Marke ohne Zählerstand schon drin: Eintrag des Zählerstands<br />
¯ Falls Marke mit Zählerstand schon drin: Fehler, da doppelte Namensvereinbarung<br />
5. Erhöhe Zähler um Länge der Instruktion (aus Op-Code zu entnehmen).<br />
6. Falls nicht Dateiende, lies nächste Zeile und gehe zu Schritt 2.<br />
Beispiel<br />
ÓÙÒØÖ ÅÖ Ð<br />
Ì×Ø Ë<br />
×ØÖØ<br />
ÂÍÅÈ ×ØÖØ<br />
¯ Beim Auftreten der Marke start in "ÂÍÅÈ ×ØÖØ" : Eintrag in Symboltabelle<br />
an Indexstelle i .<br />
ÁÒÜ ÆÑ Ö××<br />
×ØÖØ<br />
¯ Beim Auftreten der Definition Marke start in "start:" Eintrag des aktuellen<br />
Zählerstandes in Symboltabelle an Indexstelle i .<br />
ÁÒÜ ÆÑ Ö××<br />
×ØÖØ <br />
41
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Lauf 2<br />
Der Lauf 2 des Assemblers erzeugt das Objekt-Programm<br />
1. Erzeuge Header-Eintrag und schreibe ihn in Objekt-Programm.<br />
2. Initialisiere ersten Text-Eintrag. Ein Texteintrag kann beispielsweise ein<br />
Maschinenbefehl sein.<br />
3. Falls symbolischer Name in Operandenspezifikation auftritt:<br />
¯ suche Name in SYMT<br />
¯ ersetze Name in Operand durch Adresse in SYMT<br />
¯ Falls Name nicht gefunden: trage 0 als Adresse ein und setze Flag: undefined<br />
Symbol<br />
4. Konstruiere korrekten Maschinenbefehl-Code.<br />
5. füge Code dem Text-Eintrag hinzu, falls noch Platz, sonst: schreibe Text-<br />
Eintrag in Objekt-Programm und erzeuge neuen Text-Eintrag.<br />
6. Falls noch nicht Programmende: lies nächste Zeile des Eingabe-Programms<br />
und führe Schritte 3 bis 6 durch.<br />
7. schreibe letzten Text-Eintrag in Objekt-Programm.<br />
8. schreibe End-Eintrag in Objekt-Programm.<br />
PC-relative Adressierung<br />
Ein Problem bei dieser Vorgehensweise ist, dass das Programm an den<br />
angegebenen Adressen zur Ausführung kommen muss Wunsch: verschiebbare<br />
Programme. Berechnen relativer Adressen durch Assembler, und zwar relativ zum<br />
PC. Dies wird auch von der MI unterstützt.<br />
¯ Idee<br />
Anstatt der Adresse an der der symbolische Name laut Symboltabelle zu finden<br />
ist: Angabe eines PC-relativen Displacements.<br />
42
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
¯ Vorgehen<br />
Zieladresse setzt sich zusammen aus Zähler (PC) und Displacement; PC<br />
Wert wird bereits nach dem Holen des Befehls erhöht; das muss bei der<br />
Adressrechnung berücksichtigt werden. Displacement bestimmt eine Adresse<br />
relativ zum Wert des Befehlszählers (PC).<br />
– Displacement x für Adresse des symbolischen Names muss berechnet<br />
werden: Zieladresse = PC + x<br />
– Beispiel: Vorwärtsverweis<br />
ÓÙÒØÖ ÅÖ Ð<br />
Ì×Ø Ë<br />
ÂÍÅÈ ×ØÖØ<br />
×ØÖØ<br />
Als Code für den Befehl Jump start ergibt sich folgender<br />
£ F1 = Code für ÂÍÅÈ<br />
£ PC-relative Adressierung: PC =Register 15 (R15), d.h. es ergibt sich AF<br />
(F = R15 und A für relative Adressierung)<br />
£ Berechnen des Displacements x mit PC + x = 00000045 (= Adresse von<br />
start)<br />
Æ Wert des PC nach Abarbeitung von F1 AF: 00000033 + 2 = 00000035.<br />
Der Sprungbefehl umfasst 2 Byte, d.h. der PC wird nach dem Holen des<br />
Befehls um 2 inkrementiert.<br />
Æ also: Displacement x = 10, damit sich die Zieladresse 00000045 ergibt<br />
£ Insgesamt ergibt sich als Maschinencode: F1 AF10<br />
¯ Vorteile der PC-relativen Adressierung<br />
– Verschiebbarkeit der Objektprogramme durch relative Adressierung. Der<br />
Lader hat dann die Aufgabe, dazu jeweils die absolute Startadresse hinzu<br />
zu addieren.<br />
– kurze Operanden: Register-relative Adressierung (in unserem Fall durch<br />
PC).<br />
¯ Bemerkung: One-Pass Assembler: es dürfen keine Vorwärts-Referenzen<br />
auftreten, d.h. es muss das Define-before-use Prinzip gelten.<br />
43
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
2.5.6 Binder und Lader<br />
Ein Assembler-Programm kann aus mehreren logischen Einheiten (MI: Segmente)<br />
bestehen, die vom Assembler als einzelne, unabhängige Einheiten (Bindemoduln)<br />
transformiert werden. In Informatik I/II wurde bereits Modularisierung<br />
als wichtiges Konstruktionsprinzip behandelt. Aspekte sind: Problem wird in<br />
Teilprobleme zerlegt, Bausteine als Lösung von Teilproblemen, Wiederverwendung<br />
von Bausteinen, Bibliotheken (engl. library). Modularisierung ist besonders<br />
bei der Entwicklung großer Programme sehr wichtig.<br />
Binder<br />
Der Binder (engl. linker) hat die Aufgabe, aus einer Menge von einzelnen<br />
Bindemoduln ein ausführfähiges Ladeprogramm zu erzeugen, indem die noch<br />
offenen externen Referenzen aufgelöst werden.<br />
¯ Binde-Module<br />
Der Assembler erzeugt Code, der jeweils relativ zum Modul-Anfang adressiert.<br />
Segment 1<br />
JUMP start<br />
Lade<br />
objekt 1<br />
Segment 2 Segment n<br />
Lade<br />
objekt 2<br />
start:<br />
assemblieren<br />
Lade<br />
objekt n<br />
Assemblermodule<br />
Bindemodule<br />
Das Zusammenfügen der einzelnen Segmente zu einem ausführfähigen<br />
Programm ist die Aufgabe des Binders (Linker).<br />
– Externe Referenzen<br />
In einem Modul M i können Referenzen auf Daten/Marken auftreten, wobei<br />
die Daten/Marken in einem anderen Modul M j definiert werden. Beispiel:<br />
Marke start wird in Segment 1 verwendet, und erst in Segment n definiert.<br />
44
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Solche Referenzen heißen externe Referenzen. Assembler kann Referenzen<br />
nicht auflösen, Assembler besitzt keine Informationen darüber, wo sich die<br />
einzelnen Segmente zur Ausführungszeit im Arbeitsspeicher befinden.<br />
– Behandlung externer Referenzen<br />
Für jede externe Referenz erzeugt der Assembler Informationen, die<br />
es dem Binder ermöglicht, aus den Einzelmoduln ein ausführfähiges<br />
Gesamtprogramm zu erzeugen, d.h. die Referenzen (siehe Seite 47)<br />
aufzulösen. Module können unabhängig assembliert werden: keine Vorgabe<br />
über zeitliche Reihenfolge.<br />
£ Der Programmierer gibt durch spezielle Assembler-Direktiven für jeden<br />
Modul an, welche Symbole, Namen und Marken des Moduls von außen<br />
verwendet werden können, d.h. exportiert werden (in MI durch EXP bzw.<br />
EXPORT Direktive).<br />
£ Extern referenzierte Symbole in einem Modul, sogenannte importierte<br />
Symbole, müssen durch Assembler-Steuerungsanweisungen im Modul<br />
markiert werden (in MI: IMP oder IMPORT Direktive).<br />
£ Assembler erzeugt ausgehend von diesen Steuerinformationen spezielle<br />
Einträge im Objekt-Programm.<br />
Æ Für exportierte Symbole: ein Eintrag (gekennzeichnet z.B. durch ein<br />
D (=define)), der den Namen und die relative Adresse des Symbols<br />
umfasst. Aufbau des Define-Eintrag:<br />
"Kennung D, Name des exportierten Symbols, relative Adresse des<br />
Symbols im definierenden Segment".<br />
Æ Für importierte Symbole: ein Eintrag (gekennzeichnet z.B. durch ein R<br />
(=refer)), der den Namen des importierten Symbols umfasst. Adressen<br />
können hierfür noch nicht eingetragen werden. Aufbau des Refer-<br />
Eintrag:<br />
"Kennung R, Name des importierten Symbols".<br />
£ Findet der Assembler eine externe Referenz, so trägt er in dem<br />
erzeugten Code die Adresse 0 ein. Gleichzeitig erzeugt er einen<br />
Modifikations-Eintrag (gekennzeichnet z.B. durch ein M (=modify)) im<br />
Objektprogramm, der für den Binder die benötigte Information enthält:<br />
Angabe, welches Auftreten der Referenz zu modifizieren ist und<br />
Name des externen Symbols.<br />
Auftreten bezieht sich hier auf die Angabe in der Operandenspezifikation,<br />
wo die externe Referenz auftritt. Aufbau Modifikations-Eintrag:<br />
"Kennung M, Startadresse des zu modif. Adressfeldes, importiertes<br />
Symbol".<br />
45
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Lader<br />
Ein Lader (engl. loader) ist ein Systemprogramm, das die Aufgabe hat,<br />
Objektprogramme in den Speicher zu laden und deren Ausführung anzustoßen.<br />
¯ Eigenschaften<br />
In einem System ist i.d.R. nur ein Lader vorhanden, so dass Programme<br />
unterschiedlicher Quellsprachen in ein einheitliches Objektprogramm-Format<br />
transformiert werden müssen.<br />
– Viele Lader beinhalten gleichzeitig Binde-Funktion. Binde/Lader sind<br />
heutzutage typische Komponenten in Rechensystemen. Binde/Lader sind<br />
Bestandteil der Dienste, die ein Betriebssystem anbietet.<br />
£ Binde/Lader: Programmmodule werden zur Ladezeit gebunden.<br />
£ Lauf 1: Zuweisung von Adressen zu externen Referenzen (Auflösen von<br />
Referenzen)<br />
£ Lauf 2: Binden, Verschieben, Laden<br />
– Absoluter Lader<br />
Aufgaben eines absoluten Laders; ein Lauf genügt.<br />
£ Prüfen des Header-Teils im Objekt-Programm, ob es sich um das dort<br />
spezifizierte Programm handelt und ob der für das Programm vorgesehene<br />
Speicher groß genug ist (Header-Infos über Programmgröße).<br />
£ Die Text-Einträge im Objekt-Programm werden gelesen und der Code<br />
wird an die dort angegebenen, absoluten Adressen des Speichers geladen.<br />
£ Beim Lesen des END-Eintrags springt der Lader zur angegebenen Start-<br />
Adresse des Programms, um die Programmausführung zu starten. Ein<br />
absoluter Lader hat jedoch eine Reihe von Problemen: a) Programmierer<br />
muss Lade-Adressen explizit angeben, b) Probleme bei Multiprogramming:<br />
Ladeadresse nicht vorab bekannt, c) Verschiebbarkeitsforderungen<br />
können nicht erfüllt werden, und d) Probleme bei Wiederverwendbarkeit<br />
von z.B. Bibliotheksfunktionen, wenn diese vorab vergebene absolute<br />
Adressen besitzen.<br />
– Relativer Lader<br />
Laden verschiebbarer Objekt-Programme, wobei die Information, welche<br />
Adressen neu zu berechnen sind, vom Assembler zur Verfügung<br />
gestellt werden, z.B. durch Modifikations-Einträge im Objekt-Programm.<br />
Modifikations-Einträge sind jedoch nicht für jede Maschinen-Architektur<br />
geeignet. Maschinen, die z.B. keine relative Adressierung kennen, erfordern,<br />
46
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
dass u.U. fast alle Adressen neu berechnet werden müssen durch den Lader.<br />
Entsprechend umfangreich sind dann die benötigten Modifikations-<br />
Einträge und entsprechend groß wird dann das Objekt-Programm. Die Verschiebbarkeit<br />
wird in Zusammenhang mit der Seitenadressierung (siehe<br />
Seite 168) detaillierter behandelt.<br />
Datenstrukturen eines Binde/Laders<br />
Der Binder benötigt eine Tabelle ESTAB ("external symbol table") für die<br />
aufzulösenden externen Referenzen, wenn im Programm externe Referenzen<br />
auftreten. Der Tabelleneintrag besteht aus [Symbol, Adresse]. ESTAB hat analoge<br />
Aufgaben wie die Symboltabelle des Assemblers. Der Tabelleneintrag beinhaltet<br />
u.U. auch den Modul, in dem das Symbol definiert ist.<br />
¯ Hilfsvariable<br />
– PADR: Startadresse im Speicher, wohin das gebundene Programm geladen<br />
werden soll. Der Wert wird i.d.R. durch das Betriebssystem ermittelt und<br />
dem Lader mitgeteilt.<br />
– CSADR: Startadresse des jeweils bearbeiteten Moduls; dieser Wert wird zu<br />
den Relativ-Adressen des jeweiligen Moduls hinzu addiert. CSADR steht für<br />
control section adr.<br />
¯ Algorithmus zum Lauf 1<br />
Festlegen der Startadresse PADR des zu ladenden Programms (also wohin es in<br />
den Speicher geladen werden soll). Jedes Modul wie folgt bearbeiten:<br />
– Header-Eintrag lesen und Eintrag in Symboltabelle ESTAB:<br />
[Name des Moduls, Startadr. (=CSADR) des Moduls].<br />
Beim ersten Modul gilt: PADR=CSADR.<br />
– Lesen von Export-Einträgen (Symbol-Definitionen) im Objekt-Programm;<br />
alle auftretenden Symbole in ESTAB eintragen, wobei gilt:<br />
[symbolischer-Name, Adresse = Relativadr + CSADR].<br />
– Lesen des END-Eintrags: CSADR = CSADR_alt + Länge des Segments<br />
(steht als Info im Header); Bearbeiten des nächsten Moduls mit der neuen<br />
Anfangsadresse CSADR.<br />
¯ Algorithmus zum Lauf 2<br />
47
Schlichter, TU München 2.5. HARDWARENAHE PROGRAMME<br />
Nach Lauf 1 enthält ESTAB alle externen Symbole, die in Modulen definiert<br />
wurden zusammen mit deren Adresse. Der 2. Lauf erledigt die Funktionen<br />
Laden, Verschieben, Binden.<br />
– Sukzessives Lesen der Text-Einträge aus Objektprogramm; Abspeichern des<br />
Codes an "Startadresse des Segments + Relativadresse" im Eintrag. Die<br />
Startadresse des Segments ist CSADR und ist jeweils im Eintrag zum Modul<br />
in ESTAB enthalten.<br />
– Wird ein Modifikations-Eintrag gelesen, so wird das extern referenzierte<br />
Symbol bzw. dessen Adresse, in ESTAB nachgeschlagen.<br />
– Ist das letzte Modul bearbeitet und dessen END-Eintrag gelesen, so wird<br />
zum dort angegebenen Beginn des Programms gesprungen und die Kontrolle<br />
zur Ausführung des Programms wird an das Programm abgegeben. Bei<br />
mehreren Objektprogrammmodulen enthält meist nur das Hauptprogramm<br />
eine Angabe einer Startadresse. Falls kein Modul eine Startadresse definiert,<br />
wird vom Lader per default die Anfangsadresse PADR genommen. Damit<br />
gilt folgendes: Reihenfolge, in der Module geladen werden spielt keine<br />
Rolle. Andere Möglichkeit: Startadresse des geladenen Programms wird an<br />
das Betriebssystem zurückgeliefert und Benutzer muss mit einem Execute-<br />
Befehle (exec) explizit die Ausführung starten.<br />
Dynamisches Binden<br />
Binden von Unterprogrammen erst zur Laufzeit, d.h. erst wenn sie das erste Mal<br />
aufgerufen werden. Als Vorteile ergeben sich folgende:<br />
¯ Nach Bedarf laden<br />
Unterprogramme werden also nur dann, wenn sie tatsächlich gebraucht werden,<br />
zum in Ausführung befindlichen Programm hinzu gebunden. Bei großen<br />
Unterprogrammen oder bei Unterprogrammen mit vielen externen Referenzen<br />
kann man mit dynamischen Binden viel Speicherplatz und Zeit einsparen. Z.B.<br />
Nutzung einer großen Statistik-Bibliothek abhängig von Eingabewerten, die<br />
erst zur Laufzeit bekannt sind: kein Binden einer vollständigen Kopie der<br />
Bibliothek nötig.<br />
Beispiel Windows: Routinen in Dynamic Link Libraries (DLL) zusammengefasst;<br />
erst wenn sie benötigt wird, Laden der gesamten DLL.<br />
¯ Code-Sharing<br />
Dynamisches Binden wird oft verwendet, wenn mehrere ausführfähige<br />
Programme eine einzige Kopie eines Unterprogramms oder einer Bibliothek<br />
48
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
gemeinsam nutzen sollen. Z.B. werden für die Programmiersprache C<br />
die Routinen für die Laufzeit-Unterstützung in der dynamic link library<br />
zusammengefasst. Es wird dann nur eine Kopie der Routinen der Bibliothek<br />
in den Arbeitsspeicher geladen und alle ausführenden Programme können dann<br />
dynamisch diese Kopie zu ihrem Programm hinzu binden, anstatt jeweils eine<br />
eigene Kopie in ihr Objektprogramm zu binden.<br />
2.6 Hardwarenahe Datenstrukturen<br />
Dieser Abschnitt behandelt kurz einige hardwarenahe Datenstrukturen. Einige<br />
davon, z.B. Keller und Halde, wurden bereits zusammen mit ihren Operationen in<br />
der Informatik-Einführungsvorlesung ausführlich vorgestellt.<br />
2.6.1 Felder<br />
Eindimensionale Felder<br />
Angenommen wir haben ein Feld a[u:o] von Elementen mit u o; u ist die untere<br />
Grenze und o die obere Grenze des Feldes. In Java ist die untere Grenze immer<br />
0, d.h. ein Feld beginnt immer mit dem Feldelement a[0]. Es gibt jedoch andere<br />
Programmiersprachen, bei denen der Programmierer sowohl die untere als auch<br />
die obere Feldgrenze frei wählen kann, jedoch mit u o. Es ergibt sich folgende<br />
Möglichkeit einer linearen Anordnung im Speicher.<br />
a[u]<br />
a[u+1]<br />
a[u+2]<br />
a[o-1]<br />
a[o]<br />
49
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
Die Anzahl der benötigten Bytes pro Feldelement ergibt sich durch die Art der<br />
Feldelemente, z.B. bei integer wird Kennung W verwendet mit LOP = 4 Bytes.<br />
¯ Zugriff auf Feldelement<br />
Das Element a[j] (mit u j o) soll in das Register R0 gebracht werden. Die<br />
Adresse des ersten Elementes a[u] sei die symbolische Adresse a. Der Index j<br />
stehe im Register R2. Das Register R2 darf verändert werden. Die Untergrenze<br />
u steht in Register R3.<br />
– LOP ist die Länge in Bytes eines Feldelements; adr(a[u]) liefert die Adresse<br />
des Feldelements a[u].<br />
Ö ℄ Ö Ù℄ Ù ÄÇÈ<br />
– Im Assemblercode der MI<br />
ÅÇÎ Ê Ö×× ÚÓÒ Ù℄<br />
ËÍ Ï Ê Ê Ê Ê Ê Ù<br />
ÅÇÎ Ï Ê Ê Ê<br />
Die Adressrechnung ist folgendermaßen: inhalt(R1) + (j-u)*4, da die<br />
Kennung W verwendet wird. Zur Adressierung wird also das Inkrement (j<br />
- u) benötigt. Nachteil, da das Inkrement (j - u) jeweils berechnet werden<br />
muss. Dieser Nachteil lässt sich durch Einführung der sogenannten fiktiven<br />
Anfangsadresse adr(a[0]) vermeiden.<br />
¯ Fiktive Anfangsadresse<br />
Ö ℄ Ö Ù℄ Ù ÄÇÈ ÄÇÈ<br />
Ö ℄ ÄÇÈ<br />
adr(a[0]) ist die fiktive Anfangsadresse des Feldes a. Die fiktive Anfangsadresse<br />
muss im Speicher abgelegt werden. I.a. wird sie unmittelbar vor dem<br />
Feldelement a[u] abgelegt.<br />
– Im Assemblercode der MI<br />
ÅÇÎ Ê<br />
ÅÇÎ Ï Ê Ê Ê<br />
Die symbolische Adresse a zeigt auf das 1. Feldelement a[u]. Die fiktive<br />
Anfangsadresse a[0] wird unmittelbar vor a[u] gespeichert; deshalb muss -4<br />
abgezogen werden. Probleme mit obigem Programmstück:<br />
a) die Indexgrenzen werden nicht überprüft,<br />
b) es wird nicht überprüft, ob der Zugriff im Speicherbereich der Reihung<br />
bleibt.<br />
50
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
Bei eindimensionalen Reihungen sind diese beiden Prüfungen äquivalent.<br />
Bei mehrdimensionalen Feldern ist die erste genannte Prüfung die schärfere.<br />
Wir ordnen daher jeder Reihung einen unabhängig abgespeicherten<br />
Felddeskriptor zu, der auch Informationen über Dimension und Feldgrenzen<br />
enthält.<br />
Mehrdimensionale Felder<br />
Angenommen wir haben ein Feld a[u1:o1,..., uk:ok] von Elementen mit uj oj;<br />
uj ist die untere Grenze und oj die obere Grenze der j-ten Dimension des Feldes.<br />
¯ Linearisierung<br />
des Feldes im Arbeitsspeicher, so dass der letzte Index am schnellsten läuft.<br />
Beispielsweise werden bei einem 3-dimensionalen Feld die Feldelemente in der<br />
Reihe a[u1,u2,u3], a[u1,u2,u3+1], a[u1,u2,u3+2],...a[u1,u2,o3], a[u1,u2+1,u3],<br />
a[u1,u2+1,u3+1], ..., a[u1+1,u2,u3], ....a[o1,o2,o3]. Eine andere Möglichkeit<br />
ist es den ersten Index am schnellsten laufen zu lassen.<br />
¯ Vorschrift zur Adressberechnung<br />
Ö ℄ <br />
Ö Ù Ù℄<br />
Ù × Ù × Ù<br />
ÄÇÈ<br />
mit den Spannen sj = oj - uj + 1. Herausziehen der konstanten Terme und<br />
Nutzung der fiktiven Anfangsadresse:<br />
Ö ℄ <br />
Ö ℄<br />
× × ÄÇÈ<br />
Ö ℄ <br />
Ö Ù Ù℄<br />
Ù × Ù × Ù ÄÇÈ<br />
Problematisch bei der zweiten Art der Adressrechnung ist, dass die fiktive<br />
Adresse adr(a[0, ..., 0]) negativ werden kann und der Wert möglicherweise<br />
auch nicht mehr in einem Wort darstellbar ist. Dieser Fall tritt insbesondere<br />
bei sehr hohen unteren Indexgrenzen auf. Für solche Fälle ist es also besser,<br />
das erstgenannte Berechnungsschema mit den Differenzen zwischen aktuellem<br />
Index und unterer Grenze zu verwenden. Zur Kontrolle auf Indexfehler kann<br />
für jeden Index überprüft werden, ob i u und i-u s ist, wobei u die untere<br />
Grenze und s die zugehörige Spanne ist.<br />
51
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
¯ Felddeskriptor<br />
Prinzipiell ist folgende Information in einem Felddeskriptor enthalten:<br />
– Die Anzahl der Indexpositionen (Dimension) des Feldes.<br />
– die Konstanten zur Berechnung der Adresse, z.B. die Indexgrenzen bzw. die<br />
Spannen, aber auch LOP (Komponentenlänge).<br />
– die Werte, die für die vorgesehenen Kontrollen vorgesehen sind, beispielsweise<br />
zur Überprüfung der Indexgrenzen.<br />
– die Kennung der Daten oder ähnliche Angaben, damit ein einheitlicher<br />
Zugriffsalgorithmus für alle Felder realisiert werden kann.<br />
steigende<br />
Adressen<br />
adr (a[0,...,0])<br />
k<br />
u1<br />
sk<br />
LOP<br />
Gestreute Realisierung von Felder<br />
a:<br />
fiktive Anfangsadresse<br />
Dimensionenanzahl<br />
Anfangsindex der 1. Dim.<br />
s1 Spanne der 1. Dim.<br />
Spanne der k-ten Dim.<br />
Komponentenlänge<br />
a Anfangsadresse<br />
Im folgenden wird als Alternative zu linearisierten Realisierungen erklärt, wie 2dimensionale<br />
Felder (Matrix) gestreut realisiert werden können. Gestreut bedeutet<br />
dabei, dass ein Feld statt mit einem linearen Speicherbereich mit mehreren<br />
linearen Speicherbereichen, die im Arbeitsspeicher gestreut sein können, realisiert<br />
wird. Dabei kann das 2-dimensionale Feld entweder zeilenweise oder<br />
spaltenweise im Arbeitsspeicher abgelegt werden.<br />
¯ Sei a[u1:o1, u2:o2] das darzustellende Feld;<br />
52
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
a[u1, *]<br />
a[o1, *]<br />
ref(u1) ref(o1) Referenzen<br />
1. Zeile von a<br />
letzte Zeile von a<br />
Der Speicherbereich Referenzen beinhaltet die Verweise auf die jeweiligen<br />
Zeilen des 2-dimensionalen Feldes, die als linearer Speicherbereich verwaltet<br />
werden.<br />
2.6.2 Programme im Arbeitsspeicher<br />
Der Adressraum eines Programmes besteht aus drei Teilen: dem statischen Bereich<br />
mit seinen Maschinenbefehlen und statischen Variablen, der Halde für die dynamischen<br />
erzeugten Datenstrukturen und dem Keller, der die Blockschachtelung<br />
der Ablaufstruktur bzgl. der Methodenaufrufe (Unterprogrammaufrufe) widerspiegelt.<br />
Keller/Stapel<br />
Halde<br />
Maschinenbefehle<br />
statische Variable<br />
steigende<br />
Adressen<br />
¯ Der Keller/Stapel dient zur Aufnahme der Daten bei dynamischen Aktivierungen<br />
von Unterprogrammen (Methodenaufrufe); blockorientierte Speicherung.<br />
53
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
¯ Die Halde dient zur Speicherung von Daten, die dynamisch zur Laufzeit<br />
aufgebaut werden, z.B. Listen.<br />
¯ Unterprogrammaufruf bei der MI<br />
Deklaration in einer Programmiersprache<br />
ÔÙÐ ÒØ ÔÖÓ ÒØ Ô ÒØ ÔÒ ß<br />
<br />
Aufruf im Hauptprogramm: Ö×ÙÐØ ÔÖÓ Ú ÚÒ<br />
– Ablage im Keller<br />
R12<br />
R13<br />
SP<br />
......<br />
Rückgabewert<br />
vn<br />
......<br />
v1<br />
PC<br />
R14<br />
......<br />
R0<br />
lokale Variable<br />
freier Keller<br />
Keller<br />
wachstum<br />
steigende<br />
Adressen<br />
– Die Parameter stehen im Keller. Der letzte Parameter wird als erster<br />
in den Keller geschrieben. Unmittelbar vor Aufruf des Unterprogramms<br />
zeigt der Kellerpegel auf den ersten Parameter. Achtung: Bei Aufruf<br />
des Unterprogramms wird noch die Rückkehradresse im Keller abgelegt.<br />
Im Keller wird auch Speicherplatz für das Ergebnis des Unterprogramms<br />
reserviert. Falls das Ergebnis sehr groß ist bzw. die Größe nicht a priori<br />
bekannt ist. wird nur Speicherplatz für eine Adresse reserviert. Das Ergebnis<br />
54
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
wird dann auf der Halde gespeichert, und das Unterprogramm gibt nur eine<br />
Adresse auf die Datenstruktur in der Halde zurück.<br />
– Unmittelbar nach dem Aufruf werden die aktuellen Register gesichert.<br />
– Die lokalen Variablen werden ebenfalls im Keller abgelegt. Wir nennen<br />
diesen Bereich den lokalen Datenraum.<br />
– R14 (SP) ist immer der aktuelle Kellerpegel. Im Gegensatz zu R12 und R13<br />
verändert sich also SP normalerweise dynamisch in der Prozedur.<br />
– Die Register R12 und R13 werden in Unterprogrammen wie folgt benutzt:<br />
£ R12 zeigt im Keller auf den Bereich der Parameter. Es enthält die<br />
Ablageadresse des ersten Parameter.<br />
£ R13 enthält die Basisadresse für den lokalen Datenraum,<br />
– Befehlsfolge in MI Assembler<br />
Hauptprogramm:<br />
ÔÙ× Ê ÛÖØ ÎÓÖÖØÙÒ×Ô×<br />
ÔÙ× ÚÒ<br />
ÔÙ× Ú<br />
ÄÄ ÔÖÓ<br />
ÔÓÔ ÐÐ ÈÖÑØÖ<br />
Ò× ÐÐ Ò<br />
Ê ÛÖØ<br />
Unterprogramm proc:<br />
Æ ÖØÙÒ×Ô×<br />
55
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
ÈÍËÀÊ ÒÒ×Ô× Ë ÖÒ Ö<br />
Ê×ØÖ Ê×ØÖ<br />
ÛÖÒ Ö×Ø ÚÓÒ Ö ÙÖÙÒÒ<br />
ÈÖÓÞÙÖ × ÖØ ÙÒ ÞÛÖ<br />
ÚÓÖ ÔÖÓ Ê×ØÖ ÚÖÒÖØ<br />
ÙÖ È<br />
ÅÇÎ ËÈ Ê Ö×× Ö×ØÖ ÈÖÑØÖ Ò <br />
Ê Ê×ØÖ ÝØ× ÖØ<br />
ÝØ× ×ÔÐ ÑÒØ<br />
ÅÇÎ Ï ËÈ Ê ××Ö×× ÐÓÐÖ ØÒÖÙÑ<br />
Ò Ê <br />
ÐÓÐÒ ØÒÖÙÑ ÒÐÒ<br />
Ö ÒÙÒ×Ô×<br />
ÅÇÎ Ï Ê ËÈ × ÐÙ××Ô× ××Ö××<br />
ÐÓÐÖ ØÒÖÙÑ Ò ËÈ ÐÐ×<br />
ÃÐÐÖ Ò Ø ÖØ× ÚÐÐ ÙÖ <br />
ÈÖÓÞÙÖ ÙØ ÛÙÖ<br />
ÙÖ ÛÖ Ö ÐÓÐ ØÒÖÙÑ<br />
ÑÔÐÞØ Ð× Ø<br />
ÈÇÈÊ Ê×ØÙÖÖÒ Ê×ØÖ<br />
ÊÌ Ê Ö<br />
¯ Haldenverwaltung<br />
In Informatik-Einführungsvorlesung wurde bereits gezeigt, wie dynamisch<br />
erzeugte Daten, z.B. Listen und Objekte auf der Halde gespeichert werden. Die<br />
Halde wird nicht nach dem Kellerprinzip verwaltet. Zellen des Kellerspeichers<br />
werden mit Hilfe der Operation pop oder durch Zurücksetzen des Kellerpegels<br />
in Register SP (R14) freigegeben. Kellerbereiche werden nach dem LIFO-<br />
Prinzip verwaltet. Datenobjekte der Halde werden bei Bedarf erzeugt und<br />
gelöscht. Nicht mehr benötigte Datenobjekte werden entweder explizit<br />
durch einen Programmaufruf entfernt (z.B. durch Anweisung free(objref))<br />
oder implizit durch das System mit Hilfe der Speicherbereinigung (garbage<br />
collection). Garbage collection ist eine Komponente des Laufzeitsystems, das<br />
nicht referenzierte Objekte identifiziert, und deren Speicherplatz auf der Halde<br />
freigibt.<br />
– Der Speicherbereich einer Halde besteht aus zwei Klassen:<br />
£ Belegtbereiche: Speicherbereiche werden für Realisierungen von<br />
Datenobjekten verwendet.<br />
£ Freibereiche: Speicherbereiche, die momentan nicht für Realisierungen<br />
von Datenobjekten verwendet werden; d.h. sie sind frei. Belegt-<br />
56
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
/Freibereiche sind nicht konsekutiv im Arbeitsspeicher organisiert,<br />
sondern bestehen jeweils aus einer Menge von Teilbereichen.<br />
– Buchführung des Speicherbereichs einer Halde mit Hilfe von Belegungs- und<br />
Freigabeoperationen.<br />
– Freiliste<br />
Die Freibereiche der Halde werden mit Hilfe einer Liste (z.B. einfach<br />
verkettet) verwaltet.<br />
ÔÙÐ Ð×× ÖÖ ß<br />
ÒØ ×Þ<br />
ÖÖ ÒÜØ<br />
ÑØÓÒÒØÓÒÒ<br />
<br />
Für jeden verfügbaren Freibereich wird jeweils die Größe in Bytes<br />
gespeichert.<br />
£ Auswahl eines geeigneten Freibereichs<br />
Bei Erzeugung eines neuen Datenobjektes wird die Operation<br />
Ð ÒØ ×Þ ausgeführt. Der Parameter size spezifiziert hier die<br />
Größe des benötigten Speicherbereichs auf der Halde. Für die Auswahl<br />
eines geeigneten Freibereichs zur Erfüllung der gestellten Anforderungen<br />
existieren verschiedene Verfahren.<br />
Æ first-fit-Verfahren<br />
bestimme von Beginn der Freiliste den ersten Speicherbereich, der<br />
die Anforderung erfüllt. Der gefundene freie Speicherbereich wird<br />
aufgeteilt in den Belegtbereich und den nicht benötigten Bereich.<br />
Letzterer wird wieder in die Freiliste eingetragen. Es kann passieren,<br />
dass am Beginn der Freiliste die Freibereiche immer kleiner werden,<br />
d.h. nachfolgende Anforderungen müssen mehr Elemente in der<br />
Freiliste untersuchen.<br />
Æ next-fit-Verfahren<br />
bestimme in der Freiliste den ersten Speicherbereich, der die Anforderung<br />
erfüllt. Die Suche wird dort fortgesetzt, wo die letzte Suche<br />
beendet wurde.<br />
Æ best-fit-Verfahren<br />
bestimmt in der gesamten Freiliste den Speicherbereich, der am<br />
besten die gestellte Anforderung erfüllt, d.h. mit möglichst wenig<br />
Verschnitt. best-fit ist langsamer als first-fit und next-fit, da jeweils<br />
die gesamte Freiliste durchsucht werden muss. Überraschenderweise<br />
führt es im Durchschnitt zu größerer Speicherverschwendung; das<br />
Zuteilungsverfahren hinterläßt mehr sehr kleine Speicherbereiche, die<br />
nicht mehr zugeteilt werden können.<br />
57
Schlichter, TU München 2.6. HARDWARENAHE DATENSTRUKTUREN<br />
Æ worst-fit-Verfahren<br />
bestimme in der Freiliste den größten freien Speicherbereich und teile<br />
ihn in einen Belegtbereich (zur Erfüllung der Anforderung) und einen<br />
verbleibenden Freibereich auf. Mit Hilfe des worst-fit-Verfahrens soll<br />
die Problematik des best-fit-Verfahrens mit seinen vielen kleinen, meist<br />
nicht mehr nutzbaren freien Speicherbereichen vermieden werden.<br />
£ Fragmentierung<br />
Problematisch ist die Entstehung von vielen kleinen freien Rest-<br />
Freibereichen, die wegen ihrer kleinen Längen als Belegtbereiche<br />
ungeeignet sind. Zur Vermeidung dieser nutzlosen, kleinen Freibereiche<br />
ist es zweckmäßig, eine Minimallänge für Freibereiche festzulegen<br />
und bei Belegungsanforderungen entsprechend längere Belegtbereiche zu<br />
erzeugen. Auch wird mit Hilfe des worst-fit-Verfahrens die Problematik<br />
etwas reduziert.<br />
Animation Haldenverwaltung<br />
siehe online Version<br />
58
Kapitel 3<br />
Parallele Systeme<br />
Bis jetzt haben wir die Modellmaschine MI als eine Ein-Benutzer, Ein-<br />
Programm Rechenanlage betrachtet ohne ein Betriebssystem, das die Ausführung<br />
von Programmen koordiniert. Wir haben uns dabei mit der Formulierung<br />
von Benutzerprogrammen auf maschinennaher Ebene beschäftigt. Für die<br />
Ausführung von Programmen sind eine Reihe von organisatorischen Maßnahmen<br />
notwendig. Die angesprochenen organisatorischen Aufgaben sind wesentlicher<br />
Bestandteil der Aufgaben eines Betriebssystems und die <strong>Programmierung</strong> eines<br />
Betriebssystems gehört zu dem Bereich der systemnahen <strong>Programmierung</strong>.<br />
Typischerweise finden in einem allgemeinen Rechensystem eine Vielzahl<br />
paralleler Abläufe statt, die miteinander koordiniert werden müssen. Bevor<br />
wir auf die Aufgaben eines Betriebssystems und insbesondere auf die Bereiche<br />
Prozess- und Prozessorverwaltung, Speicherverwaltung sowie Ein/Ausgabe<br />
eingehen, werden wir uns zunächst mit den Problemen beschäftigen, die sich<br />
daraus ergeben, dass in einem Rechensystem eine Vielzahl von parallelen<br />
Abläufen/Prozessen, existieren kann, die sich beeinflussen können, miteinander<br />
kommunizieren oder auch in Konkurrenz zueinander stehen können.<br />
3.1 Fragestellungen<br />
¯ Bisher betrachtete Aspekte von systemnaher <strong>Programmierung</strong>:<br />
– Programm als Repräsentation eines Algorithmus; ein Algorithmus ist ein<br />
schrittweise effektiv durchführbares Verfahren, in endlichen Schritten; er hat<br />
eine endliche eindeutige Beschreibung.<br />
– Programm als sequentielle Folge von Aktionen/Anweisungen (alle Schritte<br />
nacheinander);<br />
59
Schlichter, TU München 3.2. GRUNDLAGEN<br />
– determinierte Programme: unter gleichen Bedingungen und Eingaben<br />
produziert das Programm stets das gleiche Ergebnis; dies erlaubt eine<br />
einfaches Testen bzw. Debuggen der Programme und der Prozessabläufe.<br />
– deterministische Abläufe: eindeutig vorbestimmter Ablauf, keine willkürliche<br />
Auswahl von alternativen Schritten.<br />
¯ Übergang<br />
von sequentiellen Systemen hin zu parallelen Systemen/Programmen. Dabei<br />
werden die folgenden Aspekte näher betrachtet.<br />
– gleichzeitige Aktivität von Komponenten, die möglicherweise miteinander<br />
kommunizieren oder aufeinander einwirken.<br />
– Beispiele<br />
£ HW-Komponenten eines Rechensystems, z.B. Prozessor und E/A-<br />
Controller, siehe Rechnerarchitektur (siehe Seite 14).<br />
£ SW-Komponenten eines Rechensystems, z.B. parallel ablaufende Programme,<br />
siehe MI-Mehrprozessorarchitektur (siehe Seite 19); u.U. Zugriff<br />
auf gemeinsame Ressourcen, z.B. Daten im Arbeitsspeicher.<br />
£ SW-Komponenten in einem verteilten System, d.h. Rechensysteme, die<br />
über ein Rechnernetz miteinander verbunden sind.<br />
– Aspekte des Abschnitts<br />
£ Modellierungstechniken zur Analyse und Beschreibung der Eigenschaften<br />
paralleler Systeme (Spuren, Petrinetze).<br />
£ Threads als Mittel zur Realisierung von Parallelität innerhalb eines<br />
Prozesses.<br />
£ Synchronisation in parallelen Systemen. Hier geht es darum, dass<br />
Konkurrenz in parallelen Systemen koordiniert werden muss, um<br />
Inkonsistenzen in Rechensystemen zu vermeiden. Erhaltung der kausalen<br />
Beziehungen zwischen Aktionen; Synchronisation des Zugriffs auf<br />
gemeinsame Ressourcen.<br />
£ Mechanismen zur Behandlung von Verklemmungen. Im Rechensystem<br />
gibt es keinen Fortschritt, da sich die einzelnen Prozesse gegenseitig<br />
blockieren.<br />
3.2 Grundlagen<br />
In diesem Teilabschnitt werden kurz die wichtigsten Begriffe definiert sowie<br />
Konzepte zur Formulierung paralleler Aktivitäten aufgelistet. Einige dieser<br />
Konzepte werden in nachfolgenden Teilabschnitten ausführlicher diskutiert.<br />
60
Schlichter, TU München 3.2. GRUNDLAGEN<br />
3.2.1 Begriffsdefinitionen<br />
Nebenläufigkeit<br />
Nebenläufigkeit (engl. concurrency) bezieht sich auf die zeitliche Beziehung<br />
zwischen den Aktivitäten von Komponenten, die gleichzeitig oder zeitlich<br />
verzahnt (engl. interleaving) ablaufen können.<br />
¯ z.B. gleichzeitig: Drucken eines Dokuments und Berechnen einer Formel in<br />
einer Tabellenkalkulation (CPU-Nutzung).<br />
¯ z.B. zeitlich verzahnt: Benutzerauftrag muss CPU abgeben und wartet; ein<br />
anderer Auftrag wird (teilweise) ausgeführt und gibt CPU wieder zurück bevor<br />
das Ende erreicht ist. Hier spricht man auch von Pseudoparallalität.<br />
Parallelität<br />
Parallelität wird häufig synonym zu Nebenläufigkeit verwendet;<br />
¯ spezielle Form der Nebenläufigkeit: mehrere Prozessoren stehen zur Ausführung<br />
zur Verfügung. Ein Beispiel dafür ist die MI Modellmaschine mit bis<br />
zu 4 Prozessoren. Auch bei einem Ein-Prozessor gibt es echte Parallelität zwischen<br />
E/A-Controller (z.B. mit angeschlossenem Drucker) und CPU.<br />
Verteiltheit<br />
Verteilheit (engl. distribution) ist die räumliche oder auch nur konzeptionelle<br />
Aufteilung der Komponenten eines Systems, z.B. vernetzte PCs. Die Verteilung<br />
kann sich sowohl auf der Ebene der HW-Komponenten als auch auf der Datenund<br />
Anwendungsebene abspielen. Der Verteilungsaspekt wird vor allem Ende der<br />
Vorlesung näher beleuchtet.<br />
Interaktion<br />
Kommunikation, Synchronisation, Konkurrenz betrifft die kausalen Beziehungen<br />
zwischen räumlich verteilten und nebenläufig ausgeführten Aktivitäten.<br />
¯ kausale Abhängigkeit zwischen Ampel und Fußgänger:<br />
Ereignis von Fußgänger:<br />
er drückt Knopf<br />
Ereignis beeinflusst Ampel: sie schaltet auf Rot für Auto<br />
61
Schlichter, TU München 3.2. GRUNDLAGEN<br />
¯ Kommunikation: Prozesse auf dem gleichen oder unterschiedlichen Rechensystemen<br />
tauschen Nachrichten untereinander aus.<br />
¯ Koordinierung: Beziehung zwischen Auftraggeber und Auftragnehmer<br />
Erstellen eines Auftrages durch Client und anschließendes Bearbeiten des<br />
Auftrags durch Server.<br />
¯ Konkurrenz: Aktivitäten eines Prozesses behindern die eines anderen Prozesses<br />
z.B. Warten, wenn CPU von anderem Prozess belegt.<br />
Nichtdeterminismus<br />
System zeigt bei gleichen Ausgangsbedingungen und gleichen Eingaben<br />
unterschiedliches Verhalten (Reihenfolge der Prozesse spielt eine Rolle!).<br />
Problem: Nichtreproduzierbarkeit von Ergebnissen, Testen von parallelen<br />
Programmen bei Nichtdeterminismus ist schwierig. Beispiel:<br />
Ü ÈÖÓÞ×× Ü Ü ÈÖÓÞ×× Ü Ü<br />
Ausführungsreihenfolge<br />
È ÚÓÖ È Ü <br />
È ÚÓÖ È Ü <br />
3.2.2 Beschreibungskonzepte<br />
Es gibt eine Vielzahl von Konzepten zur Formulierung paralleler Aktivitäten.<br />
In der Vorlesung werden wir uns vor allem mit den beiden modell-basierten<br />
Ansätzen, Ereignisse und Petrinetze befassen. Es gibt auch noch andere<br />
Modellansätze, z.B. formale Beschreibungssprachen (Estelle, LOTOS), die<br />
z.T. auf Prozessalgebren basieren. Eine andere Möglichkeit sind parallele<br />
Programmiersprachen.<br />
modell-basierte Konzepte<br />
¯ ereignis-orientiert: es wird der Ablauf von Prozessen betrachtet; jeder<br />
Ablauf wird durch eine Menge von Ereignissen charakterisiert. Jeder<br />
Ablauf repräsentiert eine Ereignisspur. Ereignisse können in kausalem<br />
Zusammenhang stehen. Es werden nun eine Menge von Prozessen und deren<br />
Zusammenwirken betrachtet, insbesondere auch wie deren Ereignismengen im<br />
Zusammenhang zueinanderstehen.<br />
62
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
¯ graphisch-orientiert: der Prozessablauf wird graphisch dargestellt. Petrinetze<br />
sind ein Beispiel dafür. Petrinetze sind Graphen mit 2 Arten von Knoten,<br />
den Stellen und Transitionen. Abläufe werden durch das Schalten von<br />
Transitionen und der Propagierung von Marken charakterisiert. Dabei können<br />
Aussagen bzgl. der kausalen Abhängigkeit, aber auch der Unabhängigkeit von<br />
Teilabläufen getroffen werden.<br />
Sprachkonstrukte in Programmiersprachen<br />
¯ parallele Komposition von Teilabläufen innerhalb eines Prozesses, z.B. Java<br />
Threads. Andere Beispiele sind Tasks in Ada, Prozesshierarchien in<br />
Betriebssystemen (fork, join) oder auch parbegin, parend.<br />
¯ Kommunikationskonzepte: u.a. send, receive<br />
¯ Synchronisationskonzepte: u.a. lock, unlock<br />
Konzepte in Betriebssystemen<br />
¯ Prozesskonzept = Programm in Ausführung;<br />
¯ Threadkonzept = leichtgewichtigter Prozess<br />
¯ Kommunikation: Shared memory, Dateien, Nachrichten etc.<br />
¯ Synchronisation: Unterbrechungen (Interrupts), Sperren etc. Das Auftreten<br />
von Unterbrechungen sowie deren Behandlung wird im Verlaufe der Vorlesung<br />
noch näher behandelt.<br />
3.3 Modellierung paralleler Systeme<br />
In diesem Abschnitt behandeln wir grundlegende Mechanismen zur Modellierung<br />
und Beschreibung parallel ablaufender Systeme. Siehe auch das Buch M. Broy<br />
"Informatik", Springer-Verlag. Insbesondere geht es in diesem Abschnitt um<br />
folgendes:<br />
das Verhalten von parallelen Systemen klären,<br />
sich auf wesentliche Aktionen, Ereignisse konzentrieren (Abstraktion),<br />
die Abhängigkeiten zwischen Aktionen ermitteln/klären,<br />
Fehler oder Konflikte aufgrund paralleler Tätigkeiten vermeiden und<br />
maximale Parallelität erzielen.<br />
63
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
3.3.1 Modellierungsziele<br />
Ziel ist die einfache Analyse und Beschreibung von parallelen Systemen.<br />
¯ Spezifikation eines Modells im Sinne von Abstraktion: Vereinfachung,<br />
Weglassen von Details, Beschränken auf interessierende Eigenschaften.<br />
¯ Beispiele interessanter Eigenschaften sind:<br />
– Determiniertheit.<br />
– Störungsfreiheit. Für störungsfreie Systeme gilt, dass unter Einhaltung der<br />
durch eine Ordnung (später wird hierzu die Kausalitätsordnung zwischen<br />
Ereignissen verwendet) festgelegten Reihenfolge, die Ausführungsreihenfolge<br />
der parallelen Ereignisse und deren Aktionen keinen Einfluss auf die<br />
berechneten Ergebnisse hat.<br />
– wechselseitiger Ausschluss. Hier geht es darum, dass bei konkurrierenden<br />
Zugriffen auf gemeinsame Ressourcen, die exklusiv benutzt werden, immer<br />
nur ein Prozess zu einem Zeitpunkt darauf zugreift. Damit soll die<br />
Konsistenz sichergestellt werden.<br />
– Endloses Blockieren (engl. Deadlock).<br />
– Verhungern (engl. Starvation). Beispielsweise erzielt ein Prozess keinen<br />
Fortschritt, da er nie rechnend gesetzt wird, obwohl er rechenbereit wäre.<br />
In Rechnerkernvergabe gibt es jedoch immer einen anderen rechenbereiten<br />
Prozess, der eine höhere Priorität hat.<br />
¯ Die Eigenschaften können in zwei Klassen eingeteilt werden.<br />
– Sicherheitseigenschaften (engl. safety): Sicherstellen, dass gewisse unerwünschte<br />
Zustände und unerwünschte Aktionsverläufe nicht auftreten; Formulierung<br />
durch Invarianten (z.B. wechselseitig ausgeschlossene Nutzung<br />
der CPU). Hier geht es nicht um Datensicherheit, d.h. persistente Speicherung<br />
von Daten, damit sie auch nach einem Systemcrash noch zur Verfügung<br />
stehen. Es geht auch nicht um Datenschutz, d.h. dass Daten nicht für<br />
andere Prozesse lesbar sind.<br />
– Lebendigkeitseigenschaften (engl. liveness): Garantieren, dass gewisse<br />
erwünschte Zustände bzw. Aktionsmuster in den Abläufen irgendwann<br />
auch auftreten (z.B. erwünschter Ablauf:<br />
irgendwann auch Fortschritte).<br />
rechenbereiter Prozess macht<br />
64
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
3.3.2 Verhaltensbeschreibung<br />
Verhaltensbeschreibungen eines dynamischen Systems sind Beschreibungen der<br />
Eigenschaften des Systems und deren Veränderungen über der Zeit. Das Verhalten<br />
eines Systems kann mit Spuren in einem 2-dimensionalen Raum beschrieben<br />
werden: Die 1. Dimension ist die Zeit, mit der das Fortschreiten der Ausführung<br />
der Berechnungen erfasst wird, d.h. hier geht es um die Folge von Ausführungen<br />
von elementaren Aktionen, die jeweils Ereignisse auslösen. Die 2. Dimension<br />
ist der Raum der Eigenschaften des Systems, mit dem die Systemzustände in<br />
Zeitpunkten erfasst werden. Zu Zeitpunkten wird jeweils ein Schnappschuss des<br />
Systemzustands genommen. Über die Zeitachse wird eine Folge von Zuständen<br />
angegeben, die sich mit der Ausführung der Aktionen (Berechnungen) ergibt.<br />
Verhaltensbeschreibung setzt sich aus den Basiseinheiten Aktionen und Zustände<br />
zusammen.<br />
¯ Eigenschaftenveränderungen sind Ausführungen von elementaren Aktionen.<br />
Ausführungen von komplexen Berechnungen können mit Kombinationen von<br />
Ausführungen dieser elementare Aktionen beschrieben werden. Es sind zwei<br />
Sichten möglich.<br />
– Die Sicht auf die Tätigkeiten, die auszuführen sind; in dieser Sicht sind<br />
die Basiseinheiten die atomaren Aktionen. Eine Aktion kann ein MI-<br />
Maschinenbefehl oder eine Java Anweisung sein.<br />
– Die Sicht auf die Veränderungen, die erfolgen und beobachtet werden<br />
können; in dieser Sicht sind die Basiseinheiten die atomaren Ereignisse,<br />
die eintreten. Die Ausführung einer Aktion führt zu einem Ereignis im<br />
Rechensystem. Aktionen und Ereignisse sind dual zueinander und können<br />
nach Bedarf genutzt werden; hier wird überwiegend die Ereignissicht<br />
benutzt. Aktionen können mehrfach ausgeführt werden; sie resultieren in<br />
mehrfachen Ereignissen.<br />
¯ Aktionen bzw. Ereignisse sind zeitlich geordnet. In einem sequentiellen<br />
System werden die elementaren Aktionen sequentiell ausgeführt, d.h. es<br />
existiert eine lineare Ordnung bzgl. der Zeitachse. Diese Zeitachse für<br />
Verhaltensbeschreibungen hat mit der gewohnten, physikalischen Zeit die<br />
lineare Ordnung gemeinsam; sie abstrahiert jedoch als diskrete Zeit von der<br />
Dauer der Ausführung von Aktionen. In einem parallelen System müssen<br />
zusätzlich auch nebenläufige Aktionen betrachtet werden, d.h. für sie kann<br />
keine zeitliche Reihenfolge bestimmt werden.<br />
¯ Zustände des Systems anhand der Werte der Datenobjekte. Folge von<br />
Zuständen gemäß der Zeitachse. Jedem Zeitpunkt wird der Zustand der<br />
65
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Datenobjekte, der mit dem Zeitpunkt erreicht ist, zugeordnet. Für jeden<br />
Zeitpunkt wird damit ein Schnappschuss vom System angegeben; über der<br />
Zeitachse wird die Folge der Zustände angegeben, die sich mit der Ausführung<br />
der Berechnungen ergibt.<br />
¯ Spuren<br />
Es sind zwei Varianten von Verhaltensbeschreibungen zweckmässig.<br />
1. Ereignisspuren: Sie beschreiben den zeitlichen Ablauf der Berechnungen<br />
eines Systems mit Ereignissen, die linear geordnet sind.<br />
2. Zustandsspuren: Sie beschreiben den zeitlichen Ablauf der Berechnungen<br />
eines Systems anhand der auftretenden Zustände der Datenobjekte. Ereignisspur<br />
und Zustandsspur sind eng miteinander verknüpft. Bei Auftreten eines<br />
Ereignisse wird jeweils ein Schnappschuss des Systems genommen. Dieser<br />
Schnappschuss stellt ein Element der Zustandsspur dar.<br />
3.3.3 Ereignisse und Aktionsstrukturen<br />
In diesem Abschnitt wird der Begriff Prozess anhand von Aktionsstrukturen mathematisch<br />
gefasst. Dies ermöglicht die mathematische Modellierung beliebiger,<br />
nichtkontinuierlicher ("diskreter") Abläufe verteilter Systeme, insbesondere von<br />
Prozessen in Rechenanlagen. Durch diese Modellierung können jedoch auch andersartige,<br />
technische oder betriebliche Vorgänge, die sich aus Einzelaktionen<br />
zusammensetzen und zwischen denen kausale Beziehungen bestehen, erfasst werden.<br />
Prozess<br />
Gegeben seien eine Menge (das "Universum") E * von Ereignissen (engl. events)<br />
und eine Menge A von Aktionen (engl. actions).<br />
¯ Definition<br />
Ein Triple p = (E, , «) nennen wir einen Prozess oder auch eine<br />
Aktionsstruktur, falls folgende Aussagen gelten:<br />
– E E * , E heißt die Ereignismenge.<br />
– ist eine partielle Ordnung über E, ist die Kausalitätsrelation.<br />
Die partielle Ordnung ist eine reflexive, transitive und antisymmetrische<br />
Relation.<br />
66
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
– «: E A ist die Aktionsmarkierung des Prozesses p.<br />
– Die Abbildung « ordnet jedem Ereignis eine Aktion zu. In dieser Definition<br />
von Aktionsstrukturen ist auch der leere Prozess eingeschlossen. Er wird<br />
durch die Aktionsstruktur dargestellt, die aus der leeren Ereignismenge<br />
besteht.<br />
¯ Beispiel: Fußgängerübergang<br />
Wir betrachten einen Prozess mit 14 Ereignissen.<br />
Aktionen zugeordnet.<br />
Jedem Ereignis werden<br />
Ereignis Aktion<br />
e1 Knopf wird gedrückt<br />
e2 Ampel für Autofahrer schaltet auf Gelb<br />
e3 Ampel für Autofahrer schaltet auf Rot<br />
e4 Auto hält auf Spur 1<br />
e5 Ampel für Fußgänger schaltet auf Grün<br />
e6 Auto hält auf Spur 2<br />
e7 Fußgänger benutzt Fußgängerübergang<br />
e8 Auto hält auf Spur 3<br />
e9 Ampel für Fußgänger schaltet auf Rot<br />
e10 Ampel für Autofahrer schaltet auf Rot und Gelb<br />
e11 Ampel für Autofahrer schaltet auf Grün<br />
e12 Auto fährt an auf Spur 1<br />
e13 Auto fährt an auf Spur 2<br />
e14 Auto fährt an auf Spur 3<br />
e15 Knopf wird gedrückt<br />
Die Ausführung einer Aktion löst ein Ereignis aus. Wird dieselbe Aktion<br />
mehrmals ausgeführt, so wird jeweils ein neues Ereignis ausgelöst.<br />
– Die Relation der kausalen Abhängigkeiten sei gegeben durch:<br />
e1 e2, e2 e3, e3 e4, e3 e5, e3 e6, e5 e7,<br />
e3 e8, e5 e9, e9 e10, e10 e11, e11 e12,<br />
e11 e13, e11 e14, e4 e12, e6 e13, e8 e14, e9 e15.<br />
Die kausalen Abhängigkeiten ergeben keine vollständige Ordnung.<br />
– Diese Beziehungen erzeugen (durch die Bildung der reflexiv transitiven<br />
Hülle) eine partielle Ordnung, die die Kausalität zwischen den Ereignissen<br />
beschreibt. Man beachte, dass hier beispielsweise das Ereignis e12 nur<br />
stattfindet, nachdem die Ereignisse e4 und e11 stattgefunden haben.<br />
– Graphische Darstellung<br />
Endliche Prozesse lassen sich anschaulich durch knotenmarkierte, gerichtete,<br />
zyklenfreie Graphen darstellen, Die Knoten repräsentieren die Ereignisse<br />
und sind durch Aktionen markiert.<br />
67
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
e6<br />
e1 e2 e3 e4<br />
e8<br />
e5<br />
e9 e10 e11<br />
¯ Charakterisierung von Prozessen<br />
In einem Prozess hat jedes Ereignis eine eindeutige Identität ("X betritt am<br />
19.1.2001 um 11:15 h den Hörsaal"), eine Aktion kann jedoch mehrfach<br />
stattfinden (die Aktion "X betritt den Hörsaal" kann mehrfach stattfinden).<br />
Dementsprechend kann in einem Prozess verschiedenen Ereignissen die gleiche<br />
Aktion zugeordnet sein. Ereignisse haben einen eindeutigen Bezeichner;<br />
Prädikate sind eine andere Möglichkeit, Ereignisse zu identifizieren, z.B.<br />
Ermordung Kennedy's oder die Kreuzigung Christi.<br />
– Parallel, nebenläufig<br />
Für einen Prozess p = (E, , «) nennen wir zwei Ereignisse e1, e2 E<br />
parallel oder nebenläufig (engl. concurrent), falls sie im Prozess p nicht in<br />
einer kausalen Relation stehen, d.h. wenn gilt:<br />
(e1 e2 oder e2 e1)<br />
£ Parallele Ereignisse sind kausal unabhängig, sie können zeitlich<br />
nebeneinander oder in beliebiger Reihenfolge stattfinden.<br />
– Sequentiell<br />
Ein Prozess p = (E, , «) heißt sequentiell, wenn in ihm kein Paar von<br />
parallelen Ereignissen auftritt, d.h. die Kausalitätsrelation eine lineare<br />
Ordnung ist. Damit gilt für zwei beliebige Ereignisse e1 und e2 stets<br />
entweder e1 e2 oder e2 e1.<br />
– Der Ablauf eines Programmes kann auch als Prozess im Sinne der<br />
Aktionsstrukturen gedeutet werden.<br />
sequentielle Prozesse.<br />
– Endlich<br />
Sequentielle Programme erzeugen<br />
Ein Prozess p = (E, , «) heißt endlich, falls seine Ereignismenge<br />
eine endliche Menge ist. Beispielsweise ist das Abspielen einer CD ein<br />
endlicher Prozess, während ein Betriebssystem einen unendlichen Prozess<br />
68<br />
e7<br />
e15<br />
e14<br />
e13<br />
e12
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
repräsentiert. Unendliche Prozesse besitzen unendliche Ereignismengen.<br />
Unendliche Prozesse können jedoch endlich beschreibbar sein.<br />
Kausale Abhängigkeiten<br />
Häufig finden wir in Prozessen unterschiedliche Ursachen für die kausale<br />
Beziehung e d zwischen Ereignissen e und d. Im einzelnen können drei<br />
verschiedene Arten unterschieden werden.<br />
¯ Echt kausale Beziehungen<br />
das Ereignis e ist kausal für das Ereignis d in dem Sinn, dass d ohne e niemals<br />
auftreten kann.<br />
– Beispiel<br />
e = Geldeinwurf, d = Kartenausgabe; e d, d.h. der Fahrkartenautomat gibt<br />
erst dann eine Fahrkarte aus, wenn der passende Geldbetrag eingeworfen<br />
wurde.<br />
¯ zeitliche Beziehungen<br />
das Ereignis e endet, bevor das Ereignis d beginnt.<br />
– Beispiel: Nachricht muss gesendet sein, bevor sie empfangen werden kann.<br />
– Happend-before<br />
Die kausale Beziehung impliziert also eine zeitliche Relation; das ist die<br />
bekannte happened before-Beziehung von L. Lamport. Die Umkehrung<br />
gilt nicht. Die zeitliche Relation zwischen Ereignissen sagt nichts über die<br />
Kausalität aus.<br />
£ Wichtig: Ereignisse wurden als atomare Ereignisse modelliert. D.h.<br />
Beginn und Ende fallen auf einen Zeitpunkt zusammen. Ein alternativer<br />
Ansatz wäre die Spezifikation eines Zeitintervalls für ein Ereignis mit<br />
Anfangs- und Endzeitpunkt. Damit könnten auch Überlappungen von<br />
Ereignissen modelliert werden.<br />
£ Die Happend-before Relation spielt im Kontext von Verteilten Anwendungen<br />
eine wichtige Rolle (siehe Vorlesung Verteilte Anwendungen (URL:<br />
http://www11.in.tum.de/lehre/vorlesungen/archiv)). Insbesondere in der<br />
Gruppenkommunikation wird sie eingesetzt, um die an alle Gruppenmitglieder<br />
gesandten Nachrichten zu sequentialisieren. Auf diese Weise soll<br />
erreicht werden, dass alle Gruppenmitglieder in einen für die Gruppe konsistenten<br />
Zustand versetzt werden.<br />
69
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
¯ Systembeschränkungen<br />
Systembeschränkungen, z.B. wechselseitiger Ausschluss. Das Ereignis e darf<br />
nicht parallel zum Ereignis d auftreten.<br />
– Beispiel<br />
e = Fußgänger überquert die Fahrbahn beim Übergang, und<br />
d = Auto überfährt den Übergang.<br />
e und d dürfen nicht parallel ausgeführt werden, aber es besteht keine<br />
echte kausale Abhängigkeit zwischen e und d. Ereignisse, welche<br />
mit Aktionen markiert sind, die aufgrund von Systembeschränkungen<br />
nicht parallel stattfinden können, sind nicht unbedingt in einem "echt"<br />
kausalen Zusammenhang. Trotzdem sollten sie nicht als parallele<br />
Ereignisse auftreten. Um diese Systembeschränkungen mit den eingeführten<br />
Modellierungskonzepten (uninterpretierte Aktionen) zu erfassen, müssten sie<br />
in eine (künstliche) kausale Abhängigkeit gebracht werden; das ist aber meist<br />
nicht sehr sinnvoll. Wir werden später sinnvollere Möglichkeiten kennen<br />
lernen.<br />
Sequentialisierung<br />
Idee: vereinfachte Darstellung paralleler Abläufe, aus der Sicht eines<br />
Beobachters. Ein sequentieller Prozess hat genau eine vollständige Sequentialisierung,<br />
nämlich die vorgegebene Reihenfolge der Ereignisse. Durch « ergibt<br />
sich aus der Ereignisreihenfolge eine Folge von Aktionen. Vollständige Sequentialisierung:<br />
partielle Kausalitätsordnung zu linearer Ordnung ergänzen.<br />
¯ Definition<br />
Ein Prozess p 1 =(E 1 , 1 , « 1 ) heißt eine Sequentialisierung eines Prozesses p 2<br />
=(E 2 , 2 , « 2 ), falls gilt:<br />
E 1 =E 2<br />
e, d E 1 :e 2 d e 1 d<br />
« 1 = « 2<br />
– Ist p 1 sequentiell, so heißt die Sequentialisierung vollständig. Die<br />
Vervollständigung einer partiellen Ordnung zu einer linearen Ordnung heißt<br />
topologisches Sortieren.<br />
¯ Ein sequentieller Beobachter eines Prozesses ist ein Beobachter, der die<br />
Ereignisse eines Prozesses und die entsprechenden Aktionen in einem<br />
70
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
sequentiellen Ablaufprotokoll festhält. Parallele Aktionen werden dabei in<br />
eine zufällige Reihenfolge gebracht. Das Ergebnis der Beobachtung ist<br />
ein sequentieller Prozess, der einer zufällig gewählten Sequentialisierung<br />
entspricht. Gehen wir von sequentiellen Beobachtern aus, so erhalten wir<br />
sequentielle Prozesse als Beobachtungen.<br />
¯ Existiert für einen nichtsequentiellen Prozess eine Reihe von Beobachtungen,<br />
die alle vollständige Sequentialisierungen des Prozesses darstellen, so lässt sich<br />
aus diesen Beobachtungen der Prozess teilweise rekonstruieren. Kennen wir die<br />
Gesamtmenge aller Sequentialisierungen, so lässt sich der Prozess eindeutig<br />
rekonstruieren. Für den Beweis siehe Broy, Band II, S 12.<br />
¯ Die Ausführung eines Programms auf einer Rechenanlage kann ebenfalls<br />
als Prozess dargestellt werden. Gewisse Anweisungen ("Aktionen") eines<br />
Programms zerfallen bei der Ausführung auf einer Rechenanlage in eine Reihe<br />
von Einzelaktionen.<br />
¯ Beispiel<br />
Für das Beispiel des Fußgängerübergangs ist folgendes eine vollständige<br />
Sequentialisierung.<br />
e1 e2 e3 e4 e6 e5 e7 e8 e9 e10 e11 e14 e12 e13 e15<br />
¯ Spuren<br />
Darstellung sequentieller Prozesse mittels Spuren. Sequentielle Prozesse sind<br />
Spezialfälle paralleler Prozesse. Für die Darstellung sequentieller Prozesse<br />
bietet sich eine wesentlich einfachere Darstellungsform als Aktionsstrukturen.<br />
Wir können ganz auf die explizite Angabe von Ereignismengen verzichten und<br />
stattdessen endliche oder unendliche Sequenzen von Aktionen verwenden.<br />
– Sei A eine Menge von Aktionen, dann bezeichnen wir mit A + die Menge<br />
der endlichen Folgen von Aktionen aus A und mit A die Menge der<br />
unendlichen Folgen von Aktionen.<br />
– Jedem sequentiellen Prozess können wir eindeutig eine Folge von Aktionen<br />
zuordnen, wie sprechen von der Spur (engl. trace). Sei p = (E, , «) ein<br />
sequentieller Prozess. Wir definieren:<br />
£ spur : {p | p ist sequentieller Prozess } A + A<br />
£ spur(p) = empty, falls E = Ø<br />
£ spur(p) = a ¡ spur(p | E \ {e}), falls E Ø, wobei e das gemäß der<br />
Kausalitätsordnung kleinste Ereignis in p ist und «(e) = a gilt.<br />
71
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
p|E\{e}bezeichnet den Teilprozess, der durch die Einschränkung<br />
auf die Ereignismenge E \ {e} entsteht.<br />
"¡" ist hier die Konkatenation von Aktionen, die sich durch die Ereignisse<br />
ergeben. spur ist eine Abbildung auf die Menge der Aktionen. Als<br />
Ergebnis liefert spur einen Strom (engl. stream) von Aktionen.<br />
– Für einen nichtsequentiellen Prozess p gilt:<br />
Spuren(p) = {spur(q) : Prozess q ist eine vollständige Sequentialisierung<br />
von p}<br />
Spuren sind ein einfacheres Modell für sequentielle Abläufe eines Systems<br />
als Aktionsstrukturen. Da bei nichtsequentiellen Systemen in vielen<br />
Anwendungen die Frage, welche Aktionen parallel zueinander auftreten<br />
können, von untergeordneter Bedeutung ist, ist es naheliegend für<br />
solche Systeme vereinfachend statt ihrer nebenläufigen Prozesse deren<br />
Sequentialisierungen als Abläufe zu betrachten. In vielen Ansätzen zur<br />
Modellierung verteilter Systeme werden nicht Prozesse mit ihrer expliziten<br />
Darstellung von Parallelität, sondern die technisch etwas einfacher zu<br />
handhabenden Mengen von Aktionsströmen verwendet.<br />
3.3.4 Aktionen als Zustandsübergänge<br />
Neben der Beschreibung von Systemen durch die Prozesse, die deren Abläufe<br />
bilden, ist es naheliegend, Systeme durch Zustände und ihr Verhalten durch<br />
Zustandsänderungen zu modellieren. Zustandsraum = Menge aller<br />
Systemzustände. Dazu geben wir für ein System einen Zustandsraum an. Dieser<br />
besteht aus der Menge aller Zustände, die das System einnehmen kann. Auf dieser<br />
Basis beschreiben wir dann alle Zustandsübergänge, die im System auftreten<br />
können.<br />
Interpretierte Aktionen<br />
Aktionen werden Zustandsänderungen als Bedeutung zugeordnet. Bisher haben<br />
wir die Mengen der Aktionen nicht weiter interpretiert. Eine Deutung für<br />
Aktionen in Prozessen liefern Zustandsänderungen. Dazu weisen wir Aktionen<br />
Zustandsänderungen als Bedeutung zu. Dabei interessiert uns insbesondere die<br />
Frage, ob wir über diese Zuordnung auch Prozessen eindeutig eine Bedeutung<br />
zuordnen können.<br />
72
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Modell<br />
Zu diesem Zweck definieren wir nichtdeterministische Zustandsautomaten mit<br />
Transitionsaktionen.<br />
¯ Zustandsautomat<br />
Ein nichtdeterministischer Zustandsautomat ist gegeben durch:<br />
– S, eine Menge von Zuständen, genannt Zustandsraum,<br />
– A, eine Menge von Transitionsaktionen,<br />
– R S ¢ A ¢ S eine Zustandsübergangsrelation,<br />
£ Seien s , s S und a A gegeben. (s , a, s ) R bedeutet,<br />
0 1<br />
0 1<br />
dass im Zustand s die Aktion a ausgeführt werden kann und dies zum<br />
0<br />
Nachfolgezustand s S führen kann.<br />
1<br />
£ Diese Art von Automaten heißt nichtdeterministisch, da in einem<br />
Zustand mehrere Transitionsaktionen möglich sein können und eine<br />
Transitionsaktion zu unterschiedlichen Nachfolgezuständen führen kann.<br />
£ Wir schreiben (für gegebene Relation R) s s , um auszudrücken, dass<br />
0 a 1<br />
(s ,a,s ) R gilt.<br />
0 1<br />
– S0 eine Menge von möglichen Anfangszuständen.<br />
¯ Beispiel Fahrkartenautomat<br />
Akzeptiert werden 1- und 2 DMark Münzen. Mittels zweier Knöpfe kann man<br />
zwischen einer Kurzstrecke für 1 DM oder einer normalen Fahrt für 2 DM<br />
wählen. Der Automat gibt Wechselgeld zurück.<br />
– Menge der Zustände<br />
S S xS mit<br />
1 2<br />
S = {Wahl, kurz, normal} und S = {-1,0,1,2}<br />
1 2<br />
mit der folgenden Interpretation eines Zustandes s = (a, b):<br />
a= Wahl: Automat wartet auf die Wahltaste<br />
a= kurz: eine Kurzstreckenkarte wurde gewählt und ist noch auszugeben<br />
a= normal: eine normale Fahrkarte wurde gewählt und ist noch<br />
auszugeben<br />
b= -1 : es ist noch 1 DM zurückzugeben<br />
b= 0 : es ist kein Geld mehr einzuwerfen oder zurückzugeben<br />
b= 1 : es muss noch 1 DM eingeworfen werden<br />
b= 2 : es muss noch 2 DM eingeworfen werden<br />
73
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
– Menge der Transitionsaktionen<br />
A = {Wk, Wn, E1, E2, R1, Ak, An }, mit<br />
Wk: Wahl einer Kurzstreckenkarte<br />
Wn: Wahl einer normalen Fahrkarte<br />
E1: Einwurf eines 1DM Stückes<br />
E2: Einwurf eines 2DM Stückes<br />
R1: Rückgabe eines 1DM Stückes<br />
Ak: Ausgabe einer Fahrkarte für Kurzstrecke<br />
An: Ausgabe einer Normalfahrkarte<br />
– Zustandsübergänge<br />
(normal, 2)<br />
E1<br />
Wn Wk<br />
(Wahl, 0)<br />
E1<br />
E1<br />
(normal, 1) (normal, 0) (kurz, 0) (kurz, 1)<br />
E2<br />
E2<br />
R1<br />
An<br />
R1<br />
An<br />
Ak<br />
(normal, -1) (Wahl, -1) (kurz, -1)<br />
¯ Aktionsspur<br />
Jedem Zustandsautomaten lassen sich ausgehend von der gegebenen Menge<br />
von Anfangszuständen Spuren zuordnen.<br />
– Definition<br />
Gegeben sei ein Zustandsautomat Z = (S, A, R, S0). Eine Folge a , wobei 1<br />
i<br />
i k mit k IN { }, ist eine endliche oder unendliche Aktionsspur<br />
des Zustandsautomaten, falls eine Folge von Zuständen s existiert, mit s<br />
i i<br />
S, s S0 und<br />
1<br />
s s für alle i mit 1 i k<br />
i-1 ai i<br />
– Eine Aktion a heißt deterministisch, wenn für jeden Zustand s S höchstens<br />
ein Nachfolgezustand s S existiert mit<br />
f<br />
s s<br />
a f<br />
– Beispiel<br />
Auswertung eines Programms mit Zustandsänderung. Das Programm<br />
74<br />
Ak<br />
R1<br />
E2
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Ý <br />
Ü <br />
ÛÐ Ü ß<br />
Ý Ü Ý Ü Ü <br />
<br />
berechnet die Fakultätsfunktion. Nach seiner Ausführung gilt 10! = y. Der<br />
Ablauf dieses Programms entspricht der sequentiellen Aktionsstruktur, deren<br />
Ereignisse, Aktionen und Zustände in der folgenden Tabelle angegeben sind.<br />
Ereignis Aktion Zustand x Zustand y<br />
a0 y = 1 undefiniert 1<br />
b0 x = 10 10 1<br />
a1 (x 0) ? 10 1<br />
b1 y=x*y 10 10<br />
c1 x=x-1 9 10<br />
... ... ... ...<br />
a10 (x 0)? 1 10!<br />
b10 y = x * y 1 10!<br />
c10 x = x - 1 0 10!<br />
a11 (x 0)? 0 10!<br />
Hier bezeichnen Aktionen wie (x 0)? Abfrageaktionen, die den<br />
Datenzustandsraum nicht ändern. Nehmen wir jedoch Kontrollzustände<br />
(etwa einen Befehlszähler) mit zu den Zuständen des Zustandsraums hinzu,<br />
so ändert auch diese Abfrage den Zustand. Zustände sind für das obige<br />
Programm durch Paare (n, m) von Zahlen gegeben, die die Werte der<br />
Programmvariablen x und y repräsentieren. Mit jeder Zuweisung in<br />
einem prozeduralen Programm wird eine Aktion verbunden, die einer<br />
Zustandsänderungsabbildung entspricht.<br />
Konflikte<br />
Im Zusammenhang mit Programmen und den ihnen zugeordneten Prozessen<br />
stellt sich die Frage, welche Zustandsänderungen ohne Schwierigkeiten zeitlich<br />
parallel durchgeführt werden können. In vielen Fällen lassen sich Aktionen<br />
zeitlich nebeneinander ausführen. Gewisse Zustandsänderungen können aber<br />
nicht ohne Konflikte nebeneinander ausgeführt werden. Wir sagen, dass diese<br />
Aktionen im Konflikt miteinander sind. Sollen beispielsweise zwei Anweisungen<br />
an die gleiche Programmvariable x parallel ausgeführt werden, so lässt sich dem<br />
entsprechenden Prozess keine Zustandsänderungsabbildung eindeutig zuordnen.<br />
In beiden Aktionen soll die Programmvariable x geändert werden. Die Aktionen<br />
sind im Konflikt.<br />
¯ Beispiel<br />
75
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Mögliche Konflikte bei parallel durchzuführenden Zuweisungen. Wir<br />
betrachten die Aktionen, die den folgenden beiden Zuweisungen entsprechen:<br />
Ü Ü<br />
Ü Ü<br />
Die Zuweisungen stehen im Konflikt zueinander.<br />
– Werden sie parallel ausgeführt, so kann dem entsprechenden Prozess<br />
keine Zustandsänderung eindeutig zugeordnet werden; wir sagen auch, die<br />
Aktionen stören sich wechselseitig.<br />
¯ Störungsfreiheit<br />
Frage: Gibt es Kriterien, um auf einfache Weise solche Konflikte zu erkennen,<br />
so dass sie durch eine Reihenfolgeregelung gelöst werden können? Die Frage<br />
kann mit ja beantwortet werden; es gibt Kriterien, und zwar die Bernstein-<br />
Bedingungen.<br />
– Vorbereitung<br />
Für eine Transitionsaktion a gelte:<br />
V(a) S ist Vorbereich, d.h. die Menge der gelesenen Zustandsvariablen.<br />
N(a) S ist Nachbereich,<br />
Zustandsvariablen.<br />
– Beispiel<br />
Sei S = {x, y, z}<br />
d.h. die Menge der geschriebenen<br />
Ü Ü Ý Þ Î ßÜ Ý Þ Æ ßÜ<br />
Ý Ü Î ßÜ Æ ßÝ<br />
– Definition Störungsfreiheit<br />
Gegeben sei ein Prozess p= (E, , «). Der Prozess p heißt störungsfrei,<br />
genau dann, wenn für jedes Paar von Ereignissen e1, e2 E gilt:<br />
1.) e1 e2 oder e2 e1 oder<br />
2.) V(«(e1)) N(«(e2)) = N(«(e1)) V(«(e2)) = N(«(e1)) N(«(e2)) = Ø.<br />
£ Die Bedingung (2) nennt man auch Bernstein-Bedingungen. Gilt die<br />
Bernsteinbedingung, dann ist die Reihenfolge der Ausführung der beiden<br />
Zuweisungen ohne Einfluss auf den schließlich erreichten Endzustand.<br />
Der Störungsbegriff umfasst auch unerwünschte Situationen in konventionellen<br />
Rechnern, in denen ein gleichzeitiges Lesen und Schreiben,<br />
sowie mehrfaches gleichzeitiges Schreiben auf einer Speicherzelle auch<br />
technisch zu einem Konflikt führt.<br />
76
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
– Für störungsfreie Systeme gilt, dass unter Einhaltung der mit festgelegten<br />
Reihenfolge, die Ausführungsreihenfolge der parallelen Ereignisse und<br />
deren Aktionen keinen Einfluss auf die berechneten Ergebnisse hat. Die<br />
Bernstein-Bedingungen sind auch im Zusammenhang mit parallelisierenden<br />
Compilern wichtig. Parallelisierende Compiler versuchen, Anweisungen<br />
des zu compilierenden Programms, die keine gemeinsamen Datenobjekte<br />
benutzen, für eine parallele Bearbeitung vorzubereiten (z.B. Ausnutzen vieler<br />
Pipeliningstufen von RISC-Prozessoren). Dazu kann man die sequentiellen<br />
Aktionen (Anweisungen) schrittweise aus der Relation entfernen, solange<br />
die Bernsteinbedingung nicht verletzt wird.<br />
3.3.5 Petri-Netze<br />
Ein Prozess beschreibt einen möglichen Ablauf eines verteilten Systems. Verteilte<br />
Systeme besitzen viele Abläufe, d.h. ein System wird durch die Menge von<br />
Abläufen beschrieben. Gesucht wird deshalb ein Modell zur Beschreibung<br />
von Systemen und deren möglichen Abläufen. Drei Vertreter solcher<br />
Formalismen sind Petri-Netze (graphische Beschreibungsmethode), Agenten<br />
(formale Beschreibungssprache, siehe Broy Band II) und prädikatenlogische<br />
Formeln zur Beschreibung von Abläufen. Im folgenden werden Petri-Netze<br />
vorgestellt, die eine graphen-orientierte Beschreibung verteilter Systeme und<br />
deren Abläufen ermöglicht.<br />
Allgemeines<br />
Formalismus von C.A. Petri 1962 entwickelt. Ansatz führte zu einer Theorie zur<br />
Analyse und Entwicklung nebenläufiger Systeme.<br />
¯ informelle Charakterisierung<br />
Ein Petri-Netz ist gerichteter Graph mit Kanten und zweierlei Knoten.<br />
– Knoten: Stellen (graphisch Kreise) und Transitionen (graphisch Rechtecke)<br />
– Kanten: von Stellen zu Transitionen oder von Transitionen zu Stellen<br />
– Belegung der Stellen mit Marken/Werten (Token);<br />
£ In einem boolschen Netz sind als Werte nur 0 oder 1 zugelassen.<br />
£ In einem Stellen/Transitionsnetz sind für die Belegung der Stellen<br />
natürlichzahlige Werte zugelassen; die maximale Belegung definiert die<br />
Stellenkapazität.<br />
77
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
– Zustand: definiert durch Belegung der Stellen;<br />
£ Zustandsübergang durch sogenannte Schaltregeln (engl. firing rule);<br />
Belegung ändert sich. Man spricht in diesem Zusammenhang vom<br />
Schalten einer Transition. Das dynamische Verhalten eines Netzes läßt<br />
sich durch Schaltvorgänge beschreiben. Transitionen schalten, indem sie<br />
von allen Eingangsplätzen eine Marke abziehen (bei 1-wertigen Kanten)<br />
und auf allen Ausgangsplätzen eine Marke hinzufügen. Ein Schaltvorgang<br />
kann insbesondere die Gesamtzahl der Marken in einem Netz ändern. Eine<br />
einzelne Transition ist schaltbereit, wenn alle ihre Eingangsplätze echt<br />
positiv belegt sind; bei mehrwertigen Kanten müssen entsprechend viele<br />
Marken bei den Eingangsplätze vorhanden sein.<br />
– Markierung der Kanten, Kantengewichte: Gewichtung gibt an, wieviele<br />
Marken beim Schalten einer Transition von den Eingangsknoten (Stellen)<br />
der Transition abgezogen und den Ausgangsknoten (Stellen) der Transition<br />
hinzugefügt werden.<br />
¯ Beispiel eines Petri-Netzes<br />
grobe Modellierung einer Materialverwaltung.<br />
Bestellung<br />
Bestellaufnahme<br />
Produktionsauftrag<br />
Lieferauftrag<br />
Produktion<br />
Auslieferung<br />
Lager<br />
Waren<br />
Bestellungen kommen zur Bestellaufnahme, die Lieferaufträge an die<br />
Auslieferung erteilt. Diese bedient sich aus einem Lager und liefert<br />
Ware aus. Um das Lager aufzufüllen, müssen Produktionsaufträge an die<br />
Produktion erteilt werden. Aus der graphischen Darstellung ist ersichtlich,<br />
dass Transitionen Tätigkeiten repräsentieren, während Stellen eher Zustände<br />
darstellen. Marken, die Stellen zugeordnet sind und einen dynamischen Ablauf<br />
ermöglichen, werden erst weiter unten eingeführt.<br />
78
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Definition: Petri-Netz<br />
Ein Petri-Netz ist ein Tripel (S, T, F) mit<br />
S ist eine endliche Menge von Stellen (engl. place)<br />
T ist eine endliche Menge von Transitionen (engl. transition) und es gilt: S <br />
T = Ø d.h. Stellen und Transitionen sind disjunkt.<br />
F ist die Flussrelation mit F (S ¢ T) (T ¢ S)<br />
Für einen Knoten x (S T) gilt:<br />
¡x = {y | y F x} den Vorbereich von x<br />
x¡ = {y | x F y} den Nachbereich von x<br />
x kann dabei eine Stelle oder eine Transition sein.<br />
¯ Mit obiger Definition ist die statische Struktur eines Netzes formal erfasst. Die<br />
graphische Darstellung wurde bereits anhand des obigen Beispiel gezeigt.<br />
¯ Für das Beispiel Materialverwaltung gilt beispielsweise:<br />
¡Bestellaufnahme = {Bestellung}<br />
Bestellaufnahme¡ = {Produktionsauftrag, Lieferauftrag}<br />
¯ Verfeinerung<br />
Netzstrukturen können schrittweise verfeinert, konkretisiert werden. Wichtig:<br />
Globale Struktur muss bei Verfeinerung erhalten bleiben, d.h. alle Kanten in<br />
eine verfeinerte Transition und alle Kanten aus dieser verfeinerten Transition<br />
bleiben erhalten. Die innere Struktur der Transition kann wiederum in<br />
Stellen, Transitionen und Kanten aufgeschlüsselt werden. Die zu verfeinernde<br />
Transition kann nach außen hin als Blackbox gesehen werden.<br />
– Beispiel<br />
Verfeinerung der Materialverwaltung: z.B. der Komponente Auslieferung.<br />
Anhand des Lieferauftrages wird der Lieferschein geschrieben, der zusammen<br />
mit dem Produkt verpackt und versandt wird.<br />
79
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Lieferauftrag<br />
Lager<br />
Lieferscheinerstellung<br />
Lieferschein<br />
Verpacken<br />
Auslieferung<br />
Versenden<br />
verpackte<br />
Produkte<br />
Auslieferung ist nach außen hin eine Transition.<br />
Markierung und Schaltregeln<br />
Waren<br />
Zur Erfassung des dynamischen Verhaltens erweitern wir die Definition eines<br />
Petri-Netzes zunächst um Markierungen und geben dann die Schaltregeln an.<br />
¯ Markierung<br />
Gegeben sei ein Petri-Netz (S, T, F).<br />
– Eine Abbildung c: S IN { } gibt die Kapazität einer Stelle an. Wenn<br />
in den von uns verwendeten Netzen die Stellen nicht explizit markiert sind,<br />
dann bedeutet dies eine unbeschränkte Kapazität.<br />
– Eine Abbildung w: F IN {0} gibt die Gewichtung einer Kante an. Wenn<br />
in den von uns verwendeten Netzen die Kanten nicht explizit markiert sind,<br />
dann bedeutet dies eine implizite Gewichtung mit dem Gewicht 1.<br />
– Eine Abbildung M: S IN heißt natürlichzahlige Markierung der Stellen.<br />
Die Markierung beschreibt einen Zustand des Netzes.<br />
Es muss gelten: s S: M(s) c(s)<br />
Ein solches Netz heißt Stellen-Transitionsnetz<br />
– Falls gilt M: S IB, dann heißt das Netz: Bedingungs/Ereignisnetz<br />
oder Boolesches Netz. Demnach entsprechen Boolsche Petri-Netze<br />
natürlichzahligen Petri-Netzen, bei denen jede Stelle die Kapazität 1 hat<br />
(oder einen boolschen Wert mit true (=1) bzw. false (=0) hat). Eine Transition<br />
80
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
t kann nur dann schalten, wenn alle Stellen, zu denen Kanten von t aus führen,<br />
mit false markiert sind.<br />
¯ Schaltregeln<br />
Das Verhalten eines Netzes wird durch Schaltvorgänge beschrieben. Gegeben<br />
sei ein Petri-Netz (S, T, F), die Funktionen c, w und eine Anfangsmarkierung<br />
M0.<br />
– Ein Zustandsübergang erfolgt durch das Schalten von Transitionen, wobei<br />
gilt: Eine Transition t T kann schalten (ist transitionsbereit), wenn folgende<br />
Bedingungen erfüllt sind:<br />
Für alle s ¡tgilt: M(s) w((s,t))<br />
Für alle s t¡ gilt: M(s) c(s) - w((t,s))<br />
w(s,t) spezifiziert die für die Kante s t erforderliche Marken. Der zweite<br />
Fall bedeutet, dass vor Schalten der Transition die aktuelle Markierung der<br />
Stelle kleiner oder gleich als die Kapazität der Stelle minus der zugehörigen<br />
Kantengewichtung ist.<br />
– Durch das Schalten von t wird eine Folgemarkierung M' zu M erzeugt, mit<br />
Für alle s ¡t\t¡ gilt: M'(s) = M(s) - w((s,t))<br />
Für alle s t¡ \ ¡t gilt: M'(s) = M(s) + w((t,s))<br />
Für alle s (¡t t¡) gilt: M'(s) = M(s) - w((s,t)) + w((t,s))<br />
Sonst: M'(s) = M(s)<br />
– Das Modell trifft keine Festlegungen über die Schaltdauer einer Transition,<br />
man geht davon aus, dass das Schalten keine Zeit erfordert. Die Definition<br />
besagt, dass eine Transition nur schalten kann, wenn in ihren Eingangsstellen<br />
mindestens soviele Marken liegen, wie die Gewichtung der jeweiligen<br />
Kanten angibt und wenn außerdem gewährleistet ist, dass durch das Schalten<br />
die Kapazität ihrer Ausgangstellen nicht überschritten wird. Durch das<br />
Schalten werden entsprechend der Kantengewichtung Marken von allen<br />
Eingangstellen abgezogen und ebenfalls entsprechend der Gewichtung<br />
Marken den Ausgangstellen hinzugefügt.<br />
– Beispiel: Schalten einer Transition<br />
Gegeben sei eine Kantengewichtungsfunktion w, die jede Kante mit 1<br />
gewichtet, also<br />
w: F 1<br />
81
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
vor dem Schalten nach dem Schalten<br />
– Beispiel: Schalten mit Kantengewicht<br />
2<br />
3<br />
vor dem Schalten nach dem Schalten<br />
– Beispiel: nichtschaltbare Transition<br />
In der linken Situation enthält die Stelle s2 zu wenige Tokens, da gemäß<br />
der Kantenmarkierung bei jedem Schalten der Transition t1 zwei Marken<br />
von der Stelle s2 konsumiert werden. In der rechten Situation liegt dagegen<br />
ein Markenüberfluss vor. Die Transition t1 kann nicht schalten, da auf<br />
der Stelle s4 bereits ein Token liegt und damit die Kapazität der Stelle (1)<br />
bereits erschöpft ist. Durch ein Schalten würde ein weiteres Token der Stelle<br />
hinzugefügt werden und die Kapazität überschreiten.<br />
s1<br />
s2<br />
t1 s3 s1<br />
t1<br />
s4<br />
s2<br />
2 2<br />
Markenmangel Markenüberfluss bei s4<br />
82<br />
2<br />
3<br />
s3<br />
s4<br />
Kapazität 1
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Animation Petrinetz<br />
siehe online Version<br />
Nebenläufigkeit<br />
Mit Petri-Netzen lassen sich nebenläufige Systeme auf einfache Weise modellieren.<br />
Betrachten wir zum Beispiel vier Aktivitäten t1, ... , t4, wobei jede Aktivität<br />
durch eine Transition modelliert wird. Nach Beendigung von t1 (z.B. Einlesen<br />
von Eingabewerten) können t2 (z.B. Berechne ggt) und t3 (z.B. Berechne Fibonacci)<br />
nebenläufig aktiv sein, aber sie müssen beide beendet sein, bevor t4 ausgeführt<br />
wird.<br />
t1<br />
t2<br />
t3<br />
¯ Nichtdeterminismus<br />
Bei einer gegebenen Markierung M können Konflikte hinsichtlich der<br />
Schaltbereitschaft von Transitionen auftreten. Zwei Transitionen t1 und t2 sind<br />
im Konflikt, wenn sie gemeinsame Eingangs- und Ausgangsstellen besitzen, die<br />
so markiert sind, dass nur eine von beidem Transitionen schalten kann. Es<br />
erfolgt eine nichtdeterministische Auswahl.<br />
– Beispiel<br />
Erzeuger/Verbraucher mit Konfliktbelegung. Nach dem nebenläufigen<br />
Schalten der Transitionen a und b des Netzes (siehe Situation oben) ergibt<br />
sich eine Konfliktbelegung (siehe Situation unten), in der nur entweder die<br />
Transition c oder die Transition d schalten kann.<br />
83<br />
t4
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
a c e d b<br />
Erzeuger Verbraucher<br />
a c e d b<br />
Eigenschaften von Netzen<br />
nebenläufiges<br />
Schalten<br />
Konflikt<br />
belegung<br />
bzgl c und d<br />
Für ein gegebenes Petri-Netz, das das Verhalten eines verteilten Systems<br />
modelliert, sind wir daran interessiert, zu klären, ob das System bei gegebener<br />
Anfangsmarkierung bestimmte Eigenschaften besitzt. Eigenschaften sind die<br />
Erreichbarkeit und die Lebendigkeit.<br />
¯ Erreichbarkeit<br />
Häufig ist man an der Frage interessiert, ob das Netz ausgehend von einer<br />
Markierung M irgendwann eine Folgemarkierung M' erreicht. Das ist die Frage<br />
der Erreichbarkeit von Zuständen.<br />
– Erreichbare Markierung<br />
Gegeben sei ein Petri-Netz (S, T, F) mit der Markierung M. Eine endliche<br />
Sequenz = t1, t2, ..., tn mit t T heißt von M aktivierte endliche<br />
i<br />
Schaltfolge, wenn Markierungen M1, M2, ..., Mn existieren mit<br />
t1<br />
M M1<br />
t2<br />
M2<br />
tn<br />
Mn<br />
ρ<br />
d.h. M Mn<br />
£ M' ist von M erreichbar, wenn es eine Sequenz gibt, die von M<br />
in den Endzustand M' führt. Um die erreichbaren Markierungen eines<br />
Netzes zu erfassen, modelliert man das Netz als einen Zustandsautomat,<br />
84
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
der üblicherweise als Graph dargestellt wird (Erreichbarkeitsgraph). Die<br />
Knoten des Graphen sind die erreichbaren Markierungen und die Kanten<br />
sind mit Transitionen beschriftet.<br />
– Beispiel: Bahnnetz<br />
Vier Städte sind durch Bahngleise, die nur in einer Richtung befahrbar sind,<br />
im Kreis verbunden. Zwei Züge fahren auf der Strecke.<br />
Aufgabe: Das System ist so zu konstruieren, dass sich niemals beide Züge<br />
auf derselben Strecke befinden.<br />
Lösung: Die Strecken werden mit Stellen s1, ... , s4 modelliert. Eine<br />
Marke auf der Stelle s bedeutet, dass ein Zug auf der i-ten Strecke fährt.<br />
i<br />
Durch die zusätzlichen Kontrollstellen k1, .. , k4 soll garantiert werden,<br />
dass in keiner erreichbaren Markierung mehr als eine Marke auf einer der<br />
Stellen s liegt. k kontrolliert den Zugang zur Strecke s (Stelle).<br />
i i i<br />
t3<br />
t4<br />
t3<br />
s4 k4<br />
s1<br />
s3<br />
k1, s2, k3, s4<br />
k1<br />
k3<br />
t2 t4<br />
k2<br />
k1, k2, s3, s4 s1, s2, k3, k4<br />
t4<br />
s1, k2, s3, k4<br />
k1, s2, s3, k4 s1, k2, k3, s4<br />
t2<br />
t1 t3<br />
t1<br />
t1<br />
t2<br />
s2<br />
Bahnnetz<br />
Erreichbarkeitsgraph<br />
Die Zustände werden durch die Menge der markierten Stellen in einer<br />
erreichbaren Markierung beschrieben. Der Erreichbarkeitsgraph zeigt, dass<br />
kein Zustand ereichbar ist, in dem mehr als eine Marke, also mehr als ein<br />
Zug, auf einer Stelle s i liegt. Damit ist die gewünschte Eigenschaft korrekt<br />
85
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
modelliert. Die Frage, ob es einen Algorithmus gibt, der entscheidet, ob<br />
eine Markierung aus einer gegebenen Anfangsmarkierung aus erreichbar<br />
ist oder nicht, (Entscheidbarkeit des Erreichbarkeitsproblems), war ca. 10<br />
Jahre lang offen. Prof. E.W. Mayr hat die Entscheidbarkeit 1980 in seiner<br />
Dissertation (an der TUM) bewiesen. Aber: der Algorithmus besitzt sehr<br />
hohe Komplexität, er ist nicht effizient durchführbar.<br />
¯ Lebendigkeitseigenschaften<br />
Wie zu Beginn des Kapitels bereits erwähnt, verwendet man Systemmodelle<br />
häufig, um Lebendigkeitseigenschaften zu analysieren. Stellen-<br />
Transitionsnetze werden oft in Bereichen verwendet, in denen es auf die Anzahl<br />
und die Verteilung veränderlicher Objekte ankommt. (z.B. Daten in einem<br />
Rechner, Waren in einem Lager, Werkstücke). Man ist daran interessiert zu<br />
erkennen, ob es in einem System zu Blockierungen kommen kann, so dass Teile<br />
des Systems blockiert sind oder der gesamte Ablauf zum Stillstand kommt. Ursachen<br />
für solche Situationen sind Mangel oder Stau, der durch die veränderlichen<br />
Objekte ausgelöst wird.<br />
– Netzdarstellung<br />
aktive Systemelemente als Transitionen (Prozessor, Maschine, etc.)<br />
passive Systemteile als Stellen (Speicher, Lager, etc.)<br />
veränderliche Objekte als Marken<br />
Für Lebendigkeitsuntersuchungen sind Netzteile interessant, die niemals<br />
markiert werden oder die niemals ihre Marken verlieren.<br />
– Definition<br />
Gegeben sei ein Petri-Netz (S, T, F) mit der Anfangsmarkierung M0.<br />
£ Das Netz heißt lebendig, wenn für jede erreichbare Markierung M und für<br />
jede Transition t T eine Markierung M' existiert, die aus M erreichbar ist<br />
und in der t transitionsbereit ist. Informell: jede Transition t kann immer<br />
wieder schalten.<br />
£ Die von M0 aus erreichbare Markierung M beschreibt eine vollständige<br />
Verklemmung (eng. deadlock), wenn es keine Transition t T gibt, die<br />
unter M schalten kann.<br />
£ Die von M0 aus erreichbare Markierung M beschreibt eine lokale<br />
Verklemmung, wenn es eine Transition t T gibt, so dass ausgehend von<br />
M keine Folgemarkierung M' erreichbar ist, in der t transitionsbereit ist.<br />
£ Ist (S, T, F) mit Anfangsmarkierung M0 lebendig, dann ist es auch<br />
verklemmungsfrei. Lebendige Netze stellen sicher, dass es weder zu<br />
einem Markenmangel noch zu einem Überfluss kommt. Die Eigenschaft<br />
86
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
der Lebendigkeit eines Netzes ist insbesondere für Systeme, die für einen<br />
Endlosbetrieb ausgelegt sind (z.B. Betriebssysteme) wichtig.<br />
– Beispiel: Lebendiges Netz<br />
Aufgabe: Modellierung eines FIFO-Puffers mit Kapazität 3. Und zwar so,<br />
dass er verklemmungsfrei und lebendig ist.<br />
Lösung: Das System besteht aus 3 Zellen, die jeweils eine Nachricht<br />
aufnehmen können die Stellen repräsentieren die Speicherzellen). Die<br />
Transition t1 modelliert das Eingeben einer neuen Nachricht und die<br />
Transition t4 modelliert die Ausgabe der Nachricht. Mit den Transitionen<br />
t werden die Nachrichten von Zelle s zur Zelle s weitergereicht.<br />
i i-1 i<br />
Voraussetzung ist, dass die entsprechende Zelle leer ist. Der Zustand "Zelle<br />
s ist leer", wird durch die markierte Stelle k modelliert.<br />
i i<br />
Das modellierte Netz ist verklemmungsfrei und lebendig. Falls der FIFO<br />
Puffer in ein größeres System eingebunden ist, müssen die Transition t1 (für<br />
die Eingabe von Nachrichten) und t4 (für die Ausgabe von Nachrichten) in<br />
das Gesamtsystem integriert werden.<br />
t1 t2 t3 t4<br />
Eingabe einer<br />
Nachricht<br />
k1 k2 k3<br />
s1 s2 s3<br />
Ausgabe einer<br />
Nachricht<br />
– Beispiel: Verklemmung<br />
2 Studenten benötigen ein 2-bändiges Lehrbuch. Student 1 leiht sich<br />
zunächst nur Band 1 aus und Student 2 leiht sich vorsorglich den noch<br />
vorhandenen Band 2 aus. Bevor Student 1 seinen ersten Band zurückgibt,<br />
möchte er noch den zweiten ausleihen. Auch Student 2 gibt seinen<br />
ausgeliehenen Band nicht zurück, sondern versucht, den ersten Band<br />
auszuleihen.<br />
£ Vor der Ausleihe<br />
Anfangszustand des Netzes vor der Ausleihe. Die nachfolgende<br />
Abbildung zeigt zunächst das Netz, bevor einer der Studenten ein Buch<br />
ausleiht und die zweite Abbildung zeigt das Netz, nachdem die Studenten<br />
die jeweiligen Bände ausgeliehen haben.<br />
87
Schlichter, TU München 3.3. MODELLIERUNG PARALLELER SYSTEME<br />
Student 1 Band1 Band 2 Student 2<br />
Band 1<br />
ausleihen<br />
Band 2<br />
ausleihen<br />
beide Bände<br />
zurückgeben<br />
Band 2<br />
ausleihen<br />
Band 1<br />
ausleihen<br />
beide Bände<br />
zurückgeben<br />
£ Nach der Ausleihe<br />
Zustand des Netzes nach dem Schalten Transitionen, d.h. nach der<br />
Ausleihe. Diese Abbildung zeigt, dass das Netz verklemmt ist; es<br />
gibt keinen Systemfortschritt mehr. Was ist das Problem unseres<br />
Modells? Bei Student 1 und Student 2 haben die Ausleihe der Bänder<br />
unterschiedliche Reihenfolgen; bei gleicher Reihenfolge der Ausleihe tritt<br />
keine Verklemmung ein.<br />
88
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
Student 1 Band1 Band 2 Student 2<br />
Band 1<br />
ausleihen<br />
Band 2<br />
ausleihen<br />
beide Bände<br />
zurückgeben<br />
¯ Weitere interessante Eigenschaften: nur ganz informell:<br />
Band 2<br />
ausleihen<br />
Band 1<br />
ausleihen<br />
beide Bände<br />
zurückgeben<br />
– Fairness<br />
Gegeben sei ein Netz N mit Anfangsmarkierung M. Das Netz ist unfair für<br />
eine Transition t, wenn es eine unendliche Sequenz gibt, in der t nur endlich<br />
oft auftritt, obwohl t unendlich oft transitionsbereit ist.<br />
– Verhungern<br />
t verhungert (engl. Starvation): Es gibt eine unendliche Sequenz, in<br />
der die Transition t niemals auftritt. Falls unfaire Sequenz: trotz<br />
Transitionsbereitschaft von t.<br />
3.4 Thread-Konzept<br />
Threads sind ein BS-Konzept für die Modellierung und Realisierung von<br />
nebenläufigen Aktionen in einem Rechensystem. Threads (Kontrollflüsse)<br />
beschreiben die Aktivitäten in einem Rechensystem. Sie können als virtuelle<br />
Prozessoren aufgefasst werden, die jeweils für die Ausführung eines zugeordneten<br />
Programms in einem Adressraum verantwortlich sind. Threads konkurrieren um<br />
die physische CPU, um ablaufen zu können. In traditionellen Umgebungen hat<br />
89
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
jeder Prozess genau einen Thread, der den Kontrollfluss des Prozess repräsentiert,<br />
während in Multithreaded-Umgebungen ein Prozess mehrere Threads besitzen<br />
kann.<br />
Prozess<br />
Thread<br />
Betriebssystem<br />
3.4.1 Charakterisierung von Threads<br />
Thread<br />
Benutzer<br />
Adressraum<br />
Aus BS-Sicht ist ein Prozess definiert<br />
durch einen Adressraum<br />
eine darin gespeicherte Handlungsvorschrift in Form eines sequentiellen<br />
Programms<br />
einen oder mehreren Aktivitätsträgern, die dynamisch die<br />
Handlungsvorschrift ausführen Threads<br />
Motivation<br />
Ein Thread ist die Abstraktion eines physischen Prozessors; er ist ein Träger einer<br />
sequentiellen Aktivität. Gründe für die Einführung von Threads:<br />
¯ mehrere Threads ermöglichen Parallelität innerhalb eines Prozesses unter<br />
Nutzung des gemeinsamen Adressraums. Durch die Nutzung des gemeinsamen<br />
Adressraums wird die Interaktion zwischen den Threads (Kommunikation und<br />
gemeinsame Nutzung von Daten) erleichtert. Jeder Thread für sich gesehen<br />
repräsentiert ein streng sequentielles Verarbeitungsmodell.<br />
¯ Aufwand für Erzeugen und Löschen von Threads ist geringer als für Prozesse.<br />
Threads führen nicht so einen großen Ballast an Information mit sich als<br />
Prozesse. Beispielsweise muss bei der Erzeugung eines Prozesses durch<br />
das BS ein neuer Adressraum generiert werden; Threads laufen im bereits<br />
vorhandenen Adressraum ab. Deshalb spricht man bei Threads auch von<br />
90
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
Leichtgewichtsprozesse ("light weight processes"), da sie erheblich weniger<br />
Verwaltungsinformation als normale Prozesse ("heavy weight processes")<br />
benötigen.<br />
¯ Verbesserung der Performanz der Applikationsausführung durch Nutzung<br />
mehrerer Threads. Dieses Argument ist besonders dann von Bedeutung, wenn<br />
die Applikation (und damit der zugehörige Prozess) sowohl CPU- als auch E/Aintensive<br />
Aktivitäten beinhaltet. E/A-Wartezeiten innerhalb einer Applikation<br />
können durch andere rechenbereite Threads der Applikation genutzt werden.<br />
Dagegen gewinnen CPU-dominierte Applikationen durch die Parallelisierung<br />
mittels Threads weniger an Performanz (falls nur eine CPU zur Verfügung<br />
steht).<br />
¯ Threads ermöglichen bei einem Multiprozessor-Rechensystem echte Parallelität<br />
innerhalb einer Applikation.<br />
Prozess vs. Thread<br />
Prozesse und Threads haben unterschiedliche Zielsetzungen:<br />
Prozesse gruppieren Ressourcen,<br />
Threads sind Aktivitätsträger, die zur Ausführung einer CPU zugeteilt<br />
werden.<br />
¯ Threadspezifische Information<br />
Jeder Thread umfasst eine Reihe von Informationen, die seinen aktuellen<br />
Zustand charakterisieren:<br />
Befehlszähler, aktuelle Registerwerte, Keller, Ablaufzustand des Thread.<br />
Ebenso wie ein Prozess befindet sich ein Thread in einem der nachfolgenden<br />
Zustände: rechenwillig, rechnend, wartend, terminiert. Entsprechend den<br />
ausgeführten Aktionen (z.B. Ein-/Ausgabe) und der Zuteilung bzw. des<br />
Entzugs der CPU finden Zustandsübergänge statt. Jeder Thread hat seinen<br />
eigenen Keller, in dem die Unterprogrammaufrufe verwaltet werden. Jeder<br />
Thread hat seinen eigenen Satz an Registern (darunter auch den Befehlszähler),<br />
die gerettet werden müssen, falls einem Thread die CPU entzogen wird. Bei<br />
einer CPU-Zuteilung werden die geretteten Registerwerte des Threads in die<br />
jeweiligen Register geladen.<br />
¯ Prozessspezifische Information<br />
Die nachfolgende Information/Ressourcen wird von allen Threads eines<br />
Prozesses geteilt. Jeder Thread kann sie verwenden bzw. darauf zugreifen.<br />
91
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
Adressraum, globale Variable, offene Dateien, Kindprozesse (z.B. erzeugt<br />
mit fork), eingetroffene Alarme bzw. Interrupts.<br />
Dies sind Eigenschaften des Prozesses und nicht eines einzelnen Threads.<br />
Beispielsweise, wenn ein Thread eine Datei öffnet, dann ist diese Datei auch<br />
für die anderen Threads sichtbar, und sie können in sie schreiben bzw. aus ihr<br />
lesen. Diese Information ist i.a. zwischen Prozessen nicht sichtbar, außer dass<br />
vom Vaterprozess an den Kindprozess Dateideskriptoren vererbt werden.<br />
Beispiel: Web-Server<br />
Ein Prozess kann mehrere Threads umfassen, die unterschiedliche Aufgaben<br />
übernehmen. Beispielsweise kann ein Web-Server einen Verteiler-Thread<br />
("dispatcher") und mehrere Server-Threads ("worker-thread") umfassen.<br />
Anforderung<br />
Verteiler-<br />
Thread<br />
Netzwerk-Verbindung<br />
Web-Serverprozess<br />
Betriebssystem<br />
Server-<br />
Thread<br />
Benutzer<br />
Adressraum<br />
Auf diese Weise ist es möglich den Server als eine Menge von sequentiellen<br />
Threads zu realisieren. Der Verteiler-Thread ist eine unendliche Schleife, die die<br />
Schnittstelle für den Eingang von Service-Anforderungen darstellt.<br />
¯ Der Verteiler-Thread dient zur Annahme von Service-Anforderungen und gibt<br />
sie weiter an einen der Server-Threads zur Bearbeitung. Server-Threads<br />
können bei Eintreffen einer Service-Anforderung jeweils dynamisch erzeugt<br />
werden, und nach Erledigung der Anforderung bzw. nach Zustellung der<br />
Antwort terminiert werden. Zur Verbesserung der Antwortzeiten kann der<br />
Web-Server bereits einen Pool von Server-Threads erzeugen, denen jeweils<br />
dynamisch Anforderungen zugeteilt werden können. Server-Threads werden<br />
nach Bearbeitung einer Anforderung nicht beendet, sondern wieder in den Pool<br />
92
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
der freien Server-Threads eingereiht. Bei Bedarf können zusätzliche Server-<br />
Threads erzeugt werden, falls alle Server-Threads aus dem Pool bereits belegt<br />
sind.<br />
¯ Alle Server-Threads nutzen den gemeinsamen Web-Cache.<br />
3.4.2 Threads in Java<br />
Java unterstützt die <strong>Programmierung</strong> mit Threads, um nebenläufige Abläufe<br />
innerhalb einer Applikation zu ermöglichen. Java Threads können sowohl in Java-<br />
Programmen als auch in Java-Applets verwendet werden. Die Implementierung<br />
der Java Virtual Machine selbst beruht auf Threads: Der Java-Müllsammler<br />
(Garbage collector) ist z.B. ein Thread zum Sammeln von nicht länger<br />
referenzierten Objekten, der nebenläufig zum Java Programm abläuft. Hier<br />
erfolgt nur eine sehr kurze Wiederholung einiger der Aspekte, die bereits in der<br />
Informatik-Einführungsvorlesung behandelt wurden.<br />
Definition<br />
Threads können durch Implementierung der Schnittstelle ÊÙÒÒÐ realisiert<br />
werden. Eine weitere Möglichkeit besteht durch SubClassing der Klasse Thread<br />
selbst, d.h.<br />
ÔÙÐ Ð×× ÊØÙÖÒ×Ø ÜØÒ× ÌÖ ß <br />
¯ Thread Implementierungen überschreiben die run-Methode der Schnittstelle<br />
Runnable.<br />
ÔÙÐ Ð×× ÐÐ ×Ø ÑÔÐÑÒØ× ÊÙÒÒÐ ß<br />
<br />
ÔÙÐ ÚÓ ÖÙÒ ß <br />
¯ Thread Instanzen werden durch den Aufruf der start-Methode der Schnittstelle<br />
Runnable gestartet. Diese Methode sollte durch eine Thread-Implementierung<br />
nicht verändert werden. Die start-Methode ruft die run-Methode auf, wodurch<br />
die Instanz der Thread-Implementierung ausgeführt wird.<br />
ÔÙÐ ×ØØ ÚÓ ÑÒ ËØÖÒ℄ Ö× ß<br />
<br />
ÐÐ ×Ø ÒÛ ÐÐ ×Ø <br />
ÌÖ Ø ÒÛ ÌÖ <br />
Ø ×ØÖØ <br />
93
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
Ergebnisrückgabe<br />
Der Ablauf der Threads ist asynchron, d.h. die Ausführungsreihenfolge einer<br />
Menge von Threads ist nicht fest vordefiniert. Dies ist insbesondere bei<br />
kooperierenden Threads von Bedeutung, z.B. bei der Ergebnisrückgabe eines<br />
Thread an einen anderen Thread. Die Ausführungsreihenfolge ergibt sich<br />
durch die CPU-Zuteilung (Scheduling) durch das Betriebssystem oder das<br />
Laufzeitsystem. Dies bedeutet, dass bei der Kommunikation zwischen Threads<br />
nicht von einem bestimmten Zeitverhalten der Threads ausgegangen werden kann,<br />
d.h. wann welche Threads ausgeführt werden.<br />
¯ Direkter Ansatz<br />
Angenommen jeder Thread liest eine Datei und erzeugt daraus die zugehörige<br />
Hash-Information (z.B. verwendet für verschlüsselte Datenübertragung).<br />
ÔÙÐ Ð×× ÊØÙÖÒ×Ø ÑÔÐÑÒØ× ÊÙÒÒÐ ß<br />
ÔÖÚØ Ð ÒÔÙØ<br />
ÔÖÚØ ÝØ℄ ×Ø<br />
ÔÙÐ ÊØÙÖÒ×Ø Ð ÒÔÙØ ß Ø× ÒÔÙØ ÒÔÙØ <br />
ÔÙÐ ÚÓ ÖÙÒ ß<br />
<br />
×Ø <br />
<br />
ÔÙÐ ÝØ℄ Ø×Ø ß ÖØÙÖÒ ×Ø <br />
ÔÙÐ Ð×× ÊØÙÖÒ×ØÍ×ÖÁÒØÖ ß<br />
<br />
ÔÙÐ ×ØØ ÚÓ ÑÒ ËØÖÒ℄ Ö× ß<br />
<br />
ÓÖ ÒØ Ö× ÐÒØ ß<br />
Ð ÒÛ Ð Ö×℄ <br />
ÊØÙÖÒ×Ø Ö ÒÛ ÊØÙÖÒ×Ø <br />
ÌÖ Ø ÒÛ ÌÖ Ö Ø ×ØÖØ <br />
<br />
ÝØ℄ ×Ø Ö Ø×Ø <br />
Jeder Thread speichert das Ergebnis der Dateibearbeitung in der Variablen<br />
digest, auf die über die Methode getDigest zugegriffen werden kann.<br />
94
Schlichter, TU München 3.4. THREAD-KONZEPT<br />
– Die Ausführung führt zu dem Fehler<br />
Ü ÔØÓÒ Ò ØÖ ÑÒ Ú ÐÒ ÆÙÐÐÈÓÒØÖÜ ÔØÓÒ<br />
Ø ÊØÙÖÒ×ØÍ×ÖÁÒØÖ ÑÒ<br />
Die Variable digest wird vor der Thread-Initalisierung benutzt. Dies liegt<br />
daran, dass das Hauptprogramm nach der Erzeugung des Thread Zugriff<br />
auf die Variable digest erhält, ohne dass der Thread die Möglichkeit der<br />
Intialisierung bzw. zur Berechnung des Wertes für ×Ø erhielt. In einer<br />
single-threaded Umgebung würde dieses Programm korrekt ablaufen, da<br />
nach Ø ×ØÖØ sofort der entsprechende Thread ausgeführt wird. In einer<br />
multi-threaded Umgebung laufen die Threads dagegen asynchron ab, d.h.<br />
die CPU-Zuteilung hängt vom Laufzeitsystem (bzw. Betriebssystem ab).<br />
Man könnte nun den main-Thread künstlich verzögern, jedoch es besteht<br />
eine "Race-Condition" zwischen den kooperierenden Threads und man kann<br />
keine Aussage über die Java Virtual Machine machen, wann welcher Thread<br />
rechnend gesetzt wird.<br />
¯ Callback Ansatz<br />
Nicht das main-Programm holt die Ergebnisse ab, sondern die aufgerufenen<br />
Threads rufen jeweils eine Methode des main-Programms auf, um die<br />
Ergebnisse zu übergeben Callback<br />
ÔÙÐ Ð×× ÐÐ ×Ø ÑÔÐÑÒØ× ÊÙÒÒÐ ß<br />
ÔÖÚØ Ð ÒÔÙØ<br />
ÔÙÐ ÐÐ ×Ø Ð ÒÔÙØ ß Ø× ÒÔÙØ ÒÔÙØ <br />
ÔÙÐ ÚÓ ÖÙÒ ß<br />
<br />
ÝØ℄ ×Ø <br />
ÐÐ ×ØÍ×ÖÁÒØÖ Ö Ú×Ø ×Ø <br />
<br />
ÔÙÐ ÝØ℄ Ø×Ø ß ÖØÙÖÒ ×Ø <br />
ÔÙÐ Ð×× ÐÐ ×ØÍ×ÖÁÒØÖ ß<br />
95
Schlichter, TU München 3.5. SYNCHRONISATION<br />
<br />
ÔÙÐ ×ØØ ÚÓ Ö Ú×Ø ÝØ℄ ×Ø ß<br />
<br />
ÔÙÐ ×ØØ ÚÓ ÑÒ ËØÖÒ℄ Ö× ß<br />
<br />
ÓÖ ÒØ Ö× ÐÒØ ß<br />
Ð ÒÛ Ð Ö×℄ <br />
ÊØÙÖÒ×Ø Ö ÒÛ ÊØÙÖÒ×Ø <br />
ÌÖ Ø ÒÛ ÌÖ Ö Ø ×ØÖØ <br />
<br />
Im Gegensatz zum main-Programm des direkten Ansatzes dient das main-<br />
Programm dieses Ansatzes nur zum Starten der verschiedenen Threads. Es<br />
versucht nicht die Berechnungen der getriggerten Threads direkt zu lesen und<br />
zu verarbeiten. Dies wird durch separate Methode Ö Ú×Ø erledigt.<br />
3.5 Synchronisation<br />
Eine wichtige Systemeigenschaft<br />
betrifft die Synchronisation paralleler Ereignisse, z.B. In einem<br />
Rechensystem konkurrieren parallele Aktivitäten um wiederholt exklusiv (d.h. zu<br />
einem Zeitpunkt darf höchstens eine Aktivität die Ressource nutzen) benutzbare<br />
Ressourcen, wie beispielsweise die CPU, den Drucker etc. Zusätzlich dazu können<br />
aber parallele Aktivitäten auch kooperieren, indem sie Daten über gemeinsam<br />
benutzte exklusive Objekte austauschen oder sich Nachrichten zusenden. In<br />
all diesen Fällen haben wir das Problem, den wechselseitigen Ausschluss (engl.<br />
mutual exclusion) zu gewährleisten, d.h. sicherzustellen, dass nur höchstens ein<br />
Prozess zu einem gegebenen Zeitpunkt eine exklusiv benutzbare Ressource belegt.<br />
3.5.1 Beispiele<br />
Die beiden Beispiele basieren auf der speicherbasierten Prozessinteraktion,<br />
d.h. Prozesse (oder auch Threads) interagieren über gemeinsam zugreifbare<br />
Speicherzellen.<br />
96
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Beispiel: gemeinsame Daten<br />
P1 und P2 sind nebenläufige Prozesse, die in einem Multiprozessorsystem (z.B.<br />
MI) parallel ablaufen. Die Variable x ist gemeinsame Variable. Prozess P1 läuft<br />
auf CPU 0 mit Prozess P2 auf CPU 1 gleichzeitig ab. Das Problem ergibt sich auch<br />
bei quasiparallelem Ablauf (zeitliche Verschränkung der Arbeit der Prozesse). Z<br />
sei ein Zeitpunkt nach Ausführung der Aktionen A und B. Welchen Wert hat x<br />
zum Zeitpunkt Z?<br />
Das Ergebnis des Ablaufs kann je nach zeitlichem Ablauf x = 1, 2, 3 sein.<br />
Der grundlegende Nichtdeterminismus der Nebenläufigkeit wegen der Asynchronität<br />
schlägt hier auf die Ergebnisse der Prozesse durch.<br />
Zeitpunkt Z<br />
int x; x = 0<br />
Prozess P1 Prozess P2<br />
A: x = x + 1<br />
B: x = x + 2<br />
¯ Das Ergebnis ist vom zeitlichen Ablauf abhängig. Es sind folgende Fälle<br />
möglich:<br />
– Fall 1<br />
P1 liest x = 0, erhöht, speichert x = 1;<br />
P2 liest x = 1, erhöht, speichert x = 3; = Wert von x = 3<br />
– Fall 2<br />
P2 liest x = 0, erhöht, speichert x = 2;<br />
P1 liest x = 2, erhöht, speichert x = 3; = Wert von x = 3<br />
– Fall 3<br />
P1 und P2 lesen x = 0;<br />
P1 erhöht, speichert x = 1;<br />
P2 erhöht, speichert x = 2; = Wert von x = 2<br />
97
Schlichter, TU München 3.5. SYNCHRONISATION<br />
– Fall 4<br />
P1 und P2 lesen x = 0;<br />
P2 erhöht, speichert x = 2;<br />
P1 erhöht, speichert x = 1; = Wert von x = 1<br />
¯ Verhinderung des Nichtdeterminismus nur dadurch möglich, dass man<br />
garantiert, dass die Veränderung von x in den beiden Prozessen unter wechselseitigem<br />
Ausschluss (engl. mutual exclusion) erfolgt. Die Asynchronität der<br />
Prozesse muss also genau an dieser Stelle eingeschränkt werden.<br />
Erzeuger-Verbraucher-Problem<br />
Ein Beispiel für einen synchronisierten Zugriff auf eine gemeinsame Ressource,<br />
nämlich einen Puffer, haben wir bereits kennengelernt. Interpretiert man das<br />
bereits angesprochene Petri-Netz (siehe Seite 83) als eine Komposition aus<br />
drei Teilen:<br />
einem Erzeuger von Nachrichten (links),<br />
einem Puffer für Nachrichten (Mitte) und<br />
einem Verbraucher von Nachrichten (rechts).<br />
So kann man sehen, dass durch diese Modellierung gewährleistet wird, dass<br />
Erzeuger und Verbraucher nicht gleichzeitig etwas in den Puffer eintragen und<br />
aus ihm entnehmen können; sie greifen wechselseitig ausgeschlossen zu.<br />
3.5.2 Definition: Wechselseitiger Ausschluss<br />
Gegeben sei ein Petri-Netz (S, T, F) und eine Anfangsmarkierung M0. Wenn<br />
zwei Transitionen t1, t2 T wechselseitig ausgeschlossen sind, dann ist keine<br />
Markierung M' erreichbar, so dass t1 und t2 unter dieser Markierung gleichzeitig<br />
transaktionsbereit sind. Wir sagen, dass Transitionen, die wechselseitig<br />
ausgeschlossen auszuführen sind, kritische Abschnitte (engl critical section,<br />
critical region) eines Systemablaufs modellieren.<br />
¯ Beispiel: gemeinsame Daten<br />
Der Zugriff auf gemeinsame Ressourcen, z.B. auf gemeinsame Variable,<br />
muss koordiniert werden. Bei exklusiven Ressourcen wird die Nutzung<br />
sequentialisiert. Die gemeinsame Ressource wird in einem kritischen Bereich<br />
bzgl. der gemeinsamen Ressource benutzt.<br />
98
Schlichter, TU München 3.5. SYNCHRONISATION<br />
3.5.3 Modellierung<br />
Prozess P1:<br />
main () {<br />
.......<br />
region x do<br />
x = x + 1:<br />
end region<br />
........<br />
}<br />
Prozess P2:<br />
main () {<br />
.......<br />
region x do<br />
x = x + 2:<br />
end region<br />
........<br />
}<br />
Modelliert man parallele Einheiten, die kritische Abschnitte besitzen, durch Petri-<br />
Netze, so sind vier Phasen dieser parallelen Aktivitäten von Interesse:<br />
1. Ausführen unkritischer Abschnitte/Transaktionen<br />
2. Betreten eines kritischen Abschnitts<br />
3. Ausführen der Transaktion(en) des kritischen Abschnitts<br />
4. Verlassen des kritischen Abschnitts.<br />
Modellierung jeder Phase durch eine Transition; Koordinierung des wechselseitigen<br />
Ausschluss durch Kontrollstelle s.<br />
t1<br />
Prozess 1<br />
t2<br />
t3<br />
t4<br />
Eintritt<br />
k.A.<br />
s<br />
Eintritt<br />
Austritt Austritt<br />
Prozess 2<br />
t1: Phase 1; unkritische Transition<br />
t2: Phase 2; Eintritt in kritischen Abschnitt<br />
t3: Phase 3; Ausführung des kritischen<br />
Abschnitts<br />
t4: Phase 4; Verlassen des kritischen<br />
Abschnitts<br />
s: Kontrollstelle<br />
¯ Beispiel: Leser-Schreiber-Problem<br />
Betrachten wir als Beispiel ein weiteres klassisches Synchronisationsproblem:<br />
das Leser-Schreiber-Problem. Eine einfache Version des Leser-Schreiber-<br />
Problems ist wie folgt gegeben. Das System umfasst Lese-Aktivitäten, die in<br />
99
Schlichter, TU München 3.5. SYNCHRONISATION<br />
ihrem kritischen Abschnitt lesend auf eine gemeinsame Ressource zugreifen<br />
und Schreib-Aktivitäten, die in ihrem kritischen Abschnitt schreibend auf die<br />
gemeinsame Ressource zugreifen. Wir fordern:<br />
1. Lese-Aktionen im kritischen Abschnitt können parallel stattfinden,<br />
wobei die Anzahl der parallelen Leser begrenzt sei, z.B. auf drei.<br />
2. Lese- und Schreib-Aktionen sind wechselseitig ausgeschlossen.<br />
3. auch Schreib-Aktionen sind untereinander wechselseitig ausgeschlossen.<br />
Die Abbildung zeigt eine mögliche Modellierung mittels eines Petri-Netzes mit<br />
drei Lesern und drei Schreibern. Die Anfangsmarkierung der Stelle s beschreibt<br />
die Anzahl der Leser, die maximal parallel im kritischen Abschnitt arbeiten<br />
dürfen. Will ein Schreiber den kritischen Abschnitt betreten, so müssen<br />
alle drei Marken auf s liegen (kein Leser im kritischen Abschnitt). Durch<br />
die Kantengewichtung, werden beim Betreten des kritischen Abschnitts alle<br />
Marken von s durch den Schreiber konsumiert. Beim Verlassen des kritischen<br />
Abschnitts, werden wieder drei Marken produziert, also allen Lesern wieder ein<br />
Zugriff ermöglicht.<br />
Leser Schreiber<br />
Eintritt<br />
Eintritt<br />
k.A. k.A.<br />
s<br />
3<br />
3<br />
Austritt Austritt<br />
3.5.4 Synchronisierungskonzepte<br />
Ziel: Einführung wichtiger Realisierungskonzepte zur Synchronisierung paralleler<br />
Abläufe. Dazu Konkretisierung des Prozessbegriffs. Um die modellierten<br />
100
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Eigenschaften eines System zu realisieren, benötigt man Konzepte zur Formulierung<br />
paralleler Abläufe. Auf der programmiersprachlichen Ebene sind dies<br />
Sprachkonstrukte, wie die Java-Threads oder die Ada-Tasks. Für das Folgende<br />
benötigen wir einen Prozessbegriff, der den abstrakten Prozessbegriff (siehe<br />
Seite 68) zunächst nur so konkretisiert, dass wir damit erste wichtige Konzepte<br />
zur Realisierung von Abhängigkeiten zwischen parallelen Aktivitäten einführen<br />
können. Dies ist ein erster Schritt in die Richtung auf einen Prozess, wie er auf<br />
der Betriebssystemebene benötigt wird. Auf die weitere Konkretisierung eines<br />
Prozesses als Betriebssystem-Verwaltungseinheit zusammen mit den Maßnahmen<br />
zur seiner Verwaltung gehen wir in den folgenden Abschnitten ein.<br />
Prozess - Konkretisierung<br />
Ein Prozess ist ein eindeutig identifizierbarer Ablauf eines Programms in<br />
einem Rechensystem. Der Ablauf ist bestimmt durch die Befehle und Daten<br />
des Programms. Ein Prozess kann unterschiedliche Zustände besitzen. Wir<br />
unterscheiden die Zustände erzeugt, rechnend, rechenwillig, wartend, terminiert.<br />
Die Zustände eines Prozesses und Beispiele für Aktionen, die Zustandsübergänge<br />
zur Folge haben, sind in der Abbildung angegeben.<br />
fork start<br />
erzeugt<br />
terminiert<br />
end<br />
kill<br />
rechnend<br />
CPU<br />
Zuteilung<br />
E/A-Auftrag<br />
CPU-Entzug<br />
rechenwillig<br />
wartend<br />
E/A-<br />
Auftrag<br />
beendet<br />
Zur Verwaltung eines Prozesses wird eine Datenstruktur benötigt, die alle Information,<br />
die einen Prozess charakterisiert, beinhaltet. Dies ist der Prozess-<br />
Kontext (Prozess-Deskriptor), der auf dieser Konkretisierungsebene den eindeutigen<br />
Prozessnamen und die Prozesszustände umfasst und später noch erweitert<br />
wird (z.B. Priorität, Registerinhalte). Dazu gibt es in der Modellmaschine<br />
das Programmstatuswort PSW, das alle relevanten Kontextinformation eines Programmablaufes<br />
speichert.<br />
101<br />
kill
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Konzepte für wechselseitigen Ausschluss<br />
Die Netz-Modellierung hat bereits gezeigt, dass man zur Synchronisation von<br />
Prozessen spezifische Kontrollkomponenten benötigt (z.B. zusätzliche Stellen<br />
im Petri-Netz, oder Kapazitätsbeschränkungen, die implizit durch die abstrakte<br />
Kontrollkomponente, die die Transitionsbereitschaft von Transitionen prüft,<br />
kontrolliert werden). Weiterhin hat die Modellierung gezeigt, dass durch<br />
die Synchronisationsmaßnahmen, die ja im wesentlichen durch absichtlich<br />
herbeigeführte Konflikte modelliert wurden, ggf. unfaire Abläufe auftreten, die in<br />
einem realisierten System natürlich unerwünscht sind. Das heißt, wir benötigen<br />
geeignete Konzepte, durch die diese Kontrollaufgaben wahrgenommen werden<br />
und ein unfaires Verhalten vermieden wird.<br />
¯ Anforderungen<br />
Folgende Anforderungen sind an eine Realisierung des wechselseitigen<br />
Ausschlusses (w.A.) zu stellen:<br />
– Die kritischen Abschnitte der Prozesse sind wechselseitig auszuschließen.<br />
– Eine Realisierung des w.A. darf nicht von einer Annahme über die<br />
Reihenfolge, in der die Prozesse ihre kritischen Abschnitte ausführen,<br />
abhängen.<br />
– Eine Realisierung des w.A. darf nicht von Annahmen über die Ausführungszeit<br />
der Prozesse abhängen.<br />
– Unter der Annahme, dass Prozesse nach endlicher Zeit ihre kritischen<br />
Abschnitte wieder verlassen, muss gelten, dass kein Prozess einen anderen<br />
Prozess unendlich lange daran hindern darf, seinen kritischen Abschnitt<br />
auszuführen.<br />
¯ Jede Realisierung des w.A. benötigt eine Basis, mit festgelegten atomaren,<br />
d.h. nicht teilbaren Operationen. Diese unteilbaren Basisoperationen sind<br />
von Hard- und/oder Software zur Verfügung zu stellen. Informell: mit einer<br />
atomaren Operation kann überprüft werden, ob der kritische Abschnitt frei ist;<br />
falls ja kann er sofort betreten werden (und der kritische Abschnitt damit belegt<br />
werden). Falls die Abfrage und das Betreten nicht atomar sind, können mehrere<br />
zeit-verzahnte Abfrage stattfinden, und gegebenenfalls mehrere Prozesse den<br />
kritischen Abschnitt betreten.<br />
¯ Unterbrechungssperre<br />
Der Ablauf des Prozesses darf nicht unterbrochen werden.<br />
102
Schlichter, TU München 3.5. SYNCHRONISATION<br />
– In Ein-Prozessorsystemen kann es ausreichend sein, mit Enable und Disable<br />
Interrupt Operationen dafür zu sorgen, dass der Prozess, der einen kritischen<br />
Abschnitt ausführt, dabei nicht unterbrochen wird. Dies ist nur eine<br />
sinnvolle Vorgehensweise, wenn die kritischen Abschnitte kurz sind, da<br />
ansonsten die Systemantwortzeiten sehr hoch werden und u.U. E/A-Aufträge<br />
nicht rechtzeitig behandelt werden. Atomarität durch Unterbrechungssperre<br />
funktioniert nicht unbedingt bei Mehrprozessormaschinen.<br />
– Realisierung mit Unterbrechungssperre ist nützlich für den Betriebssystemkern,<br />
aber sollte nicht für allgemeine Benutzerprogramme zur Verfügung stehen.<br />
Maschine 'hängt', wenn Benutzerprogramm den kritischen Abschnitt<br />
nicht mehr verlässt.<br />
¯ Test-and-Set Operationen<br />
Test-and-Set Operationen (Test-And-Set-Lock, TSL) erlauben auf Hardware-<br />
Ebene (Maschinenbefehl) das Lesen und Schreiben einer Speicherzelle als<br />
atomare Operation.<br />
– Semantik eines Test-and-Set-Befehls<br />
Die Instruktion liest den Inhalt eines Speicherwortes in ein Register und<br />
schreibt einen Wert ungleich Null an die Adresse des Wortes. Diese beiden<br />
Operationen werden als atomare, unteilbare Sequenz ausgeführt. Durch<br />
Hardware bereitgestellte atomare Lese- und Schreiboperationen findet man<br />
z.B. in Sun SPARC mit Befehl: compare and swap; entsprechendes gibt es<br />
bei Motorola, Intel 80x86, Mips R4000.<br />
£ Problem: wie Atomarität gewährleistet?<br />
£ Lösung: Die CPU, die die TSL-Instruktion ausführt, blockiert den<br />
Speicherbus, um andere CPU's (Multi-Prozessorumgebung) daran zu<br />
hindern, auf die Speichereinheit zuzugreifen.<br />
– Atomare MI-Befehle<br />
Die Modellmaschine MI unterstützt die beiden atomaren Befehle JBSSI<br />
("Jump on bit set and set interlocked") und JBCCI ("Jump on bit cleared<br />
and clear interlocked")<br />
£ Bitfeld<br />
Ein Bitfeld ist eine lückenlose Folge von bits, die über Byte-Grenzen<br />
hinweg gehen kann. Die maximale Länge S eines Bitfelds in MI ist 32<br />
bit, d.h. S liegt zwischen 0 .. 32. Die Kennzeichnung eines Bitfeldes B(P,<br />
S, A) erfolgt durch drei Parameter:<br />
103
Schlichter, TU München 3.5. SYNCHRONISATION<br />
A: Adresse der Speicherzelle<br />
P: Adresse eines Bit (Bitnummer) relativ zum Bit b0 im Byte mit der<br />
Adresse A; P kann auch negativ sein !<br />
S: Länge des Bitfeldes, 0 S 32.<br />
£ JBSSI a1, a2, a3<br />
Æ Es wird das Bitfeld B(S[a1], 1, a2) angesprochen. Dieses umfasst genau<br />
ein Bit b. Unteilbare Ausführung von<br />
ØÑÔ ÙÖ ØÐ ÒÒÞ ÒØ× Ø <br />
Ö ÙÖ ØÐ ÒÒÞ ÒØ× Ø <br />
ØÑÔ ß È <br />
PC ist hier der Befehlszähler; das Programm wird an der Adresse a3<br />
fortgesetzt, falls der Wert des Bitfeldes 1 war. Andernfalls setze mit<br />
dem nachfolgenden Befehl fort.<br />
£ JBCCI a1, a2, a3<br />
Æ Es wird das Bitfeld B(S[a1], 1, a2) angesprochen. Dieses umfasst genau<br />
ein Bit b. Unteilbare Ausführung von<br />
ØÑÔ ÙÖ ØÐ ÒÒÞ ÒØ× Ø <br />
Ö ÙÖ ØÐ ÒÒÞ ÒØ× Ø <br />
ØÑÔ ß È <br />
¯ Dienste mit passivem Warten<br />
Unterscheidung zwischen<br />
aktivem Warten: Prozess muss periodisch selber prüfen, ob die Voraussetzungen<br />
zum Weiterarbeiten erfüllt sind.<br />
passivem Warten: Prozess wird in Warte-Zustand versetzt und aufgeweckt,<br />
wenn das Ereignis, auf das er wartet, eingetreten ist.<br />
Aktives Warten hat den Nachteil, dass für andere Prozesse nutzbare CPU-<br />
Zyklen durch das wiederholte Anfragen, ob die Voraussetzungen zum<br />
Weiterarbeiten erfüllt sind, nutzlos verschwendet werden. Nachfolgend werden<br />
einige Dienste für passives Warten kurz aufgelistet.<br />
– Methoden oder Dienste, so dass unter Mithilfe des Betriebssystems ein<br />
Prozess in den Zustand wartend übergeführt wird.<br />
– beim Eintreten eines passenden "Weckereignisses" wird der Prozess<br />
vom Betriebssystem vom Zustand wartend in den Zustand rechenbereit<br />
übergeführt.<br />
– Beispiel: Java wait und notify.<br />
Wait: passives Warten auf das Eintreffen einer Bedingung und<br />
atomar gleichzeitig Freigabe der umfassenden Sperre einer synchronized<br />
Methode. Wait/notify nur innerhalb von synchronized nutzbar!<br />
Notify: Benachrichtigen eines wartenden Threads.<br />
104
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Aber: in Java nicht möglich anzugeben, auf WAS ein Thread warten soll<br />
bzw. WELCHE Bedingung eingetreten ist. Nach dem Aufwecken muss<br />
der Thread also noch einmal explizit prüfen, ob der Weckruf den Thread<br />
wirklich betrifft. In Unix gibt es die Aufrufe Sleep and Wakeup; dies<br />
sind Betriebssystemaufrufe, mit denen der Aufrufer blockiert und wieder<br />
rechenbereit gesetzt werden kann.<br />
¯ Semaphor-Konzept<br />
Das Semaphor-Konzept ermöglicht die Realisierung des w.A. auf einem<br />
höheren Abstraktionslevel als die bereits angesprochenen Hardwareoperationen.<br />
Zur Realisierung wird aber auf diese wieder zurückgegriffen. Realisierung<br />
von Semaphoren mit aktivem und passivem Warten möglich. Ein weiterer Synchronisationsansatz<br />
sind Monitore. Darunter versteht man einen abstrakten Datentyp,<br />
dessen Operationen konzeptuell wechselseitig ausgeschlossen sind.<br />
¯ Monitor-Konzept<br />
Das Monitor-Konzept basiert auf der Idee, die in einem kritischen Bereich<br />
bearbeiteten Daten zusammen mit den darauf definierten Zugriffsalgorithmen<br />
in einer sprachlichen Einheit - dem Monitor - zusammenzufassen. Prozesse<br />
können zwar jederzeit die Methoden (Prozeduren) eines Monitors aufrufen,<br />
jedoch können sie nicht direkt auf die internen Daten eines Monitors zugreifen.<br />
Zu jedem Augenblick kann nur ein Prozess innerhalb des Monitors aktiv sein.<br />
Der erfolgreiche Aufruf einer Monitorprozedur ist gleichbedeutend mit der<br />
Sperre des Monitors, die bis zum Verlassen der Monitorprozedur bestehen<br />
bleibt. Die Vorteile des Monitor-Konzepts gegenüber den Semaphoren ist a) die<br />
gemeinsamen Daten werden in der Programmstruktur der beteiligten Prozesse<br />
explizit sichtbar gemacht, und b) Monitore kapseln alle relevanten Daten<br />
und Algorithmen des kritischen Bereichs. Innerhalb eines Monitors können<br />
Condition-Variable definiert werden, die anwendungsspezifische Bedingungen<br />
formulieren. Jede Condition-Variable steht für eine Bedingung, die für die<br />
Fortsetzung eines Prozesses in einer Monitorprozedur erfüllt sein. Falls die<br />
Bedingung nicht erfüllt ist, wird die wait-Operation auf dieser Condition-<br />
Variable ausgeführt. Mit sleep können Prozesse wieder aufgeweckt werden.<br />
3.5.5 Semaphore<br />
Semaphore wurden 1968 von Dijkstra eingeführt. Ein Semaphor (Signalmast)<br />
ist eine ganzzahlige Koordinierungsvariable s, auf der nur die drei vordefinierten<br />
Operationen (Methoden) zulässig sind:<br />
105
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Initialisierung,<br />
Prolog P (kommt von protekt),<br />
Epilog V (kommt von vrej).<br />
Operationen<br />
Die Operationen P und V sind atomar; sie sind unteilbar und sie werden<br />
wechselseitig ausgeschlossen ausgeführt.<br />
Sei s die Koordinierungsvariable, dann sind die P und V Operationen wie<br />
nachfolgend definiert.<br />
Die Operation müssen mit Hilfe von Systemdiensten so realisiert werden, dass<br />
sie wechselseitig ausgeschlossen ausgeführt werden. Vorsicht: hier gibt es keine<br />
ganz einheitliche Definition in der Literatur für die beiden Operationen.<br />
¯ Informelle Charakterisierung<br />
ÔÙÐ ÚÓ È ÒØ × ß<br />
× × <br />
× ß ÈÖÓÞ×× Ò ÅÒ Ö ÞÐ ×<br />
ÛÖØÒÒ ÈÖÓÞ×× ÒÖÒ <br />
<br />
ÔÙÐ ÚÓ Î ÒØ × ß<br />
× × <br />
× ß Ö ÒÙ ÒÒ Ö ÞÐ × ÛÖØÒÒ<br />
ÈÖÓÞ×× Ò Ò Ù×ØÒ Ö ÒÛÐÐ Ö <br />
<br />
s ist mit einem ganzzahligen Wert vorbesetzt, z.B. s = 1. Falls s mit einem Wert<br />
größer 1 vorbesetzt ist, bedeutet dies die Anzahl der Prozesse, die gleichzeitig<br />
im kritischen Bereich erlaubt sind.<br />
¯ Binäres Semaphor: die Kontrollvariable s nimmt nur boolesche Werte an.<br />
Einsatz von Semaphoren<br />
Notation: Zur Vereinfachung gehen wir im Folgenden von einem vordefinierten<br />
Typ ×ÑÔÓÖ ÒØ × aus, der die P und V Operationen als vordefinierte,<br />
atomare Methoden anbietet. Semaphor-Objekte werden als Instanzen bezüglich<br />
des Typs ×ÑÔÓÖ erzeugt, wobei bei der Instantiierung das Semaphor mit dem<br />
Parameter s initialisiert wird.<br />
106
Schlichter, TU München 3.5. SYNCHRONISATION<br />
¯ Zugang zu kritischen Abschnitten<br />
Realisierung der kritischen Abschnitte von Prozessen, in denen auf eine<br />
exklusiv benutzbare Ressource X zugegriffen wird:<br />
1. Definition eines Semaphor-Objekts wa: semaphor(1), d.h. Initialisierung der<br />
Kontrollvariable des Semaphor-Objekts wa mit 1.<br />
2. Klammern der kritischen Abschnitte, in denen auf die Ressource X<br />
zugegriffen wird, mit P und V Operationen:<br />
wa.P<br />
Code mit Zugriffen auf X<br />
wa.V<br />
¯ Die Anforderungen an Lösungen des wechselseitigen Ausschlusses sind mit<br />
dem Semaphor-Konzept aus folgenden Gründen erfüllt:<br />
– Wechselseitiger Ausschluss für alle kritischen Abschnitte. Aufgrund der<br />
Initialisierung der Koordinierungsvariablen mit 1 kann sich stets nur ein<br />
Prozess in einem kritischen Abschnitt befinden.<br />
– keine Reihenfolge-Annahmen. Annahmen über die Ausführungsreihenfolge<br />
der kritischen Abschnitte gehen nicht ein.<br />
– keine Ausführungszeit-Annahmen. Es werden keine Annahmen über die<br />
Ausführungszeiten der Prozesse gemacht.<br />
– kein Verhungern. Hier muss bei der Realisierung des Semaphors eine<br />
faire Strategie, z.B. FIFO (First-In-First-Out), zum Einsatz kommen. Wenn<br />
die blockierten Prozesse aus dem Warteraum nach FIFO entnommen<br />
werden und jeder kritische Abschnitt mit der P- und der V-Operation des<br />
Semaphors geklammert ist, wird jeder blockierte Prozess nach endlicher Zeit<br />
entblockiert und kann somit seinen kritischen Abschnitt ausführen.<br />
¯ Semaphor in MI<br />
MI-Realisierung eines binären Semaphors mit aktivem Warten. Es ist auch<br />
möglich Semaphore in MI auf der Basis des passiven Wartens zu realisieren.<br />
Für ein binäres Semaphor gilt, dass die Semaphor-Variable s nur die Werte 0<br />
oder 1 annehmen kann.<br />
Wirkung von s.P: if s == 1 {s = 0} else {warten bis s.V ausgeführt wird}<br />
Wirkung von s.V: s = 1; falls ein Prozess wartet, dann aufwecken.<br />
Für die Realisierung gehen wir davon aus, dass beim Aufruf von s.P und s.V<br />
die Adresse von s auf den Stack geschrieben wird.<br />
107
Schlichter, TU München 3.5. SYNCHRONISATION<br />
È ÂÁ Á ËÈ <br />
ÊÌ ÙÒ ÞÙÑ ÖØ× Ò Ö <br />
Î ÅÇÎ Ï Á ËÈ<br />
ÊÌ<br />
JBCCI steht für "Jump on Bit Cleared and Cleared Interlocked". Das Bitfeld ist<br />
gegeben durch (31, 1, !(4+!SP)). Die Länge des Bitfeldes ist gleich 1; betrachtet<br />
wird das Bit 31 der durch !(4+!SP) spezifizierten Speicherzellen (4 Bytes).<br />
Falls das durch das Bitfeld spezifizierte Bit gleich 0 ist, erfolgt ein Sprung zur<br />
Adresse a.<br />
Beispiel Erzeuger-Verbraucher<br />
Im Modellierungsteil wurde das Erzeuger-Verbraucher-Problem (siehe<br />
Seite 83) bereits kurz vorgestellt.<br />
Der Erzeuger-Prozess E erzeugt Datenelemente und schreibt sie in einen<br />
Puffer W.<br />
Der Verbraucher-Prozess V liest Datenelemente aus dem Puffer und<br />
verarbeitet sie.<br />
Der Zugriff der beiden Prozesse auf den Puffer ist zu synchronisieren.<br />
Lösung dieses Problems mittels Semaphor.<br />
¯ Variante 1<br />
Zugriff auf Puffer W erfolgt durch Semaphor wa: semaphor(1); sowohl der<br />
Erzeuger-Prozess E als auch der Verbraucher-Prozess V rufen vor jedem Zugriff<br />
auf den Puffer W die entsprechenden Operationen des Semaphors wa auf.<br />
Erzeuger E:<br />
while (true) {<br />
produziere<br />
wa.P<br />
schreibe nach W<br />
wa.V<br />
}<br />
Verbraucher V:<br />
while (true) {<br />
wa.P<br />
entnimm aus W, falls Element da; sonst warte<br />
wa.V<br />
verarbeite<br />
}<br />
Problem: es kann eine Verklemmung auftreten, wenn der Verbraucher wa.P<br />
ausführt und warten muss, weil der Puffer kein Element enthält. Andererseits<br />
kann der Erzeuger den Puffer nicht betreten, da bereits der Verbraucher den<br />
Puffer exklusiv belegt hat.<br />
¯ Variante 2<br />
108
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Einführen eines zusätzlichen Semaphors voll: semaphor(0), das die Datenelemente<br />
im Puffer zählt:<br />
Erzeuger E:<br />
while (true) {<br />
produziere<br />
wa.P<br />
schreibe nach W<br />
wa.V<br />
voll.V<br />
}<br />
Verbraucher V:<br />
while (true) {<br />
voll.P<br />
wa.P<br />
entnimm aus W<br />
wa.V<br />
verarbeite<br />
}<br />
Für den Erzeuger ergibt sich natürlich ein analoges Problem, falls der Puffer W<br />
nur eine beschränkte Kapazität besitzt. Eine Abhilfe kann analog wieder durch<br />
die Einführung eines weiteren Semaphors "leer" erreicht werden. Dieses stellt<br />
sicher, dass der Erzeuger den Puffer nicht betritt, wenn der Puffer bereits voll<br />
ist.<br />
¯ Variante 3<br />
Einführen eines zusätzlichen Semaphors leer: semaphor(n), das die Anzahl der<br />
freien Elemente im Puffer zählt:<br />
wa.semaphor(1); //kontrolliert den Zugang zum kritischen Bereich<br />
voll.semaphor(0); //zählt die Anzahl der Einheiten im Puffer<br />
leer.semaphor(n), //zählt die Anzahl der freien Pufferplätze<br />
Erzeuger E:<br />
while (true) {<br />
produziere Einheit;<br />
leer.P;<br />
wa.P;<br />
schreibe Einheit nach W;<br />
wa.V;<br />
voll.V;<br />
}<br />
Verbraucher V:<br />
while (true) {<br />
voll.P;<br />
wa.P;<br />
entnimm Einheit aus W<br />
wa.V<br />
leer.V;<br />
verarbeiteEinheit;<br />
}<br />
Darf die Reihenfolge der P-Operationen für die Semaphore leer, voll, wa<br />
beim Erzeuger bzw. beim Verbraucher vertauscht werden, ohne dass sich<br />
Ablaufprobleme ergeben?<br />
109
Schlichter, TU München 3.5. SYNCHRONISATION<br />
Beispiel Philosophenproblem<br />
Zu den klassischen Synchronisationsproblemen zählt das Problem der speisenden<br />
Philosophen ("Dining Philosophers"). In einem Elfenbeinturm leben fünf<br />
Philosophen. Der Tageszyklus eines jeden Philosophen besteht abwechselnd aus<br />
Essen und Denken. Die fünf Philosophen essen an einem runden Tisch, auf dem in<br />
der Mitte eine Schüssel voller Spaghetti steht. Jeder Philosoph hat seinen festen<br />
Platz an dem Tisch und zwischen zwei Plätzen liegt genau ein Stäbchen. Das<br />
Problem der Philosophen besteht nun darin, dass die Spaghetti nur mit genau<br />
zwei Stäbchen zu essen sind. Darüber hinaus darf jeder Philosoph nur das direkt<br />
rechts und das direkt links neben ihm liegende Stäbchen zum Essen benutzen. Das<br />
bedeutet, dass zwei benachbarte Philosophen nicht gleichzeitig essen können.<br />
¯ Realisierung mit Semaphoren<br />
4<br />
3<br />
3<br />
4<br />
0<br />
2<br />
– Variante 1<br />
Für eine Lösung des Philosophenproblems seien die folgenden 5 Semaphore<br />
definiert: stab_0, stab_1, ...., stab_4, wobei jedes der 5 Semaphore mit 1<br />
initialisiert ist. Jeder Philosoph j, mit j {0,...,4}, führe den folgenden<br />
Algorithmus aus:<br />
ÔÐÓ×ÓÔ<br />
ÛÐ ØÖÙ ß<br />
ÒÒ<br />
×Ø È ÑØ <br />
×Ø È<br />
××Ò<br />
ÑØ ÑÓ <br />
×Ø Î ÑØ <br />
×Ø Î ÑØ ÑÓ <br />
<br />
Der angegebene Algorithmus liefert keine korrekte Lösung des wechselseitigen<br />
Ausschlusses! Wenn alle fünf Philosophen gleichzeitig die er-<br />
0<br />
110<br />
1<br />
2<br />
1
Schlichter, TU München 3.5. SYNCHRONISATION<br />
ste P-Operation (stab_i.P mit i = j) ausführen, d.h. alle gleichzeitig ihr<br />
linkes Stäbchen nehmen, folgt daraus eine Verklemmungs-Situation, da kein<br />
Philosoph das zweite Stäbchen nehmen kann. Bei Ausführung der zweiten<br />
P-Operation stab_i.P; mit i=j+1 mod 5 werden alle Philosophen blockiert.<br />
Die Philosophen verhungern somit.<br />
– Variante 2<br />
Nur vier Philosophen dürfen gleichzeitig zu ihrem linken Stäbchen<br />
greifen. Dies wird durch Einführung eines weiteren Semaphors Ø× <br />
×ÑÔÓÖ , das mit 4 initialisiert wird, erreicht. Der "Anweisungsteil"<br />
jedes Philosophen wird zusätzlich mit Ø× È und Ø× Î geklammert.<br />
Dadurch ist gewährleistet, dass höchstens vier Philosophen gleichzeitig ihr<br />
linkes Stäbchen nehmen können und somit immer mindestens ein Philosoph<br />
auch sein zweites Stäbchen nehmen und damit essen kann. Es ergibt sich<br />
also folgende Lösung: Jeder Philosoph j, mit j<br />
Algorithmus aus:<br />
ÔÐÓ×ÓÔ<br />
{0,...,4} führt den folgenden<br />
ÛÐ ØÖÙ ß<br />
ÒÒ<br />
Ø× È<br />
×Ø È ÑØ <br />
×Ø È<br />
××Ò<br />
ÑØ ÑÓ <br />
×Ø Î ÑØ <br />
×Ø Î<br />
Ø× Î<br />
ÑØ ÑÓ <br />
<br />
Ein weiterer wichtiger Aspekt ist die Reihenfolge bei der Ausführung<br />
von P Operation, falls mehrere Semaphore belegt werden müssen.<br />
Eine unterschiedliche Reihenfolge der P-Operationen bei den beteiligten<br />
Prozessen kann leicht zu einer Verklemmung führen.<br />
Animation Semaphor<br />
siehe online Version<br />
3.5.6 Synchronisierung von Java Threads<br />
Java unterstützt synchronisierte Methoden.<br />
ÔÙÐ ×ÝÒ ÖÓÒÞ ÚÓ ÑØÓÒÑ ß <br />
Eine synchronisierte Methode kann nur exklusiv von einem Java Thread betreten<br />
werden. Andere Threads können die synchronisierte Methode erst dann betreten,<br />
111
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
wenn der erste Java Thread die Methode wieder verlässt. Einer der wartenden<br />
Threads wird aktiviert.<br />
Java Monitor<br />
Ein Monitor ist ein Java-Objekt, das synchronisierte Methoden enthält.<br />
¯ Ein Monitor stellt sicher, dass nur ein Thread zur Zeit in einer der<br />
synchronisierten Methoden sein kann. Bei Aufruf einer synchronisierte<br />
Methode wird das Objekt gesperrt.<br />
¯ Während das Objekt gesperrt ist, können keine anderen synchronisierten<br />
Methoden des Objekts aufgerufen werden. Andere Threads müssen warten,<br />
bis der erste Thread die synchronisierte Methode wieder verlässt, und damit die<br />
Sperre freigibt.<br />
¯ Kritische Abschnitte können in Java als Objekte mit den zugehörigen<br />
synchronisierten Methoden spezifiziert werden. Synchronisierte Methoden<br />
können je nach der verwendeten Java Virtual Machine sehr aufwendig und<br />
langsam sein. Weiterhin ist zu beachten, dass zwar innerhalb eines Monitors<br />
eine synchronisierte Methode andere synchronisierte Methoden blockiert, nicht<br />
jedoch unsynchronisierte Methoden. Dies kann u.U. zu Problemen führen.<br />
3.6 Verklemmungen<br />
Mit Verklemmung (engl. deadlock) bezeichnet man einen Zustand, in dem die<br />
beteiligten Prozesse wechselseitig auf den Eintritt von Bedingungen warten, die<br />
nur durch andere Prozesse in dieser Gruppe selbst hergestellt werden können.<br />
Verklemmungen können durch die gemeinsame Verwendung von Ressourcen<br />
(synonym verwenden wir auch den Begriff Betriebsmittel), wie z.B. CPU, Arbeitsspeicher,<br />
E/A-Geräte, Dateien auftreten. Der Austausch von Information<br />
über gemeinsame Speicherbereiche ist eine häufige Situation (speicherbasierte<br />
Prozessinteraktion), die bei unkorrekter Verwendung von Synchronisationsoperationen<br />
(z.B. P und V bei Semaphoren) leicht zu Verklemmungen führen<br />
kann; siehe die Variante 1 (siehe Seite 108) des Erzeuger-Verbraucher Lösungsansatzes.<br />
Dieser Abschnitt skizziert nur die Ansätze zur Erkennung, Vermeidung<br />
und Verhinderung von Verklemmungen. Ein ausführliche Behandlung<br />
dieser Thematik erfolgt in der Vorlesung Betriebssysteme des Hauptstudiums. In<br />
vielen Systemen wird eine 'Vogel-Strauß'-Politik in bezug auf die Deadlockproblematik<br />
verfolgt, d.h. es werden keine Maßnahmen eingesetzt, sondern es wird<br />
112
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
gehofft, dass alles gut geht. In Unix wird diese Philosophie z.B. bei der Verwaltung<br />
der Prozesstabelle verfolgt.<br />
3.6.1 Allgemeines<br />
Es lässt sich zeigen, dass die folgenden Bedingungen notwendig und hinreichend<br />
dafür sind, dass eine Verklemmung auftreten kann.<br />
1. Die gemeinsam benutzbaren Ressourcen können nicht parallel genutzt werden,<br />
d.h. sie sind nur exklusiv benutzbar.<br />
2. Die zugeteilten/belegten Ressourcen können nicht entzogen werden, d.h. die<br />
Nutzung ist nicht unterbrechbar.<br />
3. Prozesse belegen die schon zugeteilten Ressourcen auch dann, wenn sie auf<br />
die Zuteilung weiterer Ressourcen warten, d.h. wenn sie weitere Ressourcen<br />
anfordern.<br />
4. Es gibt eine zyklische Kette von Prozessen, von denen jeder mindestens eine<br />
Ressource belegt, die der nächste Prozess in der Kette benötigt, d.h. zirkuläre<br />
Wartebedingung.<br />
3.6.2 Belegungs-Anforderungsgraph<br />
Die Zuteilung/Belegung und Anforderung von Ressourcen kann man sich an<br />
einem Graphen, dem Belegungs-Anforderungsgraph, veranschaulichen. Die<br />
Knoten sind die Prozesse und Ressourcen, die Kanten spiegeln Belegungen und<br />
Anforderungen wider.<br />
¯ Beispiel<br />
Seien P = {P1, ... , Pn} Prozesse und R= {R1, ... , Rm} Ressourcen, z.B. n = 3<br />
und m = 4. Beispiel eines Belegungs/Anforderungsgraphen.<br />
fordert<br />
P1 R1<br />
R2<br />
belegt<br />
fordert<br />
P2<br />
belegt<br />
fordert<br />
113<br />
P3<br />
R3<br />
belegt<br />
belegt<br />
R4
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
P1 und P2 warten gegenseitig aufeinander. P1 wartet auf R1, die durch P2<br />
belegt ist, und P2 wartet auf R2, die durch P1 belegt ist.<br />
3.6.3 Verklemmungs-Erkennung<br />
In der Praxis häufig angewendete Strategie: Verklemmungen in Kauf nehmen,<br />
sie erkennen und beseitigen. Man versucht eine Verklemmung festzustellen<br />
und sie, sollte sie eingetreten sein, zu beseitigen. Indiz für Verklemmungen,<br />
z.B. angeforderte Ressource ist nach einer gewissen Zeit immer noch nicht<br />
zugewiesen.<br />
¯ Erkennungs-Algorithmus<br />
Ansatz 1: Suche nach Zyklen im Belegungs/Anforderungsgraph.<br />
Ansatz 2: Prüfen, ob es eine Reihenfolge für die Ausführung der Prozesse gibt,<br />
so dass alle Prozesse terminieren können.<br />
¯ Vorgehen für Ansatz 2<br />
1. Starte mit Prozessmenge P, die alle Prozesse enthält,<br />
2. suche Prozess p aus P, dessen zusätzliche Anforderungen im aktuellen<br />
Zustand erfüllbar sind,<br />
3. falls gefunden, simuliere, dass p seine belegten Ressourcen wieder<br />
freigibt,<br />
4. entferne p aus P und gehe zu 2<br />
5. falls kein Prozess mehr in P, dann terminiert Suche: keine Verklemmung,<br />
6. falls P Ø und in Schritt 2 kein Prozess mehr gefunden wird, dessen<br />
Anforderungen erfüllbar sind, dann terminiert die Suche; P enthält die<br />
Menge der verklemmten Prozesse.<br />
¯ Auflösung einer Verklemmung in der Regel durch Abbruch einzelner<br />
Prozesse. Dies erfordert in der Regel einen manuellen Eingriff bei der<br />
Auswahl der abzubrechenden Prozesse. Problem: Prozesse müssen erneut<br />
ausgeführt werden, bei Nicht-Determinismus ggf. anderes Verhalten der neuen<br />
Prozessabläufe.<br />
3.6.4 Verklemmungs-Vermeidung<br />
Die Vermeidungsverfahren beruhen darauf, dass man durch die Festlegung von<br />
Regeln dafür sorgt, dass mindestens eine der für das Auftreten von Deadlocks<br />
notwendigen Bedingungen nicht erfüllt ist. Aber: solche Regeln lassen sich nicht<br />
für jedes Verklemmungsproblem finden. Deshalb wird meist ein allgemeinerer<br />
Algorithmus gesucht: Verhinderungs-Algorithmus.<br />
114
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
¯ Festgelegte lineare Reihenfolge<br />
Bedingung "Zyklus tritt auf" in Belegungs-/ Anforderungsgraph darf nicht<br />
erfüllt werden. Dazu wird eine lineare Ordnung über den Ressourcen definiert:<br />
R1 R2 ... Rm.<br />
Die Prozesse dürfen dann Ressourcen nur gemäß dieser Ordnung anfordern,<br />
d.h. ein Prozess, der Ressource Ri belegt, darf nur Ressourcen Rj anfordern,<br />
für die gilt: Rj Ri.<br />
Problem: wie Ordnung festlegen? Daumenregel: wichtige Ressourcen, die<br />
gut ausgelastet genutzt werden sollten, dürfen nicht zulange einem Prozess<br />
zugeordnet werden. Deshalb sollte für eine solche Ressource Rj gelten: Rj<br />
Ri, für unwichtigere Ri.<br />
¯ Andere Möglichkeiten sind:<br />
a) Zuteilung aller benötigten Ressourcen zu einem Zeitpunkt.<br />
b) zwangsweiser Entzug aller belegter Ressourcen, falls eine Ressourcen-<br />
Anforderung nicht erfüllt werden kann. Gerade durch die Virtualisierung<br />
und die Bereitstellung logischer Geräte konnte dieser Ansatz verfolgt<br />
werden. Durch die eingeführte Indirektionsstufe war es möglich, einem<br />
Prozess ohne dessen Wissen physische Ressourcen in kritischen Situationen<br />
zu entziehen und damit eine der 4 notwendigen Bedingungen für das<br />
Entstehen einer Verklemmung außer Kraft zu setzen.<br />
3.6.5 Verklemmungs-Verhinderung<br />
Die Verhinderungsverfahren basieren auf der Idee,<br />
die zukünftigen Betriebsmittelanforderungen von Prozessen zu analysieren<br />
(bzw. diese geeignet abzuschätzen) und<br />
solche Zustände zu verbieten (sie also zu verhindern), die zu Verklemmungen<br />
führen könnten.<br />
Ein Beispiel ist der Bankiers-Algorithmus, der 1965 von Dijkstra entwickelt<br />
wurde.<br />
Veranschaulichung des Algorithmus<br />
Veranschaulichung des Verfahrens anhand eines Bankenszenarios. Das Verfahren<br />
wird im Hauptstudium in der Betriebssystem-Vorlesung ausführlicher behandelt.<br />
115
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
¯ Ausgangspunkt<br />
Idee: Verwaltung von nur einer Ressourcen-Klasse, nämlich den Bankkrediten.<br />
– Bankier besitzt festen Geldbetrag und verleiht Geld an seine Kunden.<br />
– Alle Kunden sind dem Bankier bekannt, jeder Kunde hat einen eigenen<br />
maximalen Kreditrahmen, der kleiner als die zur Verfügung stehende<br />
Geldmenge des Bankiers ist.<br />
– Bankier hat weniger Geld als die Summe dieser Kreditrahmen.<br />
– Kunden können jederzeit Geld in der Höhe ihres jeweiligen Kreditrahmens<br />
fordern, müssen aber ggf. in Kauf nehmen, dass der Bankier diese Forderung<br />
erst nach endlicher Zeit erfüllt.<br />
¯ Aufgabe des Bankiers<br />
Verleihen des Geldes so, dass jeder Kunde seine Geschäfte in endlicher<br />
Zeit durchführen kann und Kunden möglichst parallel bedient werden. Die<br />
sequentielle Abfolge ist natürlich eine triviale Lösung.<br />
Idee: Reihenfolge für Kreditvergabe finden, so dass selbst bei denkbar<br />
ungünstigsten Kreditforderungen die Durchführung aller Geschäfte<br />
sichergestellt ist.<br />
ungünstigster Fall: alle Kunden fordern Geld bis zu ihrem jeweiligen max.<br />
Kreditrahmen, ohne Kredite zurückzuzahlen.<br />
¯ Grobes Vorgehen<br />
1. falls ein Kunde (Prozess) eine Anforderung hat, die aktuell erfüllbar ist,<br />
so teilt man das Geld (die Ressource) probeweise zu und<br />
2. untersucht für den sich damit ergebenden Zustand, ob jetzt eine<br />
Verklemmung vorliegt, indem<br />
3. für alle anderen Kunden von deren ungünstigsten Anforderungen<br />
ausgegangen wird und<br />
4. ein Erkennungs-Algorithmus ausgeführt wird.<br />
Falls keine Verklemmung auftritt, kann die Zuteilung tatsächlich erfolgen,<br />
anderenfalls könnte bei einer Zuteilung ein Deadlock auftreten (muss aber<br />
nicht), weshalb die Anforderung nicht erfüllt wird.<br />
¯ Beispiel<br />
Ausgangspunkt ist die folgende Situation der vier Kunden A, B, C, D (Einheiten<br />
jeweils in Tausend DM):<br />
116
Schlichter, TU München 3.6. VERKLEMMUNGEN<br />
Kunde aktueller Kredit max. Kreditrahmen<br />
A 1 6<br />
B 1 5<br />
C 1 4<br />
D 4 7<br />
Es seien noch 3 Einheiten (Tausend DM) in der Bank als Kredit verfügbar.<br />
– Annahme: Kunde C fordere eine weitere Einheit als Kredit an. Diese<br />
Anforderung wird probeweise zugeteilt und mündet nicht in einem Deadlock,<br />
da zuerst C (max noch 2 Einheiten bis Kreditrahmen) bedient werden kann.<br />
Wenn C seine Einheiten wieder zurückgezahlt hat, können B oder D und<br />
schließlich A bedient werden.<br />
Probleme bei Verhinderungsverfahren: zukünftige maximale Anforderungen<br />
müssen bekannt sein; anderenfalls nur worst-case Abschätzungen<br />
möglich. Algorithmus ist sehr zeit- und speicherplatzaufwendig. Der<br />
Habermann-Algorithmus ist eine Erweiterung des Verfahrens auf verschiedene<br />
Ressourcen.<br />
117
Kapitel 4<br />
Prozess- und Prozessorverwaltung<br />
Erst die Verfügbarkeit von Betriebssystemen gestattet es einem Anwender, die<br />
Anwendungen einfacher zu organisieren und zu realisieren; Betriebssysteme<br />
machen damit eine Rechenanlage zu einem leistungsfähigen, einsetzbaren<br />
Werkzeug. Unter einem Betriebssystem (im weitesten Sinne) verstehen wir<br />
den Komplex von Programmen, dessen Funktionen die Benutzerschnittstelle des<br />
betreffenden Rechensystems bestimmen. Erst das Betriebssystem schafft die<br />
Schnittstelle für einen bequemen Umgang und Zugriff auf die Funktionen der<br />
Rechenanlage.<br />
4.1 Fragestellungen<br />
Dieser Abschnitt gibt eine kurze Einführung in einige wichtige Verwaltungsaufgaben<br />
eines Betriebssystems:<br />
¯ Verwaltung von Prozessen.<br />
¯ Verwaltung des Prozessors, d.h. Zuteilung der CPU an rechenbereite Prozesse<br />
(Scheduling).<br />
¯ Unterbrechungskonzept. Bei Eintreten von bestimmten Ereignissen kann die<br />
Ausführung von Benutzerprogrammen unterbrochen werden, z.B. Warten auf<br />
Eingabe. Dies ermöglicht den Mehrprogrammbetrieb, und damit eine bessere<br />
Ausnutzung und Auslastung aller Geräte eines Rechensystems.<br />
118
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
4.2 Betriebssystem - Überblick<br />
Ein Betriebssystem realisiert die Schnittstelle zwischen dem Benutzer und der physischen<br />
Rechenanlage. Aus der Sicht des Benutzers entsteht durch ein Betriebssystem<br />
eine virtuelle Maschine. Für einen Benutzer ist es nicht wichtig, ob in<br />
einem Rechensystem Systemfunktionen durch Hardware oder Software realisiert<br />
sind. Ein Betriebssystem realisiert insbesondere eine Benutzerschnittstelle. Der<br />
Entwurf und die Implementierung von Betriebssystemen gehören zu den klassischen<br />
Aufgabenstellungen der Systemprogrammierung. Je nach Art der Hardware<br />
gibt es sehr unterschiedliche Typen von Betriebssystemen. Sie reichen von Betriebssystemen<br />
für Großrechner, über Server-BS bis hin zu PC-Betriebsystemen<br />
und eingebetteten Betriebssystemen (z.B. in einem Palm PDA).<br />
4.2.1 BS-Hauptaufgaben<br />
Ein Betriebssystem (engl. operating system) erfüllt folgende Hauptaufgaben:<br />
¯ Veredeln der Hardware (Virtualisierung).<br />
¯ Steuerung und Kontrolle der Programmausführung. Dazu werden die<br />
beiden Betriebsmittel CPU und Arbeitsspeicher geeignet den Programmen<br />
zugeteilt. Zur Durchführung dieser Aufgabe ist eine Prozessverwaltung (die im<br />
System momentan auszuführenden Programme) und eine Benutzerverwaltung<br />
notwendig.<br />
¯ Verwaltung der Ressourcen (Speicher, CPU, Platten, Netz etc.). Die Benutzung<br />
der Ein-/ Ausgabegeräte sowie des Netzanschlusses erfolgt stets über das<br />
Betriebssystem.<br />
¯ Anbieten von Diensten in Form von Schnittstellen, so dass die Ressourcen<br />
genutzt werden können.<br />
¯ Struktureller Aufbau<br />
119
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
berechtigte<br />
Benutzer<br />
Schnittstelle zur Systemumgebung<br />
System-<br />
Prozesse<br />
Speicher<br />
verwaltung<br />
Scheduler &<br />
Dispatcher<br />
Betriebssystem-Schnittstelle<br />
Prozess<br />
verwaltung<br />
Unterbrechungs-<br />
System<br />
Hardware<br />
Konfiguration<br />
4.2.2 Systemprogrammierung<br />
berechtigte<br />
Benutzer<br />
Benutzer-<br />
Prozesse<br />
Datei<br />
system<br />
EA-<br />
System<br />
zu überprüf.<br />
Benutzer<br />
login<br />
Die <strong>Programmierung</strong> eines Betriebssystems gehört zu dem Bereich der Systemprogrammierung.<br />
¯ Definition<br />
Die Systemprogrammierung befasst sich mit der Darstellung, der Realisierung,<br />
den Eigenschaften und der Konstruktion derjenigen Algorithmen für ein<br />
Rechensystem, die die Bearbeitung und Ausführung von Benutzerprogrammen<br />
unter vorgegebenen Qualitätsgesichtspunkten organisieren, d.h. steuern und<br />
kontrollieren, und zum Teil selbst durchführen.<br />
¯ Qualitätskriterien können z.B. sein:<br />
120<br />
Z<br />
u<br />
g<br />
a<br />
n<br />
g<br />
s<br />
k<br />
o<br />
n<br />
t<br />
r<br />
o<br />
l<br />
l<br />
e
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
– Zuverlässigkeit der durchgeführten Berechnung (Behandlung von Systemcrashs,<br />
Netzausfällen, fehlerhafter Nachrichtenübermittlung etc.).<br />
– Effizienz und Performanz einerseits systemglobal, d.h. es wird versucht,<br />
das System optimal auszulasten, andererseits Auftrags-lokal, z.B. es wird<br />
versucht, zu garantieren, dass eine Auftragsbearbeitung eine festgelegte<br />
Zeitdauer nicht überschreitet.<br />
– Einhaltung von Realzeitanforderungen: zeitkritische Aufträge besitzen z.B.<br />
eine Deadline bis zu der sie ausgeführt sein müssen.<br />
– Durchsetzung von Sicherheitsanforderungen: Schutz der Daten und<br />
Informationen vor unberechtigten Zugriffen und Einsichtnahme.<br />
– Benutzerfreundlichkeit: bequeme Formulierungsmöglichkeit von Benutzeraufträgen.<br />
4.2.3 Betriebssystem-Architektur<br />
In der Praxis findet man einige verschiedene BS-Architekturkonzepte, wobei der<br />
monolithische Ansatz und zunehmend auch der Mikrokern-Ansatz am weitesten<br />
verbreitet sind.<br />
Monolithischer Ansatz<br />
Das Betriebssystem besteht aus einer umfangreichen Menge an Funktionen,<br />
die sich bei Bedarf gegenseitig aufrufen können. Die Funktionen werden in<br />
einem großen BS-Kern zusammengefasst. Der BS-Kern wird durch Aufruf<br />
von Systemdiensten betreten. Der BS-Kern hat i.a. nur wenig Struktur. Als<br />
Grundstruktur kann man von 3 Schichten ausgehen: a) die Hauptprozedur, b)<br />
den Systemfunktionen, die von der Hauptprozedur aufgerufen werden, sowie c)<br />
den Hilfsfunktionen. Unix kann als BS mit einer monolithischen Architektur<br />
betrachtet werden.<br />
121
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
Anwendung<br />
Benutzerprozess<br />
Systemdienste<br />
Hardware<br />
Anwendung<br />
Benutzerprozess<br />
Hilfs<br />
funktionen<br />
Benutzer-Modus<br />
(User Mode)<br />
System-Modus<br />
(Kernel Mode)<br />
¯ komplexe, monolithische Betriebssystem sind sehr schwierig zu warten und<br />
zu erweitern. Es ist oft schwierig nachzuvollziehen, welche Konsequenzen<br />
Änderungen von Systemdiensten bzw. Hilfsfunktionen im Betriebsystem<br />
nach sich ziehen. Dies bezieht sich vor allem auf die Synchronisation von<br />
nebenläufigen Zugriffen und die Nutzung von Ressourcen. Es sind oft viele<br />
Detailkenntnisse des gesamten Betriebssystems notwendig.<br />
¯ Geschichtete Systeme<br />
Einen Ausweg aus der Problematik monolithischer Systeme bieten geschichtete<br />
Systeme;<br />
das Betriebssystem besteht aus einer Hierarchie abstrakter Maschinen.<br />
Jede Schicht hat wohldefinierte Schnittstellen und eine wohldefinierte<br />
Aufgabe<br />
Reduktion der Systemkomplexität.<br />
Geschichtete Systeme ermöglichen die Realisierung von Systemfamilien,<br />
d.h. ein neues Familienmitglied (und damit eine neue Plattform für<br />
Anwendungen) kann mit vergleichsweise geringem Aufwand auf irgendeiner<br />
abstrakten Maschine aufgesetzt werden. Unter der Voraussetzung, dass die<br />
122
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
Schnittstellen präzis und in sich konsistent festgelegt wurden, kann jede<br />
abstrakte Maschine unabhängig von anderen erstellt werden. Dies bietet<br />
auch die Möglichkeit, einzelne Schichten durch neue Implementierungen zu<br />
ersetzen, solange Schnittstelle und Aufgabe eingehalten werden. Gerade im<br />
Bereich der Rechnernetze spielt das Schichtenkonzept eine sehr wichtige Rolle<br />
(siehe ISO/OSI Referenzmodell oder TCP/IP Referenzmodell).<br />
Mikrokern-Ansatz<br />
Anwendungen<br />
Funktionsschnittstelle<br />
abstrakte Maschine N<br />
Funktionsschnittstelle<br />
abstrakte Maschine N - 1<br />
Funktionsschnittstelle<br />
abstrakte Maschine 0<br />
Rechnerhardware<br />
Schicht N<br />
Schicht N-1<br />
Schicht 0<br />
Der Trend moderner Betriebssystem geht hin zu einem Mikrokern-Ansatz. Im<br />
Mikrokern sind nur mehr Basismechanismen, z.B. Prozesskommunikation (Austausch<br />
von Nachrichten), CPU-Zuteilung. Möglichst viele Subsysteme sind als<br />
Systemprozesse außerhalb des Mikrokerns realisiert. Sie laufen im Benutzermodus<br />
ab, z.B. Dateisystem, Verwaltungsstrategien, Speicherverwaltung. Man<br />
spricht im Zusammenhang mit diesem Ansatz auch von einer Client/Server-<br />
Struktur. Systemfunktionen werden als Serverprozesse im Benutzermodus ausgeführt.<br />
Benötigt ein Prozess (Client) eine Dienstleistung schickt er eine An-<br />
123
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
forderung an einen anderen Prozess (Server), der die Dienstleistung erfüllt und die<br />
Antwort an den Client zurückgibt. Die Kommunikation zwischen den beteiligten<br />
Prozesse erfolgt über den Mikrokern. Durch die Ausgliederung in Serverprozesse<br />
ist eine Trennung von Mechanismus und Strategie möglich. Die Strategien werden<br />
in Serverprozesse im Benutzermodus realisiert, während der Mikrokern wenige<br />
Basismechanismen beinhaltet.<br />
Einfaches Austauschen von Subsysteme ermöglicht die einfache Anpassung<br />
von Systemanforderungen.<br />
Benutzer<br />
Programm<br />
Prozess<br />
Server<br />
Anforderung<br />
Antwort<br />
Memory<br />
Server<br />
Beispiel: BS-Architekturen<br />
File<br />
Server<br />
Mikrokern<br />
Hardware<br />
Netzwerk<br />
Server<br />
Display<br />
Server<br />
Benutzer-Modus<br />
(User Mode)<br />
System-Modus<br />
(Kernel Mode)<br />
¯ Unix Betriebssystem<br />
Die nachfolgende Abbildung skizziert die wesentlichen Komponenten des<br />
Unix Betriebssystems. Der Unix-BS-Kern enthält die Datei-, Prozess- und<br />
Prozessorverwaltung, die Speicherverwaltung und die Geräte-Treiber. Zur<br />
Nutzung beispielsweise der Systemdienste des Dateisystems bietet das BS u.a.<br />
die Dienste ÓÔÒ Ö ÛÖØ ÐÓ× an. Bibliotheken enthalten u.a. die<br />
Routinen für Standard-Ein/Ausgabe sowie malloc-Funktion zur dynamischen<br />
Speicherverwaltung.<br />
124
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
Programme Shells<br />
Datei<br />
System<br />
Geräte Treiber<br />
Systemschnittstelle<br />
u.a. open,close, read, write; fork, exec, kill, ...<br />
Prozess<br />
verwaltung<br />
Unterbrechungs<br />
behandlung<br />
Hardware<br />
Prozessor<br />
verwaltung<br />
Bibliotheken<br />
(z.B. lib.a)<br />
Arbeitsspeicher<br />
verwaltung<br />
Benutzungsschnittstelle<br />
Programmierschnittstelle<br />
Betriebssystemkern<br />
¯ Windows NT Betriebssystem<br />
Mit Hilfe von HAL wird versucht, die meisten Maschinenabhängigkeiten zu<br />
verbergen. HAL präsentiert dem restlichen BS abstrakte Hardwaregeräte.<br />
Der Kernel ist kein Mikrokern. Zielsetzung des Kernels ist die vollständige<br />
Hardware-Unabhängigkeit, und damit sehr portable BS-Komponenten höherer<br />
Ebene. Der Kernel besteht aus Control-Objects (z.B. primitive Prozessobjekte,<br />
Unterbrechungsobjekte) und Dispatcher-Objects (z.B. Semaphore, Ereignisse,<br />
Timer). Der Kernel ist auch zuständig für das Scheduling von Threads. Der<br />
Objektmanager verwaltet alle Systemobjekte (z.B. Prozesse, Threads, Dateien,<br />
Semaphore). Der Prozessmanager ist für die Erzeugung, Verwaltung und<br />
Löschung von Prozessen und Threads verantwortlich. Die Local Procedure<br />
Call Facility realisiert eine effiziente Interprozess-Kommunikation zwischen<br />
den Subsystemen. Die System Services stellt die Schnittstelle zum Executive<br />
bereit.<br />
125
Schlichter, TU München 4.2. BETRIEBSSYSTEM - ÜBERBLICK<br />
Object<br />
Manager<br />
Logon<br />
Process<br />
Security<br />
Subsystem<br />
Security<br />
Manager<br />
Hardware Manipulation<br />
OS/2 Client<br />
OS/2<br />
Subsystem<br />
4.2.4 Betriebsarten<br />
System Interface (DLL)<br />
Process<br />
Manager<br />
Kernel<br />
System Services<br />
Local<br />
Procedure<br />
Call Facility<br />
Hardware Abstraction Layer (HAL)<br />
Hardware<br />
Win32<br />
Client<br />
Win32<br />
Subsystem<br />
Virtual<br />
Memory<br />
Manager<br />
Posix Client<br />
Posix<br />
Subsystem<br />
Systemaufrufe<br />
I/O Manager<br />
File Systems<br />
Cache Mgr<br />
User Mode<br />
Kernel Mode<br />
Device Drivers<br />
Network<br />
Drivers<br />
Applications<br />
Protected<br />
Subsysteme<br />
(Servers)<br />
NT Executive<br />
Beim Betrieb von Rechenanlagen können bzgl. des Zusammenwirkens von Benutzer<br />
und Rechensystem die Betriebsweisen Stapelverarbeitung, Dialogbetrieb<br />
und Echtzeitbetrieb unterschieden werden.<br />
¯ Stapelbetrieb<br />
Das Rechensystem verarbeitet Ströme von Auftragspaketen (engl. batch processing).<br />
Ein Benutzer deklariert vollständig alle Teile eines Auftragspaketes,<br />
bevor es in das System eingegeben wird. Anschließend wird das Auftragspaket<br />
durch das Rechensystem abgearbeitet, ohne dass der Benutzer noch Einflussmöglichkeiten<br />
hat. Bei Auftreten eines Fehlers muss i.a. nach der Korrektur<br />
das gesamte Auftragspaket nochmals gestartet werden. Auftragspakete<br />
können in Unterabschnitte zerfallen, z.B. Teilprogrammabläufe. Diese Betriebsart<br />
war in den Anfängen von Rechenanlage sehr verbreitet (Nutzung von<br />
Lochkarten und Lochstreifen).<br />
¯ Dialogbetrieb<br />
126
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Im Dialogbetrieb erteilt der Benutzer dem Betriebssystem einen Auftrag<br />
nach dem anderen im Dialog. Innerhalb eines Benutzerauftrags findet<br />
eine Interaktion zwischen dem Benutzer und der Systemumgebung statt<br />
(z.B. Eingabe weiterer Daten, Ausgabe von Zwischenergebnissen). Der<br />
Dialogbetrieb erfordert eine besondere Gestaltung der Benutzerschnittstelle.<br />
Oft wird Betriebssystem und Benutzerschnittstelle (engl. user interface) in<br />
einem Atemzug genannt und auch oft gleichgesetzt. Beide sind jedoch getrennt<br />
voneinander zusehen. Beispielsweise existierten mit dem X11-Windowsystem<br />
und Sun Windowsystem (auf der Basis von Postscript) zwei unterschiedliche<br />
Benutzerschnittstellen auf demselben Betriebssystem.<br />
¯ Echtzeitbetrieb<br />
In der Prozessteuerung (automatische Fertigungssysteme, Roboter) und im<br />
Multimediabereich sind die Reaktionszeiten des Betriebssystems von großer<br />
Bedeutung. Dies erfordert spezielle Mechanismen bei der Behandlung von<br />
Ereignissen und Unterbrechungen sowie der CPU-Zuteilung an rechenbereite<br />
Prozesse / Threads. Beispielsweise ein Videoserver (bei Nutzung des Streaming<br />
Ansatzes) benötigt ein Betriebssystem, das gewisse Echtzeitfähigkeiten hat.<br />
Videos müssen mit einer bestimmten Geschwindigkeit abgespielt werden. Die<br />
Bilder dürfen an das Abspielprogramm nicht zu langsam (ansonsten ruckelt<br />
das Videobild) und nicht zu schnell ausgeliefert werden (sonst gehen bei<br />
Pufferüberlauf Videobilder verloren).<br />
4.3 Prozessverwaltung<br />
Dieser Abschnitt behandelt das Prozesskonzept, Datenstrukturen zur Beschreibung<br />
des aktuellen Prozesszustandes sowie Dienste zum Aufruf von Systemfunktionen.<br />
4.3.1 Prozesskonzept<br />
Wir unterscheiden Benutzerprozesse, die Anwendungsprogrammen in Ausführung<br />
entsprechen, und Systemprozesse, die Programme/Dienste des Betriebssystems<br />
durchführen.<br />
127
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
a) Jeder Prozess besitzt einen eigenen Prozessadressraum.<br />
b) Spezielle Systemprozesse sind die Dämonen (engl. daemon); das sind<br />
Hilfsprozesse, die ständig existieren, die meiste Zeit aber passiv sind. Sie<br />
erfüllen i. d. R. Service-Funktionen und werden dazu durch das Eintreten<br />
von Ereignissen aufgeweckt (z.B. Datei zum Drucken eingetroffen) oder<br />
werden von Zeit zu Zeit aktiv, um selber zu prüfen, ob Dienste zu<br />
erbringen sind.<br />
Dienste der Prozessverwaltung<br />
Die Prozesse werden durch das Betriebssystem verwaltet.<br />
Prozess-Erzeugen und Starten (z.B. fork).<br />
Terminieren, Auflösen (z.B. exit, kill).<br />
Prozess-Auswahl, Strategien zur Prozessorzuteilung: Scheduling (z.B.<br />
change-priority). Der Scheduler ist für Multitasking-Betriebssysteme<br />
von Bedeutung. Er wählt den nächsten auszuführenden Prozess aus<br />
der Menge der rechenbereiten Prozesse aus. Dabei werden in den<br />
Betriebssystemen unterschiedliche Verfahren angewandt, z.B. Auswahl nach<br />
Prioritäten oder Zeitscheibenverfahren (Round Robin). Eine ausführlichere<br />
Diskussion verschiedener Strategien erfolgt später und im Hauptstudium<br />
(Betriebssysteme).<br />
Prozessor-Anbindung; Dispatching (z.B. suspend, resume, sleep, wakeup).<br />
Die Durchführung des Übergangs eines Prozesses in den Zustand rechnend<br />
erfolgt durch den Dispatcher. Beispielsweise teilt er den vom Scheduler<br />
ausgewählten Prozess der CPU zu. Der Prozess geht vom Zustand<br />
rechenbereit in den Zustand rechnend über; der Prozess wird ausgeführt.<br />
Dabei muss sichergestellt, dass der entsprechende Kontext des Prozesses (z.B.<br />
seine Register) geladen werden.<br />
Prozesskontrollblock<br />
Jeder Prozess muss als eine Verwaltungseinheit beschrieben sein. Ein Prozess<br />
wird durch seinen Prozess-Kontext und dieser durch den Prozesskontrollblock<br />
(PCB) beschrieben. Ein PCB wird meist programmiersprachlich als Verbund<br />
(record) spezifiziert. Ein PCB (process control block) enthält i.d.R. folgende<br />
Informationen:<br />
¯ eindeutiger Name, z.B. fortlaufende Nummerierung des Prozesses (z.B. pid in<br />
Unix)<br />
128
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
¯ Name des Benutzers, dem der Prozess zugeordnet ist<br />
¯ der momentane Prozesszustand (wartend, rechnend, rechenwillig, ...)<br />
¯ falls der Prozess rechnend ist, Angabe des zugeordneten Rechnerkerns<br />
¯ falls der Prozess wartend ist, eine Spezifikation des Ereignisses, auf das der<br />
Prozess wartet (z.B. Adresse eines Semaphors).<br />
¯ die Ablaufpriorität des Prozesses<br />
¯ die Inhalte der programmierbaren Register (z.B. in MI: R0, .. , R15)<br />
¯ die Inhalte der Register, in denen die Anfangsadresse und Länge<br />
der prozessspezifischen Speicherabbildungstabellen enthalten sind (virtuelle<br />
Adressierung) (z.B. in MI P0B für die Seitentabelle)<br />
¯ das Programmstatuswort (PSW). Das PSW enthält weitere Informationen,<br />
die der Rechnerkern über den Prozess kennt. In der MI enthält PSW den<br />
Zustand der CC-Register, z.B. das N-Register (Ergebnis der letzten Operation<br />
negativ), den Ablaufmodus (Benutzer- oder Systemmodus), die momentane<br />
Ablaufpriorität, die Adressierungsart im Benutzermodus (virtuelle oder direkte<br />
Adressierung). Daneben kann ein PCB noch weitere Statistiken über die<br />
Historie des Prozesses speichern, die beim Scheduling ausgewertet werden.<br />
Prozesslisten<br />
Die Prozesse werden in Zustandslisten verwaltet, die als verkettete Liste der PCBs<br />
realisiert sind.<br />
für E/A-Geräte (z.B. Platte, Terminal) existiert i.d.R. eine jeweils eigene<br />
Warteschlange, die die PCBs der wartenden Prozesse enthält.<br />
Rechnend<br />
Rechenwillig<br />
Ready-Queue<br />
Wartend<br />
129<br />
Prozessidentifikation<br />
Registerzustand<br />
Scheduling Information (z.B.<br />
Priorität)<br />
Adressrauminformation<br />
Sonstiges<br />
nächster PCB<br />
Prozesskontrollblock PCB
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Bei der MI steht während der Zuordnung eines Rechnerkerns an einen Prozess<br />
die Maschinenadresse des zugehörigen PCB in dem Sonderregister PCBADR des<br />
Rechnerkerns. Da der PCB eine Datenstruktur des Betriebssystems ist, kann die<br />
Ablage nicht im Benutzeradressraum erfolgen. Da weiterhin die Prozesse nicht<br />
kellerartig aktiviert und deaktiviert werden, sondern in beliebiger Folge, kann er<br />
auch nicht im Keller des Betriebssystems abgelegt werden, sondern es wird dafür<br />
die Halde verwendet.<br />
Zustandsmodell<br />
Das Prozess-Zustandsmodell unterscheidet neben den bereits vorgestellten<br />
Zuständen rechenwillig, rechnend, wartend auch den Zustand ausgelagert.<br />
Letzterer Zustand tritt ein, wenn der Adressraum aufgrund Speichermangels aus<br />
dem Arbeitsspeicher auf den Hintergrundspeicher verlagert wird ("swapping").<br />
add retire<br />
rechenwillig<br />
assign rechnend<br />
swap in<br />
ready<br />
resign<br />
wartend<br />
ausgelagert<br />
130<br />
block<br />
swap out
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Zustandsübergänge<br />
: ein neu erzeugter Prozess wird zu der Menge bereiten Prozesse<br />
hinzugefügt;<br />
××Ò: als Folge eines Kontextwechsels wird dem Prozess die CPU<br />
zugeordnet;<br />
ÐÓ : aufgrund eines EA-Aufrufs oder einer Synchronisationsoperation<br />
wird der Prozess wartend gesetzt;<br />
ÖÝ: nach Beendigung der angestoßenen Operation wechselt der Prozess<br />
in den Zustand rechenwillig; er bewirbt sich erneut um die CPU;<br />
Ö×Ò: dem rechnenden Prozess wird die CPU entzogen; er bewirbt sich<br />
anschließend erneut um die CPU;<br />
ÖØÖ: der aktuell rechnende Prozess terminiert;<br />
×ÛÔ ÓÙØ: der Prozess wird auf den Hintergrundspeicher ausgelagert;<br />
×ÛÔ Ò: der Prozess wird vom Hintergrundspeicher in den Arbeitsspeicher<br />
geladen.<br />
4.3.2 Dispatcher<br />
Aufgabe des Dispatchers: Realisieren der Zustandsübergänge zwischen rechnend<br />
und rechenwillig: Prozessor binden und entbinden. Dazu ist ein Kontextwechsel<br />
erforderlich. Dabei ist zu berücksichtigen, dass die Prozesslisten entsprechend<br />
aktualisiert werden, d.h. der PCB des ausgewählten Prozess muss aus der<br />
rechenwillig-Liste entfernt werden und in die rechnend-Liste eingetragen werden.<br />
Kontextwechsel<br />
CPU wird entzogen und einer anderen Aktivität zugeteilt; ein Kontextwechsel ist<br />
erforderlich, falls der rechnende Prozess P1 in den Zustand wartend oder z.B.<br />
durch Prozessorentzug in den Zustand rechenwillig übergeführt wird.<br />
¯ Problem<br />
aktueller Ausführungskontext des Prozesses muss gesichert werden und<br />
Kontext des nächsten rechenbereiten Prozesses muss geladen werden. Falls<br />
für den Zugriff auf eine Datei X nur ein Dateizeiger zur Verfügung steht, dann<br />
muss die Position des Dateizeigers gerettet werden. Wenn Prozess P1 wieder<br />
rechnend wird, dann soll er an der Position weiterlesen, an der er unterbrochen<br />
wurde; falls zwischenzeitlich ein anderer Prozess P2 ebenfalls die Datei gelesen<br />
und den Dateizeiger verändert hat, darf dies bei der Fortsetzung von P1 keine<br />
Auswirkung haben. In Unix erhält jeder Prozess einen eigenen Dateizeiger.<br />
Achtung: je umfangreicher ein PCB ist, desto "teurer" sind Prozesswechsel,<br />
d.h. das Umschalten der CPU zwischen den Prozessen.<br />
131
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Threads<br />
Threads haben einen sehr viel kleineren Kontext Umschalten zwischen<br />
Threads innerhalb eines Prozesses sehr schnell, da Adressraum und andere<br />
Ressourcen (z.B. Dateien) gemeinsam genutzt werden. Auch TLB-Einträge<br />
( Translation lookaside Buffer (siehe Seite 175)) der Seitenadressierung<br />
müssen nicht invalidiert werden. Damit ergeben sich auch keine Folgekosten<br />
durch Laden von Seiten bei cache-Misses. Dagegen ist das Umschalten von<br />
Threads, die unterschiedlichen Prozessen angehören, ebenso aufwendig wie ein<br />
Prozesswechsel.<br />
Beispiel: Kontext-Wechsel in Unix<br />
Kontextwechsel z.B. durch den Aufruf der Systemoperation sleep durch einen<br />
Prozess. Beim Aufruf der Operation sleep ist ein Warteraum, in den der<br />
Prozess eingefügt werden soll, anzugeben (z.B. E/A-Warteraum, oder warten<br />
auf Terminieren eines Kind-Prozesses). Bei der Ausführung von sleep werden<br />
vergröbert folgende Schritte durchgeführt.<br />
1. Maskieren von Interrupts;<br />
2. Lokalisieren der benötigten Warteschlange;<br />
3. Neuberechnung der Priorität des Prozesses;<br />
4. Einfügen des Prozesses in die Warteschlange;<br />
5. Aufruf der Operation zum Kontextwechsel.<br />
Bei der Ausführung der Operation zum Kontextwechsel wird vom Scheduler<br />
der nächste Prozess ausgesucht, dem der Prozessor zugeteilt werden soll,<br />
und mit dieser Information wird die Operation resume aufgerufen. Zunächst<br />
wird der Zustand des noch aktuellen Prozesses aus den Registern in den<br />
Prozesskontrollblock des Prozesses gespeichert. Dann wird die Adresse des<br />
Prozesskontrollblocks des neu zu bindenden Prozesses sowie der Zustand des<br />
neuen Prozesskontrollblocks in die Register geladen und der Kontextwechsel<br />
ist durchgeführt. Durch das Maskieren von Interrupts kann das Warten auf die<br />
relevanten Ereignisse eingestellt werden, d.h. die anderen Interrupts werden<br />
ausgeblendet.<br />
4.3.3 Arbeitsmodi<br />
Ziel für den Betrieb von Rechensystemen: kontrollierter Zugriff auf Hardwarekomponenten<br />
nur durch BS. Dadurch soll verhindert werden, dass Benutzer<br />
oder Softwaresysteme Hardwarekomponenten unsachgemäß nutzen, und implizit<br />
andere nebenläufige Aktivitäten schädigen.<br />
132
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Lösung: alle Zugriffe auf Hardware nur über privilegierte Befehle zulässig;<br />
Frage: wer darf privilegierte Befehle ausführen Antwort: Prozesse in<br />
einem privilegierten Modus.<br />
Herkömmlich unterscheidet man zwischen dem Benutzer- (engl. user mode) und<br />
dem Systemmodus (engl. kernel mode).<br />
¯ Benutzermodus<br />
Es sind nur die nicht privilegierten Befehle verfügbar. Damit ist der Zugriff auf<br />
Prozessadressraum und unkritische Register, wie Befehlszähler, Indexregister<br />
möglich. Benutzerprozesse werden im Benutzermodus ausgeführt.Kritische<br />
Register, über die der Kontext eines Prozesses beeinflusst werden kann<br />
(z.B. Ablaufpriorität, Arbeitsmodus) können nur im Systemmodus verändert<br />
werden. Wird versucht, einen privilegierten Befehl auszuführen, gibt es einen<br />
Befehlsalarm.<br />
¯ Systemmodus<br />
Es sind auch die privilegierten Befehle verfügbar (z.B. Anhalten der Maschine,<br />
Verändern der Ablaufpriorität). Die Dienste des Betriebssystemkerns werden<br />
im Systemmodus ausgeführt.<br />
¯ Nutzung der Hardware-Komponenten nur über Dienste des BS: Aufruf eines<br />
BS-Dienstes über spezielle Befehle: den Systemaufruf. Dies führt zu einer <br />
Unterbrechung (siehe Seite 150) und damit zu einem kontrollierten Eingang in<br />
das BS (z.B. Zugriffsrechte des aufrufenden Prozesses prüfen).<br />
4.3.4 Systemaufrufe<br />
Ein Systemaufruf ist eine Funktion, die von einem Benutzerprozess aufgerufen<br />
wird, um einen BS-Kerndienst aufzuführen.<br />
1. Der Systemaufruf überprüft die übergebenen Parameter und bildet daraus<br />
eine Datenstruktur, um die Daten an den BS-Kern weiterzureichen.<br />
2. Danach wird eine spezielle Instruktion, ein Software Interrupt (Trap),<br />
ausgeführt. Diese Instruktion identifiziert über einen Operanden den<br />
gewünschten Systemdienst.<br />
3. Bei Ausführung der Trap-Instruktion wird der Zustand des Benutzerprozesses<br />
gerettet und es findet ein Wechsel in den Systemmodus statt.<br />
In manchen Programmiersprachen sind Systemaufrufe (C, C++) über den<br />
Aufruf von Prozeduren der Laufzeitumgebung, die ihrerseits die Systemaufrufe<br />
durchführen, möglich. In Java ist kein direkter Systemaufruf möglich, sondern<br />
dies erfolgt i.a. über Methoden, die in anderen Sprachen geschrieben sind, meist<br />
C, C++.<br />
133
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Beispiel<br />
Lesen von Daten aus einer Datei und Kopieren in eine andere Datei. Dabei treten<br />
die folgenden Systemaufrufe auf:<br />
(1) Schreiben des Prompts auf Bildschirm: Angabe der Dateinamen<br />
(2) Lesen der Tastatureingabe (bzw. Analoges bei Mouse-Eingabe)<br />
(3) Öffnen der zu lesenden Datei (open)<br />
(4) Erzeugen der neuen Datei<br />
(5) ggf. Fehlerbehandlung: Nachricht auf Bildschirm<br />
(6) Schleife: Lesen von Eingabedatei (ein Systemaufruf) und schreiben in zweite<br />
Datei (auch Systemaufruf)<br />
(7) Schließen beider Dateien<br />
(8) Ausgabe auf Bildschirm<br />
Durch die Verwendung von Laufzeitroutinen ergibt sich eine höhere Abstraktionsebene<br />
Aufruf einer Routine, die die notwendigen Systemaufrufe durchführt.<br />
Privilegierte MI Befehle<br />
Die Modellmaschine MI unterstützt die folgenden privilegierten Maschinenbefehle:<br />
134
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
a) das Anhalten der Maschine: HALT<br />
b) Laden des PCB in den Rechnerkern: LPCB; die Adresse des PCB steht im<br />
Register PCBADR. Die Adresse des PCB im Speicher ergibt sich durch<br />
die Adresse im Register PCBADR. PC und PSW aus dem PCB werden<br />
nicht in die aktuellen Register übernommen, sondern in den Keller für<br />
den Systemmodus abgelegt (bei der Fortsetzung können dann die Register<br />
entsprechend besetzt werden).<br />
c) Speichern des PCB (vom Rechnerkern) : SPPCB; Die Zieladresse im<br />
Speicher ergibt sich durch die Adresse im Register PCBADR. PC und<br />
PSW werden nicht aus den aktuellen Registern, sondern vom Keller<br />
genommen und im PCB im Speicher abgelegt.<br />
d) Rückkehr aus Unterbrechungen: REI (return from interrupt); Prozess,<br />
dessen PCB geladen ist, wird fortgesetzt.<br />
e) Speichern von Sonderregistern: SPSR a1; Das Sonderregister wird<br />
immer rechtsbündig in ein Wort abgespeichert. Links wird mit Null<br />
aufgefüllt, da die Sonderregister nicht immer Wortlänge haben. Siehe<br />
dazu auch die MI-Beschreibung.<br />
f) Laden von Sonderregistern: LSR a1; Es wird der zu ladende Wert für das<br />
Sonderregister immer rechtsbündig in einem Wort erwartet.<br />
g) Auslösen eines Rechnerkernalarms : RKALARM a1, wobei S[a1] die<br />
Nummer des zu unterbrechenden Rechnerkerns angibt. Die MI kann bis<br />
zu 4 Rechnerkerne haben, die von 0 bis 3 nummeriert sind.<br />
Folgende Größen bzw. Hardware Komponenten sind nur mit privilegierten<br />
Befehlen veränderbar:<br />
¯ der Arbeitsmodus.<br />
¯ der Adressierungsmodus. Hier wird zwischen virtuelle Adressierung<br />
(Seitenadressierung) und direkter Adressierung unterschieden. Letztere<br />
Adressierungsart ermöglicht auch den Zugriff auf den physischen Speicher.<br />
¯ die Register mit Adresse oder Länge der Speicheradressierungstabellen.<br />
¯ das Register PCBADR mit der Adresse des Prozesskontrollblocks PCB.<br />
¯ das Register SCBADR mit der Adresse des Systemkontrollblocks SCB.<br />
Der Systemkontrollblock beinhaltet die Startadressen für die Routinen zur<br />
Behandlung von Unterbrechungen, z.B. wenn Befehlsalarm auftritt oder eine<br />
Rückmeldung eines Kanals eintrifft.<br />
135
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Systemaufrufe in der MI<br />
Systemdienste werden über Zahlen identifiziert. Ein Systemaufruf in der MI<br />
erfolgt mit dem Befehl CHMK. Nach dem oben Gesagten führt der Befehl<br />
CHMK daher bei der MI zu einer Unterbrechung. Es findet ein Übergang von<br />
einem Programmstück im Benutzermodus (Benutzer- oder Systemprozess) zu<br />
einem Programmstück im Systemmodus statt. Der Systemkern läuft in einem<br />
anderen Programmadressraum und mit einem anderen Arbeitsmodus. Daher muss<br />
muss der momentane Ablaufzustand (Rechnerkernzustand) gesichert werden und<br />
ein neuer Ablaufzustand eingestellt werden. Es muss auch mindestens der<br />
aktuelle PC und das PSW gerettet werden. Der neue Ablaufzustand, z.B. die<br />
Einsprungadresse in den Systemkern, darf aus Sicherheitsgründen nicht durch den<br />
Aufrufer, der ja nicht vertrauenswürdig ist, definiert werden. Es erfolgt daher eine<br />
Standardbesetzung. Diese Standardbesetzung ist wie bei den später besprochenen<br />
Unterbrechungen. Der Befehl CHMK führt in der MI zu einer Unterbrechung.<br />
CHMK a1 Aufruf eines Systemdienstes a1 ("CHange Mode to<br />
Kernel")<br />
¯ Wirkung des Befehls CHMK<br />
Auch das Betriebssystem verfügt analog zum Benutzerprozess über einen<br />
Systemkeller.<br />
– Ablage von PSW und PC des aufrufenden Prozesses auf dem Systemkeller.<br />
– Der Operand a1 gibt die Nummer des aufgerufenen Systemdienstes<br />
an. Diese Nummer wird ebenfalls auf dem Systemkeller abgelegt<br />
und kann vom Systemkern ausgewertet werden. Weitere Parameter<br />
für den Systemdienst werden in einem Versorgungsblock bereitgestellt.<br />
Der Ort dieses Versorgungsblockes muss vereinbart werden. Achtung<br />
bei Adressübergaben:<br />
Adressräumen!<br />
Aufrufer und Systemkern evtl. in getrennten<br />
– Zustand des Systemkellers nach der Ausführung von CHMK:<br />
Bit 0 - 31<br />
SP+8 PSW<br />
SP+4 PC<br />
SP S[a1]<br />
– Der Eintrag "vorhergehender Arbeitsmodus" im PSW wird mit dem<br />
gegenwärtigen Arbeitsmodus besetzt. Der gegenwärtige Arbeitsmodus ist<br />
der beim Aufruf von CHMK gültige Arbeitsmodus. Er wird gerettet.<br />
– Der aktuelle Arbeitsmodus wird der Systemmodus.<br />
136
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
– Der Befehlszähler PC wird auf die Adresse für die Behandlung von Systemdiensten<br />
gemäß dem Systemkontrollblock eingestellt (Systemdienstanfangsbehandlung).<br />
¯ Rückkehr von Systemaufruf in MI<br />
REI Rückkehr von der Unterbrechungsbehandlung (REturn from Interrupt)<br />
Es werden die Register PC und PSW gemäß den Werten auf dem<br />
Systemkeller besetzt. Dies entspricht der Einstellung des alten Zustandes<br />
des aufrufenden Prozesses, insbesondere bezüglich PC, Arbeitsmodus und<br />
Adressierungsmodus. Der Prozess, der den Systemdienst aufgerufen hat, wird<br />
also fortgesetzt.<br />
– Zustand des Systemkellers vor der Ausführung von REI:<br />
Bit 0 - 31<br />
SP+4 PSW<br />
SP PC<br />
4.3.5 Realisierung von Threads<br />
Es existieren zwei grundlegende Ansätze, Threads in einem Rechensystem<br />
zu realisieren: im Benutzer-Adressraum (Benutzermodus) oder im System-<br />
Adressraum (Systemmodus).<br />
¯ im Benutzer-Adressraum<br />
Der BS-Kern verwaltet nur single-threaded Prozesse. Damit ist es auch möglich<br />
ein Thread-Package für ein Betriebssystem zu realisieren, das auf BS-Ebene<br />
keine Threads unterstützt.<br />
Prozess Thread<br />
Laufzeitsystem<br />
Thread<br />
tabelle<br />
BS-Kern<br />
137<br />
Prozesstabelle<br />
Benutzer<br />
Adressraum<br />
(Benutzermodus)<br />
System<br />
Adressraum<br />
(Systemmodus)
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
– Threads werden durch Laufzeitsystem im Benutzeradressraum verwaltet.<br />
Eine Thread-Tabelle speichert Informationen (Register, Zustand, etc.) über<br />
Threads pro Prozess.<br />
– Prozessorzuteilung im BS-Kern erfolgt an Prozesse. Laufzeitsystem<br />
bestimmt, welcher Thread rechnend gesetzt wird. Jedes Laufzeitsystem<br />
hat seinen eigenen Thread-Scheduler. Damit ist es möglich, dass jeder<br />
Prozess seine eigene Variante des Scheduling-Algorithmus nutzt, d.h. die<br />
Auswahl des nächsten auszuführenden Threads. Beispielsweise kann durch<br />
eine geeignete Strategie verhindert werden, dass der Garbage Collector zu<br />
einem ungünstigen Zeitpunkt unterbrochen wird.<br />
– Problem: Systemaufruf eines Threads blockiert die anderen Threads<br />
des Prozesses. Dadurch wird gerade dies verhindert, was mit Threads<br />
in Applikationen erreicht werden soll, d.h. die Ausführung paralleler<br />
Aktivitätssequenzen, wobei trotz Blockierung einiger Threads andere<br />
Threads desselben Prozesses weiterlaufen können.<br />
– Problem: wie wird einem Thread die CPU entzogen? Threads müssen die<br />
CPU durch Ausführung des Befehls ØÖÝÐ freiwillig aufgeben. Das<br />
Laufzeitsystem wählt dann einen anderen rechenwilligen Thread aus. Falls<br />
ein Thread die CPU nicht aufgibt, kann ihm vom Laufzeitsystem (nicht wie<br />
im BS-Kern) die CPU entzogen werden. Innerhalb eines Prozesses existieren<br />
keine Clock-Interrupts.<br />
¯ im System-Adressraum<br />
Neben den Prozessen werden im BS-Kern auch alle Threads verwaltet.<br />
Damit können auch alle Funktionen zur Verwaltung von Prozessen, z.B.<br />
CPU-Zuteilung, für Threads verwendet werden. Dies bedeutet jedoch auch,<br />
dass bei Erzeugung bzw. Terminierung von Threads jeweils Systemaufrufe<br />
durchgeführt werden müssen. Systemaufrufe sind jedoch i.a. aufwendig.<br />
138
Schlichter, TU München 4.3. PROZESSVERWALTUNG<br />
Prozess Thread<br />
BS-Kern<br />
Thread<br />
tabelle<br />
Prozesstabelle<br />
Benutzer<br />
Adressraum<br />
(Benutzermodus)<br />
System<br />
Adressraum<br />
(Systemmodus)<br />
– Thread-Tabelle speichert Informationen (Register, Zustand, etc.) über<br />
Threads.<br />
– Prozessorzuteilung im BS-Kern erfolgt an Threads. Falls ein Thread<br />
blockiert, kann die Prozessorzuteilung entscheiden, ob ein anderer Thread<br />
aus demselben Prozess die CPU zugeteilt bekommt, oder ein Thread eines<br />
anderen Prozesses.<br />
– Der Systemaufruf eines Threads blockiert nicht die anderen Threads des<br />
Prozesses.<br />
¯ Es wurden auch hybride Ansätze verfolgt, die eine Kombination beider<br />
Möglichkeiten realisieren. Der BS-Kern verwaltet sogenannte Kernel-Threads.<br />
Das Betriebssystem kennt nur Kernel-Threads und teilt diese der CPU zur<br />
Ausführung zu. Threads des Benutzer-Adressraums (User-Threads) werden<br />
jeweils auf Kernel-Threads abgebildet, dabei können gleichzeitig mehrere<br />
User-Threads eines Prozesses verfügbaren Kernel-Threads zugeordnet werden.<br />
Auf einen einzelnen Kernel-Thread können gleichzeitig auch mehrere User-<br />
Threads abgebildet werden (Multiplexing).<br />
139
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
Benutzer<br />
Adressraum<br />
(Benutzermodus)<br />
System<br />
Adressraum<br />
(Systemmodus)<br />
Prozess<br />
BS-Kern<br />
4.4 Prozessorverwaltung<br />
Kernel<br />
Thread<br />
User-Thread<br />
Eine wesentliche Aufgabe der Prozessorverwaltung besteht darin zu entscheiden,<br />
welcher der um den bzw. die Prozessor(en) konkurrierenden Prozesse (bzw.<br />
Threads) zu einem Zeitpunkt an den bzw. die Prozessor(en) gebunden wird.<br />
Dazu steht die BS-Komponente Scheduler zur Verfügung. Die Durchführung<br />
der Zustandsübergangs (siehe Seite 130) eines Prozesses von rechenwillig<br />
nach rechnend ist Aufgabe des Dispatchers, während der Scheduler aus der Liste<br />
der möglichen Prozesse einen geeigneten auswählt. Der Scheduler wählt den<br />
Prozess aus, der durch einen assign-Zustandsübergang nach rechnend übergeht.<br />
In folgenden Situationen muss ein Scheduler auf jeden Fall aktiviert werden:<br />
ein neuer Prozess wird erzeugt;<br />
ein Prozess terminiert;<br />
ein Prozess blockiert aufgrund eines EA-Auftrags;<br />
eine EA-Unterbrechung tritt auf.<br />
Daneben kann das Betriebssystem einem Prozess die CPU entziehen, falls er<br />
bereits zulange rechnend ist (Ablauf der Zeitscheibe).<br />
4.4.1 Kriterien<br />
Der Scheduler wählt aus der Menge der rechenwilligen Prozesse den nächsten<br />
auszuführenden Prozess aus. Es existieren unterschiedliche Verfahren die von der<br />
jeweiligen Prozessorverwaltungsstrategie abhängen. Mögliche Leistungskriterien<br />
für ein Schedulingverfahren:<br />
140
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
¯ Fairness. Jeder Prozess soll einen fairen Anteil der CPU zum Rechnen erhalten.<br />
¯ Effizienz, Prozessorauslastung. Dies ist ein Maß für die Auslastung eines<br />
Prozessors. Ziel sollte es sein, dass möglichst alle Teile der Rechenanlage<br />
effizient genutzt werden, z.B. CPU und EA-Geräte sollten möglichst gut<br />
ausgelastet sein.<br />
¯ Antwortzeit für interaktive Benutzer (Dialogverarbeitung). Für interaktive Anwendungen<br />
muss die Zeitspanne zwischen Ankunft, z.B. einer Benutzereingabe<br />
und einer potentiellen Reaktion möglichst kurz sein.<br />
¯ Wartezeit, insbesondere für Batch-Jobs (Stapelverarbeitung). Darunter ist die<br />
Verweilzeit in der Bereit-Liste zu verstehen, d.h. die Zeitdauer in der einem<br />
rechenwilligen Prozess kein physischer Prozessor zugeordnet ist.<br />
¯ Abschlusszeit, insbesondere für Realzeitsysteme. Hier geht es darum, ob die<br />
Realzeitgarantien eingehalten werden. Wird der Prozess rechtzeitig fertig und<br />
liefert seine Ergebnisse aus (z.B. das Videobild)?<br />
¯ Durchsatz, Anzahl der Aufträge pro Zeiteinheit. Mit den Leistungskriterien gibt<br />
es Probleme: a) die Optimierungsziele sind teilweise widersprüchlich (z.B. eine<br />
Strategie, die den Durchsatz optimiert ist nicht notwendigerweise geeignet, um<br />
kurze Antwortzeiten zu ermöglichen), und b) das Prozessverhalten (z.B. wann<br />
kommt der nächste E/A-Befehl) kann vom Scheduler nicht exakt vorausgesagt<br />
werden. Deshalb werden Scheduling-Strategien meist abhängig von der<br />
Betriebsart gewählt (Dialogbetrieb vs. Stapelbetrieb).<br />
4.4.2 Scheduling-Strategien<br />
Es werden zwischen zwei Klassen unterschieden: nicht-unterbrechende (nonpreemptive)<br />
und unterbrechende Strategien (preemptive).<br />
nicht unterbrechend: Scheduling nur dann möglich, wenn der rechnende<br />
Prozess blockiert wird oder wenn er terminiert, d.h. Prozess behält CPU bis<br />
er sie selber abgibt.<br />
Beispiel: Microsoft Windows 3.x; unterbrechende Strategien erst ab<br />
Windows 95<br />
unterbrechend: Unterbrechung beim Eintreten von speziellen Ereignissen,<br />
u.a. Eintreffen eines Prozesses mit höherer Priorität oder Prozess geht in<br />
Wartezustand. Problematisch ist diese Strategie für die Ausführung von<br />
Operationen des BS-Kerns. Lösung bei vielen Betriebssystemen (z.B. Unix):<br />
Ausführung von BS-Kernoperationen sind nicht unterbrechbar (Vorsicht: hier<br />
in bezug auf Scheduling, Interrupts können auftreten).<br />
141
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
Zeitscheibenstrategie<br />
Die Zeitscheibenstrategie (Round Robin) ist unterbrechend. Ziel ist die<br />
gleichmäßige Verteilung der Rechenzeit auf rechenwillige Prozesse. Round Robin<br />
ist eine weit verbreitete preemptive Schedulingvariante. Das Verfahren ordnet<br />
jedem rechenwilligen Prozess ein definiertes Zeitquantum (Zeitscheibe) zu. In 4.3<br />
BSD Unix beträgt z.B. die Zeitscheibe 100 ms. Nach dem Kontextwechsel ist der<br />
Prozess entweder bis zum Ablauf des Zeitquantums oder bis zum Auftreten einer<br />
blockierenden Systemfunktion im Besitz der CPU. Alle rechenwilligen Prozesse<br />
werden in einer FIFO-Warteschlange verwaltet. Nach Ablauf der Zeitscheibe wird<br />
der Prozess am Ende der FIFO-Warteschlange eingereiht.<br />
¯ Es werden die Prozesse an den Prozessor jeweils für ein festgelegtes<br />
Zeitquantum q gebunden und spätestens nach dem Ablauf dieser Zeitspanne<br />
wird den Prozessen der Prozessor wieder entzogen.<br />
¯ zyklisches Bedienen der Prozesse (Round Robin).<br />
¯ Ready-Queue (Liste der rechenwilligen Prozesse) als zyklische Warteschlange<br />
realisiert.<br />
¯ Wahl des Zeitquantums:<br />
falls q zu klein: viele unproduktive Kontextwechsel. Es scheint, als ob jeder<br />
Prozess seinen eigenen Prozessor besitzt; process sharing.<br />
falls q zu groß: Round Robin wird zu einem reinen FCFS Scheduling<br />
(First Come First Served), da die Wahrscheinlichkeit für einen Aufruf<br />
eines blockierenden Systemdienst steigt. Ein zu großes Zeitquantum ist<br />
insbesondere für interaktive Anwendungen nicht empfehlenswert, da sich<br />
die Wartezeit für einen Prozess, und damit die Reaktionszeit erhöht.<br />
Typische Werte für q: 10 bis 100 Millisekunden.<br />
¯ Für q = 100 ms gilt bei 1 MIPS Maschine: ca. 100.000 Instruktionen/q.<br />
Prioritäten<br />
Diese Strategie ist i.a. unterbrechend. Sie basiert darauf, an die Prozesse<br />
Prioritäten zu vergeben. Die Prioritätenvergabe kann dabei statisch oder<br />
dynamisch sein. Die Prioritätenstrategie kann unterbrechend und nichtunterbrechend<br />
sein. Im ersten Fall wird der Ablauf eines Prozesses unterbrochen,<br />
wenn ein anderer Prozess mit höherer Priorität rechenwillig wird. Im zweiten<br />
Fall behält der Prozess die CPU solange bis er entweder eine blockierende<br />
Systemfunktion aufruft oder die CPU freiwillig abgibt.<br />
142
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
¯ Prioritäten sind i.a. ein festgelegter Zahlenbereich, z.B. 0, ..., 7. Achtung:<br />
häufig: niedrige Zahl entspricht hoher Priorität, muss aber nicht so sein.<br />
¯ Statische Prioritätenvergabe<br />
jeder Prozess besitzt für die Dauer seiner Existenz eine feste Priorität.<br />
Problem: Gefahr des Verhungerns von Prozessen mit niedriger Priorität<br />
Lösung: Erhöhung der Priorität von lange wartenden Prozessen, d.h.<br />
dynamische Prioritäten.<br />
¯ Dynamische Prioritätenvergabe<br />
die Prioritäten der Prozesse können sich dynamisch verändern, d.h. sie werden<br />
in gewissen Zeitabständen neu berechnet.<br />
Idee: lange Wartezeiten berücksichtigen (Erhöhen die Priorität).<br />
Prozesse mit großem CPU-Verbrauch sinken in Priorität.<br />
E/A-intensive Prozesse steigen in Priorität (damit E/A-Geräte und CPU<br />
parallel genutzt werden).<br />
¯ Zeitscheibenstrategien und Prioritätenvergabe können zu effizienten Verwaltungsstrategien<br />
kombiniert werden. Beispielsweise können Prozesse in Prioritätsklassen<br />
gruppiert werden. Innerhalb einer Gruppe wird die Zeitscheibenstrategie<br />
verwendet. Beispielweise können 4 Prioritätsklassen eingerichtet werden,<br />
wobei die Klasse 4 die höchste Priorität hat. Solange Prozesse in der<br />
Klasse 4 sind, werden diese jeweils im Zeitscheibenverfahren ausgewählt. Falls<br />
die Klasse 4 leer, werden Prozesse der Klasse 3 ausgewählt usw. Prozesse<br />
müssen dynamisch den Klassen zugeordnet werden, damit nicht Prozesse der<br />
untersten Klasse verhungern.<br />
First-Come First-Served<br />
Dieses nicht-unterbrechende Verfahren (FCFS) teilt einen Prozessor in der<br />
Reihenfolge des Auftragseingangs zu. Ready-Queue wird als FIFO-Liste<br />
verwaltet; Verfahren einfach zu realisieren.<br />
¯ Ein Kontextwechsel findet nur statt, wenn der Prozess eine blockierende<br />
Systemfunktion aufruft oder der Prozess die CPU freiwillig abgibt. Im letzten<br />
Fall wird der Prozess sofort wieder am Ende der Ready-Queue eingereiht. Im<br />
ersten Fall wird der Prozess nach Ende der Blockierungsursache wieder am<br />
Ende der Ready-Queue eingereiht.<br />
¯ Es kann eine hohe CPU Auslastung erreicht werden.<br />
143
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
¯ Problem: Durchschnittliche Wartezeit ist hoch.<br />
Beispiel: Prozesse P1,P2,P3 kommen nahezu gleichzeitig zum Zeitpunkt 0<br />
an;<br />
Dauer ihrer Berechnungszeiten: P1: 24 ms, P2: 3ms, P3: 3ms;<br />
bei Reihenfolge P1, P2, P3: mittlere Wartezeit: (0 + 24 + 27)/3 = 17 ms<br />
bei Reihenfolge P2, P3, P1 mittlere Wartezeit (0+3+6)/3 = 3 ms<br />
Shortest-Jobs-First<br />
Dieses Verfahren (SJF) führt die Prozessorzuteilung in der Reihenfolge der<br />
wachsenden Rechenphasen ("CPU-Burst") zu, d.h. Prozess mit kürzester,<br />
nächster Rechenphase erhält Prozessor als nächster. Das Verfahren ist nur bedingt<br />
realisierbar, da die Länge des nächsten CPU-Bursts a priori nicht bekannt ist.<br />
In der Praxis wird daher eine Approximation eingesetzt, die auf der Basis der<br />
gemessenen Länge der zurückliegenden Bursts und einem Schätzwert für den<br />
nächsten Burst ermittelt wird.<br />
¯ anwendbar, falls die Dauer der nächsten Rechenphase bis E/A-Befehl, Interrupt<br />
etc. bekannt ist.<br />
¯ Beispiel: P1: 6ms, P2: 8ms, P3: 7ms, P4: 3ms<br />
Schedule bei SFJ : P4, P1, P3, P2; Wartezeit: (3+16 +9 +0) /4 = 7 ms<br />
bei FCFS: 10.25 ms (P1 vor P2 vor P3 vor P4)<br />
¯ Problem: Kenntnis über die Bedienzeiten erforderlich. Für Stapelbetrieb<br />
geeignet, da dort Information über Rechenzeiten zur Verfügung stehen<br />
(Benutzer geben bei Batch-Jobs Rechenzeit an). Für SJF gibt es wieder<br />
nicht-unterbrechende und unterbrechende Varianten. Im letzteren Fall wird<br />
ein Prozess unterbrochen, falls ein Prozess rechenwillig, dessen nächste<br />
Rechenphase kürzer ist als die noch verbleibende Rechenzeit des momentan<br />
rechnenden Prozesses.<br />
Animation Scheduling<br />
siehe online Version<br />
4.4.3 Beispiel Unix Scheduling<br />
Beim Unix-Scheduling handelt sich um eine Zeitscheibenstrategie mit dynamischer<br />
Prioritätenvergabe. Unix vergibt für seine Prozesse Prioritäten von 0 - 127<br />
(0 ist die höchste Priorität), die in 32 Warteschlangen verwaltet werden. Alle<br />
144
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
Prozesse einer Prioritätsklasse befinden sich in einer Warteschlange, die nach einer<br />
Round-Robin Strategie abgearbeitet wird. Zunächst wird allen Prozessen der<br />
höchsten Priorität die CPU zugeteilt bis die Warteschlange leer ist. Dann kommen<br />
die Prozesse mit der nächstniedrigeren Priorität zum Zuge. Die Prioritäten werden<br />
fortlaufend neu berechnet (multilevel-feedback-queue). Die Prioritäten der<br />
Prozesse, die in einem gewissen Zeitabschnitt viel Rechenzeit verbraucht haben,<br />
werden erniedrigt; Prozesse, die lange gewartet haben, erhalten eine höhere Priorität.<br />
¯ Zeitscheibenstrategie pro Warteschlange bis Warteschlange leer; dann Scheduling<br />
mit nächst niedrigerer Warteschlange.<br />
¯ dynamische Berechnung der Prozesspriorität:<br />
ÙÔÖÓ ÍËÊÈÊÁÇ Ô ÔÙ ÔÒ <br />
p_cpu ist die Prozessornutzung des rechnenden Prozesses und wird alle 10 ms<br />
um 1 inkrementiert.<br />
p_nice ist ein vom Benutzer bestimmter Gewichtungsfaktor (-20 p_nice <br />
20).<br />
USER_PRIO ist die Priorität, die dem Prozess beim Start zugeteilt worden ist.<br />
Der Wert von p_cpu wird jede Sekunde angepasst<br />
Ô ÔÙ ÐÓ ÐÓ Ô ÔÙ ÔÒ <br />
load ist eine Abschätzung der CPU-Auslastung.<br />
Die Anpassung (2) sorgt dafür, dass die bisher verbrauchte Rechenzeit nach<br />
einer gewissen Zeit nicht mehr ins Gewicht fällt. Das "short-term-scheduling"<br />
unterstützt das interaktive Arbeiten am Rechner. Die Prioritäten von Prozessen,<br />
die lange auf die Ausführung von E/A-Operationen warten, werden erhöht.<br />
Dagegen werden die Prioritäten von Prozessen, die wesentliche CPU-Zeiten auf<br />
sich vereinigen, verringert. Dies bedeutet zusammenfassend, dass ”interaktive”<br />
Prozesse den ”Batch”-Prozessen vorgezogen werden.<br />
4.4.4 Thread Scheduling<br />
Die Prozessorzuteilung von Threads hängt von deren Art der Realisierung<br />
(siehe Seite 137) ab.<br />
User-Threads<br />
Realisierung der Threads im Benutzeradressraum Kern hat keine Kenntnis<br />
bzgl. der Threads. BS-Scheduler wählt nur Prozess aus. Laufzeitsystem des<br />
145
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
Prozesses wählt rechenwilligen Thread des Prozesses aus; es kann ein beliebiges<br />
Scheduling-Verfahren (siehe Seite 141) für Prozesse verwendet werden. Da<br />
auf der Ebene des Laufzeitsystems keine Zeitunterbrechungen (clock interrupts)<br />
bearbeitet werden (sie werden auf Betriebssystem-Ebene bearbeitet), können<br />
Threads so lange laufen, bis sie selbst die CPU freiwillig aufgeben, d.h. ein<br />
Thread kann vom Laufzeitsystem aufgrund des Ablaufs einer Zeitscheibe nicht<br />
unterbrochen werden.<br />
¯ Java Virtual Machines verwenden unterbrechendes Prioritäten-Scheduling für<br />
Threads;<br />
10 ist die höchste und 1 die niedrigste Priorität;<br />
Ein rechenwilliger höher priorer Thread führt zur Unterbrechung eines<br />
rechnenden niedriger prioren Thread ("unterbrechender Scheduler").<br />
Weiterhin kann ein Thread auch freiwillig die CPU aufgeben ("cooperative<br />
scheduler"). Probleme treten auf, wenn Threads die gleiche Priorität haben.<br />
Falls ein cooperative Scheduler verwendet wird, ist der aktuelle Thread<br />
solange rechnend, bis er freiwillig die CPU aufgibt. Falls er dies nicht<br />
macht, sind die anderen rechenwilligen Threads nur wartend (Gefahr des<br />
Verhungerns).<br />
Ein unterbrechender Thread-Scheduler wird in gewissen Zeitabständen<br />
den aktuell rechnenden Thread unterbrechen, und andere Threads gleicher<br />
Priorität auswählen.<br />
Das Ablauf-Verhalten hängt vom Scheduling Verfahren der jeweiligen JVM<br />
Implementierung ab, z.B. Windows verwendet unterbrechendes Thread<br />
Scheduling, während der Mac cooperative Thread Scheduling realisiert<br />
(möglicherweise anders bei MacOS 10).<br />
Kernel-Threads<br />
Realisierung der Threads im Systemadressraum<br />
nächsten auszuführenden Thread aus.<br />
BS-Scheduler wählt den<br />
a) Ist ausgewählter Thread demselben Prozess zugeordnet wie der vorher<br />
rechnende Thread geringer Kontextwechsel.<br />
b) Ist ausgewählter Thread nicht demselben Prozess zugeordnet wie der<br />
vorher rechnende Thread aufwendiger Kontextwechsel. In diesem<br />
Fall findet auch ein Adressraumwechsel statt, d.h.<br />
Speicherabbildung müssen angepasst werden.<br />
die Register zur<br />
146
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
4.4.5 Mehrschichtiges Scheduling<br />
Der Scheduler wählt einen der rechenwilligen Prozesse aus. Da diese aber u.U.<br />
nicht alle im Arbeitsspeicher vorliegen (Speicherknappheit) und ein Einlagern<br />
eines Prozesses von der Platte in den Arbeitsspeicher aufwendig ist, verfügen<br />
Systeme häufig über ein Mehr-Schichten Scheduling.<br />
¯ Short-Term-Scheduler (CPU Scheduler)<br />
Auswahl eines geeigneten Prozesses aus der Ready-Queue; wird häufig<br />
aufgerufen; Verfahren siehe oben.<br />
¯ Long-Term-Scheduler<br />
Auswahl rechenwilliger neuer Aufträge (meist Jobs aus dem Hintergrundbetrieb<br />
(batch)) und Einlagerung in den Arbeitsspeicher; Einfügen der Prozesse<br />
in die Ready-Queue. Scheduler kontrolliert den Multiprogramming-Grad, d.h.<br />
wieviele Prozesse im Arbeitsspeicher liegen. Long-Term-Scheduler wird relativ<br />
selten aufgerufen. Wenn ein Prozess das System verläßt, wird dieser Scheduler<br />
u.U. erst nach mehreren Minuten aufgerufen.<br />
Kriterium: guten Prozessmix erzielen, d.h. Mischung aus E/Aintensiven<br />
und rechenintensiven Prozessen (nur E/A-intensiv: Ready-<br />
Queue häufig leer, CPU nicht ausgelastet, andersherum: E/A-Geräte<br />
schlecht ausgelastet).<br />
Long-term Scheduling ist nicht immer vorhanden: z.B. in Unix nicht, jeder<br />
neue Prozess wird in Arbeitsspeicher geladen. Eine andere Zwischenstufe ist<br />
Medium-term-Scheduling: bei Überlast werden Prozesse auf Hintergrundspeicher<br />
ausgelagert (swap out) und später wieder eingelagert (swap in).<br />
¯ Graphische Darstellung<br />
147
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
swap-in<br />
Long-term<br />
Scheduler<br />
Ready Queue<br />
CPU Scheduler<br />
E/A-Warteschlange<br />
Zeit-Interrupt-<br />
Warteschlange<br />
Ausführung im<br />
BS-Kern<br />
4.4.6 Echtzeit Scheduling<br />
ausgeswappte<br />
Prozesse<br />
neue<br />
Aufträge<br />
swap-out<br />
CPU fertig<br />
E/A-Befehl<br />
Zeitscheibe<br />
abgelaufen<br />
sleep-Befehl<br />
Systemaufruf<br />
In Multimedia-Umgebungen treten die zu verarbeitenden kontinuierlichen Daten<br />
(Empfangen, Dekodierenen und Anzeigen von Videoframes) in bestimmten,<br />
meist periodischen Zeitabständen auf. Die Operationen auf diese Daten<br />
wiederholen sich dabei immer wieder und sollen bis zu einem gewissen<br />
Zeitpunkt abgeschlossen sein. Prozesse in Multimedia-Anwendungen führen<br />
oft zeitkritische Operationen aus. Bzgl. des Scheduling existieren zwei<br />
gegensätzliche Ziele:<br />
148
Schlichter, TU München 4.4. PROZESSORVERWALTUNG<br />
a) ein unkritischer Prozess sollte nicht dauerhaft blockiert werden, weil<br />
zeitkritische Prozesse eine Ressource gänzlich auslasten. Daher sollten<br />
zeitkritische Prozesse und Verwaltungsarbeiten nicht die gesamte Kapazität<br />
einer Ressource beanspruchen.<br />
b) zeitkritische Prozesse dürfen nicht durch Scheduling-Verfahren (Round-<br />
Robin oder Prioritäten) am zeitkritischen Fortschritt gehindert werden<br />
Einhaltung von Zeitvorgaben.<br />
¯ Zuordnung von Kenngrößen zu zeitkritischen Prozessen<br />
Bereitzeit (ready time): frühestmöglicher Ausführungsbeginn einer<br />
Aktivität.<br />
Frist (deadline): spätester Zeitpunkt für die Beendigung einer Aktivität.<br />
Ausführungszeit: worst-case Abschätzung für das zur vollständigen<br />
Ausführung einer Aktivität notwendige Zeitintervall.<br />
In vielen Fällen werden Aktivitäten periodisch ausgeführt. Deshalb werden<br />
hierzu neben der Ausführungszeit Kenngrößen wie Frequenz der Aktivität<br />
(Periode) und Versatz des Ausführungsbeginns relativ zum Anfang der Periode<br />
(Phase) erfasst.<br />
¯ Earliest Deadline First (EDF)<br />
Der Prozessor wird immer dem Prozess mit der am nächsten in der Zukunft<br />
liegenden Frist zugeordnet. Es existieren die beiden Varianten: nichtunterbrechend<br />
und unterbrechend. Fasst man die Fristen als Prioritäten<br />
auf, dann entspricht dieses Verfahren im Prinzip dem prioritäten-basierten<br />
Scheduling.<br />
– nicht-unterbrechend<br />
Eine Prozessorzuordnung bleibt bis der Prozess eine blockierende Systemfunktion<br />
aufruft oder freiwillig die CPU abgibt. Neue eintreffende Prozesse<br />
mit kürzeren Fristen werden erst beim nächsten Scheduling berücksichtigt.<br />
– unterbrechend<br />
Diese Variante führt einen Kontextwechsel durch, wenn ein Prozess mit<br />
einer kürzeren Frist rechenwillig wird. Man kann zeigen, dass die<br />
preemptive Variante immer eine Abarbeitungsreihenfolge mit Einhaltung<br />
aller Zeitvorgaben findet, solange mindestens eine solche Reihenfolge<br />
existiert.<br />
¯ Rate-Monotonic Scheduling<br />
Rate-Monotonic Scheduling (RMS) ist für periodische Prozesse; RMS ordnet<br />
Prioritäten in Abhängigkeit von der Periode zu.<br />
149
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
1) Prozesse mit der höchsten Frequenz (kleinste Periode) erhalten die<br />
höchste Priorität.<br />
2) Prozesse mit der geringsten Frequenz (längste Periode) erhalten die<br />
niedrigste Priorität.<br />
Die Priorität ist statisch; sie ändert sich nicht für den Rest der Prozesslaufzeit.<br />
Prioritäten geben die relative Wichtigkeit eines Prozesses gegenüber der<br />
Wichtigkeit anderer Prozesse im Rechensystem wider. RMS ist relativ einfach<br />
zu handhaben und sie erlaubt eine Abschätzung, ob eine Echtzeitanwendung<br />
auf einer bestimmten Rechenanlage ohne Fristverletzung ausgeführt werden<br />
kann oder nicht.<br />
– Auswahl der Prozesse anhand ihrer Priorität. Jedoch auch unter Berücksichtigung<br />
der anderen Kenngrößen, wie z.B. Bereitzeit.<br />
– hochfrequente Prozesse werden minimal verzögert.<br />
– Zerstückelung niederfrequenter Prozesse, da sie häufig wegen hochfrequenter<br />
Prozesse unterbrochen werden.<br />
4.5 Unterbrechungskonzept<br />
Die optimale Ausnutzung und Auslastung aller Geräte eines Rechensystems legt<br />
Mehrprogrammbetrieb nahe. Zerlegung der Ausführungsphasen eines Programms<br />
in viele Einzelteile;<br />
- Aufbrechen der Ausführung eines Prozesses in mehrere Phasen: u.a.<br />
Rechnen, E/A, Synchronisieren mit Partner.<br />
- Die Ausführung der Programme wird in der Regel mehrfach unterbrochen.<br />
4.5.1 Motivation<br />
Ursachen für Unterbrechungen<br />
zugeteilte Prozessorzeit ist aufgebraucht;<br />
benötigte Ressourcen stehen aktuell nicht zur Verfügung;<br />
ein E/A-Gerät meldet sich zurück;<br />
ein Fehler tritt auf, z.B. Division durch 0;<br />
Systemaufruf (wurde bereits als spezielle Unterbrechung eingeführt);<br />
¯ Bei einer Unterbrechung wird ein gerade aktiver Prozess unterbrochen<br />
und eine Unterbrechungsbehandlung durchgeführt. Nach Beendigung der<br />
Unterbrechungsbehandlung kann prinzipiell ein beliebiger rechenbereiter<br />
Prozess fortgesetzt werden.<br />
150
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
¯ Es ist erforderlich, bei der Unterbrechung den Rechnerkernstatus des gerade<br />
aktiven Prozesses für die spätere Fortsetzung zu speichern. Als Minimum ist<br />
dabei der Befehlszähler und das Programmstatuswort zu retten, da diese bei<br />
der Einstellung des neuen Zustandes bei der Unterbrechungsausführung im<br />
Rechnerkern verändert werden. Weitere Zustandsinformation wird dann in der<br />
Unterbrechungsbehandlung des Betriebssystems gerettet.<br />
¯ Forderung: Eine Unterbrechung muss so kontrolliert erfolgen, dass ein<br />
definierter Prozessstatus festgehalten werden kann.<br />
4.5.2 Unterbrechungsarten<br />
Die normale Programmausführung eines Prozessors kann auch durch mehrere<br />
Arten von Unterbrechungen verändert werden. Man unterscheidet zwischen<br />
synchronen und asynchronen Unterbrechungen.<br />
Trap<br />
Unterbrechung<br />
synchron asynchron<br />
Alarme<br />
(Exception)<br />
Externe, asynchrone Unterbrechungen<br />
Interrupt<br />
Dies sind Unterbrechungen (Interrupts), die von außerhalb des zu unterbrechenden<br />
Prozesses ausgelöst werden, z.B. E/A-Kanal-"Endmeldung". Asynchrone<br />
Unterbrechungen sind Ereignisse im Rechensystem, die über besondere Steuerbusleitungen<br />
an den Prozessor weitergegeben werden. Asynchron bedeutet in<br />
diesem Kontext, dass der eintreffende Interrupt in keiner kausalen Beziehung zum<br />
aktuell ausgeführten Befehl steht.<br />
¯ Der Ablauf im Rechnerkern (RK) wird unterbrochen und eine Unterbrechungsanfangsbehandlung<br />
des Betriebsystemkerns wird aktiviert.<br />
151
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
E/A-Kanal 2 CPU (RK) BS-Kern<br />
Ende E/A<br />
Auftrag<br />
Interne, synchrone Unterbrechungen<br />
Unterbrechungsbehandlung<br />
(UBH)<br />
Dies sind Unterbrechungen (Alarme, Exceptions), die durch den zu unterbrechenden<br />
Prozess selbst ausgelöst werden, z.B. Division durch 0. Synchrone Unterbrechungen<br />
sind eine unmittelbare Folge der aktuellen Befehlsausführung. Sie<br />
können auch explizit durch einen speziellen Befehl (Trap) ausgelöst werden. Mit<br />
Hilfe von Traps werden Systemdienste im Betriebssystem aufgerufen. Im Fehlerfall<br />
spricht man auch von Alarmen ("exceptions").<br />
¯ Der Ablauf im RK wird unterbrochen und eine Unterbrechungsanfangsbehandlung<br />
des Systemkerns wird aktiv.<br />
152
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
CPU (RK) BS-Kern<br />
Unterbrechung<br />
arithmetischer Alarm<br />
Unterbrechungsbehandlung<br />
(UBH)<br />
4.5.3 Behandlung externer Unterbrechungen<br />
Synchrone und asynchrone Unterbrechungen haben hardwaremäßig die Speicherung<br />
des aktuellen Prozessorzustandes zur Folge und lösen im Anschluss<br />
daran einen indirekten Sprung über eine im Speicher befindliche Sprungtabelle<br />
aus. Dabei ordnet der Prozessor jeder synchronen und asynchronen Unterbrechung<br />
einen festen Index in der Sprungtabelle zu. An dieser Stelle steht die<br />
Anfangsadresse der Unterbrechungsroutine, die entsprechende Folgemaßnahmen<br />
einleitet. Falls möglich (Ausnahme ist z.B. ein arithmetischer Alarm) kann die unterbrochene<br />
Programmausführung durch die Wiederherstellung des gespeicherten<br />
Prozessorzustandes zu einem beliebigen, späteren Zeitpunkt fortgesetzt werden.<br />
Ablauf<br />
¯ Geräte-Controller meldet Unterbrechung über spezielle Interrupt-Leitung an<br />
CPU.<br />
¯ CPU prüft im Befehlszyklus nach jeder Befehlsausführung, ob eine Unterbrechung<br />
gemeldet wurde.<br />
¯ Falls Unterbrechung vorliegt: sichern u.a. des aktuellen Befehlszählers, des<br />
Programmstatusworts und Sprung zu einer Unterbrechunganfangsbehandlung,<br />
die an festgelegter Speicheradresse steht. Die restlichen Informationen, wie<br />
Register, werden dann von der Unterbrechungsbehandlung gesichert.<br />
153
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
Routine untersucht Unterbrechungsursache, die vom Controller über<br />
Datenleitung gemeldet wird (Unterbrechungsnummer).<br />
über Unterbrechungsnummer erfolgt die Auswahl der benötigten Unterbrechungsbehandlungsroutine;<br />
Nummer ist i.a. Index in eine Tabelle, dem<br />
Unterbrechungsvektor.<br />
Vektor enthält Speicheradresse der Unterbrechungsbehandlungsroutine.<br />
4.5.4 Konflikte<br />
Konflikte bei Unterbrechungen treten z.B. in folgenden Situationen auf:<br />
(1) während einer Unterbrechungsbehandlung treten weitere Unterbrechungen<br />
auf;<br />
(2) es treffen gleichzeitig mehrere Unterbrechungswünsche ein.<br />
¯ Beispiel<br />
E/A-Kanal 1 und E/A-Kanal 2 erledigen beide Aufträge für Prozess A.<br />
E/A-Kanal 1 Prozess A BS-Kern E/A-Kanal 2<br />
externe<br />
Unterbrechung<br />
Ende E/A<br />
Auftrag<br />
¯ Mögliche Konfliktlösungen<br />
Unterbrechungsbehandlung<br />
(UBH)<br />
Konflikt<br />
externe<br />
Unterbrechung<br />
Ende E/A<br />
Auftrag<br />
– Andere Unterbrechungen nicht zulassen, d.h. Maskierung von Unterbrechungen;<br />
anstehende Unterbrechung ignorieren oder vorläufig zurückstellen;<br />
Problem: u.a. Rechtzeitigkeit der Unterbrechungsbehandlung.<br />
154
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
– Interne Unterbrechungen erfolgen stets sofort und geben der zugehörigen<br />
Unterbrechungsbehandlung dieselbe Priorität, wie sie der unterbrochene<br />
Ablauf hatte. Die Benutzerprozesse haben die niedrigste Ablaufpriorität,<br />
nämlich 0. Unterbrechungen von Unterbrechungsbehandlungen sind<br />
möglich.<br />
– Externe Unterbrechungen erhalten Prioritäten z.B. (0,...,31) zugeordnet. Die<br />
aufgerufene Unterbrechungsbehandlung erhält die Priorität (Ablaufpriorität)<br />
der auslösenden Unterbrechung.<br />
Eine weitere externe Unterbrechung wird während einer Unterbrechungsbehandlung<br />
zugelassen, wenn die Priorität der neuen Unterbrechung<br />
höher als die Ablaufpriorität der gerade aktiven Unterbrechungsbehandlung<br />
ist. Trifft dieser Fall nicht zu, so wird der Unterbrechungswunsch<br />
zurückgestellt, bis ein Ablauf mit einer niedrigeren<br />
Ablaufpriorität aktiv wird.<br />
Konsequenz: Unterbrechungsroutinen müssen ablaufinvariant sein!<br />
¯ Integration der Unterbrechungsbehandlung in den Befehlszyklus der CPU<br />
prüfen ob interne Unterbrechung aufgetreten,<br />
falls ja, Behandlung der Unterbrechung<br />
sonst : prüfen ob externe Unterbrechung mit höherer Priorität. Wenn ja<br />
wähle eine mit höchster Priorität.<br />
Bei Unterbrechung: sichere alten Zustand, stelle neuen Zustand her und<br />
führe ersten Befehl der Unterbrechungsbehandlungsroutine aus.<br />
¯ Beispiel: Festlegungen in der MI<br />
Die Anzahl und die Anfangsadressen der Unterbrechungsbehandlungen sind<br />
vom jeweiligen Betriebssystem abhängig. Ein Systemkontrollblock (SCB)<br />
enthält Startadressen der Unterbrechungsbehandlungsroutinen. Die Adresse<br />
des SCB wird in einem Register des RK, dem Sonderregister SCBADR,<br />
gehalten.<br />
155
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
U-Nr. Relativadresse U-Priorität Art der Unterbrechung<br />
1 +0 31 katastrophaler<br />
Fehler<br />
2 +4 übernommen Befehlsalarm<br />
3 +8 übernommen Speicherschutzalarm<br />
4 +12 übernommen Seite-fehlt-alarm<br />
5 +16 übernommen Trace<br />
6 +20 übernommen arithmetischer<br />
Alarm<br />
7 +24 übernommen CHMK<br />
temaufruf)(Sys-<br />
8 +28 übernommen - (reserviert)<br />
9 +32 25 Rechnerkernalarm<br />
10 +36 24 Weckeralarm<br />
11 +40 23 von<br />
icherPlattenspe-<br />
12 +44 22 von Kanal 0<br />
Empfänger<br />
13 +48 22 von Kanal 0<br />
Sender<br />
14 +52 21 von Kanal 1<br />
Empfänger<br />
15 +56 21 von Kanal 1<br />
Sender<br />
16 +60 20 von Kanal 2<br />
Empfänger<br />
17 +64 20 von Kanal 2<br />
Sender<br />
18 +68 19 von Kanal 3<br />
Empfänger<br />
19 +72 19 von Kanal 3<br />
Sender<br />
Anmerkung 1: Die Unterbrechungsnummern und die Unterbrechungsprioritäten<br />
stehen nicht im Systemkontrollblock, sondern sie sind den Unterbrechungen<br />
implizit zugeordnet. Anmerkung 2: Das "Tracing" ist eine Testhilfe auf<br />
sehr niederer Ebene. Ist "Tracing" eingestellt, dann wird nach jedem Maschinenbefehl<br />
der Rechnerkern unterbrochen und eine Unterbrechungsbehandlung<br />
aufgerufen. In dieser UBH kann dann der Status des Rechnerkerns, insbeson-<br />
156
Schlichter, TU München 4.5. UNTERBRECHUNGSKONZEPT<br />
dere die Werte in den Registern, abgefragt oder ausgedruckt werden. Das<br />
"Tracing" ist sehr zeitaufwendig und sollte daher auf die nicht getesteten Befehlsfolgen<br />
im Benutzerprogramm beschränkt werden. Daher wird das "Tracing"<br />
automatisch ausgeschaltet, während Systemdienste oder allgemeiner Unterbrechungen<br />
im Systemkern ausgeführt werden, da diese Teile getestet sind<br />
und da der Benutzer diese Befehlfolgen ohnehin nicht kennt.<br />
157
Kapitel 5<br />
Speicherverwaltung<br />
Der Adressraum ist eine zentrale Abstraktion, die von der Systemsoftware eines<br />
Rechensystems zur Verfügung gestellt werden muss. Über den Adressraum sind<br />
alle für die Ausführung eines Anwendungsprogramms notwendigen Operationen<br />
und Datenstrukturen zugreifbar. Allgemein wird ein Adressraum durch eine<br />
zusammenhängende Menge von Adressen und deren Inhalte definiert. Die<br />
maximale Größe eines Adressraums kann aus dem Adressbusaufbau der<br />
verwendeten Prozessorarchitektur abgeleitet werden. Modernde Rechensysteme<br />
unterscheiden zwischen den Adressräumen der Programme und dem physischen<br />
Adressraum (Arbeitsspeicher).<br />
5.1 Fragestellungen<br />
Dieser Abschnitt beschäftigt sich mit den Adressräumen für Programme und<br />
deren Abbildung auf den physischen Arbeitsspeicher einer Rechenanlage:<br />
¯ Programmadressraum vs. Maschinenadressraum.<br />
¯ Direkte Adressierung, Basisadressierung.<br />
¯ Virtualisierung des Speichers; virtuelle Adressierung, insbesondere Seitenadressierung.<br />
5.2 Einführung<br />
Die unmittelbare Nutzung des physischen Adressraums (Arbeitsspeichers) bei der<br />
Anwendungsentwicklung ist nicht empfehlenswert. Probleme sind folgende:<br />
158
Schlichter, TU München 5.2. EINFÜHRUNG<br />
- Kenntnisse über Struktur und Zusammensetzung des Arbeitsspeichers<br />
notwendig.<br />
- Kapazitätsengpässe bei der Arbeitsspeichergröße. Werden die in einem<br />
Rechensystem bei der Befehlsausführung durch den Rechnerkern auftretenden<br />
Adressen direkt als reale Speicheradressen interpretiert, so kommt man<br />
schnell in Kapazitätsengpässe. Die Kapazität des Arbeitsspeichers reicht oft<br />
nicht aus, um alle Daten darin abzulegen. Deshalb weicht man auf den Hintergrundspeicher<br />
(Platte) aus, um Teile der Daten eines Prozesses dort abzulegen.<br />
deshalb Programmerstellung unabhängig von realer Speichergröße und -<br />
eigenschaften. Abstrahieren von realen Eigenschaften der Hardware. Die<br />
Handhabung der unterschiedlichen Speicher sollte für die <strong>Programmierung</strong><br />
in höheren Programmiersprachen ohne Belang sein, d.h. der Programmierer<br />
soll sich nicht explizit um das Nachladen von benötigten Daten vom<br />
Hintergrundspeicher in den Arbeitsspeicher und umgekehrt kümmern<br />
müssen. Dies soll automatisch, d.h. transparent für den Programmierer<br />
geschehen.<br />
5.2.1 Adressräume<br />
¯ Maschinenadressraum<br />
Der Arbeitsspeicher besteht aus einer Folge von fortlaufend nummerierten<br />
Bytes. Die Nummerierung beginnt bei 0. Die Nummer des Bytes bezeichnet<br />
man als seine Adresse, genauer als seine physische Speicheradresse oder seine<br />
Maschinenadresse.<br />
Die Menge der möglichen Maschinenadressen ist der Maschinenadressraum<br />
(physischer Adressraum). Man beachte, dass bei der speicherbasierten E/A-<br />
Architekturvariante (siehe MI) die Register der E/A-Controller auch über<br />
Maschinenadressen angesprochen werden, d.h. nicht jeder Maschinenadresse<br />
ist eine Speicherzelle im Arbeitsspeicher zugeordnet.<br />
¯ Programmadressraum<br />
Wenn ein Benutzer programmiert, dann benutzt er in seinem Programm<br />
Adressen, um seine Variablen zu bezeichnen. Diese Adressen nennen wir<br />
Programmadressen.<br />
Die Menge der zulässigen Programmadressen ist der Programmadressraum.<br />
Dieser ist prozessspezifisch, d.h. Programmadressen haben nur Programmlokale<br />
Bedeutung z.B. als Sprungziele.<br />
¯ Speicherabbildung<br />
159
Schlichter, TU München 5.2. EINFÜHRUNG<br />
Der Rechnerkern muss vor jedem Zugriff auf Befehle und Operanden<br />
die jeweiligen Programmadressen in Maschinenadressen umsetzen. Diese<br />
Umsetzung wird durch Speicherabbildungen geleistet. Die wichtigsten dieser<br />
Abbildungen werden in den folgenden Abschnitten kurz vorgestellt.<br />
direkte Adressierung,<br />
Basisadressierung,<br />
Seitenadressierung und<br />
Segment-Seitenadressierung.<br />
5.2.2 Organisation von Adressräumen<br />
Im Adressraum einer Anwendung müssen alle für die Programmausführung<br />
notwendigen Daten zur Verfügung gestellt werden. Darunter fallen der Programmcode<br />
(Text), der Datenbereich (statische und dynamische Daten) und der<br />
Laufzeitkeller. Für jede dieser Informationsarten wird ein Bereich im Adressraum<br />
spezifiziert, deren Platzierung und Größe durch die Adressraumverwaltung<br />
festgelegt wird.<br />
¯ Single-Threaded Adressraum<br />
niedrige<br />
Adresse<br />
Programm<br />
statische<br />
Daten<br />
dynamische Daten<br />
(Halde)<br />
Keller<br />
hohe<br />
Adresse<br />
Die Bereiche Programmcode und statische Daten verändern sich typischerweise<br />
während der Programmausführung nicht. Dagegen können der dynamische<br />
Datenbereich (Halde) und der Laufzeitkeller an Umfang erheblich zunehmen.<br />
Beim Laufzeitkeller hängen Größenschwankungen und Ausdehnung von der<br />
maximalen Tiefe der Prozedur/Methodenschachtelung und dem Bedarf an<br />
lokalen Variablen ab.<br />
¯ Multi-Threaded Adressraum<br />
160
Schlichter, TU München 5.2. EINFÜHRUNG<br />
niedrige<br />
Adresse<br />
Programm<br />
statische<br />
Daten<br />
dynamische Daten<br />
(Halde)<br />
Keller 1<br />
Keller n<br />
hohe<br />
Adresse<br />
Für jeden Kontrollfluss (Thread) wird ein eigener Kellerbereich vorgesehen.<br />
Der Abstand zwischen den einzelnen Kellern wird meist standardmäßig vom<br />
System vorgegeben. Unabhängig von der Anzahl der Laufzeitkeller muss<br />
eine Überschneidung zwischen mehreren Kellern oder zwischen dem untersten<br />
Keller und der Halde vermieden werden.<br />
¯ Beispiel - Adressräume<br />
Moderne Betriebssysteme stellen wenigstens 32 Bit große virtuelle Adressräume<br />
für die Anwendungen zur Verfügung, die jeweils in mehrere Bereiche<br />
unterteilt sind. Programmcode, statische Daten, Halde und Laufzeitkeller der<br />
Anwendung werden jeweils in dem Bereich des Adressraums abgelegt, der der<br />
Anwendung zugänglich ist.<br />
4.4 BSD Unix<br />
Windows 95<br />
Windows NT<br />
0 1 GByte<br />
2 GByte<br />
3 GByte<br />
4 GByte<br />
spezieller Adressbereich (Größe nicht proportional)<br />
161
Schlichter, TU München 5.2. EINFÜHRUNG<br />
Der für die Anwendung nutzbare Adressbereich (weißer Bereich) schwankt<br />
zwischen ca. 2 GByte und 4 GByte. Bei allen BS wird ein unterschiedlich<br />
großer Adressbereich am Anfang (Adressen 0 und aufwärts) für jeglichen<br />
Zugriff gesperrt: bei Windows 95 sind es 4 KByte, bei Windows NT 64<br />
KByte, bei BSD Unix ist es abhängig vom Rechnertyp (4 - 8 KByte). Durch<br />
diese Sperrung wird jeder Zugriff auf diesen Bereich und damit insbesondere<br />
das Dereferenzieren eines Nullzeigers vom System abgefangen und die<br />
Programmausführung mit einer Fehlermeldung abgebrochen. In Windows 95<br />
wird zusätzlich der Adressbereich von 4 KByte - 4 MByte von der Nutzung<br />
durch 32 Bit Anwendungen ausgeklammert (Adressbereich für MS-DOS<br />
bzw 16 Bit Anwendungen). Im oberen Bereich des virtuellen Adressraums<br />
wird meist der Betriebssystemcode eingeblendet (z.B. bei Windows 95 im<br />
Bereich 1 GByte - 2 GByte; dadurch können BS-Funktionen auch ohne<br />
Adressraumwechsel genutzt werden. Bei BSD Unix und Windows NT/2000 ist<br />
dieser Bereich vor jeglichem Zugriff durch die Anwendung geschützt (oft wird<br />
sogar lesender Zugriff unterbunden). Bei Windows 95 besteht dieser Schutz<br />
nicht, d.h. Anwendungen können das Betriebssystem zum Absturz bringen.<br />
5.2.3 Fragmentierung<br />
Unter dem Begriff Fragmentierung versteht man verschiedene Formen der<br />
Zerstückelung des noch freien und nutzbaren Teils des Adressraums in kleine<br />
Bereiche. Fragmentierung kann jedesmal dann entstehen, wenn eine neue<br />
Speicherplatzanforderung aus der Menge noch freier Speicherbereiche mit einem<br />
einzigen zusammenhängenden Bereich befriedigt werden muss. Unterscheidung<br />
zwischen externer und interner Fragmentierung.<br />
¯ Externe Fragmentierung<br />
Es wechseln sich benutzte und unbenutzte Speicherbereiche innerhalb des<br />
Adressraums ab. Speicheranforderungen werden jeweils genau erfüllt.<br />
162
Schlichter, TU München 5.2. EINFÜHRUNG<br />
Anforderung<br />
belegt belegt belegt<br />
belegt belegt belegt<br />
belegt<br />
freie Speicherbereiche<br />
Im Fall eines Anwendungsadressraums tritt dies beim dynamischen Datenbereich<br />
(Halde) auf; Fragmentierung entsteht durch die unterschiedlichen<br />
Speicheranforderungs- und Freigabemuster. Durch zeitaufwendiges Zusammenschieben<br />
der Belegtbereiche (Kompaktifizieren) entsteht aus vielen kleinen<br />
Speicherresten ein großer Freibereich. Beim Kompaktifizieren müssen u.U. die<br />
Adressen in den Programmen angepasst werden (z.B. Verweise auf Absolutadressen).<br />
¯ Interne Fragmentierung<br />
Der Speicher ist in Bereiche fester Größe untergliedert und Speicheranforderungen<br />
werden nur in Vielfachen dieser festen Grundgröße befriedigt.<br />
163
Schlichter, TU München 5.2. EINFÜHRUNG<br />
freier Speicherbereich<br />
Anforderung<br />
Der Verschnitt findet innerhalb dieser Bereiche fester Größe statt. Beispielsweise<br />
kann Plattenspeicher nur blockweise belegt werden; die Blockgröße<br />
schwankt zwischen 512 Byte und 4-8 Kbyte. Selbst für die Speicherung eines<br />
einzigen Bytes muss ein ganzer Block belegt werden.<br />
5.2.4 Forderungen an Adressraumrealisierung<br />
Aus der Sicht der Anwendungsprogrammierung können für einen Adressraum<br />
eine Reihe wichtiger Forderungen an dessen Realisierung gestellt werden.<br />
Hier geht es um den Programmieradressraum für Prozesse, und nicht um den<br />
Maschinenadressraum (Arbeitsspeicher).<br />
¯ Homogene und zusammenhängende Adressbereiche. Dies ermöglicht eine<br />
Programmentwicklung ohne das ansonsten notwendige Wissen über Position,<br />
Typ und Größe der referenzierbaren Speichermodule und E/A-Controller.<br />
¯ Größe des genutzten Adressraums unabhängig von der Kapazität des<br />
physischen Adressraums (Arbeitsspeichers).<br />
¯ Erkennen fehlerhafter Zugriffe.<br />
¯ Erkennen von Überschneidungen zwischen Halde und Keller sowie zwischen<br />
mehreren Laufzeitkellern.<br />
164
Schlichter, TU München 5.3. SPEICHERABBILDUNGEN<br />
¯ Schutz funktionstüchtiger Anwendungen gegenüber fehlerhaften Anwendungen.<br />
Hier geht es darum, dass die Adressbereiche der Anwendungen und auch<br />
des Betriebssystems voneinander abgeschottet werden. Ist dieser Schutz nicht<br />
gewährleistet, können fehlerhafte Programme den Adressraum einer anderen<br />
Anwendung verändern und damit Folgefehler in dieser auslösen nicht deterministische<br />
Fehler. Fehler dieser Art sind schwer zu reproduzieren und ihre<br />
Lokalisierung ist meist extrem schwierig und langwierig.<br />
¯ Kontrollierbares und kontrolliertes Aufteilen der Speicherressourcen auf alle<br />
Anwendungen.<br />
¯ Speicherökonomie, minimale Fragmentierung. Es sollten gängige Techniken<br />
eingesetzt werden, um die durch dynamische Anforderungen bedingte Speicherfragmentierung<br />
und den tatsächlichen Speicherbedarf jeder Anwendung<br />
zu minimieren, z.B. gemeinsame Nutzung von Funktionsbibliotheken durch<br />
mehrere parallel laufende Anwendungen Funktionsbibliothek muss nur einmal<br />
geladen werden.<br />
5.3 Speicherabbildungen<br />
Dieser Abschnitt behandelt einige Mechanismen zur Abbildung von Programmadressen<br />
auf Maschinenadressen des Arbeitsspeichers.<br />
5.3.1 Direkte Adressierung<br />
Bei der direkten Adressierung werden die Programmadressen direkt als<br />
Maschinenadressen interpretiert. Es treten drei Probleme auf:<br />
Verschiebbarkeit,<br />
Programmgröße,<br />
Speicherausnutzung.<br />
Verschiebbarkeit<br />
In einem Mehrprozesssystem sind i.d.R. mehrere Programme im Arbeitsspeicher.<br />
Bei direkter Adressierung werden die Programme beim Laden fixiert und müssen<br />
dann bis zu ihrem Ende an derselben Stelle bleiben. Eine Verschiebung<br />
ist nicht möglich, da der Benutzer mit Adressen rechnen kann und deshalb<br />
positionsabhängige Adressen in seinen lokalen Variablen hat.<br />
165
Schlichter, TU München 5.3. SPEICHERABBILDUNGEN<br />
¯ Problem<br />
Externe Fragmentierung des Arbeitsspeichers. Sei der Arbeitsspeicher zunächst<br />
lückenlos mit Programmen P1, P2, P3 gefüllt.<br />
P1 P2 P3<br />
Nach Beendigung des Programms P2 entsteht eine Lücke.<br />
Neue Programme passen u.U. nicht exakt in die zurückgebliebene Lücke.<br />
Auf diese Weise entstehen immer mehr Lücken. Möglicherweise passen<br />
Programme nicht mehr in den Arbeitsspeicher, weil keine ausreichend große<br />
Lücken vorhanden ist, obwohl insgesamt noch ausreichend Platz wäre. Dies<br />
ist das Problem der externen Fragmentierung des Arbeitsspeichers. Da die<br />
Programme wegen der direkten Adressen nicht verschiebbar sind, ist eine<br />
Speicherbereinigung erst möglich, nachdem alle angefangenen Programme<br />
beendet sind.<br />
Ein verwandtes Problem tritt bei Programmverdrängungen auf. Das verdrängte<br />
Programm muss zur weiteren Ausführung später wieder an dieselbe Stelle<br />
geladen werden, dies ist jedoch in der Regel nicht mehr möglich, da andere<br />
Programme im benötigten Speicherbereich stehen können.<br />
¯ Forderung<br />
In Mehrprogramme/Mehrprozesssystemen müssen daher Programme verschiebbar<br />
sein.<br />
Programmgröße<br />
Programme können wesentlich größer als der verfügbare Arbeitsspeicher werden.<br />
Bei direkter Adressierung muss der Benutzer sein Programm selbst in Segmente<br />
zerlegen und diese nach Bedarf selbst nachladen. Man spricht von der<br />
sogenannten Overlay-Technik (veraltet). Die Overlay-Technik ist aufwendig,<br />
schwierig und damit auch fehleranfällig. Da die Zerlegung statisch ist, kann<br />
die Zerlegung des Programms nicht dynamisch an den konkret vorhandenen<br />
Arbeitsspeicher angepasst werden und damit die jeweilige Maschine gut<br />
ausnutzen.<br />
¯ Forderung<br />
Es muss daher gefordert werden, dass die Programmgröße unabhängig von der<br />
realen Arbeitsspeichergröße ist.<br />
166
Schlichter, TU München 5.3. SPEICHERABBILDUNGEN<br />
Speicherausnutzung<br />
Programme bestehen aus Modulen, die nur zu bestimmten Zeiten verwendet<br />
werden. Beispielsweise wird bei einer Matrizenmultiplikation nur auf die Module,<br />
die das Multiplikationsprogramm und auf die Module, die die Daten enthalten,<br />
zugegriffen. Es ist nun wünschenswert, von einem Programm nur den Ausschnitt<br />
im Arbeitsspeicher zu halten, der momentan und in naher Zukunft benötigt wird.<br />
Damit lassen sich mehr Programme im Arbeitsspeicher unterbringen und parallel<br />
verarbeiten. Dies steigert den Datendurchsatz des Systems.<br />
¯ Forderung<br />
Arbeitsspeicher beinhaltet nur die momentan bzw. in naher Zukunft<br />
notwendigen Ausschnitte des Programms. Nutzen der Lokalitätseigenschaft<br />
von Programmen:<br />
durch Datenstrukturen z.B. Arrays, oder<br />
Programmstrukturen: Prozeduren, Schleifen.<br />
5.3.2 Basisadressierung<br />
Die Basisadressierung hat eine einfache Abbildungsvorschrift:<br />
Maschinenadresse = Basisadresse + Programmadresse<br />
¯ Die Basisadresse ist programmspezifisch. Sie steht in einem Register des<br />
Rechnerkerns und ist dem Programm, das im Benutzermodus abläuft, nicht<br />
zugänglich. Sie wird vom Betriebssystem festgelegt.<br />
¯ Die Programmadressen aller Programme beginnen jeweils mit Null. Durch die<br />
Basisadressierung wird das Problem der Verschiebbarkeit gelöst. Die anderen<br />
Probleme bestehen jedoch weiterhin. Deshalb wird die Basisadressierung in<br />
modernen Betriebssystemen nicht mehr eingesetzt.<br />
¯ Speicherverwaltungsstrategien<br />
Aufgabe: Finden eines zusammenhängenden Arbeitsspeicherbereichs, der groß<br />
genug ist, um das Programm zu speichern. Mögliche Strategien (siehe<br />
Seite 57) werden hier nur angerissen. Hier kommen die Strategien zum Einsatz,<br />
die bereits bei der Haldenverwaltung diskutiert wurden, insbesondere deren<br />
Vor- und Nachteile. Beispielsweise erfordert best-fit immer den Durchlauf<br />
durch die gesamte Freibereichsliste. Zusätzlich liefert diese Strategie mit der<br />
Zeit immer kleinere Fragmente externe Fragmentierung.<br />
167
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
– first-fit<br />
Durchsuche die Liste der Freibereiche vom Anfang an und nimm den ersten<br />
passenden Frei-Bereich: Spalte nicht benötigten Speicher ab und füge ihn als<br />
freien Bereich in die Freibereichsliste ein.<br />
– best-fit<br />
Durchsuche die Liste der Freibereiche vom Anfang an und nimm den<br />
passenden Frei-Bereich, der die Speicheranforderungen des Programms am<br />
besten erfüllt: Spalte nicht benötigten Speicher ab und füge ihn als freien<br />
Bereich in die Freibereichsliste ein.<br />
– worst-fit<br />
Durchsuche die Liste der Freibereiche vom Anfang an und nimm den Frei-<br />
Bereich, der die Speicheranforderungen des Programms am schlechtesten<br />
erfüllt: Spalte nicht benötigten Speicher ab und füge ihn als freien Bereich in<br />
die Freibereichsliste ein.<br />
5.4 Virtueller Speicher<br />
Die virtuelle Adressierung wurde Ende der 50er Jahre eingeführt. Viele<br />
weiterführende Arbeiten erfolgten dann später im Rahmen des Projektes MAC<br />
und des Systems MULTICS (Vorgänger von UNIX) in den USA in den 60er<br />
Jahren. Ziel ist<br />
Virtualisierung des Speichers,<br />
Verstecken von realen Beschränkungen, wie Speichergröße,<br />
Speicher als sehr großes Feld gleichartiger Speicherzellen zu betrachten.<br />
Die Seitenadressierung ("paging") ist die Grundform der virtuellen Adressierung.<br />
5.4.1 Seitenadressierung<br />
Ansatz<br />
Der Programmadressraum, der sogenannte virtuelle Adressraum eines Prozesses<br />
wird in aufeinanderfolgende Seiten (engl. page) gleicher Größe unterteilt.<br />
Man spricht deshalb von virtuellen Adressen des Prozesses, anstatt von<br />
seinen Programmadressen. Gängige Größen für virtuelle Adressräume heutiger<br />
Architekturen:<br />
2 32 , also 32-Bit Adressen zum Ansprechen der Speicherzellen.<br />
Fortgeschrittene Architekturen: 2 64 , also 64-Bit Adressen (z.B. Sun ULTRA-<br />
Sparc).<br />
168
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
Die Seiten sind keine logische Aufteilung des Speichers. Denkaufgabe: was hat<br />
das für Vor- und Nachteile?<br />
¯ Der Maschinenadressraum, also der physische Adressraum des Arbeitsspeichers,<br />
wird in Kacheln (engl. frame) unterteilt. Seiten und Kacheln sind i.d.R.<br />
gleich groß. Es ist auch möglich, dass die Seitengröße ist ein Vielfaches der<br />
Kachelgröße ist. Im Folgenden wird von gleichen Größen ausgegangen. Für<br />
die MI ist als Kachelgröße 512 Byte festgelegt. Dies ist für heutige Architekturen<br />
zu klein (eher4-8KByte).<br />
¯ Eigenschaften der Seitenadressierung<br />
– Die Seiten eines Prozesses können im Arbeitsspeicher oder auf dem<br />
Hintergrundspeicher (Platte) gespeichert sein.<br />
– Die Kacheln nehmen die Seiten der Prozesse auf. Wichtig ist, dass sowohl<br />
die Seiten als auch die sie aufnehmenden Container, die Kacheln, eine<br />
einheitliche Größe besitzen. Das erleichtert die Speicherverwaltungsaufgabe<br />
erheblich.<br />
– Wenn während der Prozessausführung eine virtuelle Adresse des Prozessadressraums<br />
verwendet wird, so muss die Seite, in der sich die Adresse<br />
befindet, in einer Kachel des Arbeitsspeichers geladen (eingelagert) sein. Es<br />
ist eine Abbildungsvorschrift notwendig, die die virtuelle Adresse auf die<br />
reale Kacheladresse (Maschinenadresse) abbildet. Ein Problem ist:<br />
woher weiss das BS welche Seite eines Prozesses sich wo im<br />
Arbeitsspeicher befindet, d.h. welche Kachel welcher Seite zugeordnet<br />
ist?<br />
Antwort: Beschreibung der Seiten erforderlich; dazu Verwendung von<br />
Deskriptoren, die in Tabellen verwaltet werden (Seiten-Kacheltabelle).<br />
– Die Zuordnung, welche Seite in welcher Kachel gespeichert ist, und wo sich<br />
die Seite auf dem Hintergrundspeicher befindet, erfolgt mittels der Seiten-<br />
Kacheltabelle, die die Seitendeskriptoren enthält. Die Zuordnung von Seite<br />
zu Kachel kann sich im Laufe eines Programmablaufes ändern.<br />
– Befindet sich bei einer Befehlsausführung die erforderliche Seite nicht im<br />
Arbeitsspeicher, so löst ein solcher Zugriff eine Unterbrechung aus<br />
Seitenfehler Einlagerung der Seite bei Bedarf ("Demand Paging"). Der<br />
Seitenfehler ist eine synchrone Unterbrechung, die vom Betriebssystem zu<br />
behandeln ist, d.h. die geforderte Seite ist in den Arbeitsspeicher zu laden.<br />
Man spricht deshalb auch vom Demand Paging. Nach dem erfolgreichen<br />
Laden der Seite wird der Befehl, der zum Alarm führte, erneut ausgeführt.<br />
Der gesamte Vorgang heißt Seiteneinlagerung (engl. paging).<br />
169
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
– Falls eine Seite eingelagert werden muss, aber gleichzeitig bereits alle<br />
Kacheln des Arbeitsspeichers besetzt sind, so muss eine der eingelagerten<br />
Seiten aus dem Arbeitsspeicher verdrängt werden. Man spricht von<br />
der Seitenauslagerung. Die Strategien, die man hierbei einsetzt, heißen<br />
Seitenersetzungsstrategien. Ziel dieser Strategien ist es, eine möglichst<br />
günstige Seite auszuwählen, und diese auf den Hintergrundspeicher<br />
auszulagern. Günstig bedeutet hier, eine Seite, die entweder für den<br />
Prozessablauf nicht mehr benötigt wird, oder die erst sehr viel später benötigt<br />
wird, als die anderen Seiten im Arbeitsspeicher.<br />
¯ virtueller Speicher - Arbeitsspeicher<br />
Der Zusammenhang zwischen dem virtuellen Speicher, hier den virtuellen<br />
Adressräumen der Prozesse, und dem Arbeitsspeicher sowie Hintergrundspeichermedien<br />
wird nachfolgend skizziert. Auf die einzelnen Bereiche gehen wir<br />
im Folgenden näher ein. Wir gehen hier vereinfachend davon aus, dass auch die<br />
Blöcke als Einheiten des Hintergrundspeichers die Größe einer Seite besitzen.<br />
virt. Adressraum von P1<br />
Seite 1 von<br />
P1<br />
Seite 2 von<br />
P1<br />
.....<br />
Seite 1 von<br />
P2<br />
Seite 2 von<br />
P2<br />
.....<br />
virt. Adressraum von P2<br />
Seiten-Kachel<br />
Tabelle<br />
...<br />
...<br />
...<br />
...<br />
...<br />
...<br />
...<br />
...<br />
Deskriptor<br />
Kachel 1<br />
Kachel 2<br />
Kachel 3<br />
Kachel 4<br />
Kachel 5<br />
...<br />
Arbeitsspeicher<br />
mit Kacheln<br />
Auslagern<br />
Einlagern<br />
Hintergrundspeicher<br />
mit Blöcken<br />
¯ Vorteile<br />
Bei der Seitenadressierung werden durch eine flexible Speicherabbildung alle<br />
Probleme der direkten Adressierung (siehe Seite 165) gelöst.<br />
Programme können:<br />
verschoben werden,<br />
größer als der Arbeitsspeicher sein,<br />
auch ausschnittsweise im Arbeitsspeicher sein.<br />
D.h. die<br />
170
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
– Zusätzliche positive Eigenschaften<br />
£ Es können gemeinsame Speicherbereiche zwischen Prozessen realisiert<br />
werden. Programme können gemeinsame Speicherbereiche haben,<br />
beispielsweise bei gemeinsamen Daten nebenläufiger Prozesse. Auch<br />
Code-Bereiche können im Hinblick auf eine bessere Speicherausnutzung<br />
gemeinsam sein, beispielsweise muss der Code eines Compilers nur<br />
einmal geladen werden, auch wenn ihn mehrere Benutzer gleichzeitig<br />
verwenden.<br />
£ Es ist ein differenzierter Zugriffsschutz innerhalb eines Prozesses möglich.<br />
Zugriffsrechte für einzelne Seiten können unterschiedlich vergeben<br />
werden.<br />
Adressabbildung<br />
Bei der Seitenadressierung erfolgt die Umsetzung der Programmadressen, also die<br />
Umsetzung der virtuellen Adressen in Maschinenadressen durch eine Abbildung<br />
von Seiten auf Kacheln. Eine virtuelle Adresse v ist gegeben durch v = (s, w),<br />
wobei s die Seitennummer und w das Offset in der Seite angibt. Die Adresse<br />
v wird abgebildet auf die reale Adresse p = (k, w), wobei k die Kachelnummer<br />
angibt, die die Seite enthält.<br />
Seitentabellenregister<br />
Längenregister<br />
L<br />
Seiten-Kacheltabelle<br />
k<br />
s<br />
virtuelle Adresse<br />
s w<br />
k w<br />
Adresse im Arbeitsspeicher<br />
Die Adressrechnung wird von der Hardware, der MMU (Memory Management<br />
Unit), durchgeführt. D.h. die virtuelle Adresse wird von der CPU nicht direkt<br />
auf den Speicherbus gelegt, sondern an die MMU (Chip oder mehrere Chips)<br />
weitergeleitet. Die MMU berechnet die reale Adresse und legt sie auf den Bus.<br />
Falls s L ist, kann mit Hilfe des Längenregisters sofort ein Adressierungsfehler<br />
171
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
entdeckt werden.<br />
¯ Beispiel für Adressrechnung<br />
Gegeben sei ein 16-Bit virtueller Adressraum und eine Seitengröße von 4K.<br />
D.h. der Adressraum zerfällt in 16 Seiten; man benötigt 4 Bit, um die<br />
Seitennummern zu identifizieren und 12-Bit, um die 4096 Byte innerhalb<br />
einer Seite zu adressieren. Die Anzahl der Bits von s (Seitennummer) und k<br />
(Kachelnummer) können sich unterscheiden. Die Zahl der Bits von s hängt von<br />
der Größe des virtuellen Adressraums ab, während sich die Anzahl der Bits<br />
von k aus der Größe des Maschinenadressraums ergibt. Damit können s und k<br />
unterschiedliche lang sein.<br />
v = 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0<br />
s = 2<br />
p =<br />
present/<br />
absent Bit<br />
0 010 1<br />
1 001 1<br />
2 110 1<br />
3 000 1<br />
4 100 1<br />
5 011 1<br />
6 000 0<br />
7 000 0<br />
8 000 0<br />
9 101 1<br />
10 000 0<br />
11 111 1<br />
12 000 0<br />
13 000 0<br />
14 000 0<br />
15 000 0<br />
Seiten-<br />
Kacheltabelle<br />
12 bit offset w<br />
wird kopiert<br />
1 1 0 0 0 0 0 0 0 0 0 0 1 0 0<br />
k = 6<br />
172
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
Seiten-Kacheltabelle<br />
Die Adressabbildung erfolgt mittels der Seiten-Kacheltabelle (SKT). D.h.<br />
wir benötigen Informationen über die zu verwaltenden Seiten. Dazu wird<br />
jede Seite eines Prozesses durch einen Seitendeskriptor beschrieben. Die<br />
Seiten-Kacheltabelle ist i.a. prozessspezifisch. Neben der Zuordnung Seite-<br />
Kachel enthält ein Seitendeskriptor noch weitere Informationen, wie z.B. die<br />
Zugriffsrechte und den Zustand der Seite.<br />
¯ Struktur eines Seitendeskriptor<br />
Informationen in einem Seitendeskriptor:<br />
– Zugriffsrechte (R)<br />
Angabe, ob der Prozess<br />
- ausführend (x R),<br />
- lesend (r R),<br />
- schreibend (s R).<br />
auf die Seite zugreifen darf. Diese Information wird vom Betriebssystem<br />
eingetragen und im Rechnerkern vor dem Zugriff ausgewertet.<br />
– Seite existent (e)<br />
Es müssen nicht alle Seiten existent sein. Beispielsweise sind oft die<br />
Seiten zwischen Ende der Halde und Kellerende nicht existent. Generell<br />
besteht die Vorstellung, dass in einem Adressraum mehrere Objekte (Daten<br />
oder Programmteile) sind, zwischen denen Lücken liegen können. Seiten<br />
des Adressraums, in denen keine Objekte sind, sind nicht existent. Wird<br />
versucht, auf eine nicht existente Seite zuzugreifen, dann gibt es einen<br />
Speicherschutzalarm.<br />
– Seite geladen (v)<br />
Die Abbildung ist gültig (engl. valid ). Die Seite ist also in den<br />
Arbeitsspeicher geladen, d.h. ihr ist eine Kachel zugeordnet. Es kann auf<br />
sie zugegriffen werden. Eine nicht geladene, aber existente Seite steht im<br />
Verdrängungsbereich (engl. swap area) auf dem Plattenspeicher. Soll auf<br />
eine existente, aber nicht geladene Seite zugegriffen werden, dann löst die<br />
CPU einen Seitefehltalarm aus, d.h. ein Seitenfehler tritt ein. Die Seite<br />
wird vom Hintergrundspeicher geladen, und das Programm an der Stelle<br />
fortgesetzt, die den Seitefehltalarm auslöste.<br />
– Zugriffsbit (z)<br />
Das Zugriffsbit (engl. reference bit) dient zur Unterstützung der<br />
Seitenersetzungsalgorithmen. Es wird von der CPU nach dem Laden der<br />
Seite gelöscht und bei einem Zugriff auf eine Seite von der CPU gesetzt.<br />
173
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
– Veränderungsbit (m)<br />
Die Seite wurde verändert (modifiziert). Dieses Bit dient ebenfalls zur<br />
Unterstützung der Seitenersetzungsalgorithmen. Es wird nach dem Laden<br />
der Seite von der CPU gelöscht und nach einem schreibenden Zugriff auf die<br />
Seite gesetzt. Beispielsweise könnte eine Verdrängungsstrategie bevorzugt<br />
Seiten auslagern, die nicht modifiziert wurden, d.h. ein Übertragen der<br />
Seite auf den Hintergrundspeicher ist nicht notwendig, da die Seite auf dem<br />
Hintergrundspeicher noch aktuell ist man spart sich den Datentransfer.<br />
¯ Probleme<br />
Es gibt einige Probleme mit der Seitenadressierung, die sich auf die<br />
Speichereffizienz und die Performanz beziehen.<br />
– Größe der Seiten-Kacheltabelle<br />
Die Seiten-Kacheltabelle kann sehr groß werden, z.B. bei Seitengröße 4K<br />
(früher üblich!) ergeben sich für 32-bit Adressraum insgesamt 1 Million<br />
Seiten pro Prozess.<br />
Lösung? Mehrstufige Tabellen: Seiten-Kacheltabelle ebenfalls in Seiten<br />
unterteilen, die ihrerseits in Tabellen gehalten werden. Technisch wird<br />
bei einem mehrstufigen Verfahren der Seitenanteil der virtuellen Adresse<br />
in Abhängigkeit von der Stufenzahl weiter unterteilt, z.B. s1, s2,...sn.<br />
In der Regel wird mit der Adressabbildung ab dem höchstwertigen Bit<br />
begonnen. Der Anteil s1 definiert den Index für die Seitentabelle erster<br />
Stufe, s2 den Index für den Anteil zweiter Stufe, etc.<br />
virtuelle Adresse<br />
s1 s2 w<br />
Deskriptor<br />
Deskriptor k w<br />
174
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
Beispielsweise kann ein 32-Bit Adressraum auf der ersten Stufe in 128<br />
Seiten (2 7 ) mit je 32 MByte unterteilt werden. Ein Seitentabellen-Deskriptor<br />
verweist auf eine Seitentabelle, die die 32 MByte große Seite in weitere<br />
Seiten unterteilt, z.B. 256 KByte. Es muss nicht die gesamte Tabelle im<br />
Arbeitsspeicher gehalten werden. Das schrittweise Durchlaufen u. U.<br />
mehrstufiger Seiten-Kacheltabellen nennt man Page-Table Lookup.<br />
– Performanz<br />
Schnelle Umrechnung der virtuellen auf realen Adressen erforderlich, da<br />
Berechnung bei jedem Zugriff notwendig ist! Häufig verwendete Adressen<br />
werden in einem Schnellzugriffspeicher (cache) der MMU gehalten, dem<br />
Translation Lookaside Buffer (TLB). Bei jedem Zugriff auf eine virtuelle<br />
Adresse wird überprüft, ob sich die zugehörige Seite und damit die<br />
Kacheladresse im TLB befindet. TLB ist als assoziativer Cache organisiert<br />
(vgl.TGI!), d.h. alle Einträge werden gleichzeitig auf Übereinstimmung<br />
mit dem Seitenanteil der referenzierten Adresse verglichen. Ist kein<br />
entsprechender Eintrag in der TLB, erfolgt die Adressabbildung über den<br />
relevanten Deskriptor der Seiten-Kacheltabelle. Idee ist, dass der TLB<br />
wenige Einträge enthält (ca. 8). Ein Eintrag enthält eine virtuelle und<br />
zugeordnete reale Adresse kein aufwändiges Page Table Lookup für TLB-<br />
Einträge erforderlich. Designziel bei der Verwaltung der TLB-Einträge ist<br />
eine gute Trefferrate (cache hit).<br />
¯ Varianten der SKT<br />
Varianten für den Aufbau einer Seiten-Kacheltabelle.<br />
– Prozess-spezifisch, Index-basiert (PI)<br />
Zugriff auf Deskriptor für Seite s über Index i.<br />
i<br />
– Prozess-spezifisch, assoziativ (PA)<br />
Hier wird die Seitennummer mit in den Seitendeskriptor aufgenommen. Die<br />
SKT ist prozessspezifisch. Es sind nur die geladenen Seiten aufgeführt.<br />
– Global, assoziativ (GA)<br />
Es gibt nur eine SKT im System. Da die Seitennummer allein nicht eindeutig<br />
ist, muss das Prozesskennzeichen (PID), d.h. der eindeutige Prozessname,<br />
ebenfalls in den Deskriptor aufgenommen werden. Es sind nur die geladenen<br />
Seiten aufgeführt. Die maximale Anzahl der Elemente dieser SKT ist die<br />
Anzahl der Kacheln, falls gemeinsame Speicherbereiche der Prozesse nicht<br />
betrachtet werden.<br />
– Global, indiziert (GI)<br />
175
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
Enthält auch PID und es erfolgt eine Reihung gemäß der Kachelnummer;<br />
freie Kacheln werden durch einen speziellen Eintrag gekennzeichnet.<br />
Kacheln, denen keine Seite zugeordnet ist, erhalten einen speziellen Eintrag<br />
bei PID. Damit enthält die Tabelle so viele Elemente wie Kacheln im<br />
Arbeitsspeicher vorhanden sind. Da die Angabe der Kachelnummern sich<br />
aus der Position in der Tabelle ergibt, kann diese Angabe entfallen.<br />
Seitenfehlerbehandlung<br />
Der Zugriff auf eine Seite, die nicht im Arbeitsspeicher ist, führt zu einem<br />
Seitenfehler.<br />
load M<br />
Prozess P1<br />
(1) Zugriff<br />
(6) Befehl<br />
erneuert<br />
Seiten-<br />
Kacheltabelle<br />
(2) page fault<br />
Seitenfehlerbehandlung<br />
des<br />
BS<br />
Ablauf der Seitenfehlerbehandlung<br />
(5) Aktualisieren<br />
Arbeitsspeicher<br />
Kachel 1<br />
Kachel 2<br />
Kachel 3<br />
.....<br />
(3) Seite auf Hintergrundspeicher<br />
(4) Einlagern<br />
Hintergrundspeicher<br />
mit Blöcken<br />
1. Beim Zugriff auf eine virtuelle Adresse (z.B. LOAD-Befehl) tritt ein<br />
Seitenfehler auf.<br />
2. Die Adressrechnungshardware löst einen Alarm aus, so dass die Unterbrechungsbehandlung<br />
des BS aktiviert wird. Dieser wird beispielsweise in der<br />
MMU bei der Adressrechnung ausgelöst. Der Prozesszustand muss gerettet<br />
werden, u.a. der Befehlszähler und die Registerbelegungen. Weiterhin muss<br />
der Unterbrechungsbehandlung die Adresse übergeben werden, die den Seitenfehler<br />
auslöste.<br />
3. Das BS stellt eine freie Kachel zur Verfügung, lokalisiert die Seite auf der Platte<br />
und initiiert die Einlagerung der Seite (Lesen von Platte). Falls aktuell keine<br />
Kachel frei ist, muss eine Seite auf den Hintergrundspeicher verdrängt werden.<br />
4. Die Seite wird eingelagert.<br />
176
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
5. Der Seitendeskriptor wird aktualisiert und verweist jetzt auf die belegte Kachel<br />
(im Beispiel k = 3).<br />
6. Der unterbrochene Befehl wird erneut gestartet. Die Daten sind jetzt<br />
zugreifbar. Dazu muss zunächst wieder der Prozesszustand geladen werden,<br />
d.h. Befehlszähler, Register etc. Anschließend wird der Befehl fortgeführt, als<br />
ob keine Unterbrechung stattgefunden hätte.<br />
Seitenverwaltungsstrategien<br />
Aufgabe der Arbeitsspeicherverwaltung: Laden der für die Ausführung der<br />
Prozesse benötigten Seiten in die Kacheln. Es ergeben sich drei strategische<br />
Aufgaben:<br />
¯ Ladestrategie<br />
Frage: welche Seite ist zu Laden?<br />
– Lösungsansätze<br />
£ Einzelseitenanforderungsstrategie (on demand): eine Seite wird genau<br />
dann geladen, wenn auf sie zugegriffen wird und sie sich noch nicht im<br />
Arbeitsspeicher befindet.<br />
£ Seiten-Prefetching: Seiten werden im Voraus geladen, um sie sofort bei<br />
Bedarf verfügbar zu haben. Wann ist Prefetching sinnvoll und was benötigt<br />
das BS? Beispielsweise am Anfang beim Laden des main-Programms.<br />
¯ Platzierungsstrategie<br />
Frage: in welche Kachel ist eine Seite zu Laden?<br />
– Lösung<br />
£ keine strategische Entscheidung erforderlich, da alle Kacheln gleichwertig<br />
sind und damit keine Auswahl getroffen werden muss. Vorteil der<br />
uniformen Realisierungskonzepte (Seite, Kachel).<br />
¯ Seitenverdrängungsstrategie<br />
Frage: welche Seite ist aus dem Arbeitsspeicher zu entfernen, wenn für eine zu<br />
ladende Seite keine freie Kachel mehr zur Verfügung steht? Im Idealfall baut<br />
ein Verdrängungsverfahren auf der Referenzlokalität vieler Programme und<br />
der sich ergebenden Lokalitätsmenge auf. Solange sich die Lokalitätsmenge<br />
jedes Prozesses im Arbeitsspeicher befindet, ist die Seitenfehlerrate sehr<br />
177
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
gering. Auf der Grundlage der Working-Set-Theorie ergibt sich damit<br />
für ein Seitenverdrängungsverfahren die Aufgabe, bevorzugt solche Seiten<br />
auszulagern, die nicht in der Lokalitätsmenge enthalten sind.<br />
– Strategien<br />
£ FIFO (first-in first-out): Verdrängen der ältesten Seite, einfach zu<br />
implementieren. Das Verfahren berücksichtigt die Lokalitätsmenge<br />
nicht. So werden über längere Zeit hinweg häufig referenzierte Seiten<br />
verdrängt. Umgekehrt bleiben auch wenig referenzierte Seiten im<br />
Mittel genauso lange im Arbeitsspeicher wie häufig referenzierte Seite.<br />
Vorsicht bzgl der FIFO-Anomalie, d.h. trotz Vergrössern des zur<br />
Verfügung stehenden Arbeitsspeichers können u.U. mehr Seitenfehler<br />
(d.h. Seiteneinlagerungsoperationen) auftreten, als bei kleinerem<br />
Speicher. (Warum, woran könnte so etwas liegen? Antwort: FIFO beachtet<br />
nicht, wann die Seite zuletzt benutzt wurde. D.h. eine häufig benutzte<br />
globale Variable, die zu einem frühen Zeitpunkt eingelagert wurde, könnte<br />
z.B. dazu führen, dass diese Seite nach dem Entfernen gleich wieder<br />
eingelagert werden muss.)<br />
£ LIFO (last-in first-out): Verdrängen der jüngsten Seite, einfach zu<br />
implementieren. Diese Strategie berücksichtigt Lokalitätseigenschaft von<br />
Programmen (häufig in Schleifen, Prozeduren Zugriff auf gleiche<br />
Seiten) nicht.<br />
£ LRU (Least recently used): Verdrängen der am längsten nicht genutzten<br />
Seite; wird am häufigsten realisiert, wobei LRU approximiert wird, da<br />
eine exakte Realisierung zu aufwendig ist. Eine exakte Realisierung ist<br />
durch spezielle Hardware möglich, die jeder Seite ein Zeitfeld assoziiert<br />
und dieses bei jedem Zugriff aktualisiert.<br />
Frage: wie LRU-Verhalten approximieren<br />
Antwort: Nutzen eines Referenz-Bits (siehe R-<br />
Bit der Seitendeskriptor-Informationen) pro Seite, das durch Hardware<br />
bei einem Seitenzugriff gesetzt wird. Jeder Seite wird ein Zähler zugeordnet.<br />
Das BS inspiziert in regelmäßigen Abständen die Seiten, und<br />
inkrementiert den Zähler der Seiten mit nicht gesetztem Bit. Für jede<br />
referenzierte Seite werden dagegen Zähler und R-Bit gelöscht. Verdrängt<br />
wird immer die Seite mit dem höchsten Zählerstand, da diese<br />
am längsten nicht referenziert wurde.<br />
Andere Alternativen sind z.B. Second Chance.<br />
£ Optimale Strategie: Seite, auf die in Zukunft am längsten nicht<br />
zugegriffen wird; die optimale Strategie ist unrealistisch, da das zukünftige<br />
Verhalten i.d.R. nicht bekannt ist. Leistungsdaten dieser Strategie,<br />
178
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
d.h. wieviele Seitenfehler treten bei gegebener Arbeitsspeichergröße und<br />
gegebenen Zugriffscharakteristika von Prozessen auf, gibt Obergrenze für<br />
Erreichbares an.<br />
¯ Weitere offene Fragen<br />
– Wahl einer vernünftigen Seitengröße<br />
Die Wahl der Seitengröße steht unter widersprüchlichen Zielsetzungen;<br />
daher ist ein Kompromiss erforderlich.<br />
1. Je kleiner die Seite, desto rascher die Transfers zwischen ASP und<br />
Platte.<br />
2. Je kleiner die Seite, desto geringer der Verschnitt (interne<br />
Fragmentierung) durch nicht voll ausgenützte Seiten.<br />
3. Je größer die Seite, desto geringer der Overhead für Transport<br />
zwischen Arbeitsspeicher und Platte pro Byte (Arm positionieren,<br />
Warten bis Spur unter Lese-Schreib-Kopf).<br />
4. Je größer die Seite, desto mehr Information kann zwischen<br />
Arbeitsspeicher und Platte je Zeiteinheit transportiert werden.<br />
5. Je größer die Seite, desto seltener Transfers erforderlich.<br />
£ Beispiele für Seitengrößen: Intel 80386 4K, Pentium II 4K oder 4MB,<br />
UltraSPARC 8K, 64K, 512K oder 4MB. Tendenz: grössere Seiten.<br />
Warum? schnelle CPU und großer Arbeitsspeicher, Seitenfehler haben<br />
großen Einfluss auf Performanz des Rechensystems, deshalb möglichst<br />
wenige.<br />
– Seitenflattern<br />
Seitenflattern (engl. thrashing) tritt auf, wenn im System zuviele Prozesse<br />
sind, die nebenläufig voranschreiten wollen und Kacheln beanspruchen.<br />
Gerade verdrängte Seiten müssen zu schnell wieder eingelagert werden, das<br />
System ist im schlimmsten Fall nur noch mit Ein- und Auslagern beschäftigt.<br />
5.4.2 Segment-Seitenadressierung<br />
Unterteilung des Programmadressraums in logische Einheiten unterschiedlicher<br />
Länge, sogenannte Segmente. Ein Segment umfasst inhaltlich bzw. organisatorisch<br />
zusammengehörige Speicherbereiche, z.B. Daten, Code und<br />
Laufzeitkeller-Segment. Im Gegensatz dazu ist die Seiteneinteilung systembedingt<br />
und vom jeweiligen Programm unabhängig.<br />
¯ Jedes Segment besitzt eine maximale Größe. Die Länge der einzelnen<br />
Segmente kann unterschiedlich sein und sich dynamisch verändern.<br />
179
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
¯ Jedes Segment wird durch einen Segment-Deskriptor beschrieben. Die<br />
Segmentdeskriptoren werden in einer Segmenttabelle verwaltet.<br />
¯ Jedes Segment besteht aus Seiten, die jeweils beginnend mit Null fortlaufend<br />
numeriert sind.<br />
¯ Ein Zugriff auf ein nicht existentes Segment führt zum Speicherschutzalarm.<br />
¯ Um in dieser Situation möglichst kompakte Speicherabbildungstabellen zu<br />
erhalten, wird die Seiten-Kacheltabelle aufgeteilt. je Segment eine<br />
eigene Seiten-Kacheltabelle gehalten. Die (Maschinen-) Adressen der Seiten-<br />
Kacheltabellen werden in einer Segmenttabelle gehalten.<br />
Segmenttabellenregister<br />
+<br />
sg<br />
0<br />
sg<br />
s w<br />
Segmenttabelle<br />
Seiten-Kacheltabelle<br />
Adresse im<br />
Arbeitsspeicher<br />
virtuelle Adresse k w<br />
Bei der Adressierung der Tabellen muss natürlich jeder Index mit der Länge eines<br />
Seiten- bzw Segmentdeskriptors (LOP) multipliziert werden. Falls ein Eintrag 4<br />
Byte lang ist, ergibt sich damit z.B.<br />
Adresse des Segmentdeskriptors =<br />
Wert Segmenttabellenregister + sg * 4.<br />
Entsprechendes gilt auch für die Seiten-Kacheltabelle.<br />
5.4.3 Speicherverwaltung der MI<br />
Die Modellmaschine unterstützt neben der direkten Adressierung auch eine<br />
Seitenadressierung ergänzt um drei wohldefinierte Segmente.<br />
180<br />
+<br />
0<br />
s<br />
k
Schlichter, TU München 5.4. VIRTUELLER SPEICHER<br />
virtuelle Adresse<br />
PCS s w<br />
0 1 2 22 23 31<br />
¯ PCS = 0 (P0-Bereich): dieser Bereich enthält das Benutzerprogramm und die<br />
Daten (Halde).<br />
¯ PCS = 1 (P1-Bereich): Bereich des Laufzeitkellers des Benutzerprogramms.<br />
¯ PCS = 2 (Systembereich): dieser Bereich ist in der Regel in allen Prozessen<br />
einheitlich durch die BS-Komponenten belegt, d.h. das BS ist in jedem<br />
Programmadressraum.<br />
¯ PCS = 3: unzulässiger Bereich<br />
181
Kapitel 6<br />
Dateisysteme<br />
Dateisysteme dienen der dauerhaften Speicherung von Programmen und<br />
Daten. Zum Einsatz kommen magnetische und optische Medien, die im<br />
Gegensatz zum Arbeitsspeicher auch nach dem Ausschalten der Rechenanlage<br />
den Datenerhalt sicherstellen. Beispiele von externen Speichermedien sind<br />
Festplatten, Magnetbänder, CD-ROM (wiederbeschreibbar) und neuerdings auch<br />
DVDs. Der schlechten Zugriffszeit externer Speichersysteme (im Vergleich zum<br />
Arbeitsspeicher) steht eine vergleichsweise hohe Kapazität und ein sehr gutes<br />
Preis-Leistungsverhältnis gegenüber. Zentrale Aufgabe des Dateisystems ist es<br />
die besonderen Eigenschaften externer Speichermedien optimal umzusetzen und<br />
Applikationen einen effizienten Zugriff auf die persistent gespeicherten Daten zu<br />
ermöglichen. Es gelten folgende grundlegende Forderungen<br />
a) Speicherung großer Informationsmengen (Video)<br />
b) kein Datenverlust auch bei Prozess- / Systemabsturz<br />
c) nebenläufiger Zugriff durch mehrere Prozesse<br />
Neben Dateisystemen gibt es Datenbanksysteme, die aus einer Menge von<br />
Daten und den zugehörigen Programmen zum Zugriff auf die Daten sowie zur<br />
Kontrolle der Konsistenz bestehen. Der Zugriff auf die Daten erfolgt immer<br />
über Operationen des Datenbanksystems, und nicht direkt durch die einzelnen<br />
Applikationen.<br />
6.1 Fragestellungen<br />
Dieser Abschnitt beschäftigt sich mit den Mechanismen eines Rechensystems zur<br />
dauerhaften (persistenten) Speicherung von Programmen und Daten:<br />
¯ Charakteristika von Dateisystemen.<br />
182
Schlichter, TU München 6.2. CHARAKTERISTIKA VON DATEISYSTEMEN<br />
¯ Schichtenmodell eines Dateisystems.<br />
6.2 Charakteristika von Dateisystemen<br />
Jedes Dateisystem unterstützt 2 grundlegende Abstraktionen:<br />
Datei: Behälter für die persistente Speicherung jeglicher Information.<br />
Information können Daten, der Code ausführbarer Programme, aber auch<br />
kontinuierliche Daten wie Videoströme sein. Das Dateisystem bietet<br />
besondere Zugriffsfunktionen an, die der Zugriffscharakteristik externer<br />
Speicher Rechnung tragen.<br />
Verzeichnisse: spezielle Dateien zur Strukturierung externer Speichermedien.<br />
¯ blockorientierter Datentransfer zwischen externem Speicher und Arbeitsspeicher.<br />
Aufgrund der hohen Zugriffszeiten bei externen Speichermedien ist ein<br />
kleines Lese- und Schreibgranulat nicht sinnvoll. Daher bilden sogenannte<br />
Blöcke die kleinste Übertragungseinheit. Typische Blockgrößen: 512 Byte -<br />
4 KByte. Der Zugriff auf das 1. Byte eines Blocks auf der Festplatte hängt von<br />
der Positioniergeschwindigkeit ab (im Mittel mehrere Millisekunden), während<br />
die Zugriffszeit auf die restlichen Bytes von der Umdrehungsgeschwindigkeit<br />
der Platte abhängt. Zu große Blöcke resultieren in wachsender Information, die<br />
vom Prozess primär nicht angefordert wurde.<br />
¯ Charakteristika der Dateinutzung (empirisch ermittelt):<br />
a) Dateien sind meist klein (wenige KBytes).<br />
b) Dateien werden häufiger gelesen, seltener geschrieben und noch seltener<br />
gelöscht.<br />
c) vornehmlich sequentieller Zugriff.<br />
d) Dateien werden selten von mehreren Programmen oder Personen<br />
gleichzeitig benutzt.<br />
Datei-Zugriffsoperation werden oft gemäß dieser Charakteristika optimiert. Für<br />
Multimedia verändert sich die Nutzungscharakteristik.<br />
große Dateien (mehrere GByte).<br />
gleichmäßige Zugriffsgeschwindigkeit (um Ruckeln zu vermeiden).<br />
notwendige Übertragungsbandbreite. Zum Beispiel ist bei einer unkomprimierten<br />
Videoaufzeichnung im Format 1024*768 bei 3 Byte pro Pixel<br />
und 50 Bildern pro Sekunde eine Übertragungskapazität von 112,5MByte<br />
pro Sekunde notwendig (Datei hat bereits nach einer Minute eine Größe<br />
von 6,5 GByte).<br />
183
Schlichter, TU München 6.3. DATEIEN<br />
6.3 Dateien<br />
Dateien bilden in einem Dateisystem die Behälter für die dauerhafte Speicherung<br />
beliebiger Information. Dabei nutzen nicht nur Benutzerprogramme, sondern<br />
auch die Systemsoftware greift in vielen Fällen auf Dateien zurück. Beispielsweise<br />
wird auch die Auslagerung von Seiten des virtuellen Adressraums über<br />
das Dateisystem auf einer sogenannten Auslagerungsdatei (siehe Windows 9x)<br />
vorgenommen.<br />
¯ in den meisten Systemen wird eine Datei als eine Folge von Bytes aufgefasst.<br />
Eine Datei beginnt mit dem Byte 0 und endet in Abhängigkeit von ihrer Länge<br />
n mit dem Byte n-1.<br />
¯ Dateinamen<br />
in manchen Dateisystemen haben Dateinamen die Form "name.extension".<br />
Beispiele für extension: Ú ØÑÐ Ô ØÜ<br />
Ó ÞÔ<br />
In einigen Betriebssystemen werden die Datei-Extension sematisch interpretiert<br />
und veranlassen ein bestimmtes Verhalten, z.B. ein Doppel-Click auf eine<br />
".doc"-Datei startet Microsoft Word. In Unix sind Datei-Extensions nur<br />
Konventionen. Das Mac Betriebssystem nutzt keine Extensions, um<br />
Applikationen zu triggern; dort wird eine zusätzliche Ressource-Datei genutzt.<br />
¯ Dateiaufbau<br />
Die interne Struktur einer Datei hängt von der jeweiligen Nutzung und<br />
Zielsetzung ab, z.B. ASCII Datei besteht aus Zeilen, die mit CR, LF<br />
abgeschlossen sind. MS-DOS verwendet eine Kombination von CR und LF.<br />
Beispiel einer Archivdatei<br />
184
Schlichter, TU München 6.3. DATEIEN<br />
Header<br />
Objekt Modul<br />
Header<br />
Objekt Modul<br />
Header<br />
Objekt Modul<br />
Modulname<br />
Datum<br />
Owner<br />
Zugriffsrechte<br />
Länge<br />
Ein weiteres Beispiel haben wir bereits beim Aufbau des Objektprogramms<br />
(siehe Seite 39) für die MI kennengelernt.<br />
¯ Operationen<br />
Dateisysteme unterstützen die folgenden grundlegenden Systemaufrufe:<br />
ÓÔÒ<br />
Öffnen einer Datei; Ergebnis ist ein Dateideskriptor, über den in<br />
nachfolgenden Systemaufrufen auf die Datei zugegriffen werden kann.<br />
Aufruf nach Posix-Standard<br />
ÒØ ÓÔÒ<br />
ÓÒ×Ø Ö ÐÒÑ<br />
ÒØ Ð×<br />
ÑÓØ ÑÓ<br />
Ð× spezifiziert die Zugriffsart, z.B. lesend, schreibend, erzeugend,<br />
anhängend (append); ÑÓ spezifiziert die Zugriffsrechte für neu erzeugte<br />
Dateien. In Windows NT/2000 wird der Systembefehl ÖØÐ zum<br />
Öffnen einer Datei bzw. zum Erzeugen einer neuen Datei verwendet. Analog zu<br />
ÓÔÒ wird als Ergebnis ein Dateideskriptor zurückgegeben (Datentyp Handle).<br />
Die Anzahl der Dateideskriptoren ist beschränkt.<br />
ÐÓ×<br />
Schließen einer Datei; Aufrufparameter ist ein Dateideskriptor. Bei<br />
Terminierung des Prozesses werden alle offenen Dateien automatisch<br />
geschlossen.<br />
185
Schlichter, TU München 6.3. DATEIEN<br />
ÒØ Ö ÒØ Ö ÔÙÖ ÒØ ÑÜ<br />
ÒØ ÛÖØ ÒØ Ö ÔÙÖ ÒØ Ò<br />
ÑÜ gibt die Anzahl der Bytes an, die ohne Pufferüberlauf gelesen werden<br />
können; Ö liefert als Ergebnis die Anzahl der erfolgreich gelesenen Bytes.<br />
Daneben gibt es noch weitere Operationen, z.B. Positionieren des Dateizeigers<br />
auf eine bestimmte Position in der Datei (Ð× oder ËØÐÈÓÒØÖ bei<br />
Win32).<br />
– Dateipuffer<br />
Zugriffe auf Dateien erfolgen über einen Dateideskriptor und einen<br />
Dateipuffer.<br />
Datei<br />
Deskriptor<br />
Dateipuffer<br />
Dateizeiger<br />
Puffer<br />
Pufferposition<br />
Ortsinformation<br />
Externer Speicher Datei<br />
Die für den lesenden und schreibenden Zugriff notwendigen Informationen<br />
werden in einer eigenen Datenstruktur (Dateipuffer) gespeichert, die<br />
jedem geöffneten Dateideskriptor zugeordnet wird. Diese Datenstruktur<br />
enthält neben der Ortsinformation, die Aufschluss über den physischen<br />
Aufenthaltsort der Datei auf einem externen Speichermedien gibt, einen<br />
Puffer zur Zwischenspeicherung von Daten. Der Puffer beinhaltet eine Kopie<br />
eines bestimmten Dateiausschnitts. Die Position dieser Kopie innerhalb der<br />
Datei speichert der Zeiger Pufferposition. Der Dateizeiger spezifiziert die<br />
aktuelle Lese-/Schreibposition.<br />
186
Schlichter, TU München 6.4. MEMORY-MAPPED DATEIEN<br />
6.4 Memory-Mapped Dateien<br />
Eine Datei oder Teile davon werden in den virtuellen Adressraum eines Prozesses<br />
eingeblendet. Das Dateisystem bestimmt einen hinreichend großen Bereich im<br />
virtuellen Adressraum für den Dateiausschnitt, z.B. zwischen Laufzeitkeller und<br />
Halde. Für diesen Teilbereich müssen entsprechende Seitentabellendeskriptoren<br />
initialisiert werden. Nutzung von "Prefetching", um bereits im Voraus<br />
Dateiblöcke in Seiten zu laden.<br />
¯ Lese- und Schreiboperationen, sowohl sequentiell als auch wahlfrei, erfolgen<br />
über virtuelle Adressen.<br />
¯ Einblendung immer nur Vielfacher ganzer Blöcke einer Datei.<br />
¯ veränderte Blöcke werden meist aus Effizienzgründen zu einem späteren<br />
Zeitpunkt zurückgeschrieben. Auf jeden Fall werden die veränderten Blöcke<br />
beim Schließen der Datei auf den externen Speicher zurückgeschrieben.<br />
¯ Beispiel der Win32-Programmierschnittstelle:<br />
ÀÒÐ Ñ<br />
ÖØÐ ÐÒÑ ÒÖ Ö <br />
ÐÒ ØÐËÞ <br />
Ñ ÖØÐÅÔÔÒ ÈÖÓÒÐÝ <br />
Ö ÅÔÎÛÇÐ Ñ ÐÑÔÖ <br />
Der Rückgabewert addr speichert die Anfangsadresse der Datei im virtuellen<br />
Adressraum. In der Regel wählt das Dateisystem den virtuellen Adressbereich<br />
aus.<br />
6.5 Verzeichnisse<br />
Verzeichnisse (engl. directories) erlauben eine hierarchische Strukturierung des<br />
externen Speichers.<br />
¯ baumartige Verzeichnisstruktur mit links zwischen Unterbäumen.<br />
¯ Pfadnamen zur Spezifikation von Dateien und Verzeichnissen. Der vollständige<br />
und eindeutige Namen einer Datei oder eines Verzeichnisses entsteht durch<br />
eine Aneinanderreihung aller Verzeichnisnamen beginnend beim Wurzelverzeichnis;<br />
Unterscheidung zwischen absoluten und relativen Dateinamen.<br />
¯ typische Operationen: create, delete, opendir, closedir, readdir, rename, link,<br />
unlink.<br />
187
Schlichter, TU München 6.6. SCHICHTENMODELL<br />
6.6 Schichtenmodell<br />
Ein Dateisystem kann logisch in 3 Schichten unterteilt werden, die zunehmend<br />
höhere Abstraktionen für die Speicherung persistenter Daten anbieten.<br />
6.6.1 Datenträgerorganisation<br />
Dateiverwaltung<br />
Blockorientiertes Dateisystem<br />
Datenträgerorganisation<br />
Unterteilung des gesamten Datenträgers in Blöcke, die von 0 an aufsteigend<br />
durchnummeriert sind. Das auf MS-DOS aufbauende Dateisystem FAT<br />
(Windows 95) verwendet lediglich maximal 16 Bit für eine Blocknummer.<br />
Bei einer ursprünglichen Blockgröße von 512 Byte können damit externe<br />
Speicher mit bis zu 32 MByte Speicherkapazität angesprochen werden.<br />
Zusammenfassung von Blöcken zu Clustern (32 KByte), um bis zu 2 GByte große<br />
Datenträger zu addressieren; Cluster sind die kleinste Zuteilungseinheit interne<br />
Fragmentierung.<br />
¯ Verwaltung freier und defekter Blöcke.<br />
Defekte<br />
Blöcke<br />
Freie<br />
Blöcke<br />
0<br />
0 0 1 0 0 0 0<br />
Block 0<br />
Block 1 Block 2 Block 3 Block 4 Block 5<br />
0 1 1 0 0 1 0 0<br />
Eine gängige Realisierung der Listen für freie und defekte Blöcke besteht in<br />
zusammenhängenden Bitvektoren, die auf dem Datenträger selbst gespeichert<br />
werden. Diese Realisierung erlaubt den gleichzeitigen Test von 16, 32 oder 64<br />
Bitpositionen mit Hilfe von Logikoperatoren des Prozessors.<br />
188
Schlichter, TU München 6.6. SCHICHTENMODELL<br />
¯ Blockstruktur<br />
Superblock<br />
Freie Blöcke<br />
Defekte<br />
Blöcke<br />
Block 0 Block 1 Block n<br />
Der Superblock verwaltet alle essentiellen Informationen über den Datenträgeraufbau,<br />
z.B. eine Magic Nummer für den Typ des Dateisystems, Anzahl der<br />
Blöcke. Aus Sicherheitsgründen wird der Superblock mehrfach, verteilt über<br />
den gesamten Datenträger, repliziert.<br />
6.6.2 Blockorientiertes Dateisystem<br />
Aufteilung des vorhandenen Speicherplatzes eines logisch durchnummerierten<br />
Datenträgers auf mehrere Dateien.<br />
¯ Dateien besitzen interne Namen.<br />
¯ keine hierarchische Verzeichnisstruktur. Es existieren keine hierarchischen<br />
Verzeichnisstrukturen, d.h. alle Dateien sind in einer flachen Struktur<br />
unmittelbar über den internen Dateinamen ansprechbar.<br />
¯ jede Datei besteht aus einer Menge von Blöcken, die relativ zum Dateianfang<br />
nummeriert sind. Die Blöcke können entweder zusammenhängend oder<br />
verteilt über den Datenträger zugeteilt werden. Im ersten Fall kann dies zu<br />
externer Fragmentierung führen. Die interne Fragementierung hängt von der<br />
Blockgröße ab. Dateien werden immer in Vielfachen von Blöcken belegt.<br />
¯ wesentliche Operationen:<br />
Erzeugen und Löschen von Dateien<br />
Öffnen und Schließen von Dateien<br />
Lesen und Schreiben von Blöcken<br />
6.6.3 Dateiverwaltung<br />
Bereitstellung von Dateien und Verzeichnissen; Dateien werden über Namen<br />
identifiziert. Unix verwendet Dateideskriptoren (sogenannte I-Nodes), die alle<br />
relevanten Dateiattribute einschließlich einer Indexstruktur für die Lokalisierung<br />
der einzelnen Dateiblöcke enthalten. Die Position der ersten 10 Blöcke einer Datei<br />
werden direkt im Deskriptor gespeichert.<br />
189
Schlichter, TU München 6.7. EINBETTUNG DER E/A<br />
I-Node<br />
Schutzbits<br />
Link Count<br />
uid<br />
gid<br />
Größe<br />
Adressen der<br />
ersten 10 Blöcke<br />
einfach indirekt<br />
zweifach indirekt<br />
dreifach indirekt<br />
6.7 Einbettung der E/A<br />
Die Realisierung abstrakter Geräte und die Definition einer generischen<br />
Gerätearchitektur ist charakteristisch für viele Betriebssysteme.<br />
¯ in Unix erfolgt der Zugriff auf praktisch jedes E/A-Gerät über Funktionen des<br />
Dateisystems. Vor dem eigentlichen Zugriff auf ein Gerät muss dieses analog<br />
zu einer Datei geöffnet werden.<br />
¯ in vielen Unix Systemen werden alle Geräte unter dem Teilbaum Ú<br />
verwaltet.<br />
¯ der Dateiname charakterisiert den jeweiligen Typ des E/A-Geräts.<br />
Ú ØØÝ: physische serielle Schnittstelle<br />
Ú ÔØØÝ : abstrakte serielle Schnittstellen<br />
Ú : Diskettenstationen<br />
Ú × : Festplatten<br />
Ú Ð : Netzkarten<br />
190
Kapitel 7<br />
Prozesskommunikation<br />
Disjunkte Prozesse, d.h. Prozesse, die völlig isoliert voneinander ablaufen,<br />
stellen eher die Ausnahme dar. Häufig finden Wechselwirkungen zwischen den<br />
Prozessen statt Prozesse interagieren. Die Unterstützung der Prozessinteraktion<br />
stellt einen unverzichtbaren Dienst dar. Betrachtet man diese Prozesswechselwirkungen<br />
genauer, dann lassen sich 2 grundlegende Interaktionsmuster unterscheiden:<br />
Konkurrenz und Kooperation. Eine Konkurrenzsituation liegt vor,<br />
wenn sich Prozesse gleichzeitig um ein exklusiv benutzbares Betriebsmittel bewerben<br />
Prozesssynchronisation. Bei Prozesskooperation geht es darum, dass<br />
die beteiligten Prozesse gezielt Informationen untereinander austauschen.<br />
7.1 Fragestellungen<br />
Dieser Abschnitt beschäftigt sich mit den Mechanismen von Rechensystemen<br />
zum Austausch von Informationen zwischen Prozessen.<br />
¯ Kommunikationsarten.<br />
¯ nachrichtenbasierte Kommunikation, insbesondere Client-Server-Modell. Die<br />
speicherbasierte Kommunikation, d.h. über gemeinsame Speicherbereiche wird<br />
im Zusammenhang mit Synchronisation (siehe Seite 96) und Semaphoren<br />
behandelt.<br />
¯ Netzwerkprogrammierung auf der Basis von Ports und Sockets.<br />
¯ entfernter Prozeduraufruf.<br />
191
Schlichter, TU München 7.2. EINFÜHRUNG<br />
7.2 Einführung<br />
Prozessinteraktion kann Rechner-lokal und Rechner-übergreifend stattfinden.<br />
Prozesse können auf vielfältige Weise Informationen austauschen.<br />
7.2.1 Kommunikationsarten<br />
implizit<br />
1 : 1<br />
n : 1<br />
1 : m<br />
n : m<br />
Prozesskommunikation<br />
breitbandig schmalbandig<br />
explizit<br />
asnychron<br />
1 : 1<br />
1 : m<br />
n : m<br />
Ströme<br />
explizit<br />
snychron<br />
RPC<br />
RMI<br />
Ereignisse<br />
Alarme<br />
Signale<br />
Die Bandbreite des Kommunikationskanals bestimmt die Datenrate, in der Daten<br />
zwischen Prozessen ausgetauscht werden können. Breit- bzw. schmalbandig<br />
bezieht sich hier auf die Menge der ausgetauschten Information und nicht auf<br />
die Bandbreite des darunterliegenden Kommunikationsnetzes.<br />
Schmalbandige Kanäle<br />
Schmalbandige Kanäle werden im Betriebssystem zum Melden von Ereignissen<br />
oder für die Synchronisation unterstützt. Übertragung von wenigen Bits an<br />
Information, z.B. Setzen von Flags<br />
¯ Dienste des Betriebssystems<br />
Melden von Ereignissen,<br />
Warten auf Ereignisse,<br />
Ereignisverwaltung.<br />
192
Schlichter, TU München 7.2. EINFÜHRUNG<br />
Unterbrechungsereignisse und deren Verwaltung in einem Betriebssystem<br />
wurden bereits eingeführt.<br />
¯ Beim Ablauf von Prozessen können Alarme entstehen (z.B. arithmetische<br />
Alarme). Da das BS keine genauen Kenntnisse über den internen<br />
Berechnungszustand eines Prozesses besitzt, kann man mit den allgemeinen<br />
Standardalarmbehandlungen des BS einen Prozess höchstens abbrechen. Es ist<br />
somit sinnvoll, die Alarmbehandlung dem Prozess selber zu überlassen.<br />
¯ Die Alarme werden über Namen identifiziert, die im BS vereinbart sind. Das<br />
BS stellt Dienste zur Zustellung von Alarmen zur Verfügung. Bemerkung:<br />
schmalbandige Informationskanäle sind für den Bereich der Datensicherheit<br />
problematisch, da sie schwierig zu beherrschen sind. Über diese Kanäle können<br />
sich Angreifer Informationen über das System beschaffen und ausnutzen.<br />
Beispiel: Einloggen unter Solaris 7.0 liefert bereits nach Eingabe der Kennung<br />
Informationen, dass diese Kennung korrekt ist. Lässt sich für einen Passwort-<br />
Cracking-Angriff ausnutzen.<br />
Im folgenden werden wir uns vertiefend mit breitbandigen Kommunikationsformen<br />
beschäftigen, und zwar nicht nur im lokalen Bereich, sondern auch im<br />
verteilten Bereich.<br />
Implizite Kommunikation<br />
Implizite Kommunikation ist eine breitbandige Kommunikationsform. Die<br />
Kommunikation erfolgt über einen gemeinsamen Speicher (Dateien, Register,<br />
Datenstrukturen).<br />
¯ Die Kommunikation findet ohne direkte Unterstützung und ohne Kenntnis des<br />
BS statt.<br />
¯ Vorteil: einfach und schnell (kein Kopieren zwischen Adressräumen).<br />
¯ Nachteil:<br />
a) gemeinsame Bereiche sind nicht immer vorhanden: z.B. in physisch<br />
verteilten, vernetzten Systemen gibt es i.d.R. keinen gemeinsamen<br />
Speicher. Eine Ausnahme bilden sogenannte DSM (Distributed Shared<br />
Memory) Realisierungen.<br />
b) gegebenenfalls aufwendiges busy waiting Mischform: Ereigniszustellung,<br />
d.h. schmalbandige Kommunikation, die das Vorhandensein von Daten<br />
signalisiert.<br />
193
Schlichter, TU München 7.2. EINFÜHRUNG<br />
¯ Implizite Kommunikationsformen<br />
Verschiedene Formen der impliziten Kommunikation<br />
1:1 ein Puffer pro Sender/Empfänger-Paar<br />
n:1 n Sender senden Nachrichten an einen Empfänger, z.B.<br />
Sender: Prozesse senden Druckaufträge<br />
Empfänger: Drucker-Server<br />
1:m Mitteilung an alle Prozesse (Broadcast, Multicast);<br />
Broadcast: z.B. Erfragen, wo ein spezieller Dienst angeboten wird;<br />
Shutdown Message an alle Rechner.<br />
Multicast: z.B. Nachricht an Gruppe gleichartiger Server. Broadcast und<br />
Multicast ist in lokalen Netzen mit busartigen Medien (z.B. Ethernet)<br />
sehr einfach zu realisieren; dort hören alle angeschlossenen Rechner das<br />
Medium ab.<br />
n:m n Erzeuger schreiben in Puffer und m Verbraucher lesen aus Puffer.<br />
n:m Kommunikationsform ist beispielsweise sinnvoll, wenn eine Menge<br />
gleichartiger Dienste zur Verfügung steht, und ein Auftrag von einem<br />
beliebigen dieser Dienstanbieter erledigt werden kann.<br />
194
Schlichter, TU München 7.2. EINFÜHRUNG<br />
S1<br />
prozessspezifischer<br />
oder<br />
zentraler<br />
Puffer<br />
E1<br />
1:1 Kommunikation<br />
Si i-ter Senderprozess<br />
Ei i-ter Empfängerprozess<br />
Explizite Kommunikation<br />
S1<br />
prozessspezifischer<br />
oder<br />
zentraler<br />
Puffer<br />
E1<br />
Sn<br />
n:1 Kommunikation<br />
S1<br />
Briefkasten<br />
(mail box)<br />
Sn<br />
E1 Em<br />
n:m Kommunikation<br />
Diese Kommunikationsart wird realisiert durch den Austausch von Nachrichten<br />
("message passing") nachrichtenbasierte Kommunikation. Die Nachrichtenkommunikation<br />
ist immer dann die geeignete Kommunikationsform, wenn<br />
die beteiligten Prozesse in disjunkten Adressräumen liegen, und damit keine<br />
Möglichkeit haben, auf einen gemeinsamen Speicher zuzugreifen.<br />
¯ Betriebssystem enthält einen Nachrichtendienst ND (das Kommunikationssystem),<br />
der den Austausch der Nachrichten zwischen Prozessen realisiert. ND<br />
unterstützt 2 Systemdienste:<br />
×Ò ÔÖÓ ×× Ñ Ñ××<br />
Ö Ú Ë ÔÖÓ ×× Ñ Ñ××<br />
Mittels ×Ò wird eine Nachricht m für den Empfänger E an den<br />
Nachrichtendienst ND übergeben. Mit Ö Ú entnimmt ein Empfänger E<br />
die Nachricht m, die vom Sender S gesandt wurde, von ND. Der Absender wird<br />
gewöhnlich in der Nachricht m codiert.<br />
¯ prinzipieller Ablauf<br />
195
Schlichter, TU München 7.2. EINFÜHRUNG<br />
Sende<br />
prozess S<br />
Nachricht m<br />
Nachrichten<br />
dienst ND<br />
Nachricht m<br />
Empfänger<br />
prozess E<br />
Falls die Prozesse auf unterschiedlichen Rechnern sind, sind die Nachrichtendienste<br />
der beteiligten Betriebssysteme involviert. In diesem Fall findet eine<br />
Kommunikation zwischen den beiden Nachrichtendiensten statt.<br />
¯ Aufbau einer Nachricht<br />
Eine Nachricht besteht aus 2 grundlegenden Komponenten:<br />
Nachrichtenkopf:<br />
Empfängeridentifikation<br />
Verkehrsinformation, z.B. Sender- und<br />
Nachrichteninhalt: Nutzlast (payload)<br />
¯ explizite Kommunikation ist besonders geeignet in verteilten, vernetzten Systemen.<br />
Die nachfolgenden Abschnitte werden sich mit der nachrichtenbasierten<br />
Kommunikation, insbesondere in verteilten Systemen beschäftigen.<br />
7.2.2 Verteilte Systeme<br />
Bisher haben wir uns mit systemnahen Konzepten von zentralen Systemen<br />
beschäftigt. Seit Ende der 80er Jahre haben jedoch verteilte Systeme rapide an<br />
Bedeutung gewonnen. Die Verteilung bezieht sich sowohl auf die Verteilung<br />
von Hardwarekomponenten (Rechner, Peripherie), als auch auf die Verteilung<br />
von Daten und Verarbeitung. Im letzteren Fall spricht man von einer verteilten<br />
Anwendung, d.h. eine Anwendung besteht aus mehreren kooperierenden<br />
Teilkomponenten, die auf verschiedenen Rechnern ausgeführt werden.<br />
Ansätze zur Kopplung von Recheneinheiten<br />
Vielfach CPU-<br />
Systeme<br />
eng gekoppelt lose gekoppelt<br />
Multiprozessoren Multicomputer verteiltes System<br />
196
Schlichter, TU München 7.2. EINFÜHRUNG<br />
Multiprozessor Multicomputer verteiltes System<br />
Konfiguration nur eigene CPU CPU, Arbeitsspeicher,zschnittstelleNet-<br />
Peripherie gemeinsam gemeinsam,<br />
außer Festplatte<br />
Ort gemeinsames gemeinsamer<br />
Gehäuse Raum<br />
Kommunikation gemeinsamer Ar- direkte<br />
beitsspeicher Verbindung<br />
Betriebssystem ein gemeinsames getrennt, jedoch<br />
BS<br />
gleicher Typ<br />
kompletter Rechner<br />
getrennt<br />
weltweit verteilt<br />
Netzwerk<br />
getrennt, vielfach<br />
unterschiedlich<br />
Verwaltung eine Organisation eine Organisation viele Organisationen<br />
Ein Beispiel für ein eng gekoppeltes Multiprozessor System ist die MI bestehend<br />
aus 4 Rechnerkernen; die Rechnerkerne sind untereinander durch einen Bus<br />
verbunden. Das Rechensystem wird kontrolliert durch ein gemeinsames<br />
Betriebssystem; das Betriebssystem teilt Rechnerkerne und Speicherbereiche<br />
an Prozesse zu; es erfolgt eine parallele Ausführung der Benutzerprogramme;<br />
jeder RK hat den gerade ablaufenden Prozess im gemeinsamen Arbeitsspeicher.<br />
Die Einteilung in Multicomputer oder verteiltes System hängt etwas von der<br />
verwendeten Betrachtungsweise ab.<br />
Charakteristika verteilter Systeme<br />
Netze verbinden autonome Rechner; die Rechner sind oft von verschiedenen<br />
Herstellern mit<br />
inkompatiblen Datendarstellungen,<br />
unterschiedlichen Maschinenbefehlsvorräten, und<br />
verschiedenen Betriebssystemen.<br />
Die verschiedenen Rechner haben keinen gemeinsamen Speicher, d.h. es ist keine<br />
Kommunikation über gemeinsame Variable möglich. Kommunikation zwischen<br />
Prozessen auf verschiedenen Systemen ist nur über Nachrichten möglich (nicht<br />
über gemeinsamen ASP).<br />
¯ mehrfache Existenz von verschiedenen Funktionseinheiten (physische, logische).<br />
Diese können dynamisch verschiedenen Ausführungen zugeteilt werden<br />
und unabhängig voneinander ablaufen; die Funktionseinheiten können hier<br />
Rechnerkerne, ASP, periphere Geräte, aber auch Softwarekomponenten sein;<br />
197
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Funktionseinheiten agieren autonom; Funktionseinheiten können hier auch<br />
Teilkomponenten einer verteilten Anwendung sein.<br />
¯ räumliche Verteilung von physischen und logischen Funktionseinheiten.<br />
Die Funktionseinheiten sind mittels eines oder mehrerer Netze miteinander<br />
verbunden; dies ermöglicht Kommunikation zur Synchronisation.<br />
¯ Funktionseinheiten fallen unabhängig voneinander aus.<br />
¯ Transparenz, z.B.<br />
Ortstransparenz. Der Benutzer ist sich des Ortes eines Objektes im<br />
Netz nicht bewusst; Zugriff über einen Namen; Name enthält keine<br />
Ortsinformation.<br />
Zugriffstransparenz. Auf alle, lokale oder entfernte, Objekte werden in ein<br />
und derselben Weise zugegriffen.<br />
Replikationstransparenz. Der Benutzer oder Prozess greift auf replizierte<br />
Objekte (ein Objekt existiert in mehreren Kopien) zu, als seien sie nur<br />
einmal vorhanden, d.h. das Betriebs- bzw. das Ablaufsystem übernimmt<br />
die Aufgabe, die Kopien des Objekts in einem konsistentem Zustand zu<br />
halten. Replikation ist insbesondere für die Verfügbarkeit des Systems und<br />
der Daten von großer Bedeutung.<br />
¯ kooperative Autonomie bei der Interaktion zwischen den physischen bzw.<br />
zwischen den logischen Funktionseinheiten.<br />
7.3 Nachrichtenbasierte Kommunikation<br />
Bei nachrichtenbasierter Prozessinteraktion tauschen Prozesse gezielt Informationen<br />
durch Verschicken und Empfangen von Nachrichten aus; ein Kommunikationssystem<br />
unterstützt an der Schnittstelle wenigstens die Funktionen ×Ò und<br />
Ö Ú. Nachrichtenkommunikation ist die natürliche Form der Prozessinteraktion<br />
in Rechnernetzen. Prozesse, die auf verschiedenen Rechnerknoten platziert<br />
sind, müssen ein physisches Übertragungssystem benutzen, um miteinander in<br />
Kontakt zu treten.<br />
7.3.1 Elementare Kommunikationsmodelle<br />
Klassifikationsschema für die Nachrichtenkommunikation anhand von 2 Dimensionen:<br />
198
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
generelles Muster der Nachrichtenkommunikation.<br />
zeitliche Kopplung der beteiligten Prozesse.<br />
Klassifikationsschema<br />
¯ Elementare Kommunikationsmuster sind Meldung und Auftrag.<br />
Meldung: Einweg Nachricht vom Sender zum Empfänger (unidirektional).<br />
Die entsprechende Nachrichtentransaktion beginnt mit dem Versenden der<br />
Nachricht und endet mit der Übergabe an den Empfänger. Zu einer<br />
Meldung gehört nicht die anschließende Verarbeitung der Nachricht durch<br />
den Empfänger.<br />
Auftrag: Zweiweg Nachricht zwischen Sender und Empfänger (bidirektional).<br />
Sie beginnt mit dem Versenden eines Auftrags an den Empfänger und<br />
endet mit der Übergabe einer Erfolgsbestätigung über den durchgeführten<br />
Auftrag an den Sender. Dazwischen liegt die Auftragsbearbeitung auf<br />
Empfängerseite, die weitere Nachrichten zur Übertragung der Eingabeparameter<br />
bzw. zur Rückübertragung des Resultates auslösen kann. Im<br />
einfachsten Fall genügen für einen Auftrag 2 Nachrichten: die Auftragsnachricht<br />
und die Resultatnachricht, die gleichzeitig als Quittung dient.<br />
Ausbleibende Quittungen können mittels Timeouts erkannt werden;<br />
sie sind gleichbedeutend mit negativen Quittungen, die auf Senderseite als<br />
nicht durchgeführter Auftrag interpretiert wird.<br />
¯ Synchronität definiert den Kopplungsgrad zwischen Prozessen bei der<br />
Durchführung einer Nachrichtentransaktion:<br />
asynchron: Entkopplung des Senders und Empfängers. Dies bedeutet,<br />
dass ein Sender Nachrichtentransaktionen in schnellerer Folge für einen<br />
Empfänger erzeugen kann, als dieser in der Lage ist, sie zu bearbeiten.<br />
Asynchrone Nachrichtenübertragung erfordert deshalb die Pufferung von<br />
Nachrichten auf dem Übertragungswege zwischen Sender und Empfänger.<br />
Parallelarbeit ist möglich.<br />
synchron: beide Prozesse werden zur Nachrichtenübertragung<br />
synchronisiert.<br />
Es ist auch möglich das Merkmal Synchronität nicht auf die Nachrichtentransaktion,<br />
sondern getrennt auf Sender und Empfänger anzuwenden. Damit<br />
lassen sich asynchrone und synchrone Formen des Nachrichtenversands und<br />
-empfangs beliebig kombinieren.<br />
199
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Meldung<br />
¯ Asynchrone Meldung<br />
Sender wird lediglich bis zur Ablieferung der Meldung an das Nachrichtensystem<br />
(Kommunikationssystem) blockiert.<br />
Zeit<br />
send<br />
Sender S Nachrichtendienst ND Empfänger E<br />
Meldung<br />
receive<br />
– Nachrichtendienst des Betriebssystems puffert Nachricht;<br />
Sender S kann seine Ausführung fortsetzen, sobald Nachricht N in den<br />
Nachrichtenpuffer des ND eingetragen ist.<br />
S wartet nicht, bis E die Nachricht empfangen hat.<br />
– Empfänger E zeigt durch receive an, dass er am Empfang der Nachricht N<br />
interessiert ist.<br />
Empfänger wird blockiert, bis Sender Nachricht bereit stellt.<br />
¯ Synchrone Meldung<br />
Sender und Empfänger von Meldungen sind zeitlich gekoppelt.<br />
200
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Zeit<br />
send<br />
Sender S Nachrichtendienst ND Empfänger E<br />
Meldung<br />
Quittung<br />
receive<br />
Über die Ablieferung der Nachricht wird der Sender durch eine Quittungsnachricht<br />
informiert, die zur Aufhebung der Blockade des Senders führt.<br />
– Rendezvous-Technik: Sender und Empfänger stellen vor Austausch der<br />
eigentlichen Meldung die Sende- und Empfangsbereitschaft her. In diesem<br />
Fall braucht die Nachricht nirgends gepuffert werden, sondern sie kann direkt<br />
vom Adressraum des Senders in den Adressraum des Empfängers übertragen<br />
werden. Das Erreichen des Rendezvous-Punktes definiert den Zeitpunkt, zu<br />
dem Sender- und Empfängerseite wechselseitiges Einverständnis über das<br />
Vorliegen der Sende- und Empfangsbereitschaft erklärt haben.<br />
Auftrag<br />
¯ Synchroner Auftrag<br />
Bearbeitung der Nachricht durch Empfänger und Senden der Resultatnachricht<br />
sind Teil der Nachrichtentransaktion.<br />
201
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Zeit<br />
Sender S Nachrichtendienst ND Empfänger E<br />
send Auftrag<br />
receive<br />
Resultat<br />
Auftrags<br />
bearbeitung<br />
reply<br />
Synchrone Aufträge schränken die Parallelarbeit zwischen Sender und<br />
Empfänger noch stärker ein als synchrone Meldungen, da die zeitliche Kopplung<br />
auch die Bearbeitung der Nachricht umfasst. Diese Kommunikationsform<br />
wird gerade im Zusammenhang mit dem Client-Server Modell sehr oft<br />
verwendet. Das RPC-Konzept ("Remote Procedure Call") ist eine sprachbasierte<br />
Variante der synchronen, auftragsorientierten Kommunikation.<br />
¯ Asynchroner Auftrag<br />
Auftrag und Resultat werden als Paar unabhängiger Meldungen verschickt.<br />
202
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Zeit<br />
Sender S Nachrichtendienst ND Empfänger E<br />
send Auftrag<br />
receive<br />
receive<br />
result<br />
Resultat<br />
Auftrags<br />
bearbeitung<br />
reply<br />
Zwischen ×Ò und Ö Ú Ö×ÙÐØ kann der Sender u.U. noch weitere<br />
Aufträge versenden (an den gleichen oder andere Empfänger).<br />
Vorteile/Nachteile asynchrones Senden<br />
¯ Vorteile asynchrones Senden<br />
– nützlich für Realzeitanwendungen. Speziell in Situationen, bei denen<br />
sendender Prozess nicht blockiert werden darf.<br />
– ermöglicht parallele Abarbeitung durch Sender und Empfänger.<br />
– anwendbar zum Signalisieren von Ereignissen.<br />
¯ Nachteile asynchrones Senden<br />
– Verwaltung des Nachrichtenpuffers durch BS erforderlich. Überlauf des<br />
Puffers, Zugriffsprobleme, Absturz von Prozessen.<br />
– Benachrichtigung des Senders S im Fehlerfall und Behandlung von Fehlern<br />
ist problematisch. Warum: Sender hat weitergearbeitet, gegebenenfalls<br />
bereits terminiert; Nachricht kann u.U. nicht mehr wiederholt werden.<br />
– Entwurf und Nachweis der Korrektheit des Systems ist schwierig. Auftreten<br />
von Fehlern abhängig von Pufferinhalten und dem Zeitverhalten des<br />
verteilten Systems (Last des Kommunikationssystems).<br />
203
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
7.3.2 Erzeuger-Verbraucher Problem<br />
Auf der Basis der nachrichtenbasierten Kommunikation wird das Erzeuger-<br />
Verbraucher Problem mit Hilfe von ×Ò und Ö Ú Operationen realisiert.<br />
ÖÞÙÖ Ë<br />
ÛÐ ØÖÙ ß<br />
ÔÖÓÙÞÖ ØÒÒØ<br />
×Ò ØÒÒØ <br />
<br />
ÎÖÖÙ Ö <br />
ÛÐ ØÖÙ ß<br />
Ö Ú Ë ØÒÒØ <br />
ÚÖÖÙ ØÒÒØ<br />
<br />
Es existiert kein gemeinsamer Speicherbereich, der bzgl. der Zugriffe von<br />
Erzeuger und Verbraucher synchroniert werden muss. Die Synchronisation von<br />
Erzeuger und Verbraucher erfolgt durch das Kommunikationssystem selbst.<br />
7.3.3 Modellierung durch ein Petrinetz<br />
Petri-Netze dienen häufig zur Modellierung von Kommunikationsabläufen,<br />
sogenannten Kommunikationsprotokollen. Sie ermöglichen die Analyse der<br />
Protokolle, z.B. Erkennung von Verklemmungen. Modellierung einer synchronen<br />
Kommunikation:<br />
204
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Prozess 1<br />
send<br />
message<br />
wait for<br />
ack.<br />
receive<br />
ack.<br />
Problem: unendliches Warten<br />
Sendebereit<br />
buffer full<br />
buffer full<br />
message<br />
received<br />
ack. received ack. sent<br />
pragmatische Lösung mit Hilfe von Timeouts<br />
receive<br />
message<br />
send<br />
ack.<br />
Prozess 2<br />
¯ Sender bzw. Empfänger warten nur eine festgelegte Zeit, Sender: falls kein<br />
Acknowledgement eintrifft, z.B. erneutes Senden.<br />
¯ Probleme dabei? u.a. Duplikate müssen vom Empfänger erkannt werden;<br />
gesendete Nachrichten kommen zu spät an, sind veraltet etc. Gleiche Probleme<br />
auch in Rechnernetzen: dafür gibt es Protokolle, die die Abläufe genau regeln.<br />
Sequenznummern können als Nachrichtenidentifikatoren dienen.<br />
7.3.4 Ports<br />
Bisher wurde davon ausgegangen, dass ein Prozess eine Nachricht mittels<br />
Ö Ú in seinen Adressraum entgegennehmen kann. Dazu wurde jedem<br />
Prozess ein eigener Nachrichtenpuffer für neu eingetroffene, jedoch noch nicht<br />
abgelieferte Nachrichten zugeordnet. Bisher bestand zwischen Sender und<br />
Empfänger eine feste Beziehung, die über Prozessidentifikatoren (z.B. Namen<br />
oder Nummer) hergestellt wurde. Nachteile<br />
205
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Prozessnummern ändern sich mit jedem Neustart.<br />
Prozessnamen sind nicht eindeutig, z.B. falls Programm mehrmals gestartet<br />
wurde.<br />
Deshalb Senden von Nachrichten an Ports. Sie stellen Endpunkte einer<br />
Kommunikation dar. Sie können bei Bedarf dynamisch eingerichtet und gelöscht<br />
werden. Dazu existieren folgende Funktionen:<br />
ÔÓÖØÁ ÖØÈÓÖØ <br />
ÐØÈÓÖØ ÔÓÖØÁ <br />
×Ò ÔÓÖØÁ Ñ×× <br />
Ö Ú ÔÓÖØÁ Ñ×× <br />
Mittels ÖØÈÓÖØ wird ein neuer Kommunikationsendpunkt eingerichtet und<br />
mit dem Adressraum des Prozesses (z.B. Empfänger E) verbunden. Andere<br />
Prozesse, z.B. Sender S, kann nun Nachrichten an den Port des Empfängers<br />
senden. Die Ö Ú Operation benötigt nun den Parameter portID zur<br />
Selektion des Ports. Ports sind eine logische Abstraktion der Adressräume; sie<br />
repräsentieren keine physischen Ports (z.B. serieller oder paralleler Anschluss).<br />
Der Port agiert hier wie eine Warteschlange für den Empfänger E; die<br />
Auslieferung der Nachricht kann willkürlich, festgelegt durch die Reihenfolge<br />
des Eintreffens der Nachrichten, oder festgelegt durch die Nachrichtenprioritäten<br />
erfolgen.<br />
¯ ein Port ist mit dem Adressraum des zugehörigen Prozesses verbunden.<br />
¯ der Empfängerprozess kann sender-spezifische Ports einrichten. Jedem Port<br />
kann ein eigener Thread zugeordnet werden, der die Nachrichten an diesen<br />
Port bearbeitet. Im Ruhezustand ist kein Port eingerichtet, d.h. der Empfänger<br />
kann nur über den fest mit seinem Adressraum assoziierten Nachrichtenpuffer<br />
angesprochen werden.<br />
¯ ein Rechner mit einer IP-Adresse unterstützt mehrere tausend Ports. Für das<br />
TCP-Protokoll sind es 65 535 unterstützte Ports.<br />
¯ der Name des Port ist für einen Rechner eindeutig. Ein Betriebssystem<br />
verwaltet eine bestimmte Anzahl von Ports, die es entweder fest oder<br />
dynamisch verschiedenen Protokollen bzw. deren zugehörigen Applikationen<br />
zuordnen kann.<br />
¯ die Portnummern 1 - 1023 sind fest reserviert für bestimmte Protokolle (bzw.<br />
deren Applikationen).<br />
¯ Übersicht: fest zugeordnete Ports<br />
206
Schlichter, TU München 7.3. NACHRICHTENBASIERTE KOMMUNIKATION<br />
Protokoll Port Beschreibung<br />
FTP 21 Kommandos für Dateitransfer (get, put)<br />
Telnet 23 interaktive Terminal-Sitzung mit entferntem<br />
Rechner<br />
SMTP 25 Senden von Email zwischen Rechnern<br />
time 37 Time-Server liefert aktuelle Zeit<br />
finger 79 liefert Informationen über einen Benutzer<br />
HTTP 80 Protokoll des World Wide Web<br />
POP3 110 Zugang zu Email durch einen sporadisch<br />
verbundenen Client<br />
RMI 1099 Zugang zum Registrieren von entfernten<br />
Java Objekten.<br />
7.3.5 Kanäle<br />
Bisher wurde von einer verbindungslosen Kommunikation ausgegangen, d.h. eine<br />
Nachricht kann an einen Port geschickt werden. Bei einer verbindungsorientierten<br />
Kommunikation muss zuerst eine logische Verbindung zwischen den Kommunikationspartner<br />
eingerichtet werden Kanal ("socket").<br />
¯ Einrichtung eines Kanals zwischen Ports, d.h. Verknüpfen zweier Ports.<br />
¯ bidirektionale Übertragung über Kanäle. Ports sind nicht mehr nur<br />
Empfangsstellen für Nachrichten, sondern sie dienen auch als Quellen. Damit<br />
sind komplexere Auftragsbeziehungen zwischen den Kommunikationspartnern<br />
möglich. Beispielsweise müssen nicht alle benötigten Daten in der<br />
Auftragsnachricht mitgeschickt werden, sondern sie können bei Bedarf vom<br />
beauftragten Prozess angefordert werden.<br />
¯ Die Sende- bzw. Empfangsoperation bezieht sich auf die lokale PortID;<br />
×Ò ÐÓ Ð ÔÓÖØÁ Ñ×× <br />
Ö Ú ÐÓ Ð ÔÓÖØÁ Ñ×× <br />
¯ TCP/IP unterstützt verbindungsorientierte Kommunikation.<br />
7.3.6 Ströme<br />
Ströme (engl. streams) sind eine Abstraktion von Kanälen. Sie verdecken die<br />
tatsächlichen Nachrichtengrenzen. Ein Sender schickt mittels send-Operationen<br />
Nachrichten an den Empfänger. Die Nachrichten werden jedoch logisch zu<br />
einem Bytestrom vereinigt, dem man auf Empfangsseite die Nachrichtengrenzen<br />
207
Schlichter, TU München 7.4. CLIENT-SERVER-MODELL<br />
nicht mehr entnehmen kann. Der Empfänger kann den Bytestrom in Portionen<br />
verarbeiten, ohne sich an den ursprünglichen Nachrichtengrenzen zu orientieren.<br />
Sender<br />
send (120 Bytes)<br />
send (74 Bytes)<br />
send (233 Bytes)<br />
Strom<br />
1 Byte<br />
receive (50 Bytes)<br />
receive (377 Bytes)<br />
Empfänger<br />
Bemerkung: breitbandige, verbindungsorientierte Kommunikation kann auch<br />
für Angriffe in vernetzten Systemen ausgenutzt werden. Ein Beispiel aus der<br />
Vergangenheit: Angriffe auf Yahoo und amazon.com. Aufbau einer Vielzahl Port-<br />
Verbindungen, Übertragung großer Datenmengen (1 GB) führte zum Lahmlegen<br />
des Dienstes ("Denial of Service").<br />
¯ BS-Dienste: Verbindungsauf- und -abbau, schreiben in Strom, lesen aus Strom.<br />
¯ Beispiel: Ströme innerhalb eines Unix-Rechners über Pipe-Konzept: FIFOartiger<br />
Datentransfer mit Operationen: open pipe, read, write.<br />
¯ Dienste für Dateizugriffe oder Zugriffe auf Geräte: spezielle Ausprägung der<br />
stromorientierten Kommunikation.<br />
¯ I/O in Java basiert auf Ströme.<br />
Klasse Ú Ó ÇÙØÔÙØËØÖÑ zum Schreiben von Daten<br />
Klasse Ú Ó ÁÒÔÙØËØÖÑ zum Lesen von Daten<br />
Spezialisierungen z.B. durch FileOutputStream, BufferedOutputStream oder<br />
FileInputStream.<br />
7.4 Client-Server-Modell<br />
Client-Server Modell basiert i.a. auf der Kommunikationsklasse der synchronen<br />
Aufträge. Server stellen Dienste zur Verfügung, die von vorher unbekannten<br />
Clients in Anspruch genommen werden können.<br />
208
Schlichter, TU München 7.4. CLIENT-SERVER-MODELL<br />
Zeit<br />
Client C Server S<br />
blockiert<br />
Auftrag<br />
Resultat<br />
Ausführung<br />
Client ruft Operation eines Servers auf; nach Ausführung der Operation wird<br />
das Ergebnis an Client übergeben. Während der Ausführung der Operation wird<br />
Ablauf des Client meist unterbrochen. Eine leere Antwort ist möglich, falls die<br />
Operation kein Ergebnis liefert (z.B. Eintrag einer Informationseinheit in eine<br />
Datenbank).<br />
¯ Definitionen<br />
– Definition: Client<br />
Ein Client ist eine Anwendung, die auf einer Clientmaschine läuft und i.a.<br />
einen Auftrag initiiert, und die den geforderten Dienst von einem Server<br />
erhält.<br />
Clients sind meist a-priori nicht bekannt. Clients sind oft Benutzerprozesse,<br />
die dynamisch erzeugt und gelöscht werden.<br />
– Definition: Server<br />
Ein Server ist ein Subsystem, das auf einer Servermaschine läuft und einen<br />
bestimmten Dienst für a-priori unbekannte Clients zur Verfügung stellt. Es<br />
existiert eine einfache n:1 Auftragsbeziehung zwischen den Clients und<br />
einem Server.<br />
– Server sind dedizierte Prozesse, die kontinuierlich folgende Schleife<br />
abarbeiten.<br />
ÛÐ ØÖÙ ß<br />
Ö Ú ÑÔÒ×ÔÓÖØ ÙØÖ <br />
Ö ÙØÖ Ù× ÙÒ ÖÞÙ ÒØÛÓÖØÒ Ö Ø<br />
×ØÑÑ ×ÒÔÓÖØ Ö ÒØÛÓÖØÒ Ö Ø<br />
×Ò ×ÒÔÓÖØ Ö×ÙÐØØ <br />
<br />
209
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
Der Empfangsport ist server-lokal; der Sendeport ist lokal beim Client.<br />
Ein Server kann intern aus einer Menge von Threads aufgebaut sein. Ein<br />
Thread, der sogenannte Listener wartet auf Aufträge. Er verteilt eintreffende<br />
Aufträge an sogenannte Worker-Threads, die die Bearbeitung eines Auftrags<br />
übernehmen. Die Ergebnisse werden direkt vom Worker-Thread an den<br />
Client weitergeleitet.<br />
– Client und Server kommunizieren über Nachrichten, wobei beide Systeme<br />
auf unterschiedlichen Rechnern ablaufen können und über ein Netz<br />
miteinander verbunden sind.<br />
¯ Beispiele für Dienste (Services), die mittels Server realisiert werden:<br />
Dateidienst, Zeitdienst, Namensdienst.<br />
¯ ein System kann sowohl Client als auch Server sein.<br />
– Beispiel<br />
Web<br />
Browser<br />
(Applets)<br />
Client<br />
Server<br />
Client<br />
HTTP<br />
Web<br />
Server<br />
cgi<br />
7.5 Netzwerkprogrammierung<br />
Server<br />
Anwendungs<br />
Server<br />
SQL<br />
Daten<br />
bank<br />
Bedingt durch rasche Verbreitung des Internet hat auch das Interesse an Netz-<br />
Anwendungen sehr zugenommen. Netz-Anwendungen sind verteilte Anwendun-<br />
210
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
gen, die jeweils aus mehreren Teilkomponenten bestehen und die im Netz verteilt<br />
auf verschiedenen Rechensystemen ausgeführt werden. Teilkomponenten sind<br />
hier nicht einzelne Objekte, sondern komplexe Verarbeitungseinheiten (z.B. ein<br />
Modul bestehend aus einer Menge von Objekten). Eine verteilte Anwendung<br />
ist eine Anwendung A, dessen Funktionalität in eine Menge von kooperierenden<br />
Teilkomponenten A , .., A ,n IN, n 1 zerlegt ist;<br />
1 n<br />
Jede Teilkomponente umfasst Daten (interner Zustand) und Operationen, die<br />
auf den internen Zustand angewendet werden.<br />
Teilkomponenten A i sind autonome Prozesse, die auf verschiedenen<br />
Rechensystemen ausgeführt werden können. Mehrere Teilkomponenten<br />
können demselben Rechensystem zugeordnet werden.<br />
Teilkomponenten A tauschen über das Netz untereinander Informationen aus.<br />
i<br />
Die Teilkomponenten können z.B. auf der Basis des Client-Server Modells realisiert<br />
werden. Beispiele von verteilten Anwendungen sind Softwaresysteme für<br />
verteilte Geldautomaten oder Flugbuchungssysteme. Netzwerkprogrammierung<br />
ist ein Hilfsmittel zur Implementierung verteilter Anwendungen.<br />
7.5.1 Einführung<br />
In Berkeley Unix wurde das Konzept von Sockets eingeführt, um die<br />
Netzwerkprogrammierung zu erleichtern. Sie erlauben jede Netzverbindung<br />
als einen Strom von Bytes zu betrachten, die gelesen bzw. geschrieben<br />
werden können. Ein Socket definiert einen einfachen, bidirektionalen <br />
Kommunikationskanal (siehe Seite 207) zwischen 2 Rechensystemen, mit Hilfe<br />
dessen 2 Prozesse über ein Netz miteinander kommunizieren können.<br />
Input Strom<br />
Client Server<br />
Output Strom<br />
Socket Verbindung<br />
211
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
Socket Grundlagen<br />
Sockets abstrahieren von den technischen Details eines Netzes, z.B. Übertragungsmedium,<br />
Paketgröße, Paketwiederholung bei Fehlern, Netzadressen. Anfänglich<br />
standen Sockets nur in Unix Umgebungen zur Verfügung. In der Zwischenzeit<br />
werden sie auch von Windows, dem MacOs und von Java unterstützt.<br />
¯ Ein Socket kombiniert 2 Ströme, einen Input- und einen Output-Strom.<br />
¯ Ein Socket unterstützt die folgenden Basisoperationen:<br />
richte Verbindung zu entferntem Rechner ein ("connect").<br />
sende Daten.<br />
empfange Daten.<br />
schließe Verbindung.<br />
assoziiere Socket mit einem Port.<br />
warte auf eintreffende Daten ("listen").<br />
akzeptiere Verbindungswünsche von entfernten Rechnern (bzgl. assoziiertem<br />
Port).<br />
Die ersten 4 Operationen sind sowohl für den Client als auch den Server<br />
relevant (siehe Java's Socket Class), während die letzten 3 Operationen nur<br />
für einen Server von Bedeutung sind (siehe Java ServerSocket Class). Die<br />
Endpunkte einer Socket werden durch Ports repräsentiert.<br />
7.5.2 Server Protokoll<br />
Ein Server kommuniziert mit einer Menge von Clients, die a priori nicht bekannt<br />
sind. Ein Server benötigt eine Komponente (z.B. ein Verteiler-Thread (siehe<br />
Seite 92)), die auf eintreffende Verbindungswünsche reagiert. Diese Komponente<br />
wird auch Listener genannt. Der Hauptunterschied zwischen Client und Server<br />
bzgl. der Socket-Verbindung liegt darin, dass ein Server die Assoziierung mit<br />
einem Port vornimmt und Verbindungswünsche akzeptiert.<br />
¯ Informeller Ablauf aus Serversicht<br />
1. Erzeugen eines SocketServer und Binden an einen bestimmten Port. Ein Port<br />
entspricht einer FIFO Warteschlange. Sie sammelt die Verbindungswünsche<br />
der Clients. Die maximale Länge ist abhängig vom Betriebssystem, z.B. 50.<br />
Falls die Warteschlange voll ist, werden keine weiteren Verbindungswünsche<br />
akzeptiert.<br />
212
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
2. Warten auf Verbindungswünsche von Clients. Falls der Client bzgl. einer<br />
Socketverbindung autorisiert ist, akzeptiert der Server den Verbindungswunsch.<br />
Der Server wird blockiert, bis die accept-Methode des Servers die<br />
Verbindung zwischen Client und Server eingerichtet hat. Die beiden Ströme<br />
der Socketverbindung werden eingerichtet.<br />
3. Austausch von Daten zwischen Client und Server entsprechend einem<br />
wohldefinierten Protokoll (z.B. HTTP).<br />
4. Schließen einer Verbindung (durch Server, durch Client oder durch beide);<br />
weiter bei Schritt 2.<br />
¯ Programmstück<br />
ËÓ Ø ×Ó Ø ÖÖÒ ØÓ ×Ó Ø<br />
ËÖÚÖËÓ Ø ÔÓÖØ Ø ÔÓÖØ Ø ×ÖÚÖ Ð×ØÒ× ØÓ<br />
ØÖÝ ß<br />
ÔÓÖØ ÒÛ ËÖÚÖËÓ Ø <br />
×Ó Ø ÔÓÖØ ÔØ ÛØ ÓÖ ÐÒØ ÐÐ<br />
ÓÑÑÙÒ Ø ÛØ ÐÒØ<br />
×Ó Ø ÐÓ×<br />
<br />
Ø ÁÇÜ ÔØÓÒ ß ÔÖÒØËØ ÌÖ <br />
¯ Für das Abhören des Ports kann ein eigener Verteiler-Thread spezifiziert<br />
werden; die Bearbeitung übernehmen sogenannte Worker-Threads. Der Unix<br />
FTP Server erzeugt i.a. für jede Verbindung einen eigenen Prozess, was einen<br />
großen Overhead verursacht FTP Server können deshalb oft nicht mehr als<br />
400 offene Verbindungen unterstützen, falls noch vernünftige Antwortzeiten<br />
erwartet werden.<br />
7.5.3 Client Protokoll<br />
Der Client initiiert eine Socket-Verbindung durch Senden eines Verbindungswunsches<br />
an den Port des Servers.<br />
Informeller Ablauf aus Clientsicht<br />
1. Erzeugen einer Socket Verbindung. Dazu muss die Adresse des Servers (z.B.<br />
Internet-Adresse) und der Port, auf dem der Server wartet, angegeben werden.<br />
2. Austausch von Daten zwischen Client und Server über die Duplex-Verbindung<br />
entsprechend einem wohldefinierten Protokoll (z.B. HTTP).<br />
3. Schließen einer Verbindung (durch Server, durch Client oder durch beide).<br />
213
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
Programmstück<br />
ËÓ Ø ÓÒÒ ØÓÒ ÖÖÒ ØÓ ×Ó Ø<br />
ØÖÝ ß<br />
ÓÒÒ ØÓÒ ÒÛ ËÓ Ø ÛÛÛ Ò ØÙÑ <br />
ÓÑÑÙÒ Ø ÛØ ÐÒØ<br />
ÓÒÒ ØÓÒ ÐÓ×<br />
<br />
Ø ÁÇÜ ÔØÓÒ ß ÔÖÒØËØ ÌÖ <br />
7.5.4 Bidirektionale Stromverbindung<br />
Sockets bestehen aus 2 Strömen für die Duplexverbindung zwischen Client<br />
und Server. Diese beiden Ströme werden automatisch beim Einrichten einer<br />
Socket-Verbindung erzeugt. Durch die Verwendung von Strömen kann dieselbe<br />
Programmiermethode verwendet wie bei I/O, Dateizugriff, etc.<br />
¯ Schreiben auf Socket<br />
ÚÓ ÛÖØÌÓËÓ Ø ËÓ Ø ×Ó ËØÖÒ ×ØÖ ØÖÓÛ×<br />
ÁÇÜ ÔØÓÒ ß<br />
ÓËØÖÑ ×Ó ØÇÙØÔÙØËØÖÑ <br />
ÓÖ ÒØ ×ØÖ ÐÒØ <br />
ÓËØÖÑ ÛÖØ ×ØÖ ÖØ <br />
<br />
¯ Lesen von Socket<br />
ËØÖÒ ÖÖÓÑËÓ Ø ËÓ Ø ×Ó ØÖÓÛ× ÁÇÜ ÔØÓÒ ß<br />
ËØÖÑ ×Ó ØÁÒÔÙØËØÖÑ <br />
ËØÖÒ ×ØÖ <br />
Ö <br />
ÛÐ Ö ËØÖÑ Ö Ò<br />
×ØÖ ×ØÖ <br />
ÖØÙÖÒ ×ØÖ<br />
<br />
Man beachte, dass ein Byte und Char nicht direkt verglichen werden können.<br />
Deshalb muss das Byte in ein Char konvertiert werden. Vom Strom wird jeweils<br />
bis zum nächsten Zeilenende gelesen. Die gelesene Zeile wird als Ergebnis<br />
zurückgegeben.<br />
7.5.5 Java Socket Class<br />
Java unterstützt die beiden grundlegenden Klassen:<br />
214
Schlichter, TU München 7.5. NETZWERKPROGRAMMIERUNG<br />
Ú ÒØ ËÓ Ø zur Realisierung der Client-Seite einer Socket.<br />
Ú ÒØ ËÖÚÖËÓ Ø zur Realisierung der Server-Seite einer Socket.<br />
Client-Seite einer Socket<br />
¯ Constructor<br />
ÔÙÐ ËÓ Ø ËØÖÒ Ó×Ø ÒØ ÔÓÖØ<br />
ØÖÓÛ× ÍÒÒÓÛÒÀÓ×ØÜ ÔØÓÒ ÁÇÜ ÔØÓÒ<br />
Der Parameter host ist ein Rechnername, z.B. www11.in.tum.de. Falls der<br />
Domain Name Server den Parameter host nicht auflösen kann, wird die<br />
Exception ÍÒÒÓÛÒÀÓ×ØÜ ÔØÓÒ ausgelöst. Falls die Socket aus einem<br />
anderen Grund nicht geöffnet werden kann, wird ÁÇÜ ÔØÓÒ ausgelöst, z.B.<br />
der entfernte Host akzeptiert keine Verbindungen. Es gibt noch eine Reihe<br />
anderer Konstruktoren Þ <br />
ÔÙÐ ËÓ Ø ÁÒØÖ×× Ó×Ø ÒØ ÔÓÖØ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
Das Objekt der Klasse InetAddress umfasst den Rechnernamen und seine IP-<br />
Adresse, d.h. eine Auflösung durch den Domain Name Server ist nicht mehr<br />
notwendig.<br />
¯ Information über eine Socket<br />
ÔÙÐ ÁÒØÖ×× ØÁÒØÖ×× <br />
liefert als Ergebnis den Namen und IP-Adresse des entfernten Rechners, zu<br />
dem die Socket-Verbindung existiert.<br />
ÔÙÐ ÒØ ØÈÓÖØ <br />
liefert als Ergebnis die Nummer des Ports, mit dem die Socket-Verbindung<br />
am entfernten Rechner assoziiert ist.<br />
ÔÙÐ ÒØ ØÄÓ ÐÈÓÖØ <br />
liefert als Ergebnis die Nummer des Ports, mit dem die Socket-Verbindung<br />
am lokalen Rechner assoziiert ist.<br />
¯ Ein-/Ausgabe<br />
ÔÙÐ ÁÒÔÙØËØÖÑ ØÁÒÔÙØËØÖÑ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
liefert den InputStream, von dem Daten gelesen werden können. Er<br />
unterstützt die Methode read zum Lesen der Daten. InputStream ist ein<br />
Basis-Strom, der mit Hilfe von SubClassing spezialisiert werden kann.<br />
ÔÙÐ ÇÙØÔÙØËØÖÑ ØÇÙØÔÙØËØÖÑ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
liefert den OutputStream, in dem Daten geschrieben werden können. Er<br />
unterstützt die Methode write zum Schreiben der Daten. OutputStream ist<br />
ein Basis-Strom, der mit Hilfe von SubClassing spezialisiert werden kann.<br />
215
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
Die beiden Methoden stehen natürlich auch auf Server-Seite einer Socket-<br />
Verbindung zur Verfügung.<br />
Server-Seite einer Socket<br />
¯ Constructor<br />
ÔÙÐ ËÖÚÖËÓ Ø ÒØ ÔÓÖØ<br />
ØÖÓÛ× ÁÇÜ ÔØÓÒ ÒÜ ÔØÓÒ<br />
erzeugt eine Socket auf Server-Seite und assoziiert sie mit dem Port. Falls<br />
die Socket nicht an den angegebenen Port gebunden werden kann, wird<br />
ÒÜ ÔØÓÒ ausgelöst. Es existieren noch weitere Konstruktoren, z.B.<br />
ÔÙÐ ËÖÚÖËÓ Ø ÒØ ÔÓÖØ ÒØ ÕÙÙÄÒØ<br />
ØÖÓÛ× ÁÇÜ ÔØÓÒ ÒÜ ÔØÓÒ<br />
Die Länge der mit dem Port verbundenen Warteschlange wird durch den<br />
Parameter queueLength angegeben.<br />
¯ Einrichten/Schließen einer Verbindung<br />
ÔÙÐ ËÓ Ø ÔØ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
diese Methode blockiert und wartet auf Verbindungswünsche von Clients.<br />
ÔÙÐ ÚÓ ÐÓ× ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
¯ Ein-/Ausgabe<br />
ÔÙÐ ÁÒÔÙØËØÖÑ ØÁÒÔÙØËØÖÑ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
liefert den InputStream, von dem Daten gelesen werden können.<br />
ÔÙÐ ÇÙØÔÙØËØÖÑ ØÇÙØÔÙØËØÖÑ ØÖÓÛ× ÁÇÜ ÔØÓÒ<br />
liefert den OutputStream, in dem Daten geschrieben werden können.<br />
7.6 Remote Procedure Call<br />
Realisierung des Client-Server Modells auf der Basis von Auftrags- und<br />
Antwortnachrichten ist komplex und fehleranfällig, da der Programmierer explizit<br />
den Code für den Aufbau und die Interpretation der Nachrichten entwerfen und<br />
implementieren muss.<br />
Meist wird deshalb die Abstraktion des entfernten Prozeduraufrufs,<br />
genannt RPC ("remote procedure call") zur Realisierung des Client-Server<br />
Modells verwendet.<br />
¯ Lokaler Prozeduraufruf<br />
216
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
Aufruf<br />
Aufrufer Prozedur<br />
Antwort<br />
einzelner Prozess<br />
Eine Prozedur wird traditionell als Mittel zur Strukturierung der Funktionalität<br />
verwendet; die Implementierung einer Prozedur wird nach außen hin<br />
verborgen. Bei Prozeduraufruf werden sowohl Daten als auch Ablaufkontrolle<br />
vom Aufrufer an die Prozedur übergeben. Es findet ein verzahnter Ablauf<br />
von Aufrufer und Prozedur ("single-threaded execution"). Beide, Aufrufer und<br />
Prozedur, werden im gleichen Adressraum ausgeführt. RPC ist die Erweiterung<br />
derselben Art von Kommunikation auf Programme, die auf verschiedenen<br />
Rechnern ablaufen; der verzahnte Ablauf und Transfer von Daten bleiben<br />
erhalten.<br />
¯ RPC ist eine Verallgemeinerung des lokalen Prozeduraufrufs. Ähnlich wie ihn<br />
höhere Programmiersprachen wie Modula, Pascal oder C unterstützen.<br />
¯ Das RPC-Konzept ist die sprachorientierte Variante der synchronen, auftragsorientierten<br />
Kommunikation., d.h. RPC = synchroner Auftrag.<br />
¯ Definition<br />
– Remote Procedure Call (nach Nelson 1982)<br />
"RPC" ist die synchrone Kontrollfluss- und Datenübergabe in Form von<br />
Prozeduraufrufen und von aktuellen Parametern zwischen Programmen in<br />
unterschiedlichen Adressräumen über einen schmalen Kanal (im Sinne von<br />
Durchsatz und Verweilzeit).<br />
217
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
synchron: der aufrufende Client blockiert solange bis er vom Server<br />
eine Rückmeldung nach Ende der Prozedurbearbeitung erhält; in der<br />
Praxis wird dies nicht so streng durchgeführt, da sonst der Client<br />
möglicherweise unendlich lange warten müsste (Absturz eines Servers)<br />
deshalb Timeout.<br />
Programmiersprache: RPC Aufrufe sind syntaktisch und semantisch in<br />
eine Programmiersprache eingebettet.<br />
unterschiedliche Adressräume: für Client und Server können keine netzweit<br />
eindeutigen Speicheradressen vorausgesetzt werden. Sonderbehandlung<br />
von Pointer erforderlich.<br />
schmaler Kanal: RPC Kommunikation findet über einen speziellen<br />
Interaktionspfad statt (z.B. lokales Netz), der nicht so leistungsfähig ist<br />
wie der lokale, im eigenen Rechner vorhandene Interaktionspfad.<br />
– Ablaufkontrolle<br />
ausführend<br />
wartend<br />
ausführend<br />
Aufruf<br />
Schnittstelle zum<br />
entfernten System<br />
Programm Prozedur<br />
aufrufender<br />
Prozess<br />
Aufruf<br />
Schnittstelle zum<br />
entfernten System<br />
Antwort<br />
Antwort<br />
aufgerufener<br />
Prozess<br />
wartend<br />
ausführend<br />
¯ Lokaler vs. entfernter Prozeduraufruf<br />
Die Attraktivität des RPC-Konzeptes liegt darin, dass mit dem Prozedurkonzept<br />
218
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
vertraute Programmierer nicht umdenken müssen, wenn sie die Interaktion<br />
zwischen Programmen in disjunkten Adressräumen organisieren. Da es<br />
prinzipiell unerheblich ist, ob die Adressräume auf einem oder mehreren Rechnerknoten<br />
installiert sind, bietet sich der RPC-Mechanismus als elementares<br />
Kommunikationsmittel in verteilten Systemen an. Inzwischen werden RPC-<br />
Systeme als Laufzeitpakete von gängigen Betriebssystemen und Plattformen<br />
für verschiedene Sprachen angeboten.<br />
– Unterschiede zwischen RPC und lokalem Prozeduraufruf<br />
Bei einem RPC haben beide Prozesse (aufrufender und aufgerufener)<br />
£ keinen gemeinsamen Adressraum. Damit ist kein Zugriff auf gemeinsame<br />
globale Daten möglich. Auch ist die Übertragung von Arbeitsspeicheradressen<br />
problematisch, d.h. call-by-reference" ist i.a. nicht realisierbar.<br />
Die aufgerufene Prozedur kann mit Zeigern auf einen fremden Adressraum<br />
wenig anfangen.<br />
£ keine gemeinsame Ausführungsumgebung. Es sind heterogene Ablaufsysteme<br />
oder Rechensystem mit unterschiedlichen Datendarstellungen<br />
möglich.<br />
£ unterschiedliche Lebensdauer. Üblicherweise sind Clients kurzlebig,<br />
während Server eher langlebig sind, d.h. sie werden bei Systemstart<br />
automatisch gestartet.<br />
£ Fehler, bedingt durch Maschinenausfälle oder Kommunikationsfehler.<br />
Bei der <strong>Programmierung</strong> von RPC basierten Anwendungen müssen<br />
Kommunikationsfehler berücksichtigt werden.<br />
– Eigenschaften des RPC-Konzepts<br />
£ einheitliche Aufrufsemantik. Für den Anwendungsprogrammierer gibt<br />
es keinen Unterschied zwischen dem Aufruf einer lokalen und einer<br />
entfernten Prozedur.<br />
£ "type-checking". Die Überprüfung der Datentypen für Parameter und<br />
Ergebnis findet zur Übersetzungszeit statt. Falls die Kommunikation<br />
zwischen Client und Server nur auf der Nachrichtenebene durch den<br />
Programmierer realisiert werden muss, dann erfolgt durch das System<br />
keine Überprüfung der Datentypen.<br />
£ volle Parameterfunktionalität. Der Anwendungsprogrammierer muss aus<br />
Übergabeparameter keine Nachrichtensequenz aufbauen. Jedoch gibt es<br />
gegenüber lokalen Prozeduraufrufen einige wesentliche Unterschiede.<br />
219
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
Beispielsweise gibt es bei lokalen Prozeduraufrufen die üblichen<br />
Parameterübergaben "call-by-value" und "call-by-reference"; letzters<br />
kann bei RPC nicht so ohne weiteres bereitgestellt werden, da kein<br />
gemeinsamer Adressraum existiert. Eine Möglichkeit besteht in dem<br />
Kopieren der Datenstrukturen zum Server und wieder zurück zum<br />
Client "call-by-copy/restore".<br />
£ Optimierung der Antwortzeit. Beim RPC-Mechanismus ist die Anwortzeit<br />
und nicht der Durchsatz die Größe, die optimiert werden soll.<br />
£ Transparenz. Transparenz für den Programmierer hinsichtlich der<br />
Ausnahmebehandlung von Kommunikationsfehler.<br />
¯ Stubs<br />
Sowohl auf der Client als auch auf der Serverseite werden Stubs verwendet,<br />
um die Besonderheiten von RPC-Aufrufen und die Kommunikation mit dem<br />
Netzwerk Code vom Anwendungsprogramm zu isolieren.<br />
Aufruf<br />
Client C Server S<br />
Antwort<br />
1 8<br />
5 4<br />
Client<br />
7<br />
Kommunikations-<br />
6<br />
Server<br />
Stub<br />
netz<br />
Stub<br />
2 3<br />
Im verteilten Fall sind Client C und Server S nicht zum selben ausführbaren<br />
Code gebunden (anders im lokalen Fall); ein Stub enthält einen Stellvertreter<br />
für jede entfernte Prozedur P; der eigentliche, ausführbare Code von P ist<br />
Teil von S. Stubs übernehmen auch die Konvertierung der Parameter und der<br />
Ergebnisse zwischen internen und externen Darstellungen, d.h. sie rufen die<br />
entsprechenden Datentransformations-Prozeduren ("Filter") auf.<br />
– Client Stub: enthält Stellvertreter Definition von Prozedur P.<br />
– Server Stub: enthält Stellvertreter Aufruf von Prozedur P<br />
– Aufgaben der Stubs<br />
Die Stubs sind gewöhnlich statisch mit dem Hauptprogramm gebunden.<br />
£ Aufgaben Client Stub<br />
Im Client Stub werden die Anforderungsnachrichten ("requests") erzeugt<br />
und an die darunterliegende TCP-Schicht weitergegeben.<br />
220
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
Æ Spezifikation der aufgerufenen Prozedur.<br />
Æ Zuordnung des Aufrufes zu Zielrechner.<br />
Æ Darstellung der Parameter im Übertragungsformat; Problem: unterschiedliche<br />
Rechner mit unterschiedlichen Datenformaten.<br />
Æ Dekodieren der Ergebnisse und Übergabe an den Client.<br />
Æ Blockierung des Client aufheben.<br />
£ Aufgaben Server Stub<br />
Æ Dekodieren des Aufrufs und der Parameter.<br />
Æ Bestimmung der Aufrufadresse der Prozedur (z.B. mittels einer<br />
Tabelle).<br />
Æ Aufruf der Prozedur. Die eigentliche Bearbeitung des Aufrufs erfolgt<br />
durch Server-Prozeduren.<br />
Æ Kodierung der Ergebnisse. Quittung, Routing und Wiederholung von<br />
Übertragungspaketen wird durch das darunterliegende Transportprotokoll<br />
erledigt.<br />
¯ Wichtige Fragestellungen<br />
Im Zusammenhang mit dem RPC-Konzept gibt es eine Reihe von Fragestellungen,<br />
die behandelt werden müssen.<br />
– Wer ist für das Schreiben der Stubs verantwortlich?Die manuelle Implementierung<br />
von Stubs und deren Schnittstellen zum Netzwerk Code ist oft<br />
aufwendig und fehlerträchtig. Deshalb wird eine automatische Erstellung<br />
der Schnittstelle zwischen Client und Server aus einer deklarativen Beschreibung<br />
angestrebt.<br />
£ Beschreibung der Schnittstelle zwischen Client und Server mit Hilfe<br />
einer deklarative Beschreibungssprache (z.B. Corba IDL); Generierung<br />
der Stubs auf der Basis der Schnittstellenbeschreibung RPC Generator.<br />
£ Erleichtert die Implementierung fehlerfreier Stubs.<br />
– Woher erfährt der Client die Adresse des Servers, an den er den Aufruf<br />
schicken muss?Der Server registriert seine Operation, d.h. er exportiert seine<br />
Schnittstelle. Daraufhin importiert der Client die Serverschnittstelle, d.h. es<br />
erfolgt ein Binden zwischen Client und Server. Der Bindevorgang selbst<br />
kann entweder statisch, semi-statisch oder dynamisch ablaufen.<br />
£ Unterschiedliche Varianten:<br />
Serveradresse fest verdrahtet im Client-Quellprogramm.<br />
Anfrage bei einem Namensdienst. Diese Anfrage kann entweder<br />
am Anfang bei der Client Initialisierung oder vor jedem RPC-Aufruf<br />
erfolgen.<br />
221
Schlichter, TU München 7.6. REMOTE PROCEDURE CALL<br />
– Wie können sich Server gegen unautorisierte Zugriffe abschirmen?Dieser<br />
Aspekt ist insbesondere im Internet, aber auch für Intranets von großer<br />
Bedeutung.<br />
£ Ausweisen über Passwörter oder Zertifikate. Im Hauptstudium werden<br />
die Authentisierungsprotokolle, z.B. Kerberos im Detail besprochen. Ein<br />
weiterer Aspekt ist die Verschlüsselung des Datenaustausches zwischen<br />
Client und Server, um die Information gegenüber Dritte geheimzuhalten.<br />
– Wie geht man Heterogenität um?<br />
£ Unterschiedliche Rechnerarchitekturen verwenden auch unterschiedliche<br />
Datendarstellungen, z.B. für integer Zahlen Stubs müssen Daten in ein<br />
allgemein gültiges Format konvertieren (z.B. XDR).<br />
– Wie geht man mit Kommunikationsfehler bzw. Rechnerabstürzen<br />
um?Rechnerabstürze beziehen sich sowohl auf die Client- als auch auf die<br />
Servermaschine. Beide Rechner können unabhängig voneinander abstürzen.<br />
Kommunikationsfehler werden teilweise durch das TCP Protokoll behandelt,<br />
während Serverabstürze durch den Client bzw. das RPC-Ablaufsystem behandelt<br />
werden müssen.<br />
222
Kapitel 8<br />
Sicherheit in Rechensystemen<br />
In Rechensystemen, die mehreren Benutzern zugänglich sind, ist eine Kontrolle<br />
des Zugriffs auf das Rechensystem selbst, dessen Dienstleistungen und<br />
Datenbestände erforderlich.<br />
8.1 Fragestellungen<br />
Dieser Abschnitt behandelt die Sicherheitsproblematik in zentralen und verteilten<br />
Rechensystemen. Dazu werden verschiedene Schutzmechanismen auf Betriebssystemebene<br />
und für verteilte Systeme vorgestellt.<br />
¯ Zugriffsschutz in Rechensystemen.<br />
¯ Schutzmatrix, insbesondere Zugriffskontrollisten und Capability-Listen.<br />
¯ kurze Einführung in Kryprosysteme.<br />
¯ Authentifizierung in verteilten Systemen.<br />
Die verschiedenen Aspekte der Sicherheit in Rechensystemen werden nur<br />
einführend, und nicht erschöpfend behandelt. Für eine detaillierte Behandlung<br />
der Sicherheitsproblematik sei der Leser auf entsprechende Lehrveranstaltungen<br />
oder die Literatur verwiesen.<br />
8.2 Motivation<br />
Was versteht man unter Sicherheit im Bezug auf Rechensysteme?<br />
223
Schlichter, TU München 8.2. MOTIVATION<br />
Jemand: Unterscheidung von Personen und Gruppen von Personen<br />
davon abhalten: durch technische und organisatorische Maßnahmen<br />
einige: Begrenzung durch unser Vorstellungsvermögen<br />
unerwünschte Dinge zu tun:<br />
1) nicht autorisiert Daten lesen (Geheimhaltung),<br />
2) nicht autorisiert Daten schreiben (Integrität),<br />
3) unter "falscher Flagge" arbeiten (Authentizität),<br />
4) nicht autorisiert Ressourcen verbrauchen (Verfügbarkeit),<br />
usw.<br />
zu tun.<br />
¯ Unterscheidung zwischen Angriffen von<br />
innen. Der Angreifer ist in das Rechensystem bereits eingeloggt und<br />
verschafft sich illegalen Zugriff auf Ressourcen oder Berechtigungen,<br />
z.B. Systemadministrator-Rechte. Mögliche Angriffstechniken sind<br />
Trojanische Pferde, Login-Attrappen, die Nutzung von Hintertüren in<br />
einem Softwaresystem ("trap door") oder die Ausnutzung eines künstlich<br />
herbeigeführten Pufferüberlaufs.<br />
außen. Durch die Vernetzung von Rechnern finden verstärkt auch Angriffe<br />
von entfernten Rechnern über ein Rechnernetz (z.B. das Internet) statt.<br />
Beispiele für mögliche Angriffstechniken sind sogenannte "war-dialer"<br />
(Ausprobieren von Telefonnummern und Passwörtern) oder die Verbreitung<br />
von Viren.<br />
¯ Beispiel: Login-Attrappe<br />
Nutzung von Login-Attrappen in Rechnerumgebungen, wo Rechner von<br />
mehreren Benutzern verwendet werden, um geschützte Benutzerpasswörter zu<br />
erfassen (z.B. in Informatikhalle der Informatik-Fakultät).<br />
– Angreifer startet ein Benutzerprogramm, das am Bildschirm einen Login-<br />
Screen simuliert. Der Angreifer gibt, ohne sich auszuloggen den Rechner<br />
frei. Da am Bildschirm der Login-Screen angezeigt wird, ist ein<br />
nachfolgender Benutzer der Meinung, dass der Rechner verfügbar ist (es sitzt<br />
ja niemand davor und der vorhergehende Benutzer habe durch Ausloggen<br />
seine Sitzung beendet, da der wohlbekannte Login-Screen angezeigt wird).<br />
– Der ahnungslose Benutzer tippt Benutzername und sein privates Passwort.<br />
Angreiferprogramm speichert Benutzername und Passwort in einer<br />
Datei.<br />
224
Schlichter, TU München 8.2. MOTIVATION<br />
Angreiferprogramm terminiert das aktuelle Shell-Programm ("kill"<br />
Systemaufruf) Login-Sitzung des Angreifers wird beendet und<br />
regulärer Login-Screen wird angezeigt.<br />
Der ahnungslose Benutzer nimmt an, dass er sich beim ersten Eintippen<br />
seines Namens oder seines Passwortes vertippt hat und gibt die beiden Daten<br />
erneut ein. Der Angreifer kann auf diese Weise die Passwörter der anderen<br />
Benutzer erfassen.<br />
– Abhilfe: Login-Sequenz wird durch Tastensequenz gestartet, die von einem<br />
Benutzerprogramm nicht erfasst werden kann, z.B. CTRL-ALT-DEL bei<br />
Windows 2000. Falls ein Benutzer diese Tastensequenz eingibt, wird der<br />
aktuelle Benutzer automatisch ausgeloggt und das Login-Programm durch<br />
das Betriebssystem gestartet. Es gibt keinen Weg, um dieses Verhalten des<br />
Betriebssystems zu umgehen.<br />
¯ Beispiel: Pufferüberlauf<br />
Durch einen künstlich herbeigeführten Pufferüberlauf kann ein Angreifer die<br />
Ausführung seines eigenen Programms veranlassen und oft auch noch die<br />
Systemadministrator-Berechtigung (root) erlangen.<br />
– Hintergrund<br />
Die meisten C-Compiler und Laufzeitsysteme überprüfen nicht die Einhaltung<br />
der Feldgrenzen. Da viele aktuelle Betriebssysteme auf der Basis von C<br />
realisiert wurden, ist diese Problematik von großer Bedeutung.<br />
ÒØ <br />
Ö ℄<br />
<br />
℄ <br />
Die Codesequenz ist zwar falsch. Das Laufzeitsystem führt jedoch keine<br />
Überprüfung durch; der Fehler bleibt unentdeckt. In den meisten Fällen<br />
führt dieser Fehler über kurz oder lang jedoch zu einem Programmabsturz<br />
(oft ein Nullpointer). Ein Angreifer kann diese Eigenschaft nutzen, um<br />
Teile des Laufzeitkellers zu überschreiben. Ein Beispiel wäre, das Feld<br />
c für die Speicherung des Pfadnamen einer Datei vorzusehen. Da das<br />
Betriebssystem nur maximal 256 Zeichen pro Pfadenamen unterstützt, ist für<br />
den Programmierer die Länge von c ausreichend. Ein Angreifer kann nun<br />
einen Datei-Pfadnamen angeben, der erheblich länger als 256 ist.<br />
– Veränderung der Rücksprungadresse<br />
225
Schlichter, TU München 8.2. MOTIVATION<br />
Keller<br />
Variable<br />
Hauptprogramm<br />
Programm<br />
Ablauf<br />
Hauptprogramm<br />
SP<br />
SP<br />
Variable<br />
Hauptprogramm<br />
Rückkehradresse<br />
lokale Variable<br />
von A<br />
Feld C<br />
Programm<br />
nach Aufruf der<br />
Prozedur A<br />
C<br />
SP<br />
Variable<br />
Hauptprogramm<br />
Rückkehradresse<br />
lokale Variable<br />
von A<br />
Feld C<br />
Programm<br />
nach Pufferüberlauf<br />
Durch sorgfältiges Analysieren und Berechnen des Speicherlayouts kann<br />
der Angreifer bei Pufferüberlauf die Rückspungadresse der aufgerufenen<br />
Prozedur überschreiben. Der Angreifer überschreibt den Laufzeitkeller<br />
von A einschließlich des Feldes C und der Rücksprungadresse. Die neue<br />
Rücksprungadresse zeigt an den Anfang des Puffers c, an deren Stelle das<br />
vom Angreifer gewünschte ausführbare Programm steht. Der Angreifer<br />
überschreibt als nicht nur die Rücksprungadresse, sondern trägt auch sein<br />
Angriffsprogramm in den Keller des Hostprogramms ein. Beispielsweise<br />
beinhaltet die Zuweisung an das Feld c nicht den Dateinamen, sondern einen<br />
ausführbaren binären Programmcode.<br />
£ Falls das attackierte Programm mit root-Berechtigung (setuid root in<br />
Unix) abläuft, läuft das aufgerufene Programm im Puffer auch mit root-<br />
Berechtigung.<br />
Angreifer kann seiner aufrufenden Shell root-Berechtigung verleihen.<br />
Dadurch erwirbt der Angreifer Superuser Rechte für seine eigenen<br />
Programme und hat damit Zugang auf das gesamte Betriebssystem.<br />
¯ Beispiel: Virus<br />
Ein Virus ist ein Programm, dessen Code an ein anderes Programm anfügt ist<br />
und sich auf diese Weise reproduziert. Zusätzlich kann ein Virus noch andere<br />
Funktionen aufrufen, z.B. Löschen von Dateien, Senden von Nachrichten etc.<br />
Oft ist die Reproduktion des Virus und die Ausführung der Virusfunktion<br />
zeitlich getrennt, d.h. die Virusfunktion wird erst nach Eintreten eines<br />
bestimmten Datums getriggert. Dadurch wird erreicht, dass sich ein Virus<br />
relativ unbemerkt ausbreiten kann (z.B. über das Internet), ohne dass die<br />
226<br />
C
Schlichter, TU München 8.2. MOTIVATION<br />
Benutzer bereits frühzeitig merken, dass ihr Rechner mit dem Virus infiziert<br />
ist.<br />
Virus schläft bis infiziertes Programm ausgeführt wird.<br />
Start des infizierten Programms führt zur Virusreproduktion.<br />
Ausführung der Virusfunktion ist u.U. mit einem zeitlichen Datum<br />
versehen.<br />
mögliche Virustypen sind<br />
– Boot Sector Virus. BIOS liest beim Start des Rechners den Master Boot<br />
Record (MBR) und führt ihn aus. Ein Boot Sector Virus trägt sich im MBR<br />
ein und wird damit bei Rechnerstart jeweils ausgeführt. Der ursprüngliche<br />
MBR Inhalt wird oft auf einen anderen Platz der Festplatte kopiert und<br />
von Virus automatisch aufgerufen, um den Rechnerstart zu ermöglichen.<br />
Nach dem Start speichert sich der Virus oft im Speicherbereich des<br />
Unterbrechungsvektors, um nach jedem Systemaufruf wieder die Kontrolle<br />
zu erhalten.<br />
– Macro Virus. Programme wie Word oder Excel erlauben dem Benutzer<br />
das Schreiben von Macroprogrammen (Visual Basic). Beispielsweise<br />
kann ein Angreifer für ein Word-Dokument ein Macro schreiben, das<br />
mit der ÇÔÒ Ð Funktion verbunden ist. Integriert in das Macro<br />
ist der Virus des Angreifers. Da Macros i.a. alle Systemfunktionen<br />
des Betriebssystems aufrufen dürfen, hat der Virus Zugriff auf die volle<br />
Funktionalität. Die Verbreitung erfolgt durch Versenden des Dokuments,<br />
z.B. als Email Attachment. Öffnen des Dokuments führt zur Ausführung<br />
des Macros und damit des Virus.<br />
– Ausführbares Programm als Virus. Das Virusprogramm sucht nach seinem<br />
Aufruf nach geeigneten ausführbaren Programmen (z.B. "exe Dateien") im<br />
gesamten Dateiverzeichnis und infiziert diese mit dem Virus; beispielsweise<br />
durch Überschreiben des Binärprogramms mit dem Virusprogramm. Die<br />
Länge der Datei wird dadurch verändert (kann genutzt werden durch<br />
Antivirenprogramme, um den Virus zu erkennen).<br />
– Verbreitung von Viren<br />
Früher diente der Austausch von Datenträgern (z.B. Floppy Disk), jetzt das<br />
Internet<br />
als Attachment zu Emails<br />
227
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
Lesen des Adressbuchs und automatische Generierung von Emails<br />
mit Virus Attachment an alle Adressbucheinträge (z.B. von Microsoft<br />
Outlook). Dabei wird oft das Subject-Feld so besetzt, dass der<br />
Empfänger das Gefühl hat, er empfange eine persönliche Email von<br />
einem Bekannten.<br />
8.3 Schutzmechanismen<br />
Schutz von gespeicherter Information vor Diebstahl, unerwünschter Manipulation<br />
und Verletzung der Vertraulichkeit ist ein zentrales Anliegen in allen Mehrbenutzersystemen.<br />
8.3.1 Anforderungen<br />
Für einen Schutzmechanismus gelten die folgenden Anforderungen<br />
¯ alle Objekte eines Systems müssen eindeutig und fälschungssicher identifiziert<br />
werden. Insbesondere muss auch der Aufrufer eines Dienstes eindeutig<br />
und fälschungssicher identifiziert werden. Dies ist gerade für Client-Server<br />
Beziehungen von großer Bedeutung, z.B. die eindeutige Identifizierung des<br />
Client bei Ecommerce Anwendungen.<br />
¯ externer Benutzer eines Systems muss eindeutig und fälschungssicher<br />
identifiziert werden Authentifizierung. Die Zuordnung zu einem<br />
Benutzerprozess muss fälschungssicher sein.<br />
¯ Zugriff auf Objekte sollte nur über zugehörige Objektverwaltung geschehen.<br />
¯ Zugriff auf Objekte nur, wenn Zugreifer die nötige Rechte hat.<br />
¯ Rechte müssen fälschungssicher gespeichert werden; Weitergabe von Rechten<br />
darf nur kontrolliert erfolgen.<br />
¯ Prinzip der minimalen Rechte. Jedem Programm oder Benutzer sollen für<br />
die Objekte nur die Rechte eingeräumt werden, die für die momentane Arbeit<br />
zwingend erforderlich sind.<br />
¯ grundlegenden Schutzmechanismen sollen ohne großen Aufwand überprüft<br />
werden können. Dies bedeutet, dass am besten ein einheitliches Schutzkonzept<br />
für alle zu schützenden Objekte verwendet wird, und dass die Implementierung<br />
zentral in einem möglichst kleinen Baustein, einem Schutzkern im Betriebssystem,<br />
erfolgt.<br />
228
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
8.3.2 Ebenen des Zugriffschutzes<br />
Man unterscheidet die folgenden Ebenen des Zugriffsschutzes.<br />
Maschinenschutz: Kontrolle des physischen Zugangs zum Rechensystem.<br />
Beim direkten Zugang erfolgt die Kontrolle typisch durch organisatorische<br />
und bauliche Maßnahmen. Bei Zugang zu einem Rechner über ein Kommunikationsnetz<br />
sind spezielle Hardware- und Software-Schutzeinrichtungen<br />
vorzusehen, z.B. Benutzer meldet seinen Zugangswunsch an und Rechner ruft<br />
auf einer vorher definierten Nummer zurück (unter der der Benutzer erreichbar<br />
ist).<br />
Zugangskontrolle: Kontrolle des logischen Zugangs zum Rechensystem,<br />
d.h. Ausführung von Aufträgen im Rechensystem. Hier spielt die<br />
Authentifizierung des Benutzers eine wichtige Rolle, z.B. Passwörter oder<br />
biometrische Eigenschaften, um den Benutzer eindeutig zu identifizieren.<br />
Im Rechensystem sind Verfahren notwendig, um die Korrektheit der<br />
angegebenen Identität zu überprüfen (z.B. Passwort-Datei).<br />
Berechtigungskontrolle: Kontrolle des Benutzerzugriffs auf einzelne<br />
Datenbestände und die Ausführung einzelner Dienste.<br />
Systemschutz: Gewährleistung der Integrität der Schutzmechanismen. Hier<br />
muss sichergestellt werden, dass die Schutzmechanismen in Hardware und<br />
Software durch Angreifer nicht modifiziert werden können, um ein Umgehen<br />
und Aushebeln des Schutzes durchzuführen.<br />
8.3.3 Schutzmatrix<br />
Das Konzept der Schutzmatrix wurde von B. Lampson eingeführt. Es verknüpft<br />
Schutzdomänen mit den zu schützenden Objekten.<br />
¯ Schutzdomänen<br />
Definition: Eine Schutzdomäne ist eine Menge von (Objekt, Rechte) Paaren.<br />
Jedes Paar spezifiziert ein Objekt und eine Menge von Operationen, die auf<br />
diesem Objekt ausgeführt werden dürfen. Meist entspricht eine Schutzdomäne<br />
einem Benutzer, d.h. sie gibt an, was dieser Benutzer tun. Negative Rechte,<br />
d.h. das was er nicht tun darf, werden nicht explizit angegeben.<br />
229
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
Domäne 1 Domäne 2<br />
Datei1[R]<br />
Datei2[RW]<br />
Printer1[W]<br />
Datei3[RW]<br />
R = read, W = write, X = execute<br />
Datei1[RWX]<br />
Datei4[R]<br />
Floppy1[R]<br />
Das Beispiel zeigt mehrere Objekte mit Ihren Rechten und ihre Zuordnung<br />
zu den Domänen. Gleiche Objekte (im Beispiel Datei1) können mit<br />
unterschiedlichen Rechten unterschiedlichen Domänen zugeordnet werden.<br />
– Verknüpfung eines Prozesses mit einer Schutzdomäne. Dadurch wird der<br />
Handlungsspielraum des Prozesses bei seiner Ausführung eingeschränkt.<br />
Die Zuordnung einer Schutzdomäne zu jedem Prozess geschieht in einer<br />
allen Zugriffsversuchen vorangeschalteten Authentisierung. Insbesondere<br />
wird die hinter einem Prozess stehende Identität geprüft, z.B. die Identität<br />
des Benutzers oder die eines Servers.<br />
– zu jedem Zeitpunkt wird ein Prozess in einer Schutzdomäne ausgeführt.<br />
Ein Prozess kann während seiner Ausführung die Schutzdomäne wechseln.<br />
Dadurch können Prozesse während ihres Lebenszyklus auf unterschiedliche<br />
Objekte mit unterschiedlichen Rechten zugreifen.<br />
Beispiel Unix: bei Ausführung eines Systemaufrufs wechselt der Prozess<br />
vom Benutzermodus in den Systemmodus ("kernel mode") entspricht<br />
einem Wechsel der Schutzdomäne. Im Systemmodus kann der Prozess<br />
auf die geschützten Ressourcen des Systems zugreifen.<br />
– Das Paar (Prozess P, Schutzdomäne D) wird als Subjekt bezeichnet. Subjekte<br />
sind im Kontext einer Schutzdomäne agierende Prozesse.<br />
Der Zugriffswunsch eines Subjektes S auf ein Objekt o ist definiert als<br />
(D, o, a), wobei D die Schutzdomäne und a die Zugriffsart ist.<br />
– Matrix-Datenstruktur<br />
Konzeptuell verwendet ein Betriebssystem eine Matrix-Datenstruktur, um<br />
die Zuordnung Objekt-Schutzdomäne zu verfolgen.<br />
230
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
Domäne<br />
1<br />
2<br />
Datei1<br />
read<br />
read write<br />
execute<br />
Datei2<br />
read write<br />
Datei3<br />
read write<br />
read write<br />
Objekt<br />
Datei4<br />
read<br />
Printer1<br />
write<br />
write<br />
Floppy1<br />
read<br />
Jedes Matrixelement spezifiziert die Zugriffsarten, die in einer Schutzdomäne<br />
auf das zugehörige Objekt erlaubt sind. Der Domänenwechsel selbst<br />
kann mit Hilfe des Matrixmodells realisiert werden. Zum Beispiel können als<br />
Objekte zusätzlich die Schutzdomänen mit aufgenommen werden. Falls ein<br />
Wechsel von einer Domäne 1 in eine Domäne 2 erlaubt sein soll, wird in die<br />
Zeile der Domäne 1 in der Spalte für Objekt Domäne 2 die Zugriffsart "enter"<br />
eingetragen.<br />
¯ Schutzmonitor<br />
Jeder Zugriff (D, o, a) eines Subjektes S wird mit Hilfe eines Schutzmonitors<br />
überprüft. Der Schutzmonitor prüft anhand seiner intern gespeicherten<br />
Schutzmatrix, ob in der Zeile von D ein Zugriffsrecht a für das Objekt o existiert.<br />
Bei positivem Ausgang wird der Zugriff zugelassen, andernfalls wird<br />
ein Schutzalarm ausgelöst und der Zugriff unterdrückt. Der Schutzmonitor läuft<br />
meist in einem geschützten Teil des Betriebssystems ab (z.B. Betriebssystemkern).<br />
Schutzdomäne<br />
D<br />
Prozess<br />
P<br />
Subjekt<br />
Benutzermodus<br />
(D,o1,a)<br />
Schutzmonitor<br />
Schutz<br />
matrix<br />
Betriebssystem - Systemmodus<br />
Objekte<br />
– der Schutzmonitor ist vertrauenswürdig.<br />
– Subjekte können in keinem Fall auf Objekte unter Umgehung des<br />
Schutzmonitors zugreifen.<br />
– neue Prozesse müssen sich gegenüber dem Schutzmonitor authentifizieren.<br />
Anonyme Prozesse, die noch ohne Schutzdomäne sind, übergeben dem<br />
231<br />
o1<br />
o2<br />
o3
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
Schutzmonitor Namen und Passwort des Benutzers, in dessen Auftrag sie<br />
handeln. Erkennt der Schutzmonitor die Identität an, veranlasst er den<br />
Betriebssystemkern einen Verweis auf die zugeordnete Schutzdomäne im<br />
Prozesskontrollblock zu speichern; der Prozess kann nun auf die erlaubten<br />
Objekten zugreifen.<br />
¯ Schutzmatrix ist typischerweise sehr groß und dünn besetzt eine direkte<br />
Implementierung ist deshalb nicht sinnvoll. Deshalb ist man dazu übergegangen<br />
die Schutzmatrix entweder spaltenweise oder zeilenweise zu speichern, um<br />
den Speicheraufwand zu reduzieren. Leere Matrixelemente werden nicht<br />
gespeichert.<br />
¯ Zugriffskontrollliste<br />
Zugriffskontrolllisten ("Access Control List", ACL) realisieren die spaltenweise<br />
Speicherung der Schutzmatrix.<br />
jedes Objekt o besitzt seine Zugriffskontrollliste.<br />
Element einer Zugriffskontrollliste (ACL-Element) besteht aus Paar<br />
(Prozess, Zugriffsarten). Anstelle des Prozesses können als Komponenten<br />
in einem ACL-Element auch Schutzdomänen verwendet werden. Die<br />
Verwendung von Schutzdomänen erleichtert die Zusammenfassung von<br />
Benutzern zu einer Gruppe, die die gleichen Zugriffsrechte besitzen. Jedes<br />
ACL-Element repräsentiert ein Kontrollrecht für das Objekt o.<br />
Prozess Benutzer<br />
A<br />
Datei D1 A: RW; B: R<br />
Datei D2 A: R; B: RW; C: R<br />
Datei D3 B: RWX; C: RX<br />
B C<br />
ACL<br />
Benutzer<br />
Modus<br />
System<br />
Modus<br />
Neben den allgemeinen Rechten wie Ö, ÛÖØ und Ü ÙØ können auch<br />
objekt-spezifische Zugriffsarten in die Zugriffskontrollliste des Objektes eingetragen<br />
werden. Für einen Prozess (Subjekt) sind nur diejenigen Zugriffsarten<br />
auf das Objekt erlaubt, die in der zugehörigen ACL eingetragen sind. Manche<br />
Schutzsysteme unterstützen auch Wildcards für die Prozessspezifikation, d.h.<br />
ein ACL-Element (*, R) bedeutet, dass beliebige Prozesse das Leserecht auf<br />
das Objekt besitzen.<br />
232
Schlichter, TU München 8.3. SCHUTZMECHANISMEN<br />
¯ Capability-Liste<br />
Capability-Listen ("Zugriffsausweislisten") realisieren die zeilenweise Speicherung<br />
der Schutzmatrix.<br />
jeder Prozess besitzt eine Menge von Capabilities, die die erlaubten<br />
Zugriffe auf Objekte repräsentieren.<br />
Element einer Capability-Liste besteht aus Paar (Objekt, Zugriffsarten). Ein<br />
Capability gibt dessen Besitzer gewisse Zugriffsrechte für das Objekt.<br />
Prozess Benutzer<br />
A<br />
D1: RW<br />
D2: R<br />
D1: R<br />
D2: RW<br />
D3: RWX<br />
B C<br />
D2: R<br />
D3: RX<br />
Benutzer<br />
Modus<br />
System<br />
Modus<br />
Capability-<br />
Liste<br />
– Capabilities müssen geschützt werden, um Modifikationen durch den Prozess<br />
selbst zu verhindern. Alternativen sind<br />
Speicherung im geschützten Bereich des Betriebssystems.<br />
Capabilities sind zwar im Benutzermodus dem Prozess zugeordnet; sie<br />
sind jedoch verschlüsselt. Der Prozess kann bei einem Objektzugriff<br />
dem Schutzmonitor sein zugehöriges Capability vorzeigen, jedoch dieses<br />
Capability nicht selbst verändern. Eine Änderung darf nur vom<br />
Schutzmonitor vorgenommen werden. Diese Alternative ist besonders<br />
für verteilte Systeme geeignet. Beispielsweise schickt ein Client<br />
zusammen mit seiner Dienstanforderung auch das zugehörige Capability<br />
an den Server. Das Capability muss sicher übertragen werden (z.B.<br />
verschlüsselt), damit ein heimliches Kopieren und missbräuchliche<br />
Nutzung durch andere Prozesse verhindert werden kann.<br />
– Capabilities können zeitlich begrenzt werden. Auch könnte mit einem<br />
Capability ein Kontofeld verbunden sein. Bei jeder Verwendung des<br />
Capability wird das Kontofeld reduziert. Ein Objektzugriff ist nur möglich,<br />
wenn der Kontostand ausreichend ist. Damit kann ein Capability nur für eine<br />
begrenzte Anzahl von Zugriffen beschränkt werden.<br />
233
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Zusammenfassung: Zugriffskontrolllisten und Capability-Listen haben in<br />
gewisser Weise komplementäre Eigenschaften<br />
ACLs erlauben das selektive Zurücknehmen von Rechten. Für Capabilities<br />
ist dies eher schwierig, da die Capabilities verteilt den einzelnen Prozessen<br />
zugeordnet sind, und auch zwischen Prozessen weitergegeben werden<br />
können.<br />
Capabilities können weitergegeben werden. Bei der Weitergabe kann der<br />
Prozess die Zugriffsrechte einschränken.<br />
8.4 Sicherheit in verteilten Systemen<br />
In einem zentralen System beruht der Zugriffsschutz letztendlich auf der Kontrolle<br />
aller Systemkomponenten (Hard-/Software) durch ein einziges, vertrauenswürdiges<br />
Betriebssystem. Die Kontrolle kann aufgrund der Gestaltung der Schnittstelle<br />
durch Benutzer nicht umgangen werden. In einem verteilten System gibt es keine<br />
zentrale Stelle, die alle Subjekte und Objekte kontrollieren kann. Jeder Rechner<br />
hat sein eigenes Betriebssystem, das in Verantwortung des jeweiligen Rechner-<br />
Administrators liegt. Dieser kann die Schutzmechanismen durchbrechen und so<br />
unberechtigten Zugriff zu bestimmten Daten und Ressourcen des Rechnernetzes<br />
erhalten will. Andere Rechner in einem Rechnernetz werden als nicht vertrauenswürdig<br />
eingeschätzt.<br />
8.4.1 Unsicherheitsfaktoren in verteilten Systemen<br />
Durch das Kommunikationsnetz ergeben sich zusätzliche Unsicherheitsfaktoren<br />
in verteilten Systemen.<br />
¯ Anzapfen von Kommunikationspfaden. Diese Gefahr wird insbesondere durch<br />
die offenen Netze erleichtert.<br />
¯ Abhören und Verfälschen von Nachrichten. Dazu gehört Kopieren, Verändern<br />
und Löschen von ganzen bzw. Teilen von Nachrichten sowie das Einfügen von<br />
falschen Teilen in eine Nachricht.<br />
¯ Wiederholen von abgehörten und kopierten Nachrichten ("Replay").<br />
¯ Verändern der Nachrichtenreihenfolge.<br />
¯ Modifizieren der Quell- oder der Zieladresse einer Nachricht.<br />
234
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Rechnername und Netzadressen allein sind nicht vertrauenswürdig. Falls<br />
ein Benutzer komplette Kontrolle über einen Rechner hat, kann er diesen so<br />
modifizieren, dass er sich für einen anderen Rechner ausgibt. Jemand gibt vor<br />
ein bestimmter Client zu sein, oder jemand gibt vor ein bestimmter Server zu<br />
sein. Es ist also die Authentifizierung von Client und Server notwendig.<br />
¯ Einschleusen von Viren.<br />
¯ Lahmlegen von Servern durch eine massive Anzahl von Anforderungen (siehe<br />
Yahoo, Microsoft). Diese "denial-of-service" Attacken können durch die<br />
automatische Generierung von Anforderungen erreicht werden.<br />
8.4.2 Sicherheitsanforderungen<br />
Für den Empfänger einer Nachricht ist nicht nur die Authentizität des Senders<br />
der Nachricht von Bedeutung, sondern auch die Integrität dieser Nachricht<br />
bzw. der Nachrichtenreihenfolge, d.h. auch eine spätere Wiederholung einer<br />
Nachricht kann die Integrität verletzen. Unterscheidung von wesentlichen<br />
Sicherheitsanforderungen:<br />
¯ Authentizität<br />
Nachweis über den Ursprung der Nachricht, Identität des Senders; Authentizität<br />
von Daten muss garantiert sein, d.h. angebliche Quelle von Daten muss<br />
tatsächlicher Quelle entsprechen. Beispiel im Web: stammen bestimmte<br />
Informationen tatsächlich vom gewünschten Anbieter oder hat jemand<br />
gefälschte Daten untergeschoben? Entsprechendes gilt auch für den Ursprung<br />
einer Email.<br />
¯ Vertraulichkeit, Geheimhaltung<br />
vertrauliche Daten auch bei einer Übertragung über ein öffentliches Netz. Es<br />
soll Dritten nicht möglich sein, die Daten auszuspähen. Gegebenenfalls ist<br />
es sogar notwendig, die Tatsache des Kommunikationsaustausches zwischen 2<br />
Partnern zu verschleiern. Die Tatsache der Kommunikation zwischen 2 Firmen<br />
kann bereits eine interessante Information für Mitbewerber sein.<br />
¯ Verbindlichkeit<br />
Beispielsweise muss der Verkäufer dem Käufer nachweisen können, dass dieser<br />
einen verbindlichen Auftrag über das Netz getätigt hat. Verbindlichkeit ist<br />
insbesondere beim elektronischen Handel von Bedeutung.<br />
235
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Integrität<br />
Daten müssen vor nicht autorisiertem Schreiben geschützt werden; Schreiben<br />
bedeutet: einfügen, modifizieren, löschen, etc. Falls die übertragenen Daten bei<br />
der Übertragung manipuliert wurden, sollte dies für den Empfänger aufgrund<br />
der verletzten Integrität der Daten bemerkbar sein.<br />
¯ Verfügbarkeit<br />
Schutz von Systemressourcen vor nicht autorisiertem Zugriff, um Verfügbarkeit<br />
für autorisierte Benutzer garantieren zu können.<br />
Systemressourcen: Prozessoren, Speicher, Kommunikationskanäle, Programme,<br />
etc.<br />
8.4.3 Kryptographie<br />
Die Schutzkonzepte in verteilten Systemen basieren auf der Anwendung von<br />
Verschlüsselungstechniken.<br />
Kryptosystem<br />
Definition: Ein kryptographisches System (Kryptosystem) ist ein Tupel (M, C,<br />
K, E ,D ) mit<br />
k k<br />
einem höchstens abzählbaren Klartextraum M<br />
einem höchstens abzählbaren Chiffretextraum C<br />
einem höchstens abzählbaren Schlüsselraum K<br />
einer Familie von Chiffriertransformationen E : M C mit k aus K<br />
k<br />
(Verschlüsselung)<br />
einer Familie von Dechiffriertransformationen D : C M mit k aus K<br />
k<br />
(Entschlüsselung).<br />
A<br />
Klartext Ciphertext<br />
x<br />
E k<br />
y = E k (x)<br />
D k<br />
Klartext<br />
¯ Es muss gelten<br />
k K, m M:D (E (m)) = m<br />
k k<br />
k ist der Schlüssel, wobei beim public-key Verfahren die Schlüssel zum<br />
Chiffrieren und Dechiffrieren unterschiedlich sind. Zusätzlich gibt es noch<br />
236<br />
x<br />
B
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
Forderungen, wie beispielsweise: es müssen einfach und leicht Schlüssel<br />
aus K gefunden werden können; die Chiffrier-und Dechiffriertransformationen<br />
müssen effizient berechenbar sein. Die Sicherheit des Systems beruht auf der<br />
Geheimhaltung von Schlüssel und nicht des Chiffrier- und Dechiffrieralgorithmus.<br />
¯ Unterscheidung zwischen zwei Klassen von Kryptosystemen:<br />
symmetrische Kryptosysteme (private-key)<br />
asymmetrische Kryptosysteme (public-key)<br />
Bei dem symmetrischen Verfahren haben Sender und Empfänger einen privaten<br />
Schlüssel, während beim asymmetrischen Verfahren eine Unterscheidung<br />
gemacht wird zwischen einem öffentlichen Schlüssel zum Verschlüsseln und<br />
einem privaten Schlüssel zum Entschlüsseln.<br />
Symmetrische Kryptosysteme (private-key)<br />
Absender A<br />
Dokument<br />
(Klartext)<br />
Dokument<br />
(chiffriert)<br />
chiffrieren<br />
mit k priv<br />
Netz<br />
Empfänger E<br />
Dokument<br />
(Klartext)<br />
dechiffrieren<br />
mit k priv<br />
Dokument<br />
(chiffriert)<br />
k priv ist ein privater Schlüssel für die Kommunikation zwischen den beteiligten<br />
Prozessen A und E.<br />
¯ Beispiel: DES (Data Encryption Standard)<br />
DES wurde von IBM entwickelt und 1977 von der amerikanischen Regierung<br />
als offizieller Standard propagiert. DES ging ursprünglich von einer festen<br />
Schlüssellänge von 56 Bit plus 8 Bit Parität aus; in der Zwischenzeit sind<br />
Schlüssel 112 Bit und länger. Verfahren gilt als sicher in dem Sinn, dass<br />
es nur mit sehr großem Aufwand möglich ist, einen Schlüssel zu knacken.<br />
Eine Verdoppelung der Schlüssellänge erhöht den zum Knacken notwendigen<br />
237
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
Aufwand um den Faktor 2 56 . Derzeit spezifiziert eine Standardisierungsgruppe<br />
einen Nachfolger von DES.<br />
¯ Schlüsselverteilung<br />
Problem: Sichere Verteilung der geheimen Schlüssel, d.h. sicherer Austausch<br />
der geheimen Schlüssel zwischen Absender A und Empfänger E. Dazu<br />
wird oft ein anderes Kommunikationsmedium gewählt, z.B. ein persönliches<br />
Treffen, d.h. gesicherter Datenaustausch erfordert gewisse Vorbereitungen, eine<br />
spontane gesicherte Übertragung ist nicht möglich.<br />
– Schlüsselverteilungsserver<br />
Ein Schlüsselverteilungsserver (KDS = key distribution server) generiert für<br />
die beiden Kommunikationspartner A und E einen gemeinsamen privaten<br />
Schlüssel und verteilt ihn chiffriert an A und E.<br />
Kommunikations-<br />
KDS<br />
wunsch A und E<br />
1 2<br />
Ek (k S )<br />
A<br />
3<br />
A<br />
M<br />
KS KS 4 5 6<br />
Verschlüsselungs<br />
einheit<br />
E k (M)<br />
S<br />
E k (k S )<br />
E<br />
Verschlüsselungs<br />
einheit<br />
£ Benutzer A und E haben jeweils mit KDS einen gemeinsamen geheimen<br />
Schlüssel (k A und k E für A bzw. E). Diese geheimen Schlüssel werden<br />
bereits a-priori festgelegt, z.B. durch Passwort.<br />
£ KDS generiert auf Anfragen einen Schlüssel für eine Sitzung. Dieser<br />
Schlüssel dient für die Kommunikation zwischen A und E. Er wird<br />
typischerweise nur für eine Sitzung verwendet. Für verschiedene<br />
Kommunikationssitzungen zwischen A und E werden unterschiedliche<br />
Schlüssel verwendet.<br />
£ KDS verteilt Sitzungsschlüssel chiffriert an Partner (Schritte 2 und 3).<br />
£ Partner kommunizieren über gemeinsamen Schlüssel (Schritt 5).<br />
Asymmetrische Kryptosysteme (public-key)<br />
Realisierung eines gesicherten Datenaustausches mittels eines Schlüsselpaares<br />
(k priv ,k pub )<br />
238<br />
M<br />
B
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
k priv ist der private Schlüssel, k pub ist der öffentliche Schlüssel<br />
was mit einem Schlüssel chiffriert wurde, kann mit dem jeweils anderen<br />
Schlüssel wieder dechiffriert werden.<br />
Vorteil des asymmetrischen Verfahrens ist, dass der öffentliche Schlüssel<br />
allgemein bekannt ist, während nur ein Partner (Empfänger oder Sender)<br />
den privaten Schlüssel kennen muss, d.h. es ist keine Vorbereitung durch<br />
Austausch von geheimen Schlüsseln notwendig. Das Verschlüsselungs- und<br />
Entschlüsselungsverfahren ist bekannt.<br />
Absender A<br />
Dokument<br />
(Klartext)<br />
Dokument<br />
(chiffriert)<br />
chiffrieren<br />
mit k E, pub<br />
Netz<br />
Empfänger E<br />
Dokument<br />
(Klartext)<br />
dechiffrieren<br />
mit k E, priv<br />
Dokument<br />
(chiffriert)<br />
¯ Beispiel: RSA-Verfahren<br />
RSA Verfahren mit Schlüssellänge 200 bits; Verschlüsselung langsam;<br />
deshalb oft Kombination mit symmetrischen Verfahren.<br />
¯ Bestimmung der Schlüssel<br />
Bedingt durch die Fortschritte in der Hardware, sollte das Schlüsselpaar alle<br />
zwei Jahre gewechselt werden und auch die Schlüssellänge vergrößert werden.<br />
– Bestimmung des öffentlichen Schlüssels k pub<br />
1. selektiere 2 positive (p = 7, q =17)<br />
2.<br />
Primzahlen p und q;<br />
x = (p-1) * (q-1); (x = 96)<br />
3. bestimme eine Zahl e<br />
mit e kein Teiler von x;<br />
(e = 5)<br />
4. n=p*q; (n = 119)<br />
5. k = (n, e);<br />
pub<br />
(k = (119, 5))<br />
pub<br />
– Bestimmung des privaten Schlüssels k priv<br />
239
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
6. bestimme d, so dass<br />
mod(d*e, x) = 1;<br />
(d * 5/96 = 1 d = 77)<br />
7. k = (n, d)<br />
priv<br />
(k = (119, 77))<br />
priv<br />
– Berechnung des Chiffretext c aus Klartext m (m = 19), d.h. Klartext wird als<br />
eine Zahl interpretiert; Anwendung des Schlüssels k pub<br />
8. c = mod (m e ,n) (c = mod(19 5 , 119) =<br />
66)<br />
– Berechnung des Klartext m aus Chiffretext c; Anwendung des Schlüssels<br />
k<br />
priv<br />
9. m = mod (c d ,n) (p = mod(66 77 , 119) =<br />
19)<br />
¯ Digitale Unterschrift<br />
Absender A<br />
Dokument<br />
(Klartext)<br />
chiffrieren<br />
mit k E, pub<br />
Dokument<br />
(chiffriert)<br />
hash<br />
Signature<br />
chiffrieren<br />
mit k A, priv<br />
Signature<br />
(chiffriert)<br />
Netz<br />
Dokument<br />
(Klartext)<br />
dechiffrieren<br />
mit k E, priv<br />
Empfänger E<br />
Dokument<br />
(chiffriert)<br />
gleich ?<br />
hash<br />
Signature<br />
dechiffrieren<br />
mit k A, pub<br />
Signature<br />
(chiffriert)<br />
Die Unterschrift ("signature") besteht u.a. aus einem Hashcode zum Dokument<br />
sowie Information über den Absender.<br />
8.4.4 Authentifizierungsdienst Kerberos<br />
Kerberos wurde am MIT als Teil des verteilten Systems Athena entwickelt.<br />
Das Authentifizierungsprotokoll von Kerberos basiert auf dem Protokoll<br />
von Needham und Schröder; siehe auch Kerberos Web-Site (URL:<br />
http://web.mit.edu/kerberos/www/)<br />
240
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
Motivation<br />
Kerberos geht von folgenden Komponenten aus<br />
Client C<br />
Server S<br />
Schlüsselverwaltungszentrum KDC ("key distribution center")<br />
Ticket Server TGS ("ticket granting service")<br />
Zielsetzung von Kerberos<br />
¯ ein Client C möchte einen Dienst des Servers S in Anspruch nehmen, dabei<br />
soll mit Hilfe von KDC und TGS die Geheimhaltungs- und Authentizitätsanforderung<br />
erfüllt werden. Der Server S muss überzeugt davon sein, dass er mit<br />
Client C kommuniziert; umgekehrt muss dasselbe aus der Clientperspektive<br />
gelten.<br />
¯ KDC verwaltet die geheimen Schlüssel der registrierten Komponenten. KDC<br />
muss sehr sicher sein und darf auch räumlich für Eindringlinge nicht zugänglich<br />
sein, d.h. der Rechner des KDC muss in einem gut verschlossenen Raum sein.<br />
¯ TGS stellt einem Client C während einer Sitzung Tickets für Authentifizierungen<br />
mit speziellen Servern des verteilten Systems zur Verfügung.<br />
Sicherheitsobjekte in Kerberos<br />
Kerberos realisiert die Authentifizierung mittels dreier Sicherheitsobjekte<br />
1. TGS-Ticket<br />
ausgestellt von KDC an Client C zur Präsentation beim TGS. Ein TGS-<br />
Ticket sagt aus, dass Client C erst vor kurzem durch Kerberos authentifiziert<br />
wurde. Das TGS-Ticket ist zeitlich beschränkt; es beinhaltet auch einen<br />
Sitzungsschlüssel für die gemeinsame Sitzung zwischen Client und TGS.<br />
2. Authentifizierer<br />
erzeugt durch Client C; dient zum Identitätsnachweis des Client sowie der<br />
zeitlichen Gültigkeit der Kommunikation mit dem Server S. Durch die<br />
zeitliche Beschränkung sollen Replays einer bereits früher stattgefundenen<br />
Kommunikation mit Server S unterbunden werden. Das Authentifizierer Objekt<br />
beinhaltet den Namen von C sowie einen Zeitstempel; beides wird mit dem<br />
Sitzungsschlüssel verschlüsselt.<br />
241
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
3. Sitzungsschlüssel<br />
erzeugt durch Kerberos für die Kommunikation zwischen Client C und<br />
Server S. Der Sitzungsschlüssel ist gültig für eine Sitzung. Er dient<br />
zur Verschlüsselung der Anforderungen und Antworten. Für nachfolgende<br />
Sitzungen muss von Kerberos ein neuer Sitzungsschlüssel erzeugt werden.<br />
Protokollablauf zur Authentifizierung<br />
¯ Nachricht 1<br />
1<br />
request<br />
TGS ticket<br />
KDC<br />
C<br />
TGS<br />
ticket<br />
2<br />
6<br />
Kerberos<br />
request Server 4<br />
ticket<br />
Server ticket<br />
3<br />
Authentifizierer<br />
Authentifizierer<br />
C KDC mit Information C, TGS<br />
Der Client C möchte von TGS für die weitere Kommunikation im verteilten<br />
System ein Ticket bekommen. Der Client C kann hier ein System oder auch ein<br />
Benutzer sein. TGS ist der Service, von dem die Tickets für die gewünschten<br />
Server erhalten werden sollen.<br />
5<br />
TGS<br />
– KDC bestimmt aus seiner Datenbank den geheimen Schlüssel k(C) für<br />
die Kommunikation zwischen Kerberos und C; KDC erzeugt einen guten<br />
zufälligen Sitzungsschlüssel k(C, tgs). Der Schlüssel k(C, tgs) gilt nur<br />
für eine Sitzung von C. Gut bedeutet in diesem Zusammenhang, dass der<br />
Schlüssel nur den beiden Kommunikationspartnern bekannt ist. k(C) ist der<br />
geheime Schlüssel von C, z.B. falls C ein Benutzer ist, dient das Passwort als<br />
geheimer Schlüssel k(C).<br />
242<br />
S
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Nachricht 2<br />
KDC C mit Information (k(C, tgs)) k(C)<br />
(C, TGS, T kdc ,L kdc , k(C, tgs)) k(tgs)<br />
= ticket(C, TGS) k(tgs)<br />
– geheimer Schlüssel k(C) wird aus dem Passwort des Benutzers abgeleitet;<br />
– zweiter Teil der Nachricht wird von C nicht interpretiert, sondern als Ganzes<br />
an TGS übergeben (d.h. TGS-Ticket). Das Ticket ist mit dem geheimen<br />
Schlüssel k(tgs) des TGS verschlüsselt.<br />
T Zeitstempel für Erzeugung des Tickets<br />
kdc<br />
L Lebensdauer des Tickets<br />
kdc<br />
Das Ticket beinhaltet neben dem Namen des Client auch dessen Adresse<br />
im verteilten System. In Kerberos sind als Lebensdauer für ein TGS-Ticket<br />
etwa 8 Stunden vorgesehen. Innerhalb der Gültigkeitsdauer kann C das<br />
Ticket beliebig oft zur Ticketanforderung für spezielle Server an TGS senden.<br />
Falls ein Ticket gestohlen wird, kann es nur für eine begrenzte Zeit genutzt<br />
werden. Ein Ticket ist mit dem geheimen Schlüssel von TGS und Kerberos<br />
verschlüsselt. Die Trennung von KDC und TGS erfolgt deshalb, damit<br />
Nachrichten, die mit einem vom Benutzerpasswort abgeleiteten Schlüssel<br />
verschlüsselt sind, möglichst selten über das Netz übertragen werden. Der<br />
Client C muss das Benutzerpasswort nicht lokal speichern.<br />
¯ Nachricht 3<br />
C TGS mit Information (C, T )<br />
C k(C,tgs)<br />
ticket(C, TGS)<br />
k(tgs)<br />
S<br />
Der 1. Teil der Nachricht dient als Authentifizierer für C. Dieser wird im<br />
Gegensatz zum Ticket für TGS nur einmal benutzt (deshalb Verwendung<br />
von T ). Bei der Festlegung von T muss die Übertragungsverzögerung<br />
C C<br />
mitberücksichtigt werden. S bezeichnet den gewünschten Server; die<br />
Komponente C beinhaltet hier wieder die Adresse von C im verteilten System.<br />
– TGS bestimmt einen zufälligen Sitzungsschlüssel k(C,S), falls<br />
TGS-Ticket noch gültig ist,<br />
243
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Nachricht 4<br />
T noch aktuell ist, und<br />
C<br />
das Feld C übereinstimmt (im 1. Parameter und Ticket).<br />
TGS C mit Information (k(C, S)) k(C, tgs)<br />
(C, S, T tgs , L tgs , k(C, S)) k(S) =<br />
ticket(C, S) k(S)<br />
– Der 2. Teil der Nachricht dient als Ticket von C für Server S.<br />
– k(S) ist der Kerberos bekannte geheime Schlüssel des Server's S. Das Ticket<br />
beinhaltet auch eine Gültigkeitsdauer, die von TGS bestimmt wird. C<br />
speichert Ticket und Sitzungsschlüssel in einem sicheren Speicherbereich.<br />
¯ Nachricht 5<br />
Die Nachrichten 5 und 6 dienen für C und S zur gegenseitigen Authentifizierung<br />
C S mit Information (C, T C ) k(C,S)<br />
ticket(C, S) k(S)<br />
Der 1. Teil der Nachricht dient wieder als Authentifizierer für C. Falls<br />
Überprüfung des Feldes C positiv ist, und das Ticket noch gültig ist, akzeptiert<br />
S den Schlüssel k(C,S) als authentifizierten Sitzungsschlüssel.<br />
¯ Nachricht 6<br />
S C mit Information (T C ) k(C,S)<br />
Die Nachricht dient für C zur Authentifizierung von S. Die weitere<br />
Kommunikation zwischen C und S erfolgt nun über den geheimen, temporären<br />
Sitzungsschlüssel. Man bezeichnet T c oft auch als "nonce".<br />
Problem von Kerberos<br />
Manipulation der lokalen Uhren der Rechner zur Umgehung der Gültigkeitsdauer<br />
von Tickets, d.h. Synchronisation der Uhren im verteilten System muss autorisiert<br />
und authentifiziert durchgeführt werden.<br />
244
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
Beispiel: Benutzerlogin mit Kerberos<br />
1. login Programm der Workstation W sendet Benutzername N an KDC<br />
2. falls Benutzer bekannt, sendet KDC einen Sitzungsschlüssel k(N) verschlüsselt<br />
mit dem Benutzerpasswort sowie ein TGS-Ticket.<br />
3. login Programm fordert Passwort von Benutzer an und entschlüsselt damit<br />
den Sitzungsschlüssel k(N); falls Passwort korrekt, stimmen entschlüsselter<br />
Sitzungsschlüssel k(N) und Sitzungsschlüssel k(N) in TGS-Ticket überein;<br />
4. Passwort kann aus Arbeitsspeicher von W entfernt werden, da für weitere<br />
Kommunikation nur k(N) und TGS-Ticket verwendet werden; beide dienen nun<br />
zur Authentifizierung bei TGS, falls Benutzer einen Server S benötigt.<br />
5. einrichten einer login-Sitzung des Benutzers an Workstation W. Das Passwort<br />
wird nicht über das Netz geschickt, im Gegensatz zur Basisauthentifizierung<br />
im WWW. Das Passwort wird aus dem Arbeitsspeicher sofort entfernt, falls<br />
TGS-Ticket und Sitzungsschlüssel vorhanden sind.<br />
Animation Kerberos<br />
siehe online Version<br />
8.4.5 Mobiler Code<br />
Das Internet führt zunehmend zu einer Verbreitung von mobilem Code. Beispiele<br />
sind<br />
Web Seiten mit Applets<br />
Postscript Dateien<br />
mobile Software-Agenten (z.B. in Ecommerce Anwendungen).<br />
Ausführung von heruntergeladenem Code birgt Risiken in sich. Beispielsweise<br />
kann ein Applet bösartigen Code enthalten, das auf Ressourcen der lokalen Umgebung<br />
zugreift und sie unberechtigterweise modifiziert oder löscht. Methoden (anhand<br />
von Applets), um mit dieser Problematik umzugehen:<br />
Sandboxing<br />
Ausführung des Applets wird auf einen bestimmten virtuellen Adressbereich<br />
beschränkt.<br />
245
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
Für eine Sandbox sind die high-order Bits aller Adressen gleich, d.h.<br />
angenommen für einen 32 Bit Adressraum werden 256 Sandboxes auf 16<br />
MByte Grenzen eingerichtet<br />
für alle Adressen innerhalb einer Sandbox sind die oberen 8 Bits<br />
identisch.<br />
¯ jedes Applet erhält zwei Sandboxes: eine Code-Sandbox, eine Daten-Sandbox.<br />
Die Code-Sandbox darf durch das Applet nicht verändert werden, um die<br />
Gefahr durch einen sich selbst-modifizierenden Code zu eliminieren.<br />
¯ nach dem Laden wird Applet-Code überprüft, ob er Befehle enthält,<br />
die ein Verlassen der Sandbox verursachen. Es werden insbesondere<br />
Sprungbefehle (JMP) und Systemaufrufe (CALL) untersucht. Sprungbefehle,<br />
deren Sprungziele erst zur Laufzeit bestimmt werden, können erst vor deren<br />
Ausführung analysiert werden.<br />
¯ ein Applet, das die Sandbox-Grenzen verletzt, wird zurückgewiesen.<br />
Interpretation<br />
Applet wird als Byte-Code geladen. Jeder Befehl wird vor seiner Ausführung von<br />
der Java Virtual Machine (JVM) analysiert.<br />
¯ jeder Systembefehl wird abgefangen und untersucht. Wie die einzelnen<br />
Systembefehle behandelt werden, hängt von der eingestellten Sicherheits-<br />
Policy ab.<br />
für vertrauenswürdige Applets (z.B. geladen von lokaler Festplatte) werden<br />
Systembefehle ausgeführt.<br />
nicht vertrauenswürdige Applets (z.B. geladen über Internet) werden<br />
innerhalb einer Sandbox ausgeführt.<br />
Signed Code<br />
Es werden nur Applets von vertrauenswürdigen Quellen geladen und ausgeführt.<br />
Applets anderer Quellen werden abgelehnt. Der Applet-Code wird mit<br />
einer digitalen Unterschrift versehen, um zu garantieren, dass der Code der<br />
vertrauenswürdigen Quelle nicht verändert wurde.<br />
¯ digitale Unterschrift basiert auf public-key Verfahren.<br />
246
Schlichter, TU München 8.4. SICHERHEIT IN VERTEILTEN SYSTEMEN<br />
¯ Erzeugung der Unterschrift durch vertrauenswürdige Quelle<br />
Hashfunktion erzeugt von Applet-Code eine 128/160 bit Zahl.<br />
erzeugte Hashzahl wird mit privatem Schlüssel der Quelle verschlüsselt.<br />
digitale Unterschrift wird mit Applet-Code verschickt.<br />
¯ Überprüfung der Unterschrift<br />
Browser führt auf Applet-Code Hashfunktion aus und berechnet selbst<br />
Hashzahl.<br />
Browser entschlüsselt Unterschrift mit öffentlichem Schlüssel der vertauenswürdige<br />
Quelle.<br />
berechnete Hashzahl und Hashzahl in Unterschrift müssen übereinstimmen.<br />
Falls die beiden Hashzahlen nicht übereinstimmen, wird die Ausführung des<br />
Applets abgelehnt.<br />
247
Kapitel 9<br />
Zusammenfassung<br />
Diese Vorlesung beschäftigte sich mit den technischen Aspekten von Rechensystemen.<br />
Es gab eine Einführung in systemnahe <strong>Programmierung</strong>. Dabei wurden<br />
sowohl nicht verteilte als auch verteilte Systeme betrachtet. Insbesondere wurden<br />
folgende Aspekte behandelt:<br />
Aufbau von Assemblerprogrammen und hardwarenahen Datenstrukturen.<br />
Modellierung von Prozessen, z.B. Petrinetze, sowie die Synchronisation von<br />
Prozessen beim Zugriff auf gemeinsame Ressourcen.<br />
Verwaltung von Prozessen und deren Zuteilung an den Rechnerkern, um sie<br />
auszuführen.<br />
Verwaltung des Arbeitsspeichers aus der Sicht des Betriebssystems (virtuelle<br />
Speicherverwaltung, Seitenadressierung).<br />
persistente Speicherung von Information in Dateien.<br />
Prozesskommunikation in lokalen und verteilten Systemen.<br />
Sicherheit in zentralen und verteilten Systemen.<br />
248