Die Reihe Xpert.press vermittelt Professionals in den Bereichen Softwareentwicklung, Internettechnologie und IT-Management aktuell und kompetent relevantes Fachwissen über Technologien und Produkte zur Entwicklung und Anwendung moderner Informationstechnologien.
Rüdiger Brause
Kompendium der Informationstechnologie Hardware, Software, Client-Server-Systeme, Netzwerke, Datenbanken
Mit 143 Abbildungen
123
Rüdiger Brause Institut für Informatik Universität Frankfurt Robert-Mayer-Str. 11-15 60054 Frankfurt
[email protected]
Bibliografische Information der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.
ISSN 1439-5428 ISBN 3-540-20911-5 Springer Berlin Heidelberg New York
Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere d ie der Übersetzung, des Nachdr ucks , des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen bleiben, auch bei nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes. Springer ist ein Unternehmen von Springer Science+Business Media springer.de © Springer-Verlag Berlin Heidelberg 2005 Printed in Germany Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutzgesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften. Text und Abbildungen wurden mit größter Sorgfalt erarbeitet. Verlag und Autor können jedoch für eventuell verbliebene fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Satz: Druckfertige Daten des Autors Herstellung: LE-TeX Jelonek, Schmidt & Vöckler GbR, Leipzig Umschlaggestaltung: KünkelLopka Werbeagentur, Heidelberg Gedruckt auf säurefreiem Papier 33/3142/YL - 5 4 3 2 1 0
Vorwort
Die Computer als universelle Maschinen haben unser modernes Leben nicht nur durchdrungen, sie verändern durch ihre neuen Möglichkeiten auch unsere Art der Kommunikation und damit unsere Arbeitsweise. Vieles von dem, was als „neues Management“ und „Prozessdenken“ in unser Bewusstsein dringt, stammt aus der Gedankenwelt der Informatik. Es ist deshalb wichtig und nützlich auch für NichtTechniker, etwas näher in die Methoden und Begriffe der Informationstechnik einzudringen. Dabei stehen wir in einem Dilemma: Werden nur die gerade aktuellen Schlagworte und modischen Trends präsentiert, so entsteht leicht das Gefühl, „alles verstanden“ zu haben, ohne aber tatsächlich die Grundlagen zu kennen. Die neuen Entwicklungen von morgen sind damit unverständlich. Werden dagegen nur die Grundlagen präsentiert, so werden zwar die Fundamente gelegt, aber das Haus darüber ist nur im Rohbau. Die Abwägung zwischen beiden Wegen bleibt umstritten. Bei diesem Buch habe ich mich für den zweiten Weg entschieden in der Hoffnung, dass mit der Kenntnis der Grundlagen alle neuen Fassaden und Tapeten der Vertriebsfirmen einfacher als solche erkannt und vom kundigen Leser von den belastbaren, wirklich neuen Mauern unterschieden werden können. Dabei möchte ich allen Kollegen vom Institut für Informatik danken, die mich mit Material für dieses Buch unterstützt haben. Ich hoffe, so alle grundsätzlich wichtigen Aspekte der Informationstechnologie berücksichtigt und sie verständlich präsentiert zu haben.
Frankfurt, im August 2004
Rüdiger Brause
Inhaltsverzeichnis
1 Rechnerarchitektur ....................................................................................... 1 1.1 Rechneraufbau: die Hardware ......................................................................... 1 1.1.1 Die Eingabe......................................................................................... 2 1.1.1 Die Ausgabe: Rastergrafik und Skalierung ......................................... 6 1.2 Die Architektur eines Rechners ....................................................................... 8 1.2.1 Busse ................................................................................................... 8 1.2.2 Buskontrolle ...................................................................................... 10 1.2.3 PC-Architektur .................................................................................. 12 1.3 Maschinensprache und Prozessorstruktur...................................................... 13 1.3.1 Ein einfaches Befehlsmodell ............................................................. 13 1.3.2 Eine Prozessorgrundstruktur ............................................................. 15 1.4 Prozessoren mit komplexem Befehlssatz (CISC) .......................................... 16 1.4.1 Charakteristik von CISC ................................................................... 18 1.4.2 Prozessoren mit reduziertem Befehlssatz (RISC).............................. 19 1.5 Rechnerbetrieb: Die Software........................................................................ 21 1.5.1 Das Betriebssystem ........................................................................... 24 1.1.2 Beispiel UNIX................................................................................... 26 1.1.3 Beispiel Windows NT ....................................................................... 28 1.5.2 Schnittstellen und virtuelle Maschinen.............................................. 30 1.5.3 Software-Hardware-Migration .......................................................... 33 1.6 Ein- und Ausgabegeräte................................................................................. 34 1.6.1 Beispiel UNIX: I/O-Verarbeitungsschichten..................................... 36 1.6.2 Beispiel Windows NT: I/O-Verarbeitungsschichten ......................... 38 1.6.3 Der Zugriff auf Ein- und Ausgabe..................................................... 39 1.6.4 Wahlfreier Zugriff: Plattenspeicher................................................... 41 1.6.5 Serielle Geräte................................................................................... 44 1.6.6 Multiple Plattenspeicher: RAIDs ...................................................... 45 1.6.7 Interleaving ....................................................................................... 51 1.6.8 Pufferung........................................................................................... 53 1.6.9 Synchrone und asynchrone Ein- und Ausgabe .................................. 55 1.7 Die Energieverwaltung .................................................................................. 55
VIII
Inhaltsverzeichnis
2 Netzwerkarchitektur ................................................................................... 59 2.1 Das Schichtenmodell für Netzwerkprotokolle ............................................... 61 2.1.1 Beispiel UNIX Kommunikationsschichten ..................................... 64 2.1.2 Beispiel Windows NT Kommunikationsschichten .......................... 65 2.1.3 Erweiterungen ................................................................................... 66 2.2 Namensgebung im Netz................................................................................. 67 2.2.1 Namen im weltweiten Netz ............................................................... 68 2.2.2 Namen im regionalen Netz................................................................ 70 2.2.3 Namen im lokalen Netz ..................................................................... 71 2.3 Kommunikationsanschlüsse........................................................................... 74 2.3.1 Ports .................................................................................................. 74 2.3.2 Sockets .............................................................................................. 76 2.3.3 Named Pipes ..................................................................................... 77 2.3.4 Mailboxdienste.................................................................................. 78 2.3.5 Remote Procedure Calls .................................................................... 79 2.4 Telefonieren über Internet: Voice over IP ..................................................... 83 2.4.1 Technische Konzepte von VoiceOverIP ........................................... 84 2.4.2 Nutzungskonzepte von VoiceOverIP ................................................ 87 2.4.3 Sicherheitsaspekte ............................................................................. 89 3 Internet Architekturen, Web-Services und Sicherheit ............................. 91 3.1 Funktionsarchitekturen .................................................................................. 91 3.1.1 Client-Server Systeme ....................................................................... 91 3.1.2 Verteilte Betriebssysteme.................................................................. 92 3.2 Dateisysteme im Netz .................................................................................... 94 3.2.1 Zugriffssemantik................................................................................ 94 3.2.2 Zustandsbehaftete und zustandslose Server....................................... 96 3.2.3 Die Cacheproblematik....................................................................... 97 3.2.4 Implementationskonzepte................................................................ 100 3.2.5 Sicherheitskonzepte......................................................................... 103 3.3 Massenspeicher im Netz .............................................................................. 105 3.4 Arbeitsmodelle im Netz ............................................................................... 108 3.4.1 Jobmanagement............................................................................... 108 3.4.2 Netzcomputer .................................................................................. 109 3.4.3 Schattenserver ................................................................................. 112 3.5 Standard-Dienste im Netz............................................................................ 116 3.6 Middleware.................................................................................................. 118 3.6.1 Transparenz und IT-Konsolidierung durch Middleware ................. 119 3.6.2 Vermittelnde Dienste....................................................................... 120 3.6.3 Universal Plug-and-Play.................................................................. 123
Inhaltsverzeichnis
IX
3.7 Sicherheitsmechanismen und Konzepte im Netz ......................................... 123 3.7.1 Vorgeschichte.................................................................................. 124 3.7.2 Eindringen über das Netz ................................................................ 124 3.7.3 Übernahme der Kontrolle auf einem Rechner ................................. 128 3.7.4 Fire-wall-Konfigurationen............................................................... 135 3.7.5 Zugriffslisten und Fähigkeiten......................................................... 136 3.7.6 Die Kerberos-Authentifizierung ...................................................... 137 4 4.1 4.2 4.3 4.4 4.5 5 5.1 5.2 5.3
Datenbanksysteme ..................................................................................... 143 Wozu Datenbanken?.................................................................................... 144 Übersicht Datenbanksysteme....................................................................... 146 Datenabstraktion (Data Abstraction) ........................................................... 148 Die Architektur eines DBMS....................................................................... 149 Data Warehouse........................................................................................... 152
Programmiersprachen und Paradigmen ................................................. 155 Natürliche Sprachen .................................................................................... 157 Imperative Sprachen .................................................................................... 159 Objektorientierte Sprachen .......................................................................... 161 5.3.1 Das Kapseln von Programmteilen ................................................... 161 5.3.2 Objekte und Vererbung ................................................................... 163 5.3.3 Die Philosophie objektorientierter Klassen ..................................... 165 5.3.4 Objektorientierte Programmentwicklung ........................................ 166 5.4 Funktionale Sprachen .................................................................................. 171 5.4.1 Nebenwirkungsfreie Funktionen ..................................................... 171 5.4.2 Sprachkonstrukte............................................................................. 174 5.4.3 Sprachenübersicht ........................................................................... 176 5.5 Datenflusssprachen ...................................................................................... 179 5.5.1 Das Kontrollflussprinzip ................................................................. 179 5.5.2 Das Datenflussprinzip ..................................................................... 180 5.5.3 Datenflussrechner und Datenflusssprachen ..................................... 181 5.6 Logische Sprachen....................................................................................... 186 5.6.1 Aussagenlogik ................................................................................. 186 5.6.2 Hornformeln.................................................................................... 188 5.6.3 Klauseln .......................................................................................... 189 5.6.4 Resolution ....................................................................................... 190 5.6.5 Prädikatenlogik ............................................................................... 191 5.6.6 Beispiel: Affe-Banane-Problem ...................................................... 192 5.6.7 Logikprogramme ............................................................................. 194
X
Inhaltsverzeichnis
5.7 Visuelle Sprachen ........................................................................................ 195 5.7.1 Visualisierung von Daten ................................................................ 196 5.7.2 Visualisierung von Programmen ..................................................... 196 5.7.3 Visuelle Spezifikation ..................................................................... 197 5.7.4 Visuelle Programmierung................................................................ 198 5.7.5 Visuelles Training ........................................................................... 201 5.7.6 Diskussion ....................................................................................... 202 5.8 Grundbegriffe zur Übersetzung von Programmiersprachen........................ 204 6 Softwareentwicklung ................................................................................. 207 6.1 Das klassische Phasenmodell....................................................................... 207 6.1.1 Der Zeitplan .................................................................................... 209 6.1.2 Phasen und Netzplantechnik............................................................ 210 6.1.3 Randbedingungen............................................................................ 213 6.1.4 Das Pflichtenheft ............................................................................. 214 6.1.5 Der Programmentwurf..................................................................... 215 6.1.6 Der Systemtest................................................................................. 222 6.1.7 Die Einführung der Software........................................................... 224 6.1.8 Dokumentation ................................................................................ 225 6.1.9 Qualitätsmanagement ...................................................................... 226 6.1.10 Der menschliche Faktor................................................................... 229 6.2 Modifizierte Phasenmodelle ........................................................................ 231 6.2.1 Das Wasserfallmodell ..................................................................... 231 6.2.2 Objektorientierte Entwicklung ........................................................ 232 6.2.3 Rapid Prototyping und Spiralenmodell ........................................... 234 6.3 Das V-Modell .............................................................................................. 237 6.3.1 Die drei Schichten der Standardisierung ......................................... 238 6.3.2 Anwendung des V-Modells ............................................................. 239 6.3.3 Vorteile des V-Modells ................................................................... 240 6.4 Selbstorganisierende Projektentwicklung .................................................... 242 6.4.1 Gruppenstrukturen........................................................................... 242 6.4.2 Projektentwicklung im Plenum........................................................ 244 6.5 CASE Werkzeuge zur Projektentwicklung.................................................. 246 6.5.1 CASE Grundtechniken .................................................................... 247 6.5.2 CASE Architektur ........................................................................... 248 6.6 Risikomanagement....................................................................................... 250 Abbildungsverzeichnis ...................................................................................... 253 Index ................................................................................................................... 261
1 Rechnerarchitektur
In den folgenden Abschnitten sollen zunächst die Grundlagen von Rechnern kurz skizziert und die Einzelkomponenten näher vorgestellt werden. Dabei soll versucht werden, von deutschen Bezeichnungen ausgehend die englischen Begriffe einzuführen.
1.1
Rechneraufbau: die Hardware
Das Grundprinzip eines Rechners hat sich seit seiner Erfindung und Entwicklung in den dreißiger Jahren des vorigen Jahrhunderts fast kaum verändert. Auch die miniaturisierte Variante, die als „Personal Computer PC“ Mitte der achtziger Jahre populär wurde und damit den Siegeszug in die Arbeitswelt angetreten hatte, wird trotz interner Feinheiten immer noch von dem gleichen Schema beherrscht. Massenspeicher Monitor Editor und immer wieder gibt es einen Terminal home>
Tastatur
Maus
Eingabe
Drucker
Rechner
Ausgabe
Abb. 1.1 Gliederung einer Computeranlage
Dieses Schema besteht aus Eingabegeräten, mit denen die Daten in den Rechner transportiert werden, aus Ausgabegeräten, mit denen die Ergebnisse angezeigt
2
Rechnerarchitektur
werden können, und dem eigentlichen Rechner, dessen Speicher um einen Massenspeicher vergrößert wird. Betrachten wir dies genauer. 1.1.1 Die Eingabe Die klassische Art und Weise, Texte und Befehle in einen Computer zu transferieren, ist die Tastatur. Üblicherweise existieren neben den Tasten für die reinen Buchstaben des Alphabets und den Tasten für Zahlen (Zehnertastatur) zusätzliche Tasten auf der Tastatur, die spezielle Kontrollfunktionen ausüben. Neben den Tasten Ç, È, Æ, Å zum Verschieben des Aufmerksamkeitspunktes (Eingabemarkierung, cursor) auf dem Bildschirm gibt es noch Tasten für das Umschalten von Klein- auf Großbuchstaben (SHIFT), von einem Buchstabensatz auf einen anderen (ALTERNATE) und von Buchstabeneingabe auf Befehle (CONTROL). Die Gesamtmenge der so erzeugten Tastenkombinationen (symbolische Eingaben) wird nun von einer unter der Tastatur befindlichen Elektronik (Tastaturprozessor) auf einen internen Zahlencode abgebildet, der im Rechner vom Betriebssystem in den offiziellen, international genormten Zahlencode für die Buchstaben umgesetzt wird. Bedingt durch die Herkunft der ersten Computer ist dies das amerikanische Alphabet, standardisiert in 128 Zeichen. Jedes Zeichen wird durch 7 Binärzeichen (Bits) aus einer Folge von „1“ und „0“ beschrieben gemäß dem American Standard Code for Information Interchange ASCII. In Abb. 1.2 sind zur Verdeutlichung einige Codes aufgeführt. Dabei ist der Binärcode, der Hexcode und der Dezimalwert der Codezahl neben dem eigentlichen Zeichen eingetragen. Man beachte, dass nur durch ein zusätzliches Bit (was einer Addition von 25 = 32 zur Codezahl entspricht) aus einem großen A ein kleines wird. Ähnliches geschieht beim Halten der Control-Taste: hier wird ein Bit unterdrückt, so dass aus dem Code für S ein Control-S (DC3)-Zeichen wird.
Binärcode
Hexcode Dezimalcode
Zeichen
0001 0010 0000 0001 0001 0010 0001 0010
11 13 30 31 41 42 61 62
17 19 48 49 65 66 97 98
Ctrl-Q (DC1) Ctrl-S (DC3) 0 1 A B a b
1010 0001 1010 0010
A1 A2
161 162
í ó
0001 0001 0011 0011 0100 0100 0110 0110
Abb. 1.2 Einige ASCII-Buchstabenkodierungen
Rechneraufbau: die Hardware
3
Der vollständige Code ist in Abb. 1.3 zu sehen.
0 0X 1X 2X 3X 4X 5X 6X
E
F
NUL SOH STX ETX EOT ENQ ACK BEL BS HT NL VT NP CR SO
1
2
3
4
5
6
7
8
9
A
B
C
D
SI
DLE DC1 DC2 DC3 DC4 NAKSYN ETB CAN EM SUB ESC FS GS RS US
SP 0 @ P `
7X p
! 1 A Q a
„ 2 B R b
# 3 C S c
$ % & ‘ ( ) 4 5 6 7 8 9 D E F G H I T U V W X Y d e f g h i
* : J Z j
+ ; K [ k
, - . / < = > ? L M N O \ ] ^ _ l m n o
q
r
s
t
z
{
|
u
v
w
x
y
}
~
DEL
Abb. 1.3 Die ASCII-Buchstabenkodierung
Außen an der Tabelle ist der Zahlencode in hexadezimaler Schreibweise notiert, bei der für 4 Bits jeweils eine von 16 Zahlen aus {0,1,...,8,9,A,B,C,D,E,F} („Hexadezimalcode“) stehen. Der Wert der ersten vier binären Ziffern (Bits) bzw. ersten Hex-Zahl ist am linken Rand vertikal eingetragen, der der zweiten vier binären Ziffern horizontal am oberen Rand. Die beiden ersten Zeilen der Tabelle enthalten nur Kontrollzeichen wie Eingabeende EOT, Horizontaltabulator HT, Zeilenrücklauf CR usw. Nun besteht die Welt nicht nur aus Nordamerika, so dass sich bald die Notwendigkeit ergab, für eine verständliche Ausgabe auch europäische Zeichen wie ü, ä, æ und ê in den bestehenden Code aufzunehmen. Dies führte zu einem 8-Bit-ANSI-Code, der von der ISO (International Standards Organization) genormt wurde, beispielsweise Latin-1 (ISO 8859-1) für Westeuropa und Latin-2 für Osteuropa. Er besteht aus dem 7-Bit-ASCII-Code mit 128 zusätzlichen Zeichen, beispielsweise in Abb. 1.2 in den unteren, zusätzlichen Zeilen für die Zeichen mit dem Hexcode A1 und A2 notiert. In der Tabelle in Abb. 1.3 würden diese zusätzlichen 128 Zeichen durch 8 zusätzliche Zeilen beginnend mit 8X,9X,...,FX notiert werden. Allerdings hört die Welt auch nicht bei Europa auf, so dass die Notwendigkeit, verschiedene Softwarepakete wie Texteditoren auch für den arabischen, chinesischen und indischen Markt zu schreiben, bald dazu führte, die Zeichenkodierung auf mehr als 8 Bit auszudehnen. Ein wichtiger Versuch in diese Richtung ist die Entwicklung eines „universellen“ Codes, des Unicodes, in dem alle Schriftzeichen der Weltsprachen enthalten sind (UNI 1997). Zusätzlicher, freier Platz in der Codetabelle garantiert ihre Erweiterbarkeit. In Abb. 1.4 ist die Auslegung des Unicodes, beginnend mit dem 16-Bit-Code 0000H und endend mit FFFFH, gezeigt. Man kann erkennen, dass der Unicode als Erweiterung des ASCII-Codes konzipiert
4
Rechnerarchitektur
0000H
FFFFH Abb. 1.4 Auslegung des Unicodes
wurde, um Verträglichkeit mit den bestehenden Standardsystemen zu ermöglichen. Obwohl es sehr viele Schriftarten (Fonts) gibt, kann man beim Unicode mit einer 16-Bit-Kodierung auskommen, da die Information, welches Zeichen verwendet wird, streng von der Information getrennt wird, wie es verwendet wird. Alle Formatierungsinformationen (Schriftart, Darstellungsart fett/ schräg usw.) werden vom Zeichencode getrennt verwaltet und sind typisch für den jeweiligen Texteditor. Der Unicode ist genormt (ISO 10646) und wird ständig weiterentwickelt. Ein weiterer wichtiger Code ist der 32-Bit-(4-Byte)-Extended-UNIX-Code EUC, der eine Multibyte-Erweiterung des UNIX(POSIX)-US-ASCII-Zeichensatzes darstellt. Asiatische und andere komplexe Schriftzeichen werden als Folge mehrerer ASCII-Buchstaben auf der normalen Tastatur eingegeben und als landesspezifischer EUC abgespeichert. Beispiel UNIX Eingabecode Seit dem Beginn wird in UNIX bzw. POSIX ausschließlich der US-ASCIIZeichensatz verwendet. Erst in neueren Versionen wird auf nationale Besonderheiten eingegangen. Beispielsweise installiert die Firma Hewlett-Packard eine spezielle Erweiterung, das Native Language Support NLS. Es besteht zum einen aus einer Reihe von Umgebungsvariablen (LANG, LC_XX), die in der Prozessumgebung eines Benutzers, wenn sie korrekt gesetzt werden, den lokalen Teil der Bibliotheksroutinen und sprachspezifischen Kommandos (ed, grep usw.) des Betriebssystems steuern. Hierzu gehören nicht nur die sprachspezifischen Besonderheiten wie die richtige Verwendung von Multibyte-Codes bei
Rechneraufbau: die Hardware
5
String-Vergleichen (z. B. ss = ß im Deutschen, LLL = LL im Spanischen) oder die Identifizierung von Buchstabencodes als Großbuchstaben, Zahlen, Kontrollzeichen usw., sondern auch die korrekte, landesübliche Anwendung von Punkt und Komma bei numerischen Ausdrücken, (z.B. 1.568,74 und dem Datumsformat wie 24.12.2004) sowie die Bezeichnung der Landeswährung. Beispiel Windows NT Eingabecode Im Gegensatz zu UNIX wurde in Windows NT bereits beim Design ein multinationaler Zeichencode für das Betriebssystem festgelegt, der Unicode. Dies bedeutet, dass alle Zeichenketten, Objektnamen, Pfade usw. nur UnicodeZeichen verwenden. Es sind also auch chinesische Dateinamen in indischen Pfaden auf deutschen Computern möglich! Die landestypischen Gegebenheiten wie Zeitzone, Währungsnotation, Sprache usw. wird unabhängig davon bei der Einrichtung des Systems festgelegt und zentral abgespeichert. Neben der Tastatur für die Eingabe von Buchstaben gibt es heutzutage noch weitere, menschengerechtere Eingabemöglichkeiten. x Funktionstasten Eine einfache Erweiterung bietet die Möglichkeit, Tasten für besondere Funktionen bereitzustellen. Dies ist ein konzeptionell wichtiger Schritt, da hiermit neben der Eingabe alphanumerischer Daten auch das Ansprechen von Funktionen ermöglicht wird. Es ist deshalb logisch wichtig, die konzeptionell getrennten Daten und Funktionen im Programm auch beim Eingabemedium getrennt zu halten, um Verwechslungen zu vermeiden. Nur eine schlechte Benutzeroberfläche vermischt beide Funktionen, etwa das Anhalten der Ausgabe mit CTRL S oder, noch schlimmer, die Steuerung von Programmen durch einzelne Buchstabentasten. Beispielsweise kann eine harmlose Texteingabe DEF aus Versehen im Kommandomodus eingegeben als Abkürzung für „Delete“+“Erase“+“Format“ verheerende Auswirkungen haben. x Zeigegeräte Einen wichtigen Schritt zur analogen Eingabe bedeutete die Einführung von Zeigegeräten wie „Maus“ oder „Trackball“. Hier kann die Position eines Zeigers auf dem Bildschirm leichter kontrolliert werden als mit speziellen Funktionstasten (Cursortasten). x Grafische Tabletts Eine große Hilfe bedeutet auch die direkte Übermittlung der Position eines Stifts auf einem speziellen elektronischen Eingabebrett. Obwohl seine prinzipielle Funktion die eines Zeigegeräts ist, kann man damit auch Formen direkt in den Computer übertragen, z. B. Daten von biomedizinischen oder architektonischen Vorlagen, Funktionen und Kurven, oder es für Unterschriften zur Scheckverifikation verwenden.
6
Rechnerarchitektur
x Scanner Das grafische Tablett ist allerdings in den letzten Jahren bei den meisten Anwendungen durch hochauflösende, optoelektronische Abtastgeräte, die Scanner, ersetzt worden. Zusammen mit der Tendenz, Ergebnisse nicht mehr auf Papier zu speichern, sondern elektronisch und damit auch direkt verarbeitbar zu machen, haben sie sich auf diesem Gebiet durchgesetzt. x Spracheingabe Eine der benutzerfreundlichsten Eingabeschnittstellen ist zweifelsohne die Spracheingabe. Obwohl die benutzerabhängige Spracherkennung einzelner Worte in leiser Umgebung gut gelingt, lassen die Systeme für eine sprecherunabhängige, störfeste Spracherkennung allerdings noch viel zu wünschen übrig. Auch ihr Nutzen ist sehr umstritten: Zwar ist die Spracheingabe bei allen Menschen, die bei der Rechnerbedienung die Hände zur eigentlichen Arbeit frei haben müssen (z.B. Mikrochirurgen bei der Mikroskopsteuerung etc.) sehr beliebt, doch findet sie bei Büroangestellten mäßige Resonanz, da die Sprache zum einen nicht unbedingt genauer ist als ein Zeigeinstrument, und zum anderen dauerndes Reden beim Arbeiten störend sein kann. 1.1.1 Die Ausgabe: Rastergrafik und Skalierung Neben der Standardausgabe von Texten auf Druckern wird für anspruchsvolle, grafisch ausgefeilte Texte und Grafiken oft ein spezielles Modell benutzt: das Modell einer Rastergrafik, das aus gleichmäßig in einem Raster angeordneten Bildpunkten (Pixeln) besteht. Es benutzt ein Koordinatensystem für Zeichnungen, wie es bei einem Bildschirmraster entsteht. Ausgangspunkt (0,0) der (x,y) Koordinate ist dabei die linke obere Ecke, wobei die Indizes der Bildpunkte (Pixel) ähnlich der einer transponierten Matrix angeordnet sind. Für einen Bildschirm mit 1024×768 Punkten ist dies in Abb. 1.5 zu sehen.
(0, 0) (0, 1) ... (0, 767)
. . . (1023, 0) . . . (1023, 1) ... ... . . . (1023, 767)
Abb. 1.5 Die Pixelkoordinaten der Rastergrafik
Das Rastergrafikmodell weist im Unterschied zu einer aus Linien bestehenden Grafik (Vektorgrafik), bei der die nur die Koordinaten (also z. B. als Anfangsund Endpunkte der Linien) gespeichert werden, jedem Punkt einen definierten Farbwert zu, der als Zahl gespeichert wird. Der Bildwiederholspeicher der Raster-
Rechneraufbau: die Hardware
7
grafik muss also, anders als bei der Vektorgrafik, für jeden Bildpunkt (Koordinate) des gesamten Bildschirms immer eine Speichereinheit vorsehen. Aus diesem Grund bevorzugte man früher Vektorgrafik, die sehr wenig Speicherplatz benötigt. Allerdings dauert das Neuzeichnen (refresh) komplizierter Grafiken durch das Neuzeichnen aller Grafikelemente in der Liste bei Vektorgrafiken sehr lange, was bei älteren Bildschirmen (phosphorbeschichtete Scheibe!) und komplexen Vektorgrafiken zu Flackern führte. Die moderne Technologie der Rasterbildschirme (Fernseh- oder Flüssigkeitskristallbildschirme mit Bildwiederholspeicher) ist stark mit dem Programmiermodell der Bildschirme verbunden und damit in die Konzeption der Grafiksoftware eingeflossen. Jeder Bildpunkt wird in seiner Farbe durch einen Farbwert (eine Zahl) aus n Bits (bn-1,...,b1,b0) beschrieben. Vielfach können die Bits mit gleichem Index vom Displaycontroller hardwaremäßig extra zu einem Bild zusammengefasst werden. Ein solches Bild wird auch als Ebene bezeichnet. Allerdings hat das Denken in Pixelflächen einen entscheidenden Nachteil: Die Auswirkungen der Funktionen sind sehr von der tatsächlich verfügbaren Hardware (Bildschirmauflösung etc.) abhängig. Beispielsweise können Vierecke und Fenster der aktuellen Bildschirmgröße leicht angepasst werden, nicht aber Schriften, deren Zeichen als feste Blöcke von Pixeln (pixmaps) definiert werden. Mischen wir nun Grafik (z. B. Fenster) und Text (z. B. Bezeichnungen), so ist die Lage und Größe des Textes in der Grafik von der Bildschirmauflösung abhängig, was zu sehr unschönen Effekten führen kann und eine hardwareunabhängige, auf viele Rechnerarten übertragbare Programme sehr erschwert. Eine wichtige Alternative zu pixelorientierten Schriften sind deshalb die skalierbaren Schriften. Bei Ihnen wird ein Buchstabe nicht durch seine Bildpunkte (Pixel) sondern durch den Umriss und die Farbe bzw. Struktur (Textur) der darin enthaltenen Fläche charakterisiert. Dies ist zwar zunächst umständlich und ineffizienter bei der Speicherung und Darstellung, aber die Umrissbeschreibung lässt sich im Unterschied zu den Pixelfeldern beliebig vergrößern und verkleinern (Beispiel: PostScript, TrueType-Schriften). In Abb. 1.6 ist links eine pixelorientierte und rechts ein skalierbare Beschreibung des Buchstabens „A“ gezeigt. Die tatsächlich zu sehenden Bildpunkte sind jeweils grau schraffiert.
(a)
(b)
Abb. 1.6 Ein Buchstabe (a) im 5×7-Raster und (b) als Umrandung
8
Rechnerarchitektur
Eine solche Beschreibungsart aus Umriss und Textur ist zwar aufwendiger, aber sie lässt sich für alle wichtigen grafischen Elemente der Benutzeroberfläche (Balken, Kreise, Ikone usw.) einheitlich vorsehen und damit sehr effizient in den Grafikroutinen des Betriebssystems verankern sowie leicht Spezialprozessoren übertragen.
1.2
Die Architektur eines Rechners
Der Kasten „Rechner“ in Abb. 1.1 ist sehr allgemein. Möchten wir besser verstehen, wie ein Programm durch den Rechner ausgeführt wird, so müssen wir ihn in einzelne Funktionseinheiten aufgliedern. In Abb. 1.7 ist eine solche Gliederung vorgenommen. Unser einfaches Rechnermodell besteht nur aus einem Speicher, der in einzelnen Speichereinheiten die Befehle des Programms enthält, und einem Prozessor (Central Processing Unit CPU), der sie ausführt. Die Speichereinheiten können dadurch einzeln angesprochen (ausgelesen und gefüllt) werden, dass jeder Einheit eine eigene Zahl (Adresse) zugeordnet ist, ähnlich einer Hausnummer. Dabei haben wir aber verschiedene Implementierungsdetails vernachlässigt, die wichtig für das Verständnis der Programmiermöglichkeiten sind.
Adresse 0 2 4 } 100 102 103 }
Befehl 1 Befehl 2 Befehl 3 } Daten 1 Daten 2 Daten 3 } Speicher
Prozessor CPU Ein/Ausgabe
Rechnerkern
Abb. 1.7 Grobe funktionelle Aufgliederung der Recheneinheit
1.2.1 Busse Die Adressen zum Ansprechen der Speichereinheiten werden über diskrete Leitungen zu der Speicherelektronik geschickt, ebenso die Daten und die Ein- und Ausgabe zu den Peripheriegeräten. Dies führte bei den ersten Rechnern zu einem ziemlichen Kabelwirrwarr zwischen den verschiedenen Einheiten. Die Abb. 1.8 gibt einen Eindruck davon.
Die Architektur eines Rechners
EinAusgabe 1
Speicher 1 RAM
Speicher 2 RAM
9
Prozessor CPU
Interrupt controller
EinAusgabe 2 EinAusgabe 3
Speicher 2 RAM
Abb. 1.8 Direkte Verbundarchitektur eines Rechners
Um die Anzahl der benötigten Leitungen und Stecker (und damit die Anzahl der Störungen!) zu verringern, fasste man nun alle Leitungen der gleichen Art zu einem gemeinsamen Transportweg (Bus) zusammen, an den alle Einheiten ohne Unterscheidung angeschlossen wurden, s. Abb. 1.9. Damit wurde es möglich, nicht nur den Speicher über den Adressbus anzusprechen, sondern auch die Ein- und Ausgabegeräte (Peripheriegeräte) fühlen sich angesprochen, wenn eine nur ihnen zugeordnete Adresse auf dem Adressbus erscheint. Dies wird als memorymapped input-output bezeichnet und ist ein wichtiger Schritt zur Vereinheitlichung der Programmierung.
Speicher RAM
Prozessor CPU
Interrupt controller
Adress-
Bus
EinAusgabe I/O
bus
Datenbus Buskontrolle
Abb. 1.9 Busarchitektur eines Rechners
10
Rechnerarchitektur
Anstelle die Peripheriegeräte wie Magnetband, Konsole, Lochkartenstanzer, Bildschirm etc. durch besondere Befehle anzusprechen, lassen sich nun die üblichen Speicherbefehle verwenden. Schreibt man Daten auf eine Ausgabeadresse, so gibt sie das Gerät aus; die Eingabe besteht nur noch im Auslesen der Daten auf der Eingabeadresse. Man sieht, dass eine modulare Erweiterung oder Modifikation des Rechners leicht möglich ist, ohne zusätzliche Anschlussprobleme. Jeder Bus besteht aus einzelnen Drähten, die meist zu einem Bandkabel zusammengefasst parallel von Einheit zu Einheit gehen. Jeder Draht führen ein elektrisches Signal, das entweder 0 Volt (bedeutet: binär null) oder 5 Volt (bedeutet: binär eins) ist. Bei einem 32 Drähte umfassenden Datenbus werden also zu einem Zeitpunkt ein Datenpaket aus 32 Bit oder 4 Byte transportiert. 1.2.2 Buskontrolle Der selbe Datenbus dient zum Schreiben und zum Lesen; unterschieden wird nur durch ein Kontrollsignal, das extra parallel geführt wird und zur Buskontrolle (bus control) gehört. Gibt es zu wenige Leitungen, so dass man Adressen und Daten abwechselnd auf den selben Leitungen transportieren muss, so benötigt auch dieser Multiplexbus besondere Kontrollleitungen dafür. Eine weitere Aufgabe der Buskontrolle besteht in der Koordination der unterschiedlichen Geräte mit dem Prozessor. Kann der Bus nicht nur zum Datentransport vom Prozessor genutzt werden, sondern auch von einem Gerät, etwa dem schnellen Massenspeicher aktiv angefordert und genutzt werden, so spricht man von einem Multi-Master-Bus. Die Abfolge, in der die Anfragen und Genehmigungen zur Buskontrolle ablaufen, wird als Busprotokoll bezeichnet. Sie sind für jeden Bus genau genormt und müssen von den Herstellern der Geräte bzw. der Einschubkarten, die Kontakt zum Bus haben, beachtet werden. Interrupts und Prioritäten
Bisher haben wir vernachlässigt, dass die Operationen in den verschiedenen Einheiten nicht sequentiell ablaufen, sondern parallel: während ein Drucker arbeitet, kann prinzipiell der Prozessor weiter Befehle abarbeiten und z.B. Daten auf dem Massenspeicher lesen. Anstelle den Prozessor in einer Schleife aktiv warten zu lassen, bis der Drucker „fertig“ signalisiert (polling, busy wait), muss nur sichergestellt werden, dass der Prozessor durch ein Signal unterbrochen wird, wenn der Drucker fertig ist und neue Daten benötigt. Dieses Signal bezeichnet man als Interrupt (Unterbrechung), die dafür vorgesehenen Leitungen sind die Interruptleitungen. Angenommen, ein Drucker ist fertig und wird gerade vom Prozessor bedient, als die Festplatte ebenfalls bedient werden will. Wer muss warten? Natürlich der Drucker, da er wesentlich langsamer ist; kommt die Festplatte nicht sofort dran, so
Die Architektur eines Rechners
11
hat sie sich schon zu weit gedreht und das Schreiben/Lesen muss bis zur nächsten Umdrehung warten. Beim Drucker hingegen wirkt sich eine Verzögerung von einigen Millisekunden praktisch nicht aus. Aus diesem Grund ist jeder Interruptleitungen zusätzlich noch eine Interrupt-Priorität (Wichtigkeit) zugeordnet nach der der Prozessor entscheidet. Sie ist vom Systemprogrammierer in Relation zu den anderen Geräten fest eingestellt. Beispiel PC-Busse
In den handelsüblichen PCs gibt es verschiedene Bussysteme: Für die Massenspeicher (Festplatten, CD-ROM, DVD) wird meist der IDE (ATA)-Bus verwendet, der pro Buskabel nur zwei Geräte kennt, deren Rechte (Master/Slave) fest eingestellt werden müssen. Für die restliche Peripherie wurde für schnelle Geräte der SCSIBus, ein Multi-Master-Bus, verwendet; für langsamere Geräte und geringerem Aufwand wurde der ISA-Bus eingeführt. Letzterer wurde dann Anfang der 90-Jahre durch den flexibleren Peripheral Component Interface (PCI)-Bus abgelöst. Ab der 66 MHz-Taktversion (PCI Version 2.2) besteht er aus einem kombinierten Adress/Datenbus, bei dem auf 64 Leitungen zuerst eine 64-Bit-Adresse und dann ein 64-Bit-Datenwort angelegt wird (Multiplexbus). Das abwechselnde Benutzen der selben Leitungen wird als „Zeitmultiplex“ bezeichnet. i Der Kontrollbus besteht aus mehreren Komponenten: dem System Bus mit zwei Leitungen (Clock/Reset), dem Interface Control Bus mit sieben Leitungen (Ready, Acknowledge, Stop,..), dem Parity Bus mit zwei Leitungen, dem Fehler-Bus mit zwei Leitungen (Parity, System), dem Kommando-Bus, der 64 MHz-Kontrolle aus sechs Leitungen (Enable, Running, Present, Acknowledge, Request), der Cache-Kontrolle aus zwei Leitungen und dem Interrupt-Bus aus vier Leitungen (also max. 24 = 32 Interruptgeräten). Dabei wurde kein fester Datentakt und Befehlstakt vorgegeben, sondern der Bus passt sich in seiner Geschwindigkeit (Taktrate) dem Gerät an, das ihn gerade benutzt. Die Notation „66 MHz“ gibt nur die maximale Taktrate an, die möglich ist. i Außerdem gibt es noch einen Bus zur Spannungsversorgung mit +5, +3.3, +12, -12 Volt und GND (Masse) Neuere Entwicklungen sehen einen Nachfolger des PCI-Busses, den PCI-Express, vor. Im Unterschied zu dem PCI-Bus, der in verschiedenen Versionen existiert und damit Probleme bei der Normierung der zahlreichen Anschluss-Pins hat, kommt die neue Version minimal mit nur zwei Anschlussdrähten aus. Die maximale Datengeschwindigkeit ist z. Z. 2,5 Gigabit pro Sekunde pro Richtung und wird prinzipiell nur noch durch die maximale Geschwindigkeit von 10 Gb/s pro Richtung begrenzt, die für eine Kupfer-Zweidrahtleitung gilt. Der Bus ist sehr flexibel: Bei der Initialisierung des Datenaustauschs zwischen zwei Geräten wird festgelegt, wie
12
Rechnerarchitektur
viele parallele Drahtleitungen dafür verwendet werden. Über diese Leitungen werden dann alle Signale (Adressen, Daten, Kontrolle) im Zeitmultiplex-Verfahren gesendet. Natürlich erfordert eine solche Flexibilität entsprechend komplexe Chips (Kontroller), die den Bus bedienen können. Man spart also Leitungen durch erhöhte Anforderungen an die beteiligten Geräte. Da durch die moderne Chipfertigung die Chips stetig billiger werden, die Leitungsführung (Platzverbrauch, Energieverbrauch, Störungen, etc.) aber nicht, ist dieses Konzept für zukünftige Architekturen gut geeignet. 1.2.3 PC-Architektur Für die heutige Architektur der Heimcomputer hat sich eine Architektur durchgesetzt, die eine Mischung aus einer diskreten Verbindungsstruktur wie in Abb. 1.8 und einer Busstruktur wie in Abb. 1.9 besteht. In Abb. 1.10 ist ein Überblick gezeigt.
Grafik USB AGP
CPU
SpeicherBrücke
I/O Brücke ATA
Speicher
PCI-Bus
PCI 1 PCI 2
PCI 3
HD
Abb. 1.10 Typische Architektur eines modernen PCs
Der Rechner gliedert sich dabei in zwei Teile: der Prozessor/SpeicherKonfiguration mit hohem Datenaustausch und schnellen Bussen, die sehr vom aktuellen Prozessortyp und Hersteller abhängig ist (schraffierter Bereich in Abb. 1.10), und der Peripheriekonfiguration, die geringeren Datentransfer und eine längere Systemlebensdauer hat. Dreht man obige Abbildung um 90° in Uhrzeigerrichtung, so wird klar, warum die dann oben „im Norden“ gelegene Speicherbrücke als Northbridge und im Gegensatz dazu die I/O-Brücke als Southbridge bezeichnet werden. Zukünftige Architekturen müssen noch die sehr datenintensiven Anwendungen wie Video und Netzwerke berücksichtigen, die einen parallelen Datentransfer vorsehen. In diesem Fall verschmelzen die Speicherbrücke und die I/O-Brücke mitein-
Maschinensprache und Prozessorstruktur
13
ander zu einer zentralen Brücke, die alle diskrete Datenpfade zwischen CPU, Speicher und I/O bereitstellt.
1.3
Maschinensprache und Prozessorstruktur
Die Computer gelten heutzutage zu Recht als „universelle Maschinen“. Im Unterschied zu allen anderen Maschinen werden sie nicht zu einem bestimmten Zweck gebaut, etwa ein Bankkonto zu verwalten oder eine Produktionssteuerung zu realisieren, sondern haben eine solch allgemeine, möglichst anwendungsunabhängige Struktur, dass sie in hohen Stückzahlen preiswert für (fast) alle Zwecke einsetzbar sind. Diese universelle Struktur hat sich in den letzten 50 Jahren fast nicht verändert, so dass es sich lohnt, einen tieferen Blick darauf zu werfen. Ein Universalprozessor besteht im wesentlichen aus einem Speicher, in dem Befehle (Aktionen) aufgelistet sind, die der Prozessor in dieser Reihenfolge ausführen soll, und dem eigentlichen Prozessorkern, der die Befehle hintereinander ausliest und ausführt, siehe Abb. 1.7. 1.3.1 Ein einfaches Befehlsmodell Die Befehle für den Prozessor werden als Maschinenbefehle, die Befehlssyntax und Semantik als Maschinensprache bezeichnet. Eine solche Maschinensprache können wir (in Anlehnung an existierende Prozessoren) uns zum besseren Verständnis selbst konstruieren. Das Aussehen einer solchen Anweisung soll einheitlich folgendes Format haben: Befehl
Arg1
Arg2
..
An Befehlen (Instruktionen) genügen uns anfangs nur folgende: JUMP JMPZ TEST MOVE
ADD
A A
Sprung zu einer Adresse A jump_on_zero: Sprung zu Adresse A, wenn die Variable Z den Wert Z = TRUE hat. A Teste den Inhalt von A, ob er null ist. Wenn ja, setze die Variable Z = WAHR, sonst Z = FALSCH. A, B Schiebe eine Kopie des Inhalts von B auf die Speicherstelle A und überschreibe damit den vorigen Inhalt von A. Dies entspricht der Zuordnung A := B . A, B, C Bilde B+C und speichere das Ergebnis auf A. Entspricht A := B+C. (Analog definiert für SUB, MUL, DIV)
Den Namen A einer Speicherstelle (label) notieren wir dabei mit „A:“ .
14
Rechnerarchitektur
Die Anzahl der möglichen Befehle (Funktionen) dieser abstrakten Maschine, der Instruktionssatz, besteht also aus nur 8 Instruktionen (Es ist sogar möglich, mit weniger Instruktionen auszukommen: Wie? Übungsaufgabe!). Für das Beispiel der Fakultätsberechnung (Bildung von n! = 123...n) für n > 0 sind im folgenden nun links die Blöcke der Hochsprache und rechts ihre Simulation aus den kleineren Blöcken aufgeschrieben. Übung: Was passiert, wenn n = 0 ist? read(n); fac:=1; FOR i:=2 TO n DO
fac:=fac*i;
write(fac);
MOVE n,inp MOVE fac,1 MOVE i,2 For:SUB R0,n,i ADD R0,R0,1 TEST R0 JMPZ EndFor MUL fac,i,fac ADD i,1,i JUMP For EndFor: MOVE out,fac
n:=inp; fac:=1; i:=2; For: R0:=n-i; R0:=R0+1; Z:=(R0=0); IF Z THEN GOTO EndFor fac:=fac*i; i:=i+1; GOTO For EndFor: out:=fac;
Abb. 1.11 Hochsprache, Assembler und Kommentar der Fakultätsberechnung
In diesem Beispiel sind Eingabe und Ausgabe nur symbolisch geschrieben worden: Um eine Zahl 42 einzugeben muss erst eine Konvertierung der Ziffernfolge (die Repräsentation der Zahl, etwa „4“ „2“), die über die alphanumerische Tastatur eingegeben wird, in eine Zahl (hier „42“) als Inhalt der Speicherzelle vorgenommen werden. Die oben definierten Maschinenbefehle haben ein festes Format. Anstatt sie mit Buchstaben zu beschreiben (Assemblercode), kann man sie auch durchnummerieren, so dass im ersten Formatfeld auch eine Zahl stehen kann. Werden die Adressen der Speicherzellen ebenfalls mit einer Zahl („Hausnummer“) versehen, so besteht der gesamte Maschinenbefehl aus mehreren Zahlen, die durch Hintereinanderschreiben beispielsweise zu einer großen Zahl zusammengefasst werden können. Ursprünglich war auf den ersten Rechnern nur die Programmierung als Liste von rein binären Zahlen (binärer Maschinencode) möglich, die man mittels Schalter eingeben (eintoggeln) musste. Die Umstellung auf eine symbolische Schreibweise durch Buchstaben als menschenlesbares Programm wurde deshalb als große Erleichterung empfunden. Der Assemblercode zeichnet sich also besonders dadurch aus, dass jedem Assemblerbefehl genau ein Maschinenbefehl entspricht, der von der Maschine direkt abgearbeitet wird. Je nach Maschine gibt es unterschiedlich komplexe Befehle, so dass Assemblercode abhängig von dem Prozessortyp ist. Beispielsweise lassen sich die arithmetischen Befehle mit dem Befehl TEST kombinieren, so dass bei
Maschinensprache und Prozessorstruktur
15
jeder Rechnung automatisch die 1-Bit Variable „Z“ gesetzt wird. Auch die Multiplikation bzw. Division wird meist von einem darauf spezialisierten Teil des Rechners (Coprozessor) unabhängig vom Hauptprozessor durchgeführt. Wie wir sahen, ist es möglich, Befehle und Daten als Zahlen darzustellen und deshalb im selben (Zahlen-)Speicher unterzubringen. Dadurch ist es auch prinzipiell möglich, dass ein Rechner seinen eigenen Befehlscode als Daten ändert und damit ein neues Programm entsteht - eine wichtige Voraussetzung für die Funktion von Übersetzern (Compiler), die das Hochsprachenprogramm in Maschinencode transferieren! 1.3.2 Eine Prozessorgrundstruktur Die Grundstruktur eines Prozessors sieht dazu folgendermaßen aus:
MOVE n,inp MOVE fac,1 MOVE i,2 For:SUB R0,n,i
TEST R0 JMPZ EndFor MUL fac,i,fac ADD i,1,i JUMP For EndFor: MOVE out,fac HALT n: .word 0 i: .word 0 fac: .word 0
Cachespeicher
Befehle Adressen
Daten
MOVE Befehle
, 2 Daten
Steuerwerk
Register
i
Rechenwerk
R0: R1: ... PC SP
Speicher
Prozessor
Abb. 1.12 Die Struktur eines von-Neumann-Prozessors
Die Befehle werden aus dem Speicher geholt und in einen besonderen, prozessorinternen Speicherplatz, den Cachespeicher. Dort wird der Befehl zerlegt in den Befehlscode(Opcode) und die Argumente (Operanden). Der Befehlscode selbst wird geladen, dekodiert und ausgeführt. Dabei ruft die eine Zahl des Befehls Aktivität auf einer Reihe von Steuerleitungen des Steuerwerks hervor, die je nach Befehl unterschiedlich kombiniert werden. Eine sequentielle Maschine, die wie die obige über die Adresse einen wahlfreien Zugriff (random access) auf jede Spei-
16
Rechnerarchitektur
cherzelle hat, wird eine Random Access Maschine (RAM) genannt. Dies sollte aber nicht mit der für Speicher gebräuchlichen Abkürzung RAM (für Random Access Memory) verwechselt werden. Typisch für einen Rechner mit von-Neumann(Princeton)-Architektur ist die Tatsache, dass Code und Daten im gleichen Speicher liegen. Im Unterschied dazu sind bei der Harvard-Architektur Befehle und Daten getrennt und können dadurch auch getrennt parallel eingelesen werden. Dieser Unterschied ist gerade für schnellere Rechner bedeutsam. Im Rechenwerk werden die Daten bearbeitet; beispielsweise addiert oder getestet und die Variable Z gesetzt. Da dies eine logische Variable ist, kann sie durch ein Bit repräsentiert werden. Dieses Bit und noch weitere Bits für die Anzeige von Überlauf und anderen Zuständen, die in unserem einfachen Modell noch nicht aufgetaucht sind, werden in einem Bitfeld, dem Statusregister, zusammengefasst. Zusätzlich zu dem (prozessorexternen) Speicher gibt es noch einen Speicher für Hilfsvariable, in unserem obigen Beispiel „R0“, der sogar besonders schnell benutzt werden kann: die Register. Sie werden normalerweise in Adress- und Datenregister unterteilt, je nachdem, für was sie eingesetzt werden können. Bei den Adressregistern ist eines ganz besonders wichtig: das des Programmzählers. Im Register des Programmzählers (program counter PC) wird die Speicheradresse des aktuellen Maschinenbefehls geführt. Dies dient nicht nur als „Lesezeichen“, um sich die aktuelle Stelle im Programm zu merken, sondern kann auch als Referenz verwendet werden. Viele Adressen, die im Programm geführt werden, können damit dramatisch verkürzt werden: beispielsweise muss man für einen Sprung zu einem Befehl drei Adressen weiter als Zieladresse nur PC+3 angeben und nicht die volle Adresse. Dies nennt sich relative Adressierung. Ein anderes wichtiges Register, das besonders bei Unterprogrammen nötig ist, ist der Stapelzeiger (stack pointer SP), der die aktuelle Adresse desjenigen Speichers enthält, auf dem die Parameter und die lokalen Variablen für Unterprogramme abgespeichert werden.
1.4
Prozessoren mit komplexem Befehlssatz (CISC)
Obwohl sich der prinzipielle Aufbau eines von-Neumann-Rechners in den letzten Jahrzehnten nicht geändert hat, gibt es doch einige Unterschiede in der Konzeption, bedingt durch neuere Hard- und Softwareerkenntnisse. Eine wichtige Rolle spielt dabei die Verfügbarkeit preiswerter Speicher. In früheren Jahren waren die Speicher Bit für Bit noch durch die Magnetisierungsrichtung in Magnetkernen implementiert, die Stück für Stück in eine Drahtmatrix eingewebt werden mussten. Diese teure Bauweise bedeutete einen teuren und deshalb kleinen Hauptspeicher, so dass das Denken der Programmierer und der Prozessorhersteller darauf ausgerichtet war, möglichst wenig Speicher für ein Programm zu verbrauchen. Deshalb wurden die Prozessoren so gebaut, dass sie mächtige, kom-
Prozessoren mit komplexem Befehlssatz (CISC)
17
plexe Befehle ausführen konnten (Complex Instruction Set Computer, CISC), von denen nur wenige benötigt wurden, um das Programm auszuführen. Damit konnte die semantische Lücke (semantic gap) zwischen Hochsprachen-Programm und Assembler-Programm verringert werden. Die interne Architektur eines solchen CISC-Prozessors ist charakterisiert durch verschiedene Einheiten wie Rechenwerk, Steuerwerk, Adresswerk und Bussystem. In Abb. 1.13 ist dies als Erweiterung des Schemas von Abb. 1.12 gezeigt.
CPU
Steuerwerk PROM
Rechenwerk Statusregister
Ende
ALU +1 HR
StartAdr Steuerbus
HR
PPC
Registersatz
Dekoder
Adresswerk
Datenbuspuffer
Datenbus
Bussteuerung
Adressbustreiber
Adressbus
Abb. 1.13 CISC-Grundstruktur
Die Abarbeitung eines Befehls lässt sich in verschiedene Phasen unterteilen. Sie beginnen mit dem Holen (instruction fetch IF) des Befehls in einen Puffer im Steuerwerk. Dort wird der Befehls dann dekodiert (instruction decode ID). Durch die Dekodierung wird ein Mikroprogramm angestoßen, in dem der Programmzähler PPC auf die Adresse des ersten Mikro-Steuerworts (die Startadresse) für die Maschineninstruktion gesetzt wird. Alle Steuerworte der Mikroinstruktionen sind im Mikro-ROM abgespeichert. Alle weiteren Mikroinstruktionen werden dann durch Erhöhen des Zählers angesprochen bis zu einem Mikro-Steuerwort mit einem ENDE-Signal. Die Steuerworte sind eine Zusammenfassung des Zustands aller benötigten Steuerleitungen beim jeweiligen Taktzeitpunkt; sie sind deshalb wesentlich breiter
18
Rechnerarchitektur
(ca. 70-100 Bit) und ihr Zustand benötigt mehr Speicherplatz als die „normalen“ Speichereinheiten. Die Hauptfunktionen der logischen und arithmetischen Funktionen werden im Rechenwerk durchgeführt. Hier ist ein Schaltnetz ALU (Arithmetic Logical Unit) implementiert, das sowohl zwei Binärzahlen addieren kann (Volladdierer!) als auch negieren oder logisch verknüpfen (z.B. bitweise UND, ODER) kann. Die jeweils gewünschte Teilfunktion des Netzes wird durch die Steuerleitungen aktiviert. Datenquelle und Datenziel ist immer ein bestimmtes Hilfsregister HR (der Akkumulator) sowie ein zweites Hilfsregister. Die Adressrechnungen der Befehle (Addition von Basisadresse und offset, Verschiebung der Folgeadresse je nachdem, ob 8, 16 oder 32 Bit Einheiten angesprochen werden etc.) sowie der Speicherverwaltung, die in engem Verbund mit dem Betriebssystem durchgeführt wird, sind in einem besonderen Teil des Prozessors untergebracht, dem Adresswerk. Die Befehle, Rechenoperanden und Adressdaten müssen zu und von den einzelnen Funktionseinheiten transportiert werden. Dies geschieht durch das prozessorinterne Bussystem. Dies vereinfacht zwar den Transport der Daten, schafft aber zusätzliche Engpässe (Flaschenhals), die nur über parallele, alternative Busse (shortcuts) ausgeglichen werden können, beispielsweise durch einen speziellen Befehlsbus, Ergebnisbus etc. Ein wichtige Maßnahme besteht dabei darin, den Daten- und Adressbus nicht auf den selben Leitungen mit einem Multiplexbus aus dem Chip zu führen, sondern sie wie in Abb. 1.13 physikalisch zu trennen (Harvard-Architektur). Die Steuerleitungen werden, obwohl sie als Bussystem gezeichnet sind, meist direkt zu den Einheiten geführt und bilden deshalb keinen Flaschenhals. 1.4.1 Charakteristik von CISC Die bisher beschriebene Rechnerarchitektur lässt sich durch die Philosophie charakterisieren, in den Prozessoren umfangreiche, mächtige Maschinenbefehle zu implementieren, um die semantische Lücke zwischen den Hochsprachen (wie JAVA) und den einfachen Maschineninstruktionen zu schließen. Die Implementierung der Maschinenbefehle wird durch Mikroprogramme realisiert, wobei ein externer Takt, also ein Maschinenbefehl, durch viele interne Zyklen abgearbeitet wird. Der Hauptprozessor besteht also wie die russische „Puppe in der Puppe“ ebenfalls wieder aus einem Prozessor und Speicherplatz; ein Maschinenbefehl besteht aus mehreren Mikrobefehlen. Die Vorteile dieses Konzepts sind: x Verschiedene, binärkompatible Implementierungen (gleiche externe Architektur) in verschiedener Technologie und damit unterschiedlicher Ausführungsgeschwindigkeit (Rechnerfamilien) sind möglich
Prozessoren mit komplexem Befehlssatz (CISC)
19
x Leichte Änderbarkeit des Befehlssatzes (Ergänzungen). Im Extremfall ist der Befehlssatz ladbar, so dass er auf die Applikationen des Benutzers zugeschnitten werden kann x Kurze Programme resultiert in geringem Speicherbedarf x Schnelle Befehlsabarbeitung bei langsamem, externen Speicher x Eine vertikale Migration (Integration) von Betriebssystemprimitiven (Speicherverwaltung, Bitblock-, string-Befehle, etc.) ist durch die Änderung des Mikrocodes jederzeit leicht möglich. x Durch die Ähnlichkeit der Hochsprache und des Maschinencodes sind einfache Compiler möglich. Allerdings bringt dieses Konzept auch Nachteile mit sich, die nicht verschwiegen werden sollen: x Eine große Anzahl von Mikroinstruktionen im Mikro-ROM (z.B. 51Kilobit beim 80386) bedeutet einen hohen Chip-Flächenbedarf des Steuerwerks (>50%), z.B. beim MC68020: 68%. Dies führte beim Intel iAPX432 (einem objekt-orientierten Prozessor) dazu, das Steuerwerk auf einen zweiten Chip auszulagern. x Hohe Prozessor-Komplexität (Zahl der Transistorfunktionen) ist nötig. Dies bedeutet eine geringe Ausbeute bei der Chipproduktion, da bei gleicher Ausfallwahrscheinlichkeit pro Transistorfunktion mehr komplexe Chips defekt sind als einfache Chips. Da die defekten Chips weggeworfen werden müssen, ergeben sich insgesamt hohe Produktionskosten x Die Anzahl möglicher Instruktionen ist sehr unübersichtlich und dabei einfach zu groß (beim MC68000 mehr als 1000), um vom Compilerbauer adäquat berücksichtigt werden zu können. Dies trifft besonders auf ladbare Mikroprogramme zu, die deshalb in den seltensten Fällen ausgenutzt werden. x Die Komplexität der Befehle ist sehr verschieden, so dass die Abarbeitung unterschiedlicher und verschieden langer Befehle (iAPX432: 6-321 Bit!) sehr unterschiedliche Zeitspannen dauert. Dies erschwert die gleichmäßige Auslastung der Prozessoruntereinheiten erheblich.
1.4.2 Prozessoren mit reduziertem Befehlssatz (RISC) Aus diesen Gründen wuchs in den 70-ger Jahren an verschiedenen Orten die Erkenntnis, dass man so nicht weitermachen konnte. Am IBM T.J. Watson Forschungszentrum untersuchte dazu eine Gruppe um John Cocke ein Reihe von Programmen für das IBM 360 Modell. Sie fanden, dass von ca. 200 möglichen Befehlen nur 10 Befehle bereits 80% des Codes ausmachte; mit 30 Befehlen hatte man 99% erfasst. Außerdem bemerkten sie, dass man nicht nur einige komplexe In-
20
Rechnerarchitektur
struktionen durch eine Folge von einfachen Instruktionen ersetzen konnte, sondern dass diese Folge auch schneller abgearbeitet wurde als der einzelne, komplexe Befehl. Dies führte zu der Idee, einen Rechner zu bauen, bei dem nicht viele komplexe Befehle, sondern nur wenige einfache Befehle vorhanden sind, wobei möglichst pro Takt ein Befehl abgearbeitet werden soll. Sie entwickelten darauf das IBM Modell 801, das nur 120 einfache Befehle kannte und erreichten mit einem darauf abgestimmten Compiler eine mittlere Rate von 1,1 Takten pro Befehl. Diese Arbeit wurde von einer Gruppe um D.A. Patterson (Berkeley) und einer Gruppe um J. Hennessy (Stanford) aufgegriffen und systematisiert. Der Rechner von Berkeley wurde „Reduced Instruction Set Computer“ (RISC) genannt; die Modelle RISC I und RISC II (39 Befehle) führten viele Ideen dieses Prozessortypus ein. Die Generation von RISC Rechnern lässt sich durch die Idee charakterisieren, in jedem Takt möglichst einen Befehl auszuführen. Folgerichtig enthält ein RISC Rechner im Steuerwerk auch keinen Mikrocode mehr, sondern ein festes Schaltnetzwerk, das weniger als 10% der Prozessorfläche belegt. Spezielle Compiler optimieren die Umsetzung der Hochsprachenkonstrukte auf Maschinencode. Der Maschinenbefehlssatz enthält deshalb nur wenige Befehle, die zum einen unabhängig voneinander sind (ein Befehl kann nicht durch eine Folge anderer ersetzt werden) und zum anderen beliebig mit den wenigen Adressierungsarten kombiniert werden können. Ein solcher Befehlssatz heißt orthogonaler, regelmäßiger Befehlssatz. Es lassen sich einige Architekturmerkmale angeben, die typischerweise in RISC Prozessoren verwendet werden. In Klammern ist jeweils die Definition von Tabak (1987) angegeben. Typisch RISC (Tabak, 1990) x Es gibt nur 30-100 Maschinenbefehle (<50) x Pro Takt wird ein Befehl abgearbeitet, außer bei den LOAD/STORE Befehlen, die auf den Hauptspeicher zugreifen. x Es existieren nur wenige Adressierungsarten (max. 3) x Meist ist das 3-Adressformat gegeben. Beispiel: SUB R0,R1,R2 x Es gibt nur wenige Befehlsformate (max. 3) (für 8, 16 und 32 Bit Operanden) x Alle Befehle (außer LOAD/STORE) haben nur Register als Operanden, so dass ein Speicherzugriff nur mit den LOAD/STORE Befehlen möglich ist (LOAD/STORE Architektur) x Es gibt sehr viele Register (>32), die meist zu Registerfeldern (Registerfiles) zusammengefasst werden. Außerdem wurden meist 32 Bit (inzwischen 64 und 126 Bit) als Breite der internen und externen Datenwege gewählt. Zur weiteren Erhöhung des Durchsatzes
Rechnerbetrieb: Die Software
21
sind Adress- und Datenbus sowie Steuerleitungen getrennt herausgeführt: ein Merkmal der Harvard-Architektur. Allerdings ist der Unterschied zwischen CISC und RISC in der Praxis nicht so stark wie bisher ausgeführt wurde. Viele Mechanismen, die zur Parallelisierung und Beschleunigung der Befehlsabarbeitung in RISC Rechnern dienen, sind bereits in CISC Architekturen eingeflossen. Die aktuellen Prozessoren sind häufig eine Mischform zwischen beiden Extremen (z.B. Schaltnetzwerk für Standardinstruktionen, Mikrocode für wenig benutzte Instruktionen); „reine“ Architekturen sind selten. Meistens versucht man, einen Kompromiss zwischen beiden Architekturen zu finden, bei dem möglichst viele Vorteile beider Konzepte erreicht werden.
1.5
Rechnerbetrieb: Die Software
In früheren Zeiten waren die Rechner zu jedem Zeitpunkt für nur eine Hauptaufgabe bestimmt. Alle Programme wurden zu einem Paket geschnürt und liefen nacheinander durch (Stapelverarbeitung oder Batch-Betrieb). Üblicherweise gibt es heutzutage aber nicht nur ein Programm auf einem Rechner, sondern mehrere (Mehrprogrammbetrieb, multi-tasking). Auch gibt es nicht nur einen Benutzer (single user), sondern mehrere (Mehrbenutzerbetrieb, multi-user). Dazu ist der Speicher aufgeteilt zwischen allen Programmen und den allen gemeinsamen Systemprozeduren, dem Betriebssystem, siehe Abb. 1.14.
BSKern
...
Nutzer n Programm
Hauptspeicher
Massen speicher Programme Daten
Nutzer 1 Programm
Prozessor
Abb. 1.14 Schema eines Computersystems
Hier dient der Massenspeicher nur dazu, als preiswertes, aber langsames Medium alle diejenigen Daten und Befehle bzw. Programmteile aufzunehmen, die nicht mehr in den Hauptspeicher hineinpassen und gerade nicht benötigt werden. Um Konflikte zwischen ihnen bei der Benutzung des Rechners zu vermeiden, muss die Verteilung des Speichers und anderer Ressourcen („Betriebsmittel“), auf die Programme geregelt werden. Dies spart außerdem noch Rechnerzeit und erniedrigt damit die Bearbeitungszeiten. Beispielsweise kann die Zuteilung des Hauptprozessors für den Ausdruck von Text parallel zu einer Textverarbeitung so geregelt werden, dass die Textverarbeitung die CPU in der Zeit erhält, in der der
22
Rechnerarchitektur
Drucker ein Zeichen ausdruckt. Ist dies erledigt, schiebt der Prozessor ein neues Zeichen dem Drucker nach und arbeitet dann weiter an der Textverarbeitung. Zusätzlich zu jedem Programm muss also gespeichert werden, welche Betriebsmittel es benötigt: Speicherplatz, CPU-Zeit, CPU-Inhalt etc. Die gesamte Zustandsinformation der Betriebsmittel für ein Programm wird als eine Einheit angesehen und als Prozess (task) bezeichnet (Abb. 1.15).
Prozess Daten
CPU Register
MMU Register
Programm
Dateiinfo, Zugriffsrechte
Kernelstack
Stack Prozesskontext
Abb. 1.15 Zusammensetzung der Prozessdaten
Ein Prozess kann auch einen anderen Prozess erzeugen, wobei der erzeugende Prozess als Elternprozess und der erzeugte Prozess als Kindsprozess bezeichnet wird. Im Unterschied zu dem Maschinencode werden die Zustandsdaten der Hardware (CPU, FPU, MMU), mit denen der Prozess arbeitet, als Prozesskontext bezeichnet, s. Abb. 1.15. Der Teil der Daten, der bei einem blockierten Prozess den letzten Zustand der CPU enthält und damit wie ein Abbild der CPU ist, kann als virtueller Prozessor angesehen werden und muss bei einer Umschaltung zu einem anderen Prozess bzw. Kontext (context switch) neu geladen werden. Ein Mehrprogrammsystem (multiprogramming system) erlaubt das „gleichzeitige“ Ausführen mehrerer Programme und damit mehrerer Prozesse (Mehrprozesssystem, multi-tasking system). Ein Programm (Job) kann dabei auch selbst mehrere Prozesse erzeugen. Zusätzlich zu dem Zustand „aktiv“ (running) für den einen, aktuellen Prozess müssen wir noch unterscheiden, worauf die anderen Prozesse warten. Für jede der zahlreichen Ereignismöglichkeiten gibt es meist eine eigene Warteschlange, in der die Prozesse einsortiert werden. Ein blockierter Prozess kann darauf warten, x x x
aktiv den Prozessor zu erhalten, ist aber sonst bereit (ready), eine Nachricht (message) von einem anderen Prozess zu erhalten, ein Signal von einem Zeitgeber (timer) zu erhalten,
Rechnerbetrieb: Die Software
x
23
Daten eines Ein/Ausgabegeräts zu erhalten (io).
Üblicherweise ist der bereit-Zustand besonders ausgezeichnet: Alle Prozesse, die Ereignisse erhalten und so entblockt werden, werden zunächst in die bereit-Liste (ready-queue) verschoben und erhalten dann in der Reihenfolge den Prozessor. Die Zustände und ihre Übergänge sind in Abb. 1.16 skizziert.
erzeugt nicht-ex.
erhalte Signal
bereit
blockiert erwarte Signal
Zuteilung
aktiv
terminiert nicht-ex.
Abb. 1.16 Prozesszustände und Übergänge
Alle Zustände enthalten eine oder mehrere Warteschlangen (Listen), in die die Prozesse mit diesem Zustand eingetragen werden. Es ist klar, dass ein Prozess immer nur in einer Liste enthalten sein kann. Die verschiedenen Betriebssysteme differieren in der Zahl der Ereignisse, auf die gewartet werden kann, und der Anzahl und Typen von Warteschlangen, in denen gewartet werden kann. Sie unterscheiden sich auch darin, welche Strategien sie für das Erzeugen und Terminieren von Prozessen sowie die Zuteilung und Einordnung in Wartelisten vorsehen. Programme und damit die Prozesse existieren nicht ewig, sondern werden irgendwann erzeugt und auch beendet. Dabei verwalten die Prozesse aus Sicherheitsgründen sich nicht selbst, sondern das Einordnen in die Warteschlangen wird von einer besonderen Instanz des Betriebssystems, dem Scheduler, nach einer Strategie geplant. Bei einigen Betriebssystemen gibt es darüber hinaus eine eigene Instanz, den Dispatcher, der das eigentliche Überführen von einem Zustand in den nächsten bewirkt. Das Einordnen in eine Warteschlange, die Zustellung der Signale und das Abspeichern der Prozessdaten werden also von einer zentralen Instanz erledigt, die der Benutzer nicht direkt steuern kann. Statt dessen werden über die Betriebssystemaufrufe die Wünsche der Prozesse angemeldet, denen im Rahmen der Betriebsmittelverwaltung vom Scheduler mit Rücksicht auf andere Benutzer entsprochen wird. Im einfachsten Fall können die Prozesse so lange laufen, bis sie von sich aus den Aktivzustand verlassen und auf ein Ereignis (I/O, Nachricht etc.) warten, die Kontrolle an andere Prozesse abgeben oder sich selbst beenden: Sie werden nicht vorzeitig unterbrochen (non-preemptive scheduling). Diese Art von Scheduling ist bei allen Systemen sinnvoll, bei denen man genau weiß, welche Prozesse existieren und welche Charakteristika sie haben. Ein Beispiel dafür ist das oben erwähnte Datenbankprogramm, das genau weiß, wie lange eine Transaktion normalerweise dauert.
24
Rechnerarchitektur
In einem normalen Mehrbenutzersystem, bei dem die verschiedenen Jobs von verschiedenen Benutzern gestartet werden, gibt es aber zwangsläufig Ärger, wenn ein Job alle anderen blockiert. Hier ist ein anderes Scheduling erforderlich, bei dem jeder Job vorzeitig unterbrochen werden kann: das preemptive Scheduling. Eine der wichtigsten Maßnahmen dieses Verfahrens besteht darin, die verfügbare Zeitspanne für das Betriebsmittel, meist die CPU, in einzelne, gleich große Zeitabschnitte (Zeitscheiben) aufzuteilen. Wird ein Prozess bereit, so wird er in die Warteschlange an einer Stelle einsortiert. Zu Beginn einer neuen Zeitscheibe wird der Dispatcher per Zeitinterrupt aufgerufen, der bisher laufende Prozess wird abgebrochen und wie ein neuer bereit-Prozess in die Warteschlange eingereiht. Dann wird der Prozess am Anfang der Schlange in den Aktivzustand versetzt. Dies ist in Abb. 1.17 dargestellt. Die senkrechten Striche symbolisieren die Prozesse, die von links in das „Gefäß“, die Warteschlange, hineingeschoben werden. Die Bearbeitungseinheit, hier der Prozessor, ist als Ellipse visualisiert. Nach einem Abbruch wird der Prozess an einen Platz zwischen die anderen Prozesse in der Warteschlange eingereiht.
Ankunft
Warteschlange
Abgang Prozessor Abbruch
Abb. 1.17 Preemptives Scheduling
1.5.1 Das Betriebssystem Historisch gesehen enthält ein Betriebssystem alle Programme und Programmteile, die nötig sind, einen Rechner für verschiedene Anwendungen zu betreiben. Die Meinungen, was alles in einem Betriebssystem enthalten sein sollte, gehen allerdings weit auseinander. Benutzt jemand einen Rechner nur zur Textverarbeitung, so erwartet er oder sie, dass der Rechner alle Funktionen der Anwendung „Textverarbeitung“ beherrscht. Betrachten wir mehrere Anwendungsprogramme, so finden wir gemeinsame Aufgaben, die alle Programme mit den entsprechenden Funktionen abdecken müssen. Statt jedes mal „das Rad neu zu erfinden“, lassen sich diese gemeinsamen Funktionen auslagern und als „zum Rechner gehörige“ Standardsoftware ansehen, die bereits beim Kauf mitgeliefert wird. Dabei beziehen sich die Funktionen sowohl auf Hardwareeinheiten wie Prozessor, Speicher, Ein- und Ausgabegeräte, als auch auf logische (Software-) Einheiten wie Dateien, Benutzerprogramme usw. Das Betriebssystem ist also die Software (Programmteile), die für den Betrieb eines Rechners anwendungs-unabhängig notwendig ist.
Rechnerbetrieb: Die Software
25
Dabei ist allerdings die Interpretation von „anwendungs-unabhängig“ (es gibt keinen anwendungs-unabhängigen Rechnerbetrieb: Dies wäre ein nutzloser Rechner) und „notwendig“ sehr subjektiv (sind Fenster und Mäuse notwendig?) und lädt zu neuen Spekulationen ein. Es gibt nicht das Betriebssystem schlechthin, sondern nur eine den Forderungen der Anwenderprogramme entsprechende Unterstützung, die von der benutzerdefinierten Konfiguration abhängig ist und sich im Laufe der Zeit stark gewandelt hat. Gehörte früher nur die Prozessor-, Speicher- und Ein-/Ausgabeverwaltung zum Betriebssystem, so werden heute auch eine grafische Benutzeroberfläche mit verschiedenen Schriftarten und -größen (Fonts) sowie Netzwerkfunktionen verlangt. Einen guten Hinweis auf den Umfang eines Betriebssystems bietet die Auslieferungsliste eines anwendungs-unabhängigen Rechnersystems mit allen darauf vermerkten Softwarekomponenten. Die Beziehungen der Programmteile eines Rechners lassen sich durch das Diagramm in Abb. 1.18 visualisieren.
Benutzer benutzt
Benutzerprogramm benutzt
Betriebssystem benutzt
Maschinenhardware Abb. 1.18 Benutzungsrelationen von Programmteilen
Dies lässt sich auch kompakter zeichnen: in Abb. 1.19 links als Schichtensystem und rechts als System konzentrischer Kreise („Zwiebelschalen“).
User 1 User 2 User 3 Compiler Editor ... Spiele Betriebssystemdienste Hardware
HW
Abb. 1.19 Schichtenmodell und Zwiebelschalenmodell
26
Rechnerarchitektur
Dabei betont die Darstellung als Schichtenmodell den Aspekt der Basis, auf der aufgebaut wird, während das Zwiebelschalenmodell eher Aspekte wie „Abgeschlossenheit“ und „Sichtbarkeit“ von „inneren“ und „äußeren“ Schichten visualisieren. Das Betriebssystem (operating system) als Gesamtheit aller Software, die für den anwendungsunabhängigen Betrieb des Rechners notwendig ist, enthält x x x x
Dienstprogramme, Werkzeuge: oft benutzte Programme wie Editor, } Übersetzungsprogramme: Interpreter, Compiler, Translator, } Organisationsprogramme: Speicher-, Prozessor-, Geräte-, Netzverwaltung. Benutzerschnittstelle: textuelle und graphische Interaktion mit dem Benutzer.
Da ein vollständiges Betriebssystem inzwischen mehrere Gigabyte umfassen kann, werden aus diesem Reservoir nur die sehr oft benutzten Funktionen als Betriebssystemkern in den Hauptspeicher geladen. In Abb. 1.20 ist die Lage des Betriebssystemkerns innerhalb der Schichtung eines Gesamtsystems gezeigt. Benutzer 1
}
Benutzer N
Benutzeroberfläche User Interface Management System Anwendung 1
Dienstprogramm
}
Werkzeug Systemaufruf
Betriebssystemkern Operating System Kernel
Maschinencode
Hardware Abb. 1.20 Überblick über die Rechnersoftwarestruktur
Der Kern umfasst also alle Dienste, die immer präsent sein müssen, wie z. B. große Teile der Prozessor-, Speicher- und Geräteverwaltung (Treiber) und wichtige Teile der Netzverwaltung. 1.1.2 Beispiel UNIX In UNIX gibt es traditionellerweise als Benutzeroberfläche einen Kommandointerpreter, die Shell. Von dort werden alle Benutzerprogramme sowie die Systemprogramme, die zum Betriebssystem gehören, gestartet. Die Kommunikation zwischen Benutzer und Betriebssystem geschieht durch Ein- und Ausgabekanäle; im
Rechnerbetrieb: Die Software
27
einfachsten Fall sind dies die Eingabe von Zeichen durch die Tastatur und die Ausgabe auf dem Terminal. In Abb. 1.21 ist das Grundschema gezeigt.
BenutzerShell 1
user mode kernel mode
BenutzerProgramm 1
BenutzerShell 2 SystemProgramm 1
} SystemProgramm 2
}
Überprüfbare Schnittstelle und Funktionsverte ilung SpeicherSerielle Ein/Ausgabe Dateisystem verwaltung ProzessDismanagePlatte Netz TTY Drucker Maus play ment Floppy Hardware
Abb. 1.21 UNIX-Schichten
Das UNIX-System wies gegenüber den damals verfügbaren Systemen verschiedene Vorteile auf. So ist es nicht nur ein Betriebssystem, das mehrere Benutzer (Multi-user) gleichzeitig unterstützt, sondern auch mehrere Programme (Multiprogramming) gleichzeitig ausführen konnte. Zusammen mit der Tatsache, dass die Quellen den Universitäten fast kostenlos zur Verfügung gestellt wurden, wurde es bei allen Universitäten Standard und dort weiterentwickelt. Durch die überwiegende Implementierung mittels der „höheren“ Programmiersprache „C“ war es leicht veränderbar und konnte schnell auf eine andere Hardware übertragen (portiert) werden. Diese Faktoren machten es zum Betriebssystem der Wahl, als für die neuen RISC-Prozessoren schnell ein Betriebssystem benötigt wurde. Hatte man bei einem alten C-Compiler den Codegenerator für den neuen Instruktionssatz geändert, so war die Hauptarbeit zum Portieren des Betriebssystems bereits getan – alle Betriebssystemprogramme sowie große Teile des Kerns sind in C geschrieben und sind damit nach dem Kompilieren auf dem neuen Rechner lauffähig. Allerdings gibt es trotzdem noch genügend Arbeit bei einer solchen Portierung. Die ersten Versionen von UNIX (Version 6 und 7) waren noch sehr abhängig von der Hardware, vor allem aber von der Wortbreite der CPU. In den folgenden Versionen (System IV und V sowie Berkeley UNIX) wurde zwar viel gelernt und korrigiert, aber bis heute ist die Portierbarkeit nicht problemlos. Die Grundstruktur von UNIX differiert von Implementierung zu Implementierung. So sind Anzahl und Art der Systemaufrufe sehr variabel, was die Portierbarkeit der Benutzerprogramme zwischen den verschiedenen Versionen ziemlich behindert. Um dem abzuhelfen, wurden verschiedene Organisationen gegründet. Eine der bekanntesten ist die X/Open-Gruppe, ein Zusammenschluss verschiedener Firmen und Institutionen, die verschiedene Normen herausgab. Eine der ersten
28
Rechnerarchitektur
Normen war die Definition der Anforderungen an ein portables UNIX-System (Portable Operating System Interface based on UNIX: POSIX), das als Menge verschiedener verfügbarer Systemdienste definiert wurde. Allerdings sind dabei nur Dienste, nicht die Systemaufrufe direkt definiert worden. Durch die auf X/Open übertragenen Rechte an dem Namen „UNIX“ konnte ein Zertifikationsprozess institutionalisiert werden, der eine bessere Normierung verspricht. Dabei wurde UNIX auch an das Client-Server-Modell angepasst: Es gibt eine Spezifikation für UNIX-98 Server und eine für UNIX-98 Client. Interessant ist, dass dabei die Bezeichnung „UNIX“ nur für eine Sammlung von verbindlichen Schnittstellen steht, nicht für eine Implementierung. Dies bedeutet, dass UNIX eigentlich als eine virtuelle und nicht als reelle Betriebssystemmaschine angesehen werden kann. 1.1.3 Beispiel Windows NT Das Betriebssystem Windows NT der Firma Microsoft ist ein relativ modernes System; es wurde unter der Leitung von David Cutler, einem Betriebssystementwickler von VMS, RSX11-M und MicroVax der Fa. Digital, seit 1988 entwickelt. Das Projekt, mit dem Microsoft erstmals versuchte, ein professionelles Betriebssystem herzustellen, hatte verschiedenen Vorgaben zu genügen: x Das Betriebssystem musste zu allen bisherigen Standards (MS-DOS, 16 BitWindows, UNIX, OS/2) kompatibel sein, um überhaupt am Markt akzeptiert zu werden. x Es musste zuverlässig und robust sein, d. h. Programme dürfen weder sich gegenseitig noch das Betriebssystem schädigen können; auftretende Fehler dürfen nur begrenzte Auswirkungen haben. x Es sollte auf verschiedene Hardwareplattformen leicht zu portieren sein. x Es sollte nicht perfekt alles abdecken, sondern für die sich wandelnden Ansprüche leicht erweiterbar und änderbar sein. x Sehr wichtig: Es sollte auch leistungsstark sein. Betrachtet man diese Aussagen, so stellen sie eine Forderung nach der „eierlegenden Wollmilchsau“ dar. Die gleichzeitigen Forderungen von „kompatibel“, „zuverlässig“, „portabel“, „leistungsstark“ sind schon Widersprüche in sich: MS-DOS ist absolut nicht zuverlässig, portabel oder leistungsstark und überhaupt nicht kompatibel zu UNIX. Trotzdem erreichten die Entwickler ihr Ziel, indem sie Erfahrungen anderer Betriebssysteme nutzten und stark modularisierten. Die Schichtung und die Aufrufbeziehungen des Kerns in der ursprünglichen Konzeption sind in Abb. 1.22 gezeigt. Der gesamte Kern trägt den Namen Windows NT Executive und ist der Block unterhalb der user mode/kernel mode-Umschaltschranke.
Rechnerbetrieb: Die Software
Logon
Security Win/DOS SubsysClient tem
Win32 Subsystem
29
POSIX POSIX OS/2 OS/2 Subsys- Client Subsys- Client tem tem
user mode kernel mode Object Manager
Process Manager
Systemdienste Local Memory Security Proc. Calls Manager Monitor Kernel Hardware AbstractionLayer HAL
I/O System
Hardware
Abb. 1.22 Schichtung und Aufrufe bei Windows NT
Zur Lösung der Designproblematik seien hier einige Stichworte genannt: x Die Kompatibilität wird erreicht, indem die Besonderheiten jedes der Betriebssysteme in ein eigenes Subsystem verlagert werden. Diese setzen als virtuelle Betriebssystemmaschinen auf den Dienstleistungen der NT Executive (Systemaufrufe, schwarze Pfeile in Abb. 1.22) auf. Die zeichenorientierte Ein/Ausgabe wird an die Dienste des zentralen Win32-Moduls weitergeleitet. Die Dienste der Subsysteme werden durch Nachrichten (local procedure calls LPC, graue Pfeile in Abb. 1.22) von Benutzerprogrammen, ihren Kunden (Clients), angefordert. Als Dienstleister (Server) haben sie also eine Client-Server-Beziehung. x Die Robustheit wird durch rigorose Trennung der Programme voneinander und durch Bereitstellen spezieller Ablaufumgebungen („virtuelle DOS-Maschine VDM„) für die MS-DOS/Windows Programme erreicht. Die Funktionen sind gleich, aber der direkte Zugriff auf Hardware wird unterbunden, so dass nur diejenigen alten Programme laufen können, die die Hardware-Ressourcen nicht aus Effizienzgründen direkt anzusprechen versuchen. Zusätzliche Maßnahmen wie ein fehlertolerantes Dateisystem und spezielle Sicherheitsmechanismen zur Zugriffskontrolle von Dateien, Netzwerken und Programmen unterstützen dieses Ziel. x Die Portierbarkeit, Wartbarkeit und Erweiterbarkeit wird dadurch unterstützt, dass das gesamte Betriebssystem bis auf wenige Ausnahmen (z. B. in der Speicherverwaltung) in der Sprache C geschrieben, stark modularisiert und von Anfang an geschichtet ist. Eine spezielle Schicht HAL bildet als virtuelle Maschine allgemeine Hardware nach und reduziert bei der Portierung auf andere Prozessoren die notwendigen Änderungen auf wenige Module. Die detaillierte Diskussion der oben geschilderten Lösungen würde zu weit führen; hier sei auf das Buch von Helen Custer (1993) verwiesen.
30
Rechnerarchitektur
Obwohl in Windows NT einige wichtige Subsysteme wie z.B. das Sicherheitssystem nicht als Kernbestandteil, sondern als Prozess im user mode betrieben werden („Integrale Subsysteme“), ist interessanterweise seit Version 4.0 das Win32Subsystem aus Effizienzgründen in den Kern verlagert worden, um die Zeit für die Systemaufrufe von Win32 zu NT Executive zu sparen. Auch die Unterstützung anderer Standards (z. B. des OS/2 HPFS Dateisystems ab Version 4.0) wurde mit fortschreitender Marktakzeptanz von Windows NT eingestellt. In Version 5, genannt Windows 2000, wurden zusätzlich viele Dienstprogramme zur Netzdateiverwaltung und Sicherheit integriert. Dies ließ den Umfang von 8 Mill. Codezeilen auf über 40 Mill. anschwellen, was an die Zuverlässigkeit und Testumgebung der Betriebssystemmodule besonders hohe Anforderungen stellt. 1.5.2 Schnittstellen und virtuelle Maschinen Betrachten wir das Schichtenmodell etwas näher. Die Relation „A benutzt B“ lässt sich dadurch kennzeichnen, dass B für A Dienstleistungen erbringt. Dies ist beispielsweise bei der Benutzung einer Unterprozedur B in einem Programm A der Fall. Betrachten wir dazu das Zeichnen einer Figur, etwa eines Vierecks. Die Dienstleistung DrawRectangle(x0,y0,x1,y1) hat als Argumente die Koordinaten der linken unteren Ecke und die der rechten oberen. Wir können diesen Aufruf beispielsweise direkt an einen Grafikprozessor GPU richten, der dann auf unserem Bildschirm das Rechteck zeichnet, siehe Abb. 1.23 links. In diesem Fall benutzen wir eine echte Maschine, um die Dienstleistung ausführen zu lassen.
DrawRectangle(x0,y0,x1,y1) Graphic Processor Unit (GPU)
Display(RAM)
DrawRectangle(x0,y0,x1,y1) DrawLine(x0,y0,x1,y0) DrawLine(x1,y0,x1,y1) DrawLine(x1,y1,x0,y1) DrawLine(x0,y1,x0,y0) SetPoint(x0,y0,black) SetPoint(x0+dx,y0,black) … Display(RAM)
V1 V2
V3
V4
Abb. 1.23 Echte und virtuelle Maschinen
In vielen Rechnern ist aber kein Grafikprozessor vorhanden. Stattdessen ruft das Programm, das diese Funktion ausführen soll, selbst wieder eine Folge von einfachen Befehlen (Dienstleistungen) auf, die diese Funktion implementieren sollen, etwa viermal das Zeichnen einer Linie. Die Dienstleistung DrawRectangle(x0,y0,x1,y1) wird also nicht wirklich, sondern mit DrawLine()
Rechnerbetrieb: Die Software
31
durch den Aufruf einer anderen Maschine erbracht; das aufgerufene Programm ist eine virtuelle Maschine, in Abb. 1.23 rechts V1 genannt. Nun kann das Zeichnen einer Linie auch wieder entweder durch einen echten Grafikprozessor erledigt werden, oder aber die Funktion arbeitet selbst als virtuelle Maschine V2 und ruft für jedes Zeichnen einer Linie eine Befehlsfolge einfacher Befehle der darunter liegenden Schicht für das Setzen aller Bildpunkte zwischen den Anfangs- und Endkoordinaten auf. Diese Schicht ist ebenfalls eine virtuelle Maschine V3 und benutzt Befehle für die CPU, die im Video-RAM die Bildpunkte (Bits) setzt. Die endgültige Ausgabe auf den Bildschirm wird durch eine echte Maschine, die Displayhardware (Displayprozessor), durchgeführt. Diesen Gedankengang können wir allgemein formulieren. Gehen wir davon aus, dass alle Dienstleistungen in einer bestimmten Reihenfolge (Sequenz) angefordert werden, so lassen sich die Anforderungen vom Anwender an das Anwenderprogramm, vom Anwenderprogramm an das Betriebssystem, vom Betriebssystem an die Hardware usw. im Schichtenmodell auf Zeitachsen darstellen, die untereinander angeordnet sind.
Schicht 3 Zeit Schicht 2
Schicht 1
Abb. 1.24 Hierarchie virtueller Maschinen
Jede der so entstandenen Schichten bildet nicht nur eine Softwareeinheit wie in Abb. 1.19 links, sondern die Schichtenelemente sind hierarchisch als Untersequenzen oder Dienstleistungen angeordnet. Bei jeder Dienstleistung interessiert sich die anfordernde, darüber liegende Schicht nur dafür, dass sie überhaupt erbracht wird, und nicht, auf welche Weise. Die Dienstleistungsfunktionen einer Schicht, also die Prozeduren bzw. Methoden, Daten und ihre Benutzungsprotokolle, kann man zu einer Schnittstelle zusammenfassen. Das Programm, das diese Dienstleistungen erbringt, kann nun selbst wieder als Befehlssequenz aufgefasst werden, die darunter liegende, elementarere Dienstleistungen als eigene Leistungen benutzt. Die allerunterste Schicht, die die Arbeit nun tatsächlich auch ausführt, wird von der
32
Rechnerarchitektur
„darunterliegenden“ Maschinenhardware gebildet. Da sich ihre Funktionen, so wie bei allen Schichten, über Schnittstellen ansteuern lassen, kann man die darüber liegende Einheit ebenfalls als eine Maschine auffassen; allerdings erbringt sie die Arbeit nicht selbst und ist deshalb eine virtuelle Maschine. Die Abb. 1.24 beschreibt also eine Hierarchie virtueller Maschinen. Das allgemeine Schichtenmodell aus Abb. 1.19 zeigt dies ebenfalls, aber ohne Zeitachsen und damit ohne Reihenfolge. Es ist auch als Übersicht über Aktivitäten auf parallel arbeitenden Maschinen geeignet. Die Funktion der virtuellen Gesamtmaschine ergibt sich aus dem Zusammenwirken virtueller Einzelmaschinen. In diesem Zusammenhang ist es natürlich von außen ununterscheidbar, ob eine Prozedur oder eine Hardwareeinheit eine Funktion innerhalb einer Sequenz ausführt. Beispielsweise werden die Konstrukte höherer Programmiersprachen wie „lese“, „schreibe“, „dividiere“, usw. vom Compiler in Maschinenbefehle umgeformt. Diese Umformung entspricht einer Simulation der Funktionen (Anweisungen) einer virtuellen Maschine durch die Funktionen einer einfacheren (virtuellen) Maschine. Diese einfacheren Anweisungen, der Maschinencode, wird dann von der reellen Maschine, dem Prozessor, ausgeführt. Bisher haben wir zwischen physikalischen und virtuellen Maschinen unterschieden. Es gibt nun noch eine dritte Sorte: die logischen Maschinen. Für manche Leute sind sie als Abstraktion einer physikalischen Maschine mit den virtuellen Maschinen identisch, andere platzieren sie zwischen physikalische und virtuelle Maschinen. Beispiel Eine virtuelle Festplatte lässt sich als Feld von Speicherblöcken modellieren, die einheitlich mit einer sequentiellen Blocknummer angesprochen werden können. Im Gegensatz dazu modelliert die logische Festplatte alles etwas konkreter und berücksichtigt, dass eine Festplatte auch unterschiedlich groß sein kann sowie eine Verzögerungszeit und eine Priorität beim Datentransfer kennt. In Abb. 1.25 ist dies gezeigt. Mit diesen Angaben kann man eine Verwaltung der Speicherblöcke anlegen, in der der Speicherplatz mehrerer Festplatten unterschiedlicher Hersteller einheitlich verwaltet wird, ohne dass dies der Schnittstelle der virtuellen Festplatte bekannt sein muss. In der dritten Konkretisierungsstufe beim tatsächlichen Ansprechen der logischen Geräte müssen nun alle Feinheiten der Festplatten (Statusregister, Fehlerinformation, Schreib/Lesepufferadressen, }) bekannt sein. Das Verwalten dieser Informationen und Verbergen vor der nächsthöheren Schicht (der Verwaltung der logischen Geräte) besorgt dann der gerätespezifische Treiber. Die Definitionen bedeuten dann in diesem Fall: virtuelles Gerät = logisches Gerät + Verwaltungstreiber, logisches Gerät = physikalisches Gerät + HW-Treiber.
Rechnerbetrieb: Die Software
Kontrolle
33
Daten Treiber log. Geräte
log. Gerät 1 Treiber 1 phys. Gerät 1
log. Gerät 2 Treiber 2 phys. Gerät 2
Virtuelles Gerät Abb. 1.25 Virtuelle, logische und physikalische Geräte
Die drei Gerätearten bilden also auch wieder drei Schichten für den Zugriff auf die Daten, vgl. Abb. 1.25.
1.5.3 Software-Hardware-Migration Die Konstruktion von virtuellen Maschinen erlaubt es, analog zu Programmen die Schnittstelle beizubehalten, die Implementierung aber zu verändern. Damit ist es möglich, die Implementierung durch eine wechselnde Mischung aus Hardware und Software zu realisieren: Für die angeforderte Dienstleistung ist dies irrelevant. Da die Hardware meist schneller arbeitet, aber teuer ist, und die Software vergleichsweise langsam abgearbeitet wird, aber (als Kopie) billig ist und schneller geändert werden kann, versucht der Rechnerarchitekt, bei dem Entwurf eines Rechensystems eine Lösung zwischen diesen beiden Extremen anzusiedeln. Ein Beispiel für dienen Gedankengang hatten wir schon in Abb. 1.23 kennen gelernt. Gibt es einen grafischen Prozessor, so erledigt er als reelle Maschine die Arbeit der virtuellen Maschinen V2 und V3; die Funktionen können statt durch Software auch durch Hardware erbracht werden; die Funktionen werden „migriert“. Ein anderes Beispiel ist die Schichtung eines symbolischen Maschinencodes (hier: Java-Code, Abb. 1.26), der entweder softwaremäßig durch einen Compiler oder Interpreter in einen realen Maschinencode umgesetzt werden („Java virtual machine“) oder aber auch direkt als Maschineninstruktion hardwaremäßig ausgeführt werden kann. Im zweiten Fall wurde die mittlere Schicht in die Hardware (schraffiert in Abb. 1.26 links) migriert, indem für jeden Java-Code Befehl eine in Microcode programmierte Funktion in der CPU ausgeführt wird.
34
Rechnerarchitektur
Programm in Java-Code Java-Code / Maschinencode CPU- Hardware
Programm in Java-Code Microcodeund CPU-Hardware
Abb. 1.26 Software-Hardware-Migration des Java-Codes
Wird für das Hardwaredesign statt Blaupausen eine formale Sprache verwendet (z. B. VHDL), so spielen die Unterschiede in der Änderbarkeit beider Implementierungen immer weniger eine Rolle. Entscheidend sind vielmehr andere Aspekte wie Kosten, Standards, Normen und Kundenwünsche.
1.6
Ein- und Ausgabegeräte
Mit der Einführung der Konzepte von „Schichten“ und „virtuellen Maschinen“ können wir nun auch leichter die Vielfalt und das Zusammenspiel der Peripheriegeräte wie Speicher und Drucker verstehen. In Betriebssystemen waren früher die Wechselbeziehungen zwischen Anwenderprogramm und Ein- und Ausgabegeräten sehr innig – jeder Anwenderprogrammierer setzte sein eigenes, „effizientes“ System auf, um den Datenfluss zwischen seiner Anwendung und dem Peripheriegerät zu steigern. Dieser Ansatz führte aber nicht nur dazu, dass jeder sein eigenes, geräteabhängiges Programm hatte, sondern auch zu Fehlern und Überschneidungen, wenn mehrere Programme auf dasselbe Gerät zugreifen wollten (z. B. MS-DOS). Da dies in multi user-Umgebungen nicht mehr tragbar war, wurden die gerätetypischen Programmteile abgetrennt und als eigene Module (Treiber) ins Betriebssystem integriert. Dies fördert nicht nur die Portabilität eines Programms über unterschiedliche Rechnerarchitekturen hinweg und hilft, Fehler zu vermeiden, sondern erspart auch dem Anwendungsprogrammierer Arbeit. Die Grundaufgabe eines Treibers ist es also, alle gerätespezifischen Initialisierungsschritte und Datentransfermechanismen vor dem Anwenderprogramm hinter einer einheitlichen, betriebssystemspezifischen Schnittstelle zu verbergen. In unserer Notation aus Kapitel 1 ist der Treiber also eine virtuelle Maschine; er vermittelt zwischen Betriebssystem und physikalischem Gerät. Die Aufgaben eines Treibers sind auf die Initialisierung der Datenstrukturen und des Geräts sowie Schreiben und Lesen von Daten begrenzt. Zusätzlich kommen aber noch weitere Aufgaben hinzu, die nur mit dem Betriebssystem zusammen durchgeführt werden können: x Übersetzung vom logischen Programmiermodell zu gerätespezifischen Anforderungen x Koordination der schreibenden und lesenden Prozesse für das Gerät x Koordination verschiedener Geräte gleichen Typs x Pufferung der Daten
Ein- und Ausgabegeräte
35
x usw. Diese zusätzlichen Aufgaben kann man in einer weiteren Softwareschicht zusammenfassen, so dass im allgemeinen mehrere Schichten virtueller Maschinen oder Treiber zwischen dem Anwenderprozess und dem physikalischen Gerät liegen. In Abb. 1.27 ist dies illustriert.
user mode kernel mode
Benutzerprozeß kernel-Verteiler Auftragsverwaltung Pufferung Treiber Controller Gerät
Abb. 1.27 Grundschichten der Geräteverwaltung
Die Einführung einer Schichtung erlaubt es, zusätzliche Aufgaben für die Datenbearbeitung in Form von Extraschichten in die Bearbeitungsreihenfolge einzufügen. Beispielsweise sieht ein Plattentreiber die Platte als ein Speichergerät an, dessen Speicheradressen durch eine Vielzahl verschiedener Parameter wie Laufwerks-, Sektor-, Plattennummer usw. bestimmt ist. Er übersetzt die Schreib- und Leseanforderungen, die für ein einfaches, lineares Modell von N Speicheradressen gelten, in die kompliziertere Adressierlogik des Plattenspeichers. Dieser Umsetzung von logischer zu physikalischer Adresse kann man nun einen weiteren Treiber vorschalten: Die Umsetzung von einer logischen, relativen Adresse innerhalb einer Datei zu der logischen, absoluten Adresse des Speichergeräts, auf dem sich die Datei befindet, wird ebenfalls von einem Dateitreiber durchgeführt. Die Problematik, für ein neues oder existierendes Gerät einen passenden Treiber zu entwickeln, sollte nicht unterschätzt werden. Fast alle neueren Betriebssysteme leiden nicht nur unter dem Problem, zu wenige Anwendungen zu haben, sondern insbesondere darin, dass die Treiber neuer Geräte meist nur für die am meisten verkauften Betriebssysteme entwickelt werden und damit die Verbreitung des neuen Betriebssystems behindern. Es ist deshalb für jedes Betriebssystem wichtig, eine einfache Schnittstelle für Treiber (oder noch besser: ein Entwicklungssystem für den Treiber) öffentlich zur Verfügung zu stellen. Eine interessante Initiative bildet in diesem Zusammenhang die seit 1994 im Unix-Bereich agierende Uniform Driver Interface UDI-Initiative aus mehreren großen Firmen. Sie versuchen, eine betriebssystemunabhängige, plattformneutrale Treiberschnittstelle zu entwickeln, um die Verwendung neuer Hardware auf mög-
36
Rechnerarchitektur
lichst vielen Systemen zu beschleunigen. Das UDI-Konzept im Kontext ist in Abb. 1.28 visualisiert. Applikation Systemaufruf Betriebssystem I/O Manager BS-Treiber zu UDI Schnittstelle
UDI-Treiber
UDI Ablaufumgebung
UDI-Services zu Hardware-mapping
Hardware
Abb. 1.28 Die Schichtung und Schnittstellen des UDI-Treibers
Die UDI-Spezifikation besteht dabei zum einen aus einer Programmierbeschreibung des UDI-Treibers und zum anderen aus einer plattformunabhängigen Ablaufumgebung, die nach oben eine Schnittstelle zum Betriebssystem hat und nach unten zur Hardwareplattform und damit dem eigentlichen Treiber eine einheitliche Umgebung anbietet. Zwar müssen sowohl Betriebssystemschnittstelle als auch Hardwareabhängigkeiten für die Ablaufumgebung programmiert werden, aber vom Implementierer des Betriebssystems nur einmal für alle Treiber. 1.6.1 Beispiel UNIX: I/O-Verarbeitungsschichten Auch Unix ist nach verschiedenen Gesichtspunkten in Schichten eingeteilt. An dieser Stelle sollen zwei Schichtungskonzepte vorgestellt werden: Das Konzept des virtuellen Dateisystems und das der Datenströme und Filter (streamsKonzept). Virtuelles Dateisystem
Unter Unix gibt es verschiedene Dateisysteme. Aus diesem Grund gab es schon früh das Bestreben, den Zugriff auf Dateien mit Standardfunktionen einer Dateisystemschnittstelle durchzuführen und die eigentliche Implementierung auf nachfolgende Schichten zu verlagern. Ein gutes Beispiel dafür ist die Schichtung in Linux, gezeigt in Abb. 1.29.
Ein- und Ausgabegeräte
37
Benutzerprozeß kernel-Verteiler Virtuelles Dateisystem (VFS) Minix Ext2 Reiser ... Pufferung Gerätetreiber Gerät1 Gerät2 Gerät3 ...
user mode kernel mode
Abb. 1.29 Die Dateisystemschichtung unter Linux
Die Dateisystemschicht, grau schraffiert in der Zeichnung, besteht selbst wieder aus Unterschichten: dem virtuellen Dateisystem VFS, das die Verwaltung der Dateiinformationen (i-nodes, i-node cache, Verzeichniscache) und die Anbindung an die Benutzerprozesse vornimmt, und den eigentlichen Dateisystemen wie das Minix FS, das Ext2-FS, das Reiser FS usw, die unterschiedliche Strategien der Implementierung von Dateioperationen (Lesen, Schreiben) und Verzeichnissen durch Allokation und Freigabe von Blöcken bereitstellen. Eine gemeinsame Pufferung dient allen Dateisystemen. Der allgemeine blockorientierte Gerätetreiber, der allen Dateisystemen zugrunde liegt, vermittelt deren Anforderungen an eine Vielzahl von speziellen Plattensystemen. Das Stream-Konzept
Die grundsätzliche Schichtung des UNIX-Kerns wurde in Abb. 1.21 gezeigt. Zusätzlich zu den normalen Schichten ist es im stream system, einer UNIXErweiterung in System V (HP-UX, SUN-OS, ...), möglich, andere Bearbeitungsstufen („Treiber“) in den Bearbeitungsablauf einzufügen. In Abb. 1.30 (a) ist dies für ein einfaches, buchstabenorientiertes Terminalsystem gezeigt, in (b) für eine Festplatte. Beim zeichenorientierten Gerät in (a) ist ein Treiber (special char recognition) in den Verarbeitungsweg eingeschoben, der besondere Buchstaben, die als Kommandos dienen (z. B. DEL zum Löschen des letzten Buchstabens, Control-C zum Abbruch des laufenden Prozesses usw.), sowie besondere Zeichenkonversion (z. B. 6 Leerzeichen für ein TAB-Zeichen usw.) erkennt und entsprechende Aktionen veranlasst. Ein gesonderter Zugang (raw interface) erlaubt es, ohne eine „höhere“ Verarbeitung des Zeichenstroms (ohne lokales Echo, ohne Control-C usw.) direkt Zeichen zu senden und zu empfangen. Dieser Weg ist besonders für schnelle serielle Datenverbindungen zwischen Computern interessant zum Zwecke des Datenaustauschs, da hier alle Zeichen als Daten aufgefasst werden und nicht als Buchstaben mit besonderer Bedeutung interpretiert werden.
38
Rechnerarchitektur raw I/O
/dev/tty
Zugang über Datei
raw block /dev/rdisk
Pufferung c-Lists
file system
special char recognition
Pufferung Blocklisten
Terminal Treiber
Platten Treiber
Terminal Tastatur
Platten DMA
(a) character devices
(b) block devices
Abb. 1.30 Das Stream-System in UNIX
1.6.2 Beispiel Windows NT:
I/O-Verarbeitungsschichten
Die Schichtung in Windows NT ist dynamisch und hängt vom jeweiligen Systemdienst ab. In Abb. 1.31 ist links die einfache Schichtung für serielle Geräte gezeigt, rechts die multiple Schichtung für Massenspeicher.
user mode kernel mode
Benutzerprozeß kernel-Verteiler I/O-Manager/Cache Treiber Gerät Monitor/Drucker/ Tastatur/Maus/…
Benutzerprozeß kernel-Verteiler I/O-Manager Dateisystem-Treiber I/O-Manager/Cache Gerätetreiber Gerät CD-ROM/Platten/ Floppy/Tape/…
Abb. 1.31 Einfache und multiple Schichtung in Windows NT
Alle Betriebssystemaufrufe für Ein- und Ausgabe werden im zentralen I/OManager zu Aufträgen in Form eines I/O Request Package IRP gebündelt und weitergeleitet. Jedes dieser Auftragspakete enthält im Kopf die Folge der Adressaten sowie den Datenplatz, so dass ein Auftrag in der Schichtung von oben nach unten und mit den Ergebnissen aktualisiert wieder zurück an den Benutzerprozess wandert. Dabei werden – je nach Auftrag – unterschiedlich viele Stationen bzw.
Ein- und Ausgabegeräte
39
Schichten durchlaufen. Zusätzlich ist auch die Existenz der Treiber dynamisch: Im Unterschied zu älteren UNIX-Versionen kann man während des Betriebs neue Treiber einklinken oder herausnehmen, um bestimmte Aufgaben zu erfüllen. 1.6.3 Der Zugriff auf Ein- und Ausgabe Für den Zugriff von Programmen bzw. dem Betriebssystem auf Peripheriegeräte muss das Verhalten der physikalischen Geräten auf ein vom Betriebssystem erwartetes Verhalten (Schnittstelle) abgebildet werden. Dazu ist es nötig, dass der Systemprogrammierer etwas mehr von den Geräten weiß. Da viele Geräte ein sehr ähnliches Verhalten haben, lohnt es sich, die typischsten Modelle zu betrachten, um die charakteristischen Parameter zu verstehen. Wir können grob zwischen zwei Arten von Geräten unterscheiden: Geräte mit wahlfreiem Zugriff, die Adressierinformation benötigen, und Geräte mit seriellem Datentransfer ohne Adressinformation. Beide Gerätearten erhalten ihre Aufträge über eine spezielle Hardwareschnittstelle. Die Geräteschnittstelle
Im Unterschied zu früher gibt es bei der Systembus-orientierten Rechnerarchitektur keine speziellen Hardwarekanäle und spezielle, dafür vorgesehene Prozessorbefehle mehr, sondern alle Geräte können einheitlich wie Speicher unter einer Adresse im Adressraum des Hauptspeichers angesprochen werden (memory mapped I/O).
I/O Adresse n
Register
I/Omapping
Programme
Gerät I/O-Controller
0 virt. Adreßraum Abb. 1.32 Adressraum, Controller und Gerät
Dazu werden die Speicherzellen eines speziellen Chips (Controller) der Hauptplatine (motherboard) oder einer Einsteckplatine im Rechner und damit interne Speicherbereiche (Register, Puffer) zur Gerätekontrolle auf den Adressbereich des
40
Rechnerarchitektur
Hauptspeichers abgebildet. In Abb. 1.33 sind die Reservierungen für die Peripheriegeräte im Adressraum eines Standard-PC gezeigt.
Adressraum (hexadezimal) 000-00F 020-021 040-043 200-20F 2FB-2FF 320-32F 378-37F 3D0-3DF 3F0-3F7 3F8-3FF
Gerät DMA Controller Interrupt Controller Timer Game controller Serial ports (secondary) Hard disk controller Parallel port Graphics controller Floppy disk controller Serial ports (primary)
Abb. 1.33 Adressreservierungen der Standardperipherie eines PC
Initialisierung der Geräteschnittstellen
Die Register der Geräte sind anfangs mit unspezifischen Daten belegt. Insbesondere die Information, welcher DMA-Kanal und welcher Interrupt von welchem Gerät benutzt werden soll, fehlen. Auch die Adressen der Befehls- und Kontrollregister können sich bei den Geräten überschneiden und müssen deshalb in einem Rechnersystem koordiniert werden. Diese Koordination wird bei jedem Neustart des Rechners durchgeführt und ist eine der frühesten Aufgaben des Betriebssystems. Diese Aufgabe wird bei neueren Controllern vereinfacht durch vordefinierte Protokolle. Ein sehr bekanntes Protokoll ist das „Plug-and-Play“ (PnP) -Protokoll. Hierbei identifiziert das Betriebssystem zunächst alle beteiligten Geräte und fragt dann bei allen Controllern an, welche Ressourcen (DMA, IRQ, Registeradressen, etc.) benötigt werden. Bei PCI-Bus-Controllern ist dies durch eine Standardschnittstelle vereinfacht, bei der sowohl der Hersteller der Karte als auch der Chips, Geräte- und Versionsnummer sowie die Geräteklasse direkt als Zahlen aus der Karte gelesen werden können. In entsprechenden Geräte-Beschreibungsdateien (INF-Dateien in Windows NT) sind dann für diese Geräte alle wichtigen Daten (mögliche Registeradressen, Interrupts usw.) hinterlegt. Feste, nichtkonfigurierbare Geräte (legacy devices) müssen aber vorher manuell angegeben werden. Danach werden die Ressourcen nach einer Strategie verteilt und dies den Geräten mitgeteilt. Da dies ein langwieriger Prozess ist, wird meist die Tabelle verwendet, die beim letzten Start gültig und auf Platte gespeichert war (Windows 98, Windows NT) bzw. die vom Computer Mainboard Betriebssystem (BIOS) errechnet und in dessen nicht-flüchtigen ESCD-Bereich abgespeichert wurde (Linux).
Ein- und Ausgabegeräte
41
Betrachten wir nun als typischen Vertreter aus der Gruppe der wahlfreien Geräte den Plattenspeicher. 1.6.4 Wahlfreier Zugriff: Plattenspeicher Das Modell eines Plattenspeichers steht für eine ganze Modellgruppe aller Speichermedien, die eine sich drehende Scheibe verwenden. Dazu gehören neben den Festplattenspeichern auch CompactDisk (CD-R, CD-RW, DVD-R, DVD-RW), Disketten (31/2 Zoll, SuperDisk,..) und Wechselplattensysteme. Alle haben gewisse Modelleigenschaften gemeinsam. Betrachten wir als Beispiel den Festplattenspeicher genauer. Der übliche magnetische Plattenspeicher besteht aus einer Aluminiumscheibe, die mit einer hauchdünnen Magnetschicht, z. B. Eisenoxid, überzogen ist. Wird nun auf einem langen Arm ein winziger Elektromagnet (Schreib-/Lesekopf) darauf gebracht und die Scheibe in Rotation versetzt, so kann der Magnetkopf auf einer kreisförmigen Bahn (Spur) die Eisenoxidpartikel verschieden magnetisieren („schreiben“). Umgekehrt rufen die kleinen, magnetischen Partikel beim Vorbeigleiten an dem Magnetkopf einen kleinen Induktionsimpuls in der Spule hervor. Bei diesem „Lesen“ der magnetisch fixierten Information wird jede Magnetisierungsänderung als Bit interpretiert, so dass auf jeder Spur eine Bitsequenz gespeichert werden kann. Da eine Spur sehr viel Information speichern kann, unterteilt man eine Spur nochmals in Untereinheiten gleicher Größe, genannt Sektoren. Nun muss man nur noch den Arm kontrolliert über die Scheibe zwischen Mittelpunkt und Rand bewegen, um viele derartige Spuren schreiben und lesen zu können. Das Grundmodell eines derartigen Plattenspeichers bleibt immer gleich, ganz egal ob man als Plattenbelag Eisenoxid, Chromdioxid oder ein optisch aktives Medium (CD-ROM, DVD) verwendet, und egal ob man die Platten flexibel aus Kunststoff mit Eisenoxidfüllung macht (floppy disk) oder mehrere davon mit jeweils einem Kopf von oben und von unten an der Scheibe übereinander anordnet (Festplatten), siehe Abb. 1.34. Sind mehrere Platten übereinander auf dieselbe Drehachse montiert, so werden alle Köpfe normalerweise von derselben Mechanik zusammen gleichartig bewegt. Alle Köpfe befinden sich also immer auf einer Spur mit der gleichen Nummer, aber verschiedenen Platten. Alle Spuren mit der gleichen Spurnummer liegen übereinander und bilden die Mantelfläche eines imaginären Zylinders, siehe Abb. 1.34. Die Menge der Spuren mit derselben Nummer wird deshalb auch als Zylindergruppe bezeichnet. Man beachte, dass diese Notation von gleichzeitig lesbaren Spuren an Bedeutung verliert, je mehr Spuren auf der Platte existieren und je ungenauer deshalb die Positionierung auf allen Spuren gleichzeitig wird. Bei den mehr als 2000 Zylindern moderner Laufwerke muss deshalb jede Spur unabhängig von den anderen Spuren eines
42
Rechnerarchitektur
Zylindergruppe
Spur mit Sektoren
Positioniermechanik
Schreib-/Lesekopf
Drehachse Abb. 1.34 Plattenspeicheraufbau
Zylinders betrachtet werden. Bei allen Konstruktionen muss ein Kopf mechanisch bewegt werden, um eine Spur zu lesen. Möchte man genau in der Spurmitte aufsetzen, den Spuranfang und die Abschnitte der Spur erkennen, so sind außerdem Hilfsinformationen wie regelmäßige Impulse zur Synchronisation (Formatierung) sowie spezielle Justierinformation (extra Servospuren an der Plattenunterseite) nötig. Eine Zugriffsoptimierung für Platten sollte nicht im Betriebssystem stattfinden, sondern in dem eingebetteten System des Plattencontrollers. Stattdessen sollte vom Betriebssystem nur ein einfaches, klares Modell eines virtuellen Geräts ohne spezielle Zeiten berücksichtigt werden, etwa das eines großen, linearen Speicherfeldes; die eigentliche detailreiche, effiziente Modellierung sollte dagegen beim Hersteller erfolgen. Er allein kennt alle internen mechanischen Konstruktionsdetails und kann mit Hilfe des internen Controllers die Schnittstelle zum Betriebssystem auf die internen Einzelschnittstellen zu Motorelektronik und Cache effizient abbilden. Dabei sollte dieser interne Treiber (Kontrollprogramm) adaptiv seine Betriebsparameter an die jeweils aktuelle Lastverteilung (Datendurchsatz, Reihenfolge der Suchaufträge, ...) beim Betrieb anpassen. Man sollte also die Geräteeigenschaften im gerätespezifischen Controller bzw. oder im Treiber kapseln und sie möglichst beim Betriebssystem im Normalbetrieb nicht berücksichtigen müssen.
Ein- und Ausgabegeräte
43
Geräteschnittstelle
Üblicherweise gibt es bei einem wahlfreien Gerät wie einer Festplatte folgende Register: x Statusregister (read only) Der Speicherinhalt dieses Dateiwortes wird vom Controller aktualisiert und gelesen. Es dient als Statusregister, jedes Bit hat eine besondere Bedeutung. Beispiel: Bit 4 = 1 bedeutet, dass dasLesen beendet wurde und das Ergebnis im Datenpuffer gelandet ist. Bit 8 = 1 heißt, das ein Lesefehler dabei aufgetreten ist. x Befehlsregister (write only) In dieses Register wird der Code für einen Befehl (Schreiben/ Lesen/ Formatieren/ Positionieren ...) geschrieben, der ausgeführt werden soll. Das Hineinschreiben des Befehlscodes in dieses Register lässt sich als Prozeduraufruf interpretieren, bei dem die Parameter aus dem Inhalt der anderen Register bestehen. x Adressregister Sie enthalten die Speicherzellen-Adresse auf dem Gerät (Gerät, Spur/Zylinder, Sektor, Platte, Kopf ...) und die Zahl der zu transferierenden Bytes. Die Angaben setzen dabei eine Information über die Geometrie der Festplatte voraus, also die Anzahl der verfügbaren Köpfe, Sektoren usw. Diese Information muss nicht echt sein, sondern kann auch nur das Modell widerspiegeln, das der Controller nach außen hin als Schnittstelle repräsentiert; die tatsächliche Kopfzahl, Spurzahl, Zylinder- und Sektorenzahl kann intern ganz anders sein. x Datenpuffer Alle Daten, die auf diese Adresse geschrieben werden, landen in einem internen Puffer in der Reihenfolge, in der sie geschrieben werden. Umgekehrt kann der Puffer hier sequentiell ausgelesen werden. Meist sind Ein- und Ausgabepuffer getrennt, so dass ein Puffer nur gelesen und der andere nur geschrieben werden kann. x Ein Interruptsystem, das nach der Ausführung eines Befehls einen Interrupt auslösen kann. Besitzt das Gerät sehr viele Parameter, die man einstellen kann, so benötigt man viele Register, und es geht sehr viel Adressraum für den Prozess dafür verloren. Es ist deshalb üblich, dass nur eine Adresse existiert, in die man den Zeiger (die Adresse) zum Auftragspaket oder auch sequentiell ganze Datenpakete (Aufträge) mit genau festgelegten Formaten schreiben muss. Ein wichtiger Mechanismus für den Datenaustausch zwischen Rechner und dem Gerät ist das Verschieben von ganzen Datenblöcken zwischen dem Gerätepuffer des Controllers und dem Hauptspeicher des Rechners. Dies wird prozessorunabhängig mit speziellen Chips, (Direct Memory Access DMA) durchgeführt, die die Multi-Master-Busse ansteuern. Die DMA-Chips existieren dabei nicht nur im
44
Rechnerarchitektur
Speichersystem, sondern auch auf den Controllerplatinen und können unabhängig voneinander arbeiten (DMA-Kanäle). RAM-Disks
Für das Verhalten eines Massenspeichers im Betriebssystem ist nur der Treiber verantwortlich: Er bildet eine virtuelle Maschine. Man kann deshalb an die Stelle des realen Massenspeichers auch einen Bereich des Hauptspeichers als virtuelle Platte verwenden, ohne dass dies im Betriebssystem auffällt und anders behandelt werden müßte. Eine solche RAM-Disk ist dadurch natürlich sehr schnell; für die darauf befindlichen Dateien gibt es keine mechanischen Verzögerungszeiten beim Schreiben und Lesen. Man muss nur vor dem Ausschalten des Rechners alle Dateien auf echten Massenspeicher kopieren, um sie dauerhaft zu erhalten. Allerdings stellt sich hier prinzipiell die Frage, wozu man einen Teil des sowieso chronisch zu kleinen Hauptspeichers als Massenspeichergerät deklarieren soll. Effiziente Strategien für Cache und paging, verbunden mit der direkten Abbildung von Dateibereichen auf den virtuellen Adressraum wie sie heutzutage in modernen Betriebssystemen üblich sind, senken ebenfalls die Zugriffszeit und verwalten dynamisch den kleinen Hauptspeicher. Die Antwort auf diese Frage hängt stark vom benutzten System ab. x Beispielsweise ist eine RAM-Disk sinnvoll, wenn man mit fester, vorgegebener Software arbeiten muss, die temporäre Dateien erzeugt. Ein Beispiel dafür sind Compiler: Liegen die Zwischendateien des Kompiliervorgangs auf einer RAMDisk, so erhöht sich die Kompiliergeschwindigkeit erheblich. Allerdings kann man auch auf ein solches RAM-Dateisystem verzichten, wenn der page-out-pool groß genug ist, um alle Dateiblöcke für das Schreiben zwischen zu lagern. Der Lesezugriff auf die temporäre Datei erfolgt dann über diese Blöcke, die nicht von einem Massenspeicher geholt werden müssen und deshalb genauso schnell sind wie eine RAM-Disk. x Eine andere Situation liegt in Systemen vor, in denen das Betriebssystem keine größeren Adressbereiche ansprechen kann, beispielsweise in MS-DOS. Verfügt man über Hauptspeicher, der größer ist als die obligatorische 640-KB-Grenze, so kann man ihn über einen RAM-Disk-Treiber nutzen. Im RAM-Disk-Treiber kann man dann durch entsprechende sequentielle Adressierungskunstgriffe die Hürde überwinden.
1.6.5 Serielle Geräte In Kapitel 4 bemerkten wir schon, dass auf fast alle Geräte sowohl sequentiell als auch wahlfrei zugegriffen werden kann, bis auf die „echten“ seriellen Geräte wie Tastatur und Drucker, bei denen dies normalerweise nicht möglich ist. Die Be-
Ein- und Ausgabegeräte
45
zeichnung „serielle Geräte“ bezieht sich also auf die Art und Weise, wie Daten übergeben werden müssen: jedes Zeichen hintereinander, ohne Adressinformation für das Gerät. Meist geschieht das mit geringer Geschwindigkeit (wenigen KB pro Sekunde). Dies sind die klassischen Randbedingungen von Terminals, langsamen Zeilendruckern und Tastaturen. Allerdings gibt es auch sehr schnelle serielle Verbindungen, die nicht zeichenweise vom Treiber bedient werden, sondern größere Datenblöcke zur Übertragung über DMA zur Verfügung gestellt bekommen. Ihre Ansteuerung wird dann mit Hilfe einer Mischung aus Techniken für Treiber von zeichen- und blockorientierten Geräten durchgeführt. Geräteschnittstelle
Die Schnittstelle zum Controller von seriellen Geräten ist (ebenso wie die bei wahlfreien Geräten) mittels Speicheradressen ansprechbar: x Ein Kontrollregister sorgt für Statusmeldungen, die Übertragungsgeschwindigkeit (z. B. 1200, 2400, 4800, 9600, 19200 Baud, gemessen in Daten- + Kontrollbits pro Sekunde), und den Übertragungsmodus „synchron“ oder „asynchron“. Bei asynchroner Übertragung werden in unregelmäßigen Abständen Daten (Zeichen) übermittelt; bei synchroner Übertragung folgen alle Zeichen in festem zeitlichen Abstand und Format. x Ein Eingaberegister enthält das zuletzt empfangene Zeichen, das vom Treiber dort ausgelesen wird. x In das Ausgaberegister wird das zu sendende Zeichen vom Treiber geschrieben; unmittelbar darauf wird es vom Controller gesendet. x Ein Interruptsystem löst nach jedem gesendeten bzw. empfangenen Zeichen einen Interrupt aus. Die meisten seriellen Geräte arbeiten außerdem mit einer Flusssteuerung, die im Kontrollregister gesetzt wird. Üblich ist entweder eine Hardwaresteuerung über extra Drähte (RS232-Norm), oder aber es werden spezielle Zeichen (Control-S, Control-Q, genannt XON und XOFF) zur Steuerung gesendet. Droht beim Empfänger der Empfangspuffer überzulaufen, so sendet er XOFF und veranlasst damit den Sender, mit dem Senden aufzuhören. Ist der Puffer leer, sendet der Empfänger XON und signalisiert so dem Sender, weiterzusenden. Allerdings funktioniert diese Mechanik nur dann, wenn der Empfänger sein Signal rechtzeitig sendet, so dass der Sender auch genügend Zeit hat, das Kontrollzeichen zu empfangen, zu interpretieren und im Treiber umzusetzen, bevor der Empfängerpuffer überläuft. 1.6.6 Multiple Plattenspeicher: RAIDs Eine weitverbreitete Methode, die Plattenkapazität zu erhöhen, besteht darin, mehrere kleine Platten gemeinsam als eine große virtuelle Platte zu verwalten. Haben
46
Rechnerarchitektur
wir für jede Platte ein extra Laufwerk, so muss man pro Laufwerk nur noch eine geringere Kapazität vorsehen und kann dafür kleine, preiswerte Laufwerke einsetzen. Ein solches System wird als RAID (Redundant Array of Inexpensive Disks) bezeichnet und ist außerordentlich beliebt bei großen kommerziellen Datenbanken. Für das Betriebssystem ist diese Hardwarevariante transparent: Der Treiber zeigt nur ein einziges, virtuelles, schnelles Laufwerk großer Kapazität; die Realisierung als Plattensammlung ist für die Schnittstelle zu den höheren Betriebssystemschichten unwichtig. Diese Basiseigenschaft wird als RAID-linear bezeichnet. Ein weiterer Vorteil multipler Plattenspeicher kann genutzt werden, wenn mehrere unabhängige Prozesse oder threads im Betriebssystem existieren. Ihre Unabhängigkeit und damit die mögliche parallele, schnellere Abarbeitung wird behindert, wenn die an sich unabhängigen Daten alle auf einem gemeinsamen Massenspeicher versammelt sind, dessen Abschnitte nicht unabhängig voneinander gelesen werden können. Setzen wir nun mehrere Laufwerke anstelle eines einzigen mit fest gekoppelten Köpfen ein, so können die Daten der unabhängigen Prozesse auch auf unabhängig parallel agierenden Plattenspeichern abgelegt werden; der Datenzugriff wird für die Prozesse erheblich schneller. In Abb. 1.35 ist eine solche Aufteilung von Daten auf multiple Laufwerke (dargestellt als Säulen) zu sehen. Disk 1 Streifen M
Disk 2
...
Disk N
max
max
max
0
0
0
...
Streifen 1 Streifen 0
Abb. 1.35 Neugruppierung der Plattenbereiche in Streifen
Dazu wird der gesamte logische Speicherplatz in Abschnitte aufgeteilt. Korrespondierende, gleich große Abschnitte verschiedener Laufwerke werden vom Treiber als gemeinsamer Speicherbereich (Streifen) angesehen und bilden einen einheitlichen Speicherbereich für eine Prozessgruppe. Der Treiber im Betriebssystem hat dabei die Aufgabe, die Verteilung und Zusammenfassung der Daten vorzunehmen und dazu die Abbildung virtueller Speichereinheiten auf die logischen Einheiten unterschiedlicher Laufwerke durchzuführen.
Ein- und Ausgabegeräte
47
Beispiel Windows NT Plattenorganisation In diesem Betriebssystem kann bei der initialen Formatierung von logischen Plattenbereichen (Partitionen) die Option angegeben werden, die Plattenbereiche als Streifen zu organisieren. Eine andere, ähnliche Option besteht darin, mehrere Partitionen beliebiger Größe von unterschiedlichen Platten zu einer neuen logischen Platte (volume) zusammenzufassen. Der virtuelle Plattenadressraum kann so unregelmäßig auf unterschiedliche Platten verteilt werden. Machen wir den Streifen sehr schmal, z. B. 4 Byte oder auch einen Block groß, so wird der gesamte Datenpuffer, aufgeteilt in sequentielle Abschnitte der Länge 4 Byte bzw. ein Block, sehr schnell auf das Plattenfeld verteilt geschrieben oder gelesen. Eine solche schnelle, aber nicht ausfallsichere Konfiguration wird als RAID-0-System bezeichnet und gern für die großen Datenmengen in der Videound Bildproduktion eingesetzt. Systeme aus multiplen Laufwerken haben neben der höheren Zugriffsschnelligkeit und größeren Speicherkapazität noch einen weiteren wichtigen Vorteil: Mit ihnen kann man Ausfälle von Platten tolerieren. Für diese Art von Fehlertoleranz, der Ausfalltoleranz, sieht man für die Daten einer Platte eine exakte Kopie auf einer Platte eines anderen Laufwerks vor; die andere Platte ist bezüglich ihrer Daten wie ein Spiegel (mirror) der einen Platte aufgebaut. In Abb. 1.36 ist das Schema einer solchen Spiegelplattenkonfiguration gezeigt. .
Disk 1 max
Disk 2 max
„Spiegel“
0
0
Abb. 1.36 Spiegelplattenkonfiguration zweier Platten
Allerdings muss diese Kopie ständig aktualisiert werden. Der Treiber, der eine solche Konfiguration bedienen kann, muss dazu bei jedem Schreibvorgang auf Disk 1 eine Kopie auf Disk 2 bewirken, was eine zusätzliche Belastung des Betriebssystems und der I/O-Hardware bewirkt. Eine solche ausfallsichere, aber ineffiziente Konfiguration von Spiegelplattenpaaren wird als RAID-1-System be-
48
Rechnerarchitektur
zeichnet. Führen wir den Gedanken der Spiegelplatten in die Streifenkonfiguration von Abb. 1.35 ein, so erhalten wir ein schnelles, ausfallsicheres RAID-0/1 System, das in Finanzanwendungen (Lohnbuchhaltung) eingesetzt wird. Man kann versuchen, den Zeitverlust zum redundanten Schreiben bei den Lesevorgängen der Spiegelplatten wieder zu kompensieren. Verfügt der Treiber über den augenblicklichen Zustand der Platten, so kann er zum Lesen diejenige der beiden Platten auswählen, deren Lesekopf der gewünschten Spur am nächsten ist; der andere Plattenkopf kann bereits für den nächsten Auftrag positioniert werden oder in anderen, nicht gespiegelten Partitionen schreiben oder lesen. Den zusätzlichen Zeitbedarf für ein schnelles, ausfallsicheres RAID-0/1-System kann man durch einen Trick drastisch senken. Dazu wird jedes Bit eines Datenwortes auf eine extra Platte geschrieben – für ein 32-Bit-Wort benötigt man also 32 Laufwerke. Dann wird jedes Wort mit einer speziellen Signatur versehen, beispielsweise einem 6-Bit-Zusatz, so dass im Endeffekt jedes Datenwort von einem Controller auf 38 Laufwerke verteilt wird. Dies bedeutet nicht nur einen enormen Datenfluss, sondern ermöglicht auch eine große Ausfalltoleranz. Da nicht jede Bitkombination der 38-Bit-Datenworte möglich ist, kann man auch bei mehreren ausgefallenen oder defekten Platten auf den korrekten 32-Bit-Code schließen, s. MacWilliams u. Sloane (1986). Beispiel Fehlerkorrektur durch Paritätsbildung Angenommen, wir wollen die Zahlen 10, 7, 3 und 12 abspeichern und garantieren, dass bei Verlust einer der Zahlen die fehlende wieder rekonstruiert werden kann. Wie können wir dies erreichen ? Dazu bilden wir zusätzlich zu den n Bits, die für eine Zahl unabhängig voneinander abgespeichert werden, als Kontrollinformation ein (n+1)-tes Bit, das Paritätsbit p, nach der folgenden XOR-Funktion zweier Binärvariablen a und b: a b 1 1 0 0
1 0 1 0
XOR p = ab 0 1 1 0
pb 1 1 0 0
Es folgt direkt aus der Tabelle a0=a aa=0 und somit pb = abb = a0 = a
Mit pb = a wissen wir nun, dass bei bekannter Parität p der Ausfall von einem Bit a toleriert werden kann, da wir a direkt aus p und b wiederherstellen können. Dies gilt natürlich auch, wenn b selbst wiederum aus vielen Termen besteht, beispielsweise b = b1b2...bn–1
Ein- und Ausgabegeräte
49
Bei n Binärvariablen, aus denen wir die Parität bilden, kann also eine davon mit Hilfe der anderen und der Parität wieder erschlossen werden. Für unsere vier Zahlen von oben bedeutet dies bei Ausfall von Zahl 3: Rekonstruktion von Zahl 3
Paritätsbildung Zahl 1: 10 = Zahl 2: 7 = Zahl 3: 3 = Zahl 4: 12 = Parität:
1010 0111 0011 1100 0010=2
Zahl 1: 10 = Zahl 2: 7 = Parität: 2 = Zahl 4: 12 =
1010 0111 0010 1100
Ergebnis: 0 0 1 1 = 3
Behandeln wir nun 8 Binärvariablen (zusammengefasst in einem Byte) parallel, so gilt dies für jedes Bit in diesem Byte und damit für das ganze Byte. Speichern wir also jedes Bit – auch das Paritätsbit – auf einem anderen Massenspeicher, so können wir beim Ausfall eines Speichers aus den n verbliebenen Bits das fehlende direkt rekonstruieren und damit den Ausfall des einen Massenspeichers tolerieren. Bilden wir mehrere Fehlerkorrekturbits (ECC, MacWilliams u. Sloane 1986), so können wir auch den Ausfall mehrerer Laufwerke tolerieren. Für ein fehlerkorrigierendes RAID-2-System werden die Bits der zu speichernden Datenworte extra behandelt und gespeichert. Die ist in Abb. 1.37 für ein Fehlertoleranzbit (Paritätsbit) pro Datenabschnitt (Wort) dargestellt.
Wort7 Wort6 Wort5
Wort0
Disk 1
.. .
Daten ...
...
...
...
Disk 2 ...
.. .
Parität
bit 7 bit 6 bit 5 bit0 1 neues Datenbyte pro Platte Controller
Disk n Disk n+1
Abb. 1.37 Datenzuweisung beim RAID-2-System
50
Rechnerarchitektur
Die Datenworte sind als Säulen gezeigt, die in ihre Bits aufgeteilt werden. Das i-te Bit aller Worte wird so auf der i-ten Platte gespeichert. Der Bitstrom für eine Platte lässt sich im Controller wieder in Bytes zusammenfassen, so dass als Speichersysteme handelsübliche Platten genommen werden können. Beim Raid-2System wird von jedem Wort ein allgemeiner fehlerkorrigierender Code (Error Correcting Bits ECC) gebildet, dessen einzelne Bits jeweils auf einer eigenen Platte gespeichert werden. Würden wir die Spindeln der einzelnen Platten zwangssynchronisieren, so wäre ein Gesamtsystem aus n Platten mit jeweils einem Kopf zwar nicht schneller als ein einzelnes Laufwerk mit n Platten und n Köpfen, aber dafür fehlertolerant. Natürlich können wir auch die Parität statt über die Säulen über die Zeilen, also über alle m Worte eines Puffers, bilden. Das sich ergebende Wort aus Paritäten (ECC) wird dann nach jeweils m Worten auf einer extra Platte gespeichert. In diesem Fall ist die Zahl der Platten nicht gleich der Zahl der Bits pro Wort, sondern m und damit beliebig; jedes Wort wird nicht in Bits aufgespalten, sondern mit anderen als Teilpuffer (etwa in einem Streifen) auf eine Platte gespeichert. Wir erhalten so ein RAID-3-System. Da hier genauso wie bei der reinen Parität von RAID-2 die gesamte Fehlertoleranzinformation aller Blöcke auf einem einzigen Laufwerk gespeichert ist, darf hier nur ein einziges Laufwerk ausfallen. Ein weiterer Nachteil ist die kurze Länge der Fehlertoleranzinformation: Hier sollten wie bei RAID-2 die Spindeln der Laufwerke synchronisiert sein, um die Zugriffszeit tD möglichst klein zu halten. Fassen wir weiter die Datenbits zu größeren Abschnitten zusammen (z.B. zu Blöcken) und speichern jeden Block wie bei RAID 0 jeweils auf einer separaten Platte, so wird dies als RAID-4-System bezeichnet. Da hier zusätzlich bei jedem Schreiben auch die Fehlertoleranzinformation aktualisiert werden muss, dauert das Schreiben länger: RAID-4 bietet keinen Geschwindigkeitsvorteil wie RAID-0. Die Lesegeschwindigkeit entspricht etwa der einer Einzelplatte im Block-Lesemodus. Ein Alternative dazu besteht darin, die häufige gelesene Fehlertoleranzinformation FI für jeden Block extra auf einem anderen Laufwerk zu sichern und so die I/OBelastung der Aktualisierung auf alle Platten gleichmäßig zu verteilen. Dies ist ein RAID-5-System. Das Schema dafür ist in Abb. 1.38 gezeigt. Die Raid-5 Systeme werden gern für Datenbankserver und Internetserver eingesetzt.
Datenstrom
Daten
FI ..., Daten
FI , ...
Zuweisung Disk 1
Disk 2
...
Abb. 1.38 Wechselseitige Abspeicherung der Fehlertoleranzinformation
Ein- und Ausgabegeräte
51
Für den Betriebssystemarchitekten besteht die Hauptaufgabe darin, die Komplexität und das Fehlertoleranzverhalten modular in das RAID-Subsystem bzw. den Treiber zu verlagern und das Betriebssystem vom Speichersystem unabhängig zu machen: Für das Betriebssystem sollte nur ein einfaches, virtuelles Speichersystem sichtbar sein. Bei der Wahl zwischen einer Softwarelösung und einer Hardwarelösung für RAID sollte man auf eine reine SW-Lösung verzichten: Sie ist sehr aufwändig und stellt viele Probleme, insbesondere das Wiederanlaufen nach einem Hardwarefehler. Beispiel Windows NT Fehlertoleranz durch Spiegelplatten und RAID Für eine fehlertolerante Dateiverwaltung gibt es in Windows NT einen speziellen Treiber: den FtDisk-Treiber. Dieser wird zwischen den NTFS-Treiber und den eigentlichen Gerätetreiber geschoben; er bildet als Zwischenschicht eine virtuelle Maschine, die alle auftretenden Fehler selbst abfangen soll, unsichtbar und unbemerkt von allen höheren Schichten. Tritt nun ein Fehler (bad sector) auf, so werden die Daten des fehlerhaften Blocks von der Spiegelplatte gelesen oder mit Hilfe der Fehlerkorrekturinformation (Paritäts-stripes) rekonstruiert. Dann wird für den fehlerhaften Sektor ein neuer Sektor vom Gerät angefordert (bad sector mapping) und die Daten darauf geschrieben. Damit existiert wieder die nötige Datenredundanz im System. Gibt es keine freien Sektoren (spare sectors) mehr oder kann das Gerät kein sector mapping, so wird der wiederhergestellte Datenblock zusammen mit einer Warnung an den Dateisystemtreiber weitergegeben. Wurde keine fehlertolerante Plattenorganisation eingerichtet, so kann FtDisk den Fehler als „Lese-/Schreibfehler“ nur weitermelden. Es ist dann Sache des Dateisystems (oder des Benutzers), darauf sinnvoll zu reagieren. Man beachte, dass die RAID-Konfigurationen zwar für Ausfalltoleranz sorgen, aber kein regelmäßiges Backup aller Daten der Platten ersetzen. Im Internet gibt es herzzerreißende Geschichten von Systemadministratoren, die durch Softwarefehler ganze RAID-Datenkonfigurationen korrumpiert erhielten und so von der RAIDFehlertoleranz bitter enttäuscht wurden. 1.6.7 Interleaving Eine weitere Optimierungsmöglichkeit bietet sich an, wenn die Blöcke schneller von der Platte zum Controller gegeben werden, als sie weitertransportiert werden können. In diesem Fall eines langsamen Controllers lässt sich der Block mit der Nummer 4 nicht sofort nach dem Block 3 lesen – die Platte hat sich während der Übertragung von Block 3 schon weitergedreht, und es wird nun z. B. Block 5 statt Block 4 gelesen. Der Controller muss erst die nächste Umdrehung abwarten, um endlich Block 4 lesen zu können. Ändert man nun die Nummerierung der Blöcke
52
Rechnerarchitektur
ab, so dass Block 5 die Nummer 4 bekommt, kann man statt dessen kontinuierlich ohne Wartezeit lesen. Wird also in diesem Fall nur jeder zweite physische Block für die logische Nummerierung gewählt, bleibt mehr Zeit für das Übermitteln der Blöcke, und der Datendurchsatz steigt an. In Abb. 1.39 (a) ist die physische Nummerierung außen an der Platte notiert; auf den Plattensegmenten innen ist die entsprechende logische Nummerierung vermerkt.
7
0 7
0
6
3
4
1
5
6
1
2
phys. Nummerierung 0 1 2 3 4 5
6
log. Nummerierung 2 4
5 3
(a) Plattensicht
0 0
4 3
1 6
5 1
2 4
7 Interleaving
6 7
3 2
7 Faktor 1 5 Faktor 2
(b) Spursicht Abb. 1.39 Block interleaving
Diese Technik wird als Interleaving bezeichnet; die Zahl der bei der Nummerierung zunächst übersprungenen Blöcke ist der Interleaving-Faktor. In Abb. 1.39 (b) sind die Nummerierungen für die Interleaving-Faktoren 1 und 2 gegenübergestellt. Dieser Faktor muss vom Treiber für die Formatierung der Festplatte dem Controller mitgeteilt werden und hängt stark von der Übertragungsgeschwindigkeit des beteiligten I/O-Systems des Betriebssystems ab. Man beachte, dass optimales Interleaving nur dann möglich ist, wenn auch die Zeit zum Spurwechsel mit einbezogen wird. Es ist deshalb sinnvoll, die Nummerierung der Sektoren bzw. Blöcke von Spur zu Spur um einen Betrag versetzt zu beginnen (cylinder skew), so dass es trotz Spurwechselzeit möglich ist, den logisch nächsten Block auf der nächsten Spur zu lesen. Allerdings gelingt dies nur in einer Richtung, so dass eine solche versetze Nummerierung nur sinnvoll ist, wenn die Suchrichtung für Blöcke immer gleich ist. Die bisher besprochenen Mechanismen der Leistungssteigerung im Treiber müssen allerdings alle im Kontext des Controllers gesehen werden. Prinzipiell kann leicht der Fall auftreten, dass alle Optimierungsmaßnahmen des Treibers vom Controller, der meist mit eigenem Mikroprozessor ausgestattet ist, durch eigene Optimierungsstrategien konterkariert werden. Beispielsweise benötigen moderne, schnelle mikroprozessorgesteuerte Controller kein Interleave mehr. Auch haben manche Controller die Möglichkeit, defekte Blöcke durch Ersatzblöcke auf speziellen, normal nicht zugänglichen Spuren zu ersetzen. Diese spezielle Abbildung tritt immer dann in Kraft, wenn ein defekter Block angesprochen wird, und ist transparent nach außen, kann also nicht vom Treiber bemerkt werden. Damit erweisen sich alle Bemühungen des Treibers, die Kopfbewegung zu minimieren, als
Ein- und Ausgabegeräte
53
obsolet: Die dazwischenliegenden Bewegungen zur Ersatzspur lassen keine Optimierung von außen mehr zu. Es ist deshalb sehr sinnvoll, die Schnittstellen zwischen Treiber und Controller vom Hersteller aus systematisch zu durchdenken und vorauszuplanen. Beispielsweise kann man bestimmte geräteabhängige Optimierungen wie Kopfbewegung und Sektorsuche dem Controller zuweisen und dort kapseln; nach außen hin sind nur die Leistungen (Speicherung von Blöcken mit logischen Adressen etc.) sichtbar, die unabhängig von der implementierten Elektronik und Mechanik erbracht werden. 1.6.8 Pufferung Eine wichtige Leistungsoptimierung bei Massenspeichern, die für starken Durchsatz benötigt werden (z. B. bei Datenbankanwendungen), kann man mit einem Puffer (Datencache) erreichen, der auf verschiedenen Ebenen eingerichtet werden kann. Auf der Ebene des Dateisystemtreibers ist es üblich, die wichtigsten, am meisten benutzten Blöcke einer Datei zu puffern, wobei man ihn in zwei verschiedene Arten (Schreib- und Lesepuffer) unterteilen kann, jeden mit eigener Verwaltung. Auf unterster Treiberebene kann man größere Schreib- und Leseeinheiten der Daten puffern, beispielsweise eine ganze Spur. Nachfolgende Schreib- und Leseoperationen beziehen sich meist auf aufeinanderfolgende Sektornummern und können mit der gepufferten Spur wesentlich schneller durchgeführt werden. Allerdings hat die Pufferung auch hier die gleichen Probleme wie in Abschnitt 3.5 beschrieben. Bei der Verwaltung muss sichergestellt werden, dass beschriebene Blöcke und Sektoren auch für die Anfragen anderer Prozesse verwendet werden, um die Datenkonsistenz zu garantieren. Wichtig ist dabei die Synchronisierung der Pufferinhalte mit dem Massenspeicher, bevor das Rechnersystem abgeschaltet wird. Bei Versorgungsspannungsausfall (power failure) muss dies sofort von den gepufferten Treibern eingeleitet werden. Auch hier muss man sorgfältig die Schnittstelle zum Controller beachten. Manche Controller haben bereits einen Cache intern integriert; hier ist es sinnlos, auch auf Treiberebene einen Cache anzulegen. Beispiel UNIX Pufferung Die Pufferung der seriellen character files wird über Listen aus Zeilen (c-list) durchgeführt. Aus diesem Grund wird ein RETURN (Zeilenende/Neue Zeile)Zeichen benötigt, um einen Text von der Tastatur einzulesen. Besondere Statusbits dieses special file erlauben es allerdings auch, für eine reine Zeicheneingabe (Cursor-Tasten etc.) sofort jedes Zeichen ohne Pufferung zu lesen oder das lokale Echo eines Zeichens auf dem Monitor zu unterdrücken. Das Puffersystem für die block devices benutzt als Speichereinheiten die Blöcke. Für jeden Treiber gibt es eine Auftragsliste von Blöcken, die gelesen bzw. geschrieben werden sollen. Die Liste aller freien Blöcke ist doppelt verzeigert
54
Rechnerarchitektur
und in einem Speicherpool zentral zusammengefasst. Außerdem gibt es zwei Zugangswege zu den block devices: zum einen über einen Dateinamen und damit über das Dateisystem und zum anderen über den special file als raw device. Da durch die Pufferung der i-nodes das gesamte Dateisystem bei einem Netzausfall hochgradig gefährdet ist, werden in regelmäßigem Takt (alle 30 Sek.) von der sync()-Prozedur alle Puffer auf Platte geschrieben und die Datenkonsistenz damit hergestellt. Dies wird auch beim Herunterfahren des Systems (shut down) durchgeführt. Beispiel Windows NT Pufferung Zur Verwaltung des Ein- und Ausgabecache gibt es einen speziellen Cache Manager. Dieser alloziert dynamisch Seiten im Hauptspeicher und stellt ein memory mapping zwischen den Hauptspeicherseiten und einer Datei her. Die Anzahl der so erzeugten section objects ist dynamisch: Sie hängt sowohl vom verfügbaren Hauptspeicher als auch von der Zugriffshäufigkeit auf die Dateiteile ab. Dies geschieht dadurch, dass diese Seiten des Cache-Managers wie die Speicherseiten eines normalen Prozesses verwaltet werden. Für die Pufferung der seriellen Ein- und Ausgabe mussten spezielle Mechanismen entwickelt werden, um die Ergebnisse der Programme aus dem Multitasking non-preemptiven Windows 3.1 (16 Bit) auf dem Multi-tasking preemptive Windows NT zu erhalten. Für jedes Gerät gibt es in Windows 3.1 eine einzige I/O-Warteschlange, auch für serielle Geräte. Werden in verschiedene Fenster Eingaben (Zeichen, Mausklicks etc.) gegeben, so werden sie als jeweils eine Eingabeeinheit in den Eingabepuffer gestellt. Üblicherweise liest und schreibt ein Prozess unter Windows 3.1 beliebig lange (non-preemptive), bis er die für ihn bestimmte Eingabe abgearbeitet hat, und wird dann beim Lesen auf den Eingabepuffer blockiert, wenn die weitere Eingabe in einem anderen Fenster erfolgte und deshalb nicht für ihn bestimmt war. Der zum Fenster gehörende Prozess wird dann vom Windows-Manager aktiviert und liest seinen Pufferanteil ein. Gehen wir nun zu einer preemptiven Umgebung über, bei der ein Prozess sofort deaktiviert werden kann, wenn seine Zeitscheibe abgelaufen ist, so führt dies bei nur einem Eingabepuffer zu Problemen – der neu aktivierte Prozess liest fehlerhaft die für den deaktivierten Prozess bestimmten Daten. Dies ist auch der Fall, wenn ein Prozess „abstürzt“, also fehlerhaft vorzeitig terminiert. Aus diesen Gründen hat in Windows NT jeder Prozess (hier: thread genannt) seine eigene Eingabewarteschlange – ungelesene Eingabe für einen thread verbleibt beim Prozess und wird nicht vom nächsten thread versehentlich gelesen; das System wird robust gegenüber fehlerhaften Prozessen. Wie kann man zwei derart unterschiedliche Systeme wie Windows 3.1 und Windows NT miteinander integrieren? Die Logik der non-preemptiven Prozesse aus Windows 3.1 ähnelt sehr der Logik von non-preemptiven Prozessen. Die Idee ist nun, den Mechanismus der threads in Windows NT dafür zu nutzen. Dazu behandelten die Designer von Windows NT das Windows 16-Bit-
Die Energieverwaltung
55
Subsystem (WOW), das die 16-Bit-Tasks als eigene threads gestartet hatte, wie einen einzigen Prozess. Diesem Prozess wird zwar regelmäßig der Prozessor entzogen (wie allen anderen NT Prozessen auch), bei der Prozessorrückgabe erhält aber der letzte laufende thread automatisch wieder die Kontrolle – als ob kein Prozesswechsel stattgefunden hätte. Somit wird genau das Verhalten erreicht, das im alten Windows/DOS System üblich war. Allerdings ist es auch möglich, die 16-Bit-Applikationen in getrennten Adressräumen als eigene Prozesse ablaufen zu lassen. In diesem Fall verhalten sie sich natürlich nicht mehr zwangsläufig wie früher unter Windows 3.1, so dass dies nicht bei allen alten Applikationen sinnvoll ist. 1.6.9 Synchrone und asynchrone Ein- und Ausgabe Bei der Ein- und Ausgabe ist es im Programm üblich, abzuwarten, bis ein Betriebssystemaufruf erfolgreich abgeschlossen wurde (synchrone Ein- und Ausgabe). Nun dauert es aber meistens eine gewisse Zeit, bis die Ein- oder Ausgabe durchgeführt wurde. Diese Zeit könnte das Programm besser mit anderen, ebenfalls wichtigen Arbeiten nutzen. Eine Möglichkeit dafür bietet asynchrone Ein- und Ausgabe. Der Systemaufruf leitet dabei die Ein- und Ausgabeoperation nur ein; das Ergebnis muss von einem Prozess später mit einem speziellen Befehl abgeholt werden. Diese Art von Systemaufrufen stellt besondere Anforderungen an das Betriebssystem, da sowohl der Auftrag als auch das Ergebnis unabhängig vom beauftragenden Prozess zwischengespeichert und verwaltet werden muss. Sowohl Unix als auch Windows NT bieten vielfältige Aufrufe für synchronen und asynchronen Datentransfer.
1.7
Die Energieverwaltung
In den letzten Jahren ist durch das stete Anwachsen der Rechnerzahl und der Verbreitung des Internet auch der Bedarf an Energie durch die Rechner gewachsen. Inzwischen verbrauchen die Rechner einen nicht unerheblichen Anteil am gesellschaftlichen Gesamtbedarf an Strom, so dass Maßnahmen in das Blickfeld des Interesses gerückt sind, um den Energiebedarf zu dämpfen. Dabei kann man einerseits so vorgehen, dass jedes Gerät wie Monitor, Drucker, Massenspeicher oder Grafikkarte für sich selbst so wenig wie möglich an Strom verbraucht, wenn es eine gewisse Zeit ungenutzt eingeschaltet ist, und in einen Energiesparmodus („Abwarten: stand-by“) übergeht. Diese Lösung ist aber nicht optimal: Weiß man, dass der Rechner bald wieder mit voller Leistung arbeiten muss, so sollten die Einzelkomponenten nicht isoliert von einander in den Stromsparmodus übergehen, sondern eher abwarten. Ist aber klar, dass der Rechner absolut nicht gebraucht wird und nur irgendwann durch einen
56
Rechnerarchitektur
externen Befehl „aufgeweckt“ werden soll, so kann auch eine fast vollständige Abschaltung durchgeführt werden („Winterschlaf: hibernation“). Entscheidend ist also eine zentrale Kontrolle der Energiesparmaßnahmen durch eine gemeinsame, aufeinander abgestimmte Strategie. Dies ist formal in der Definition für ein „Advanced Configuration and Power Management Interface ACPI” festgelegt (s. ACPI home), die von einem Firmenkonsortium (Compaq/HP, Intel, Microsoft, Phoenix, Toshiba) als Nachfolge des Advanced Power Management APM eingeführt wurde und muss sowohl von den Bauteilen bzw. Geräten als auch vom Betriebssystem (Treiber!) unterstützt werden. Erst das Zusammenspiel aller Komponenten ermöglicht ein unproblematisches Abschalten und wieder Anlaufen des Gesamtsystems. In Unix (Linux) wird dies z.Z. integriert (s. LINUX-ACPI); in Windows ist es bereits enthalten.
Anwendungen Betriebssystem-typische Anwendungs-API
Betriebssystem-typische Teile, Schnittstellen und Code
Betriebssystemunabhängige ACPI-Teile
Abb. 1.40 Überblick über die ACPI Energieverwaltung
In der Abb. 1.40 ist ein Gesamtüberblick über das System gezeigt. Wie man sieht, gliedert sich das Gesamtsystem in die von der ACPI-Spezifikation beschriebenen Teile wie Tabellen und Register, und in die betriebssystemtypischen Teile wie allgemeine Gerätetreiber, die mitspielen müssen, und spezielle ACPI-Treiber, die spezielle Geräte wie den Energiespar-Zeituhr usw. verwalten müssen. Insgesamt verlangt die ACPI-Spezifikation verschiedene Funktionen wie
Die Energieverwaltung
57
x eine Power/sleep-Taste, je eine Taste oder kombiniert in einer Taste (kurz drücken: sleep, dauernd drücken:aus) x einen Zeitzähler (timer) mit einer Taktfrequenz von 3,579 MHz, der die Energiezufuhr kontrolliert. Er muss drei Register (2 Befehls-, 1 Status-) haben. x mindestens vier Ereignis-Register, die frei belegt werden können, etwa durch „Aufwachen bei Netzaktivität“, „Aufwachen bei Tastendruck der Tastatur“, usw. x Ein Interrupt-System, das bei Auftreten des Ereignisses das ACPI-System aktiviert x Eine Stand-By-Stromversorgung (5V/500mA), die im Schlafmodus die nicht schlafenden Geräteabschnitte versorgt. Das Gesamtssystem kann man als einen Automaten ansehen, der nur wenige diskrete Zustände annehmen kann. In Abb. 1.41 sind die Zustände und ihre möglichen Übergänge als Graf gezeigt.
Abb. 1.41 Die Zustände des ACPI-Systems und ihre Übergänge
Ausgehend von einem Nicht-ACPI-Zustand („Legacy“) gelangt das System in einen von vier Zuständen: den Arbeitszustand G0, den Schlafzustand G1 mit seinen vier Stufen S1,S2,S3,S4, dem „beinahe-Aus“-Zustand G2 oder echten, elektrisch getrennten „Aus“-Zustand G3. Bis auf den G3-Zustand können alle Zustände
58
Rechnerarchitektur
durch Signale wieder verlassen werden; bei G2 ist zusätzlich ein Neustart des Betriebssystems notwendig. Das System kann je nach Anwendung alle Zusatzgeräte wie Modem, Netzkarte, Festplatte oder sogar die CPU in mehreren Stromsparstufen betreiben, wobei bei niedrigem Niveau mehr Teile vom Strom abgetrennt werden. Im Zustand D3 ist das Gerät komplett vom Strom getrennt und verliert alle noch verbliebenen Kontextinformationen, die beim Starten erst durch den Treiber wiederhergestellt werden müssen. Die ACPI-konformen Treiber haben deshalb von allen wichtigen Gerätedaten („Register“) Kopien in Form sog. „Schattenregister“. Bei der CPU äußert sich die Energiesparfunktion nicht nur in einer geringeren Taktfrequenz, sondern auch in einer geringeren Betriebsspannung und damit geringeren Leistungsaufnahme. Zusammenfassend können wir feststellen, dass die Energieverwaltung ACPI keine Software oder Hardware ist, sondern als Spezifikation auf vorhandenen Funktionen aufbaut. Fehlen diese Funktionen bei nur einem Gerät oder Gerätetreiber, so kann ACPI für das Gesamtsystem nicht funktionieren. Literatur D. A. Patterson: Reduced Instruction Set Computers, Comm. of the ACM, 28(1), 1985 D. Tabak, RISC Architecture J. Wiley, 1987; M. Slater, A Guide to RISC Microprocessors (1992) UNI: siehe http://www.unicode.org im World Wide Web Custer, H.: Inside Windows NT. Microsoft Press, Redmond, Washington 1993 Custer, H.: Inside the Windows NT File System. Microsoft Press, Redmond, Washington 1994 MacWilliams, F., Sloane, N.: The Theory of Error-Correcting Codes. North-Holland, Amsterdam 1986
ACPI-Home: http://www.acpi.info ACPI-Linux: http://acpi.sourceforge.net/
2 Netzwerkarchitektur
In heutigen Anwendungen sind Rechner meist nicht alleinstehend, sondern kommunizieren über Netzwerke miteinander. In Abb. 2.1 ist ein solches lokales Netzwerk (Local Area Network LAN) abgebildet wie es sich oft in kleineren Arztpraxen, Unternehmen oder Filialen findet.
Abb. 2.1 Ein typisches lokales Netzwerk
Dabei sind die einzelnen Rechner über ein Kabel miteinander verbunden. Üblicherweise werden dazu zwei miteinander verdrillte, isolierte Drähte verwendet (bis 1GHz) oder für größere Datenmengen Licht, das in Glasfasern geleitet wird. Ist das Signal in einer langen Leitung zu schwach geworden, so muss es durch einen Repeater verstärkt werden. Das Ziel, alle Rechner eines Netzes miteinander zu verbinden, kann man sowohl durch einen einzigen Kabelstrang erreichen, der an den Enden miteinander verbunden sein kann (Ringarchitektur), als auch dadurch, dass jeder Rechner mit einem extra Kabel zu einem zentralen Punkt verbunden ist (Sternarchitektur). In Abb. 2.2 sind mehrer solche miteinander verbundene Architekturen gezeigt, in der die Rechner als Kreise und die Vermittlungseinheiten als Vierecke gezeichnet sind. Werden die Kabelstücke durch den Apparat des zentralen Punkts zu einem einzigen, elektrisch zusammenhängenden Netzwerk geschaltet, so wird dieser als
60
Netzwerkarchitektur
Hub bezeichnet. Ein solches Netz hat zwar physisch eine Sternstruktur, logisch entspricht dies aber einer einzigen, langen Leitung. Solch eine Struktur wird gern verwendet, da sie die zentrale Kontrolle jeden Anschlusses erlaubt: fehlerhaften Rechnern im Netz kann jederzeit „der Stecker gezogen“ werden, ohne die anderen Rechner im Netz zu blockieren.
Subnetz mit Sternarchitektur
zum Internet Router Hub
Subnetz mit Ringarchitektur
Router
Backbone Router Switch Router
Repeater
Backbone-Router
Abb. 2.2 Subnetze und Backbone eines Intranets
Der Übergang der Signalinhalte von einem Netz in ein angeschlossenes anderes wird durch ein spezielles Gerät, eine Brücke (Bridge) oder ein Gateway, ermöglicht. Grundsätzlich betrachtet eine solche Bridge alle Signale und leitet diejenigen ins Nachbarnetz um, die als solche kenntlich gemacht wurden. Schnelle Brücken, die auch noch weitere Funktionen zur Vermittlung beherrschen, werden als Switch bezeichnet. Wird die Brücke gezielt angesprochen und beauftragt, die Signale aus dem lokalen Netz, wo sie erzeugt wurden, zum Zielrechner in ein anderes Netz geeignet weiterzuleiten, so spricht man von einem Router. Sowohl ein Hub als auch ein Switch kann zu einem Router ausgebaut werden. Man kann mehrere Netze über Router oder Gateways zusammenkoppeln. In Firmennetzen („Intranet“) bildet die Kopplung über ein spezielles Netz, das keine Drucker oder andere gemeinsam genutzte Geräte (Ressourcen) enthält und nur für die zuverlässige Verbindung der Subnetze zuständig ist, eine wichtige Geschäftsgrundlage. Es wird deshalb als Backbone-Netz bezeichnet im Unterschied zu Netzen, durch die nichts weitergeleitet wird, den Stub-Netzen. Router, die zu diesem zentralen Daten-Umschlagplatz vermitteln, sind die Backbone-Router. Für die logische Sicht der Nachrichtenverbindungen im Netz können wir zwei Typen unterscheiden: Entweder bauen wir erst eine Verbindung auf und senden dann die Nachrichten (verbindungsorientierte Kommunikation), oder aber wir fügen zu den Nachrichten die Empfängeradresse hinzu und erreichen so eine verbindungslose Kommunikation.
Das Schichtenmodell für Netzwerkprotokolle
61
Für die Realisierung der logisch-verbindungsorientierten Kommunikation kann man jedoch auch wiederum beide Konzepte verwenden: wir können physikalisch eine feste Verbindung errichten und dann senden (z. B. mit einer Telefon-Standleitung) oder nur den physikalischen Weg durch ein Netzwerk über mehrere Rechner durch spezielle Nachrichten initialisieren und dann die Nachrichten auf diesen vorbereiteten Weg schicken. Die exklusive Rechner-Rechner Verbindung ist dann nur logisch vorhanden; tatsächlich können mehrere solche Verbindungen dieselben Leitungen benutzen, ohne sich zu stören. Für die Realisierung beider logischer Konzepte in einem Netzwerk reicht es, die Nachrichten in Abschnitte (Datenpakete) zu zerteilen und mit einer Empfängeradresse versehen in Form zeitlicher Abschnitte des physikalischen Signals (Signalpakete) in das Netz einzuspeisen. Die Datenpakete enthalten als Zielbezeichnung eine Zahl, die Adresse des Empfängers, sowie die Adresse des Senders. Als Adresse wird beispielsweise beim Ethernet jedem Netzwerkanschluss (Platine) eines Rechners innerhalb eines LAN eine 32-Bit-Zahl zugewiesen, die MAC (Media Access Control). Dies ist eine Hardwareadresse, die nur einmal auf der Welt existiert und der Platine bei der Herstellung „eingebrannt“ wird. Damit können nicht zufällig zwei Platinen mit gleicher MAC in einem Ethernet existieren und es ist immer eindeutig, für wen ein Datenpaket bestimmt ist.. Für kleine Netze reicht dies aus; bei größeren benötigen wir aber eine andere, symbolische Adresse, aus der leicht geschlossen werden kann, in welchem Netz der Zielrechner ist. Da diese Adresse immer gleich sein sollte, egal, ob wir einen Ersatzcontroller in den Rechner stecken oder ob wir gar einen neuen Rechner an die Stelle setzen, muss diese Adresse unabhängig von der Hardware sein und zusammen mit Vorschriften (Protokollen) definiert sein, wie die Adressen zu behandeln sind. Dazu betrachten wir den Verbindungsaufbau zwischen Rechnern eines Netzes etwas systematischer.
2.1 Das Schichtenmodell für Netzwerkprotokolle Normalerweise handelt es sich bei der Netzverbindung der Rechner nicht um eine physikalische Punkt-zu-Punkt-Verbindung, sondern mehrere Computer sind mit einem Kabel verbunden. Um einen bestimmten Rechner anzusprechen, müssen auf dem Kabel nicht nur die Daten, sondern auch die Information übertragen werden, welcher Rechner gemeint ist (Adressinformation) übertragen werden. Diese weitere Verwaltungsinformation (Rechnernummer, Nachrichtenlänge, Quersumme zur Prüfung auf Übertragungsfehler usw.) wird mit den Daten in einem Nachrichtenpaket zusammengefasst. Aufgabe der Basiselektronik des Controllers ist es, die Übersetzung zwischen dem elektrischen Signal im Kabel und dem logischen Format der Nachrichtenpakete sowohl für das Lesen (Empfangen) als auch für das Schreiben (Senden) durchzuführen.
62
Netzwerkarchitektur
Mit Hilfe dieser Dienstleistung müssen weitere Funktionen wie Sendekontrolle, Aufbau einer logischen Nachrichtenverbindung zu einem anderen Rechner mit Hilfe einer Sequenz von Nachrichtenpaketen usw. aufgebaut werden. Die Folge mehrerer fest vorgegebener Schritte eines Nachrichtenaustauschs für einen Zweck (die Menge der Kommunikationsregeln) wird als Protokoll bezeichnet. Der Aufbau immer höherer Dienste, die sich auf einfache, niedere Funktionen (Dienste) stützen, entspricht unserer Strukturierung durch virtuelle Maschinen aus Kapitel 1 und ist durch das ISO-OSI-Schichtenmodell standardisiert, s. Abb. 2.3.
Rechner A 7 Anwendung 6 Präsentation 5 Sitzung 4 Transport 3 Netzwerk 2 Datenverbindung 1 Phys. Verbindung
virtuelle Verbindungen
Netzkabel
Rechner B 7 Anwendung 6 Präsentation 5 Sitzung 4 Transport 3 Netzwerk 2 Datenverbindung 1 Phys. Verbindung
Abb. 2.3 Das Open System Interconnect (OSI)-Modell der International Organization for Standardization (ISO, früher International Standards Organization)
Die verschiedenen Schichten, von 7 bis 1 absteigend durchnummeriert, haben dabei folgende Aufgaben: 7) Auf der Anwendungsschicht werden benutzerdefinierte Dienste wie Dateitransfer (FTP), spezielle Grafikanwendungen, Sicherheitsüberprüfung, Nachrichtenaustausch (electronic mail) usw. angeboten. 6) Die Präsentationsebene formatiert die Daten und legt ihre Kodierung oder ihr Kompressionsformat (Darstellung) fest und gruppiert die Daten, beispielsweise in ein anwenderabhängiges RECORD-Format. 5) Auf der Sitzungsebene wird festgelegt, wer Sender und Empfänger ist (z.B. für ein remote login), wie Fehler beim Datenaustausch behandelt werden usw. Dieses Schicht zur Kommunikationssteuerung wird meist vernachlässigt bzw. in der Anwendung realisiert. 4) Die Transportschicht wandelt nun den Strom von Daten um in Datenpakete, achtet auf die Nummerierung und, beim Empfang, auf die richtige Folge der Pakete. Hier wird auch zum ersten Mal auf die darunter liegende Hardware Rücksicht genommen. Verschiedene Ansprüche an die Fehlertoleranz (TP0 bis TP4) lassen sich hier wählen. Typische Protokollvertreter sind das Transmission Control Protocol TCP oder das User Datagram Protocol UDP.
Das Schichtenmodell für Netzwerkprotokolle
63
3) Auf der Netzwerkschicht werden alle Fragen und Probleme behandelt, die mit der globalen Netzwerktopologie und der Vermittlung zusammenhängen. Festlegen des Übertragungsweges, Nachrichtenumleitung bei Nachrichtenverkehrsstatus auf bestimmten Strecken, Maschinenkonfigurationen, Grenzen durch Bandweite und Kabellänge, kurzum, alle zu beachtenden technischen Nebenbedingungen des Netzwerks werden hier behandelt. Für eine verbindungslose Kommunikation wird meist das Internet-Protokoll IP und das X.25 Protokoll (Datex-P) verwendet. 2) Die Ebene der Datenverbindung unterteilt die großen, unregelmäßigen Datenmengen der Nachrichten in einzelne kleine Pakete (frames) von fester Größe bzw. festem Format, sendet sie und wiederholt die Sendung, wenn keine Rückmeldung erfolgt oder die Prüfsumme (CRC) beim Empfänger nicht stimmt. Sie erfüllt damit die Funktion einer Datensicherung, wobei allerdings die Reihenfolge der Datenpakete auf dem Weg zum Empfänger sich überholen können. Beispiele: LLC (Logical Link Control), LAPD (Link Access Procedure for D-channels). 1) Die Datenpakete werden zum Senden auf der physikalischen Ebene als Bits in elektrische oder optische Impulse umgesetzt und auf das Übertragungsmedium gebracht. Beispiel dafür sind die serielle ITU-T V.24-Spezifikation, der Betrieb von 10 Mbit pro Sekunde auf verdrillten Drähten (twisted pair) 10BaseT oder auch die Spezifikation der Übertragung auf Fiberglasleitungen FDDI. Üblicherweise werden Schicht 1 und 2 durch Hardware behandelt und als Network Access Layer bezeichnet. Populäres Beispiel für eine solche Zusammenfassung von der physikalischen Anschlussspezifikation und dem unteren Paketprotokoll ist das Ethernet. Die Pakete der Anwendungsdaten, die die einzelnen Schichten mit den untergeordneten Schichten austauschen, bestehen dabei aus einem Kopfteil (Header), der die Kontrollinformation des Datenpakets für die Schicht enthält, den eigentlichen Daten, die von der Schicht mit der jeweils höheren Schicht ausgetauscht werden, und einem Ende (Tail). In Abb. 2.4 ist eine solche sukzessive Kapselung der Information durch die verschiedenen Schichten schematisch gezeigt. Jeder einzelne Header hat eine genau festgelegte Länge und Format, bei der die Bedeutung jedes Bits entsprechend dem Protokoll, das die Schicht benutzt, genormt ist. Die Realisierung und Nutzung dieses Schichtenmodells ist sehr unterschiedlich und sehr umstritten.
64
Netzwerkarchitektur
Daten
Anwendungsdaten Header Schicht 6
Schicht 6 Header Schicht 5
Schicht 5 Header Schicht 4
Schicht 4
Schicht 3
Header Schicht 2
Header Schicht 3
Daten
Daten
Daten
Daten
Schicht 3 Daten
Signal-Datenpaket
Abb. 2.4 Die Kapselung der Daten und Kontrollinformationen
Beispiel: Im einfachsten Fall kann man eine Punkt-zu-Punkt-Verbindung zwischen zwei Computern herstellen, indem man die (fast immer vorhandenen) seriellen Anschlüsse, die für Drucker und andere langsame Geräte vorgesehen sind, mit einem Kabel verbindet. In diesem Fall erfüllt der Chip für die serielle Schnittstelle die Funktionen der Schicht der Datenverbindung; der elektrische Ausgangstreiber ist dann Schicht 1. Für einen Datenaustausch kann man dann ein Terminal-Emulationsprogramm verwenden, beispielsweise das populäre Kermit-Programm. Dieses beinhaltet sowohl Befehle zum Senden von Daten (Schicht 7) als auch Mechanismen zur Kodierung in Datenpakete (Schicht 6). Die Schichten 5, 4 und 3 entfallen hierbei, da es nur eine dedizierte Punkt-zuPunkt-Verbindung ist. Das Konzept, Verbindungen zwischen Rechnern direkt auf einer höheren Ebene (Ebene 5, 6 oder 7) herzustellen und damit alle Zwischenschichten zu ignorieren (end-to-end-Strategie), wird mittels gateways implementiert. 2.1.1 Beispiel UNIX Kommunikationsschichten Die einzelnen Schichten sind in UNIX sehr unterschiedlich vertreten. Historisch gewachsen war UNIX nicht für Netzwerke konzipiert. Da aber der Quellcode des Betriebssystems schon von Anfang an für Universitäten frei verfügbar war, wurden wichtige Entwicklungen und Konzeptionen für Netzwerke an UNIX ausprobiert.
Das Schichtenmodell für Netzwerkprotokolle
65
Ein interessantes Konzept wurde mit UNIX System V eingeführt: die Anordnung der Gerätetreiber in Schichten. Die Grundidee besteht darin, den gesamten Ein- und Ausgabedatenstrom einer Verbindung (stream) durch verschiedene Bearbeitungsstufen (Treiber) fließen zu lassen. Dieses streams-System ermöglicht über eine normierte Schnittstelle, beliebig Bearbeitungsstufen einzuschieben oder aus dem Strom zu nehmen. Damit ist es leicht möglich, Protokollschichten (als Treiber realisiert) auszutauschen, also auch eine andere als die populäre Kombination TCP und IP als Protokoll zu wählen. In Abb. 2.5 ist die Schichtung gezeigt, die auch einige in Abschn. 2.3 erwähnten Dienste enthält.
7 Anwendung 6 Präsentation 5 4 3 2 1
Sitzung Transport Netzwerk Datenverbindung Phys. Verbindung
named pipes, rlogin, … XDS BS-Schnittstelle: sockets ports, IP Adresse
TCP IP Network Access Layer
Abb. 2.5 Oft benutzte Protokollschichten in UNIX
Der Aufruf der Transportdienste im Kern geschieht so über spezielle Systemaufrufe. Eine Applikation kann also entweder auf hoher Ebene spezielle Konstrukte wie named pipes usw. (s. Abschnitt 2.3.3) nutzen, oder aber sie kann tief auf den Socket()-Betriebssystemdiensten (s. Abschnitt 2.3.2) aufsetzen, um eine Kommunikation über das Netzwerk zu erreichen. 2.1.2 Beispiel Windows NT Kommunikationsschichten In Windows NT sind verschiedene Netzwerkdienste untergebracht, die eine Kompatibilität sowohl zu den proprietären MS-DOS-Netzwerkprotokollen (MSNet, Server Message Block-Protokoll SMB, Network Basic Input-Output System NetBIOS-Interface, NetBIOS over TCP/IP NBT) als auch zu den Protokolldiensten anderer Hersteller erlauben sollen. Die in Abb. 2.6 skizzierte Architektur zeigt, dass die im ISO-OSI-Modell vorgesehene Schichtung nur sehr unvollständig befolgt wurde. Die Transportschicht wird durch die alternativen Protokolle NetBEUI (NETBIOS Extended User Interface) und IPX/SPX der Fa. Novell und durch das populäre TCP/IP abgedeckt. Sie setzen auf der Standardschnittstelle für Netzwerkcontroller, der Microsoft Network Driver Interface Specification NDIS, auf.
66
Netzwerkarchitektur
7 Anwendung 6 5 4 3
Präsentation Sitzung Transport Netzwerk
files, named pipes, mail slots Subsysteme Redirector NetBIOS Net BEUI
IPX/ SPX
1 Phys. Verbindung
WindowsSockets
TCP/IP
NDIS-Treiber
2 Datenverbindung NDIS Protokoll
NBT
Network Access Layer
Abb. 2.6 OSI-Modell und Windows NT-Netzwerkkomponenten
Die höheren Schichten 5, 6 und 7 werden auch alternativ von der SMB-Schicht abgedeckt, die direkt die Aktionen einer Applikation auf ein anderes Rechnersystem abbildet. Eine Applikation kann also entweder das SMB-Protokoll, den Redirector-Dienst oder aber den direkten Zugang zu den Sockets, NBT und NetBIOS nutzen, um eine Kommunikation zu erreichen. 2.1.3 Erweiterungen Die Konzeption eines solchen Schichtensystems gestattet es, zusätzliche Schichten darauf zu setzen, ohne die Funktionalität darunter zu beeinflussen. So kann man beispielsweise alle Kommunikation vor dem Senden in einer zusätzlichen Schicht verschlüsseln und beim Empfänger wieder entschlüsseln. Der dabei benutzte Kommunikationskanal kann ruhig abgehört werden; die Lauscher können mit den Daten nichts anfangen. Auf dieses Weise kann man sich trotz unsicherer Internetverbindungen ein eigenes, privates Netzwerk (Virtual Private Network VPN) schaffen. Internet Sender
VPN
Abb. 2.7 Virtual Private Network
Empfänger
Namensgebung im Netz
67
Bei Netzwerken mit höheren Ansprüchen wie E-Mail und gemeinsame Nutzung von Dateien sind die Schichten etwas komplexer. Auch die Netzwerkschichten bieten sich deshalb zur Software-Hardwaremigration an. In billigen Netzwerkcontrollern ist meist nur ein Standard-Chipset enthalten, das die zeitkritischen Aspekte wie Signalerzeugung und -erfassung durchführt. Dies entspricht der virtuellen Maschine auf unterster Ebene 2. Alle höheren Funktionen und Protokolle zum Zusammensetzen der Datenpakete, Finden der Netzwerkrouten und dergleichen muss vom Hauptprozessor und entsprechender Software durchgeführt werden, was die Prozessorleistung für alle anderen Aufgaben (wie Benutzeroberfläche, Textverarbeitung etc.) drastisch mindert. Aus diesem Grund sind bei teureren Netzwerkcontrollern viele Funktionen der Netzwerkkontrolle und Datenmanagement auf die Hardwareplatine migriert worden; der Hauptprozessor muss nur wenige Anweisungen ausführen, um auf hohem Niveau (in den oberen Schichten) Funktionen anzustoßen und Resultate entgegenzunehmen. Dabei spielt es keine Rolle, ob die von dem Netzwerkcontroller übernommenen Funktionen durch einen eigenen Prozessor mit Speicher und Programm erledigt werden oder durch einen dedizierten Satz von Chips: Durch die eindeutige Schnittstellendefinition wird die angeforderte Dienstleistung erbracht, egal wie. Es hat sich deshalb bewährt, dass der Controller nicht nur Chips enthält, die die Schichten 1 und 2 abhandeln, sondern auch ein eigenes Prozessorsystem mit Speicher und festem Programm in ROM, das den Hauptprozessor des Computersystems entlastet und Schichten 3 und 4 für ein spezielles Übertragungsmedium (z. B. Ethernet) und ein spezielles Protokoll (z. B. die Kombination Transmission Control Protocol TCP mit IP, das TCP/IP) implementiert. Diese „intelligenten“ Controller können dann mit Treibern des Betriebssystems auf hohem Niveau zusammenarbeiten. Für das Betriebssystem ist also wichtig zu wissen, welche der Schichten und Systemdienste im Controller bereits vorhanden sind und welche extra bereitgestellt werden müssen.
2.2 Namensgebung im Netz Öffnet man die einfache Punkt-zu-Punkt-Kommunikationsverbindung und bezieht mehrere Rechner mit ein, so muss man für eine gezielte Verbindung zu einem einzelnen Rechner diesen mit einem Namen oder einer Adresse ansprechen können. Es gibt verschiedene Namenskonventionen in Netzen, die je nach Anwendung eingesetzt werden. Dabei können wir grob zwischen den weltweit genutzten Namen in großen Netzen (insbesondere im Internet) und den Namen in eng gekoppelten lokalen Netzen unterscheiden
68
Netzwerkarchitektur
2.2.1 Namen im weltweiten Netz Im weltweiten Rechnerverbund des Internet hat sich eine Namenskonvention durchgesetzt, die historisch gewachsen ist. Dazu sind alle Organisationen in den USA in verschiedene Gruppen eingeteilt worden: com für Firmen, edu für Universitäten und Schulen, gov und mil für Regierung und Militär, net für Netzwerkanbieter und org für meist gemeinnützige Organisationen. Innerhalb einer solchen Gruppe (Top Level-Domäne) wird jedem Gruppenmitglied (Domäne) ein Name zugewiesen, z. B. für die Universität Berkeley aus der Top Level-Domäne edu der Name berkeley. Der volle Name für einen Rechner der Universität setzt sich aus dem Domänennamen und dem Rechnernamen zusammen, die mit einem Punkt als Trennzeichen verbunden werden. Beispiel Rechnernamen Der volle Name des Rechners OKeeffe der Universität Berkeley lautet okeeffe.berkeley.edu wobei Groß- und Kleinschreibung nicht beachtet werden. Für den Rest der Welt wurde eine andere Vereinbarung getroffen: Anstelle des Gruppennamens erscheint die Länderabkürzung. Ein Rechner diokles des Fachbereichs Informatik der Universität Frankfurt hat also den Namen diokles.informatik.uni-frankfurt.de Eine solche symbolische Adresse ist manchmal zu lang und muss nicht einzig für den Rechner sein: Für einen Rechner kann es mehrere Namen („Alias“) geben. Als kurze, maschinell leicht zu verarbeitende Bezeichnung existiert deshalb für jeden Rechner ebenfalls eine eindeutige logische Zahl, die aus der Aneinanderreihung von vier Zahlen je 8 Bit, also einer 32-Bit-Internetadresse (in der IP-Version 4) bestehen. Beispiel Numerische Internetadresse Der obige Rechner hat auch die numerische Adresse 141.2.1.2.
Die 32-Bit Adresse teilt sich dabei auf in eine Netzwerknummer und eine Rechnernummer. Die Aufteilung ist variabel, je nach Netzwerkklasse. In Abb. 2.8 ist die Aufteilung der 32-Bit-Zahl in den fünf Klassen gezeigt.
Namensgebung im Netz Byte 0
A 0
Byte 1
Netz Id
B 10 C 11 0 D 11 1 0 E 11 1 1
Byte 2
69
Byte 3
Rechner Id Netz Id
Rechner Id Netz Id
Rechner Id Multicast reserviert
Abb. 2.8 Mögliche Aufteilungen der IP-Adresse (Version 4)
In unserem Beispiel bedeutet dies, dass mit der ersten Zahl 141 = 10001101 die NetzId mit "10" beginnt und damit aus Klasse B ist. Die NetzId hat den Wert 141.2 = 10001101.00000010 = 3330. In Klasse B gibt es mit 14 Bit nur 16348 mögliche Netzadressen – viel zu wenig für die große Menge an neuen Institutionen außerhalb der USA, besonders in Europa und im asiatischen Raum, zu versorgen. Deshalb wird ein neuer IP-Standard Version 6 (IPv6) mit 128 Bit gefördert – genug Adressen, um auch allen kleinen Geräten wie Mobilrechnern, Handys oder Kühlschränken eine eigene IP-Adresse zuzuweisen. Dies erleichtert auch den Aufbau von besonders sicheren Verbindungen (Virtual Private Networks). Die IP-Nummern (NetzId) werden zentral von einer Institution (ICANN Internet Corporation for Assigned Names and Numbers bzw. für Deutschland: DENIC) genauso wie die Domänennamen vergeben, wobei die RechnerId von der Domänenorganisation (hier: der Universität) selbst verwaltet wird. Dies vereinfacht die Verwaltung und wird gern dazu verwendet, vorhandene Subnetze und ihre Rechner auf die RechnerId abzubilden. Abgesehen von der logischen Zahl existiert übrigens in jedem Netzwerkcontroller für das TCP/IP-Protokoll auch eine weitere, feste Zahl (MAC-Adresse), die für den Controller einzig ist, fest eingebaut ist und nicht verändert werden kann (im Unterschied zu den logischen Nummern der IP-Adresse). Beim Aufbau einer Verbindung wird zuerst diese physikalische Nummer referiert, dann erst die logische Bezeichnung. Die Zuordnung von der logischen numerischen Adresse zu der symbolischen Adresse aus Buchstaben muss mit Hilfe einer Tabelle vorgenommen werden. In UNIX-Systemen ist sie unter /etc/hosts zu finden, in anderen Systemen ist es eine echte Datenbank. Ein solcher Internetname kann nun dazu verwendet werden, einen Dienst auf einem Rechner anzufordern.
70
Netzwerkarchitektur
Beispiel Namen im Internet Im World Wide Web (WWW), einem Hypertext-Präsentationsdienst, setzt sich als Adresse ein Uniform Resource Locator (URL) aus dem Namen für das Protokoll des Dienstes (z.B. http:// für das Hypertextprotokoll HTTP), dem Internetnamen des Rechners und dem lokalen Dateinamen zusammen. Für den Dateikopierdienst (File Transfer Protocol FTP) lautet dann die URL der Domäne uni-frankfurt.de und der Datei /public/Text.dat ftp://ftp.informatik.uni-frankfurt.de/public/Text.dat
Man beachte, dass die Groß- und Kleinschreibung zwar unwichtig beim Internetnamen, nicht aber beim Dateinamen im lokalen Dateisystem ist! Der Rechner uni-frankfurt.de wird also auch in der Schreibweise UniFrankfurt.DE gefunden, nicht aber die Datei /public/Text.dat in der Schreibweise /Public/text.dat. Das Wurzelverzeichnis des so referierten Dateibaums ist willkürlich und wird für jedes Protokoll anders gesetzt. Wird beispielsweise das Verzeichnis /Data/Web als Wurzel eingetragen, so hat die Datei /Data/Web/images/Background.gif die URL http://informatik.unifrankfurt.de/images/ Background.gif. Die Zuordnung des Namens zur logischen IP-Adresse ist nicht automatisch, sondern muss in einer Namensliste nachgesehen werden. Im lokalen Netz wird dies von einem name server erledigt, der entweder die gewünschte Rechneradresse direkt in seiner Liste aller bekannten Rechner stehen hat oder aber bei einem weiteren Rechner nachfragen muss. Die Rechnernamen und ihre Dienste einer ganzen Domäne werden in einem speziellen Domain Name Server (DNS) geführt, wobei jeder DNS nur die direkt angeschlossenen Computer sowie weitere DNS-Rechner kennt, an die er Anfragen für ihm unbekannte Rechner weiterleitet. 2.2.2 Namen im regionalen Netz Beim Zusammenschluss mehrerer lokaler Netze zu einem regionalen Netz (wide area network WAN ), bestehend aus mehreren Domänen, ist meist das Problem zu bewältigen, netzübergreifend und herstellerneutral die Möglichkeiten und Dienste (Drucker, Suchdienst, Rechenkapazität, etc.) anzubieten. Dabei ist nicht nur das Problem zu bewältigen, einen netzübergreifenden, einheitlichen Namen für die Ressource zu finden um sie anzusprechen, sondern überhaupt das Wissen über ihre Existenz (und ihr Verschwinden beim Abschalten) zu erhalten. Eine weitverbreitete Methode dafür ist die Einrichtung eines globalen Namendienstes, beispielsweise des bereits erwähnten DNS. Allerdings sind für eine effiziente Ressourcenverwaltung noch mehr Informationen erforderlich als nur der Name, bei einem Drucker etwa, ob er das Format "PostScript" kennt, welcher Druckertyp es ist und wie viel Toner und Papier zu einem Zeitpunkt noch übrig ist. Für diese Aufgabe wurde der Standard X.500 im Jahre 1988 von der CCITT spezifiziert. Er enthält DAP (Directory Access Protocol), das zum Zugriff auf die In-
Namensgebung im Netz
71
formationen dient, DSP (Directory Service Protocol), mit dem die Kommunikation zwischen Servern durchgeführt wird, und DISP (Directory Information Shadowing Protocol). Allerdings setzt X.500 auf dem vollständigen ISO/OSI-Schichtenprotokoll auf, was alle Implementierungen sehr langsam und aufwendig machte. Deshalb wurde eine vereinfachte Version, das LDAP (Lightweight DAP), sehr begrüßt. Sie nimmt dadurch einen Teil der Last vom Klienten, indem sie ohne Zwischenschichten direkt auf der TCP/IP-Schicht aufsetzt. Der LDAP-Dienst bietet neben einem einheitlichen Namen auch die Möglichkeit, Objekte und ihre Attribute aufzunehmen. Beispiel Windows 2000 Active Directory Service ADS Der ADS in Windows 2000 setzt auf den Mechanismen und Diensten von LDAP auf und bietet einen Dienst an, bei dem die Namensgebung der Unternetze einheitlich zusammengefasst wird in der Form
://, etwa „Unix://Hera/Zentrale“. Der NamensID bezeichnet den hersteller- bzw. systemabhängigen obersten Knoten (root) einer Domäne, der Pfad den in diesem Untersystem gebräuchlichen Pfad. Jedes Untersystem wird als „aktives Objekt“ behandelt, das seine Angaben (Attribute) von selbst aktuell hält. Ein solches „aktives Verzeichnis“ kann auch selbst wieder als Eintrag (Objekt) ein anderes „aktives Verzeichnis“ enthalten, so dass das gesamte System aus Domänen eine Baumstruktur bildet. Die Blätter des Baumes sind die gewünschten Ressourcen. Eine spezielle Schnittstelle ADSI gestattet es, die Ressourcen abzufragen und zu verändern. Wird auf das selbe Objekt von zwei verschiedenen Seiten geschrieben, so bleibt nur die letzte Änderung erhalten. Sequenznummern des Objektzustandes (USN) gestatten es dem zugreifenden Dienst, Kollisionen zu anderen gleichzeitig zugreifenden Diensten zu erkennen.
2.2.3 Namen im lokalen Netz Mit dem Internetnamen haben wir eine Möglichkeit kennen gelernt, einen Namen für einen Rechner zu finden. In einem lokalen Netz (local area network LAN), beispielsweise bei mehreren Rechnern, die in einem großen Raum stehen oder über mehrere Räume einer Arbeitsgruppe verteilt sind, ist diese explizite Namenskonvention aber zu unpraktisch. Um eine Datei von einem Rechner zu einem anderen zu transportieren, muss man beide Rechnernamen sowie die Pfadnamen der lokalen Dateisysteme angeben. Diese Flexibilität bei der ausführlichen Bezeichnung interessiert den Benutzer eigentlich gar nicht; seine Arbeitsgruppe bleibt ja immer gleich. Es ist deshalb sinnvoll, vom Betriebssystem aus Mechanismen bereitzustellen, mit denen der Zugriff auf das Dateisystem eines anderen Rechners transparent ohne Angabe des vollen Rechnernamens gestaltet werden kann. Sind mehrere Rechner in einem lokalen Verbund zusammengeschaltet, so kann man beispielsweise die Dateisysteme von den Rechnern „Hera“ und „Kronos“ zu einem gemeinsamen,
72
Netzwerkarchitektur
homogenen Dateibaum wie in Abb. 2.9 zusammenschließen. Als gemeinsame, virtuelle Wurzel des Dateisystems erscheint das Symbol // ; es dient als Schlüsselzeichen für Dateianfragen. Ein solcher Ansatz stellt das Dateisystem auf beiden Rechnern gleichartig (homogen) dar; alle Benutzer der Rechner referieren den gleichen Namensraum. // }
Hera
Zentrale
EDV
} }
}
Kronos }
}
Abteilung 7
Abb. 2.9 Ein homogener Dateibaum im lokalen Netz
Es gibt aber auch andere Ansätze. Angenommen, wir stellen nur eine einseitige Verbindung her und binden den Rechner Kronos in unserem Beispiel an das Verzeichnis „AndereAbteilungen“. Alle Anfragen auf Hera werden nun an Kronos weitergeleitet, dort bearbeitet und an Hera zurückgegeben. Für den Benutzer von Hera bietet sich nun ein Bild des Dateisystems, wie es in Abb. 2.10 (a) gezeigt ist. Für den Benutzer von Kronos aber hat sich nichts geändert; er bemerkt nicht in Abb. 2.10 (b) den zusätzlichen Service, den Kronos für Hera bietet und hat nur Sicht auf das eigene, beschränkte Dateisystem: Die Dateisysteme sind inhomogen zusammengekoppelt.
/ Zentrale
EDV }
/ AndereAbteilungen Abteilung 7
(a) Sicht von Hera
}
Abteilung 7
}
} (b) Sicht von Kronos
Abb. 2.10 Lokale, inhomogene Sicht des Dateibaums
Beispiel UNIX Das DFS-System Das Andrew File System (AFS) der Carnegie-Mellon-Universität unterstützt eine globale, homogene Dateisicht und wurde als Distributed File System (DFS) dem DCE-Paket beigelegt. Für jede Datei wird entschieden, ob sie local oder remote existiert, und alle Zugriffe danach organisiert. Dabei benutzt es Stellvertreterprozesse, um Daten auszutauschen und Protokolle zwischen den Rechnern abzuwickeln.
Namensgebung im Netz
73
Beispiel UNIX Das NFS-System Die Namensgebung im Network File System (NFS), das von der Fa. SUN für Berkeley-UNIX entwickelt wurde und inzwischen auch bei anderen UNIXVersionen verbreitet ist, orientiert sich nach dem zweiten, inhomogenen Modell. Es geht davon aus, dass ein (oder mehrere) Dateiserver im Netz existieren, die jeweils ein Dateisystem zur Verfügung stellen. Möchte ein Rechner ein solches System nutzen, so kann er mit einem speziellen mount()-Befehl (der z. B. im Startup-File /etc/rc enthalten sein kann) den Dateibaum des Servers unter einem bestimmten Verzeichnis referieren. Da dies auf jedem Rechner ein anderes Verzeichnis sein kann, ist der resultierende Dateibaum auf jedem Rechner möglicherweise unterschiedlich zu seinem Nachbarn. Beim Einsatz des NFS-Systems ist also sorgfältige Planung des Namensraums nötig. Beispiels Windows NT Namensraum im Netz In Windows NT gibt es zwei verschiedene Mechanismen, um auf Dateien anderer Rechner zuzugreifen. Der eine Mechanismus bedient sich der symbolic links, also durch einen Texteintrag als Dateiverweis. In dem Verzeichnis der Geräte \Device wird für jede Netzdateiart (MS-Redirector File System, Novell NetWare File System, ...) ein besonderer Treiber eingehängt, dessen parseMethode zum Aufbau einer Netzverbindung und zur Abfrage des Netzdateisystems führt. Setzt man nun am Anfang eine Gerätebezeichnung, z. B. „V:“, auf diesen Netzwerktreiber, so wird er automatisch beim Öffnen einer Datei angesprochen. In Abb. 2.11 ist die Abfolge eines solchen umgeleiteten Dateinamens durch Pfeile angedeutet.
\ Device
\Device\NetWareFileSystem\public\text.doc DosDevices
Anfrage
Floppy0
NetWareFileSystem
A: .. V:
Verweis
UNC:
MUP Redirector
\public\text.doc
Anfrage V:\public\text.doc
Abb. 2.11 Auflösung von Netzwerk-Dateianfragen
Der zweite Mechanismus benutzt das spezielle Microsoft-Namensformat „Universal Naming Convention UNC“, in dem alle Netzwerknamen mit dem Zeichen „\\“ beginnen. Bemerkt das Windows32-Subsystem, dass ein solcher Name benutzt wird, so wird „\\“ automatisch durch das DOS-Gerät „UNC:“ ersetzt. Von diesem
74
Netzwerkarchitektur
geht ein symbolic link zu einem Treiber namens MUP (Multiple UNC Provider). Dieser fragt nun mit einem IRP bei jedem registrierten Netzwerk-Dateimanager an, ob der entsprechende Pfadname erkannt wird. Ist dies bei mehreren der Fall, so entscheidet die Reihenfolge der Registrierung der Dateimanager in der Registrationsdatenbank. Beispiel Auflösung der Dateinamen mit UNC Angenommen, wir wollen die Datei \\textserv\public\text.doc öffnen. Dann wird diese Anfrage zu UNC:\textserv\public\text.doc und weiter zu \Device\MUP\textserv\ public\text.doc übersetzt. Vom Treiber \Device\MUP wird nun die Zeichenkette textserv\public\text.doc an die Treiber Redirector, NetwareFileSystem usw. weitergegeben und die korrespondierende parseMethode aufgerufen. Jeder Treiber fragt über seine Netzwerkverbindung bei seinem Dateiserver an, ob diese Datei existiert. Wenn ja, öffnet er eine Verbindung dazu. Im Windows NT-Namensraum ist also wie beim NFS-Dateisystem keine einheitliche Sicht auf die Dateisysteme garantiert. Die Netzwerkverbindungen werden wie beim NFS-System anfangs eingerichtet und garantieren nur einen lokalen Zusammenhang eines Dateiservers zu einem Klienten.
2.3 Kommunikationsanschlüsse Außer der Namensgebung in Netzwerken benötigen auch Mechanismen, um eine Kommunikationsverbindung zu implementieren. In diesem Abschnitt sind die wichtigsten kurz beschrieben. 2.3.1 Ports Hier hat sich das Konzept der Kommunikationspunkte durchgesetzt: Jeder Rechner besitzt als Endpunkt der Netzwerkkommunikation mehrere Kommunikationsanschlüsse. An der Schnittstelle zur TCP/IP-Schicht sind dies die Ports („Türen“), die durchnummeriert sind. Einigen der 16-Bit-Portnummern von TCP ist per Konvention (well known port number) ein besonderer Dienst zugeordnet, dem man Aufträge geben kann. In Abb. 2.12 ist ein Auszug aus einer Liste von Zuordnungen gezeigt, die man beispielsweise in UNIX in der Datei /etc/services findet. Einen solchen Kommunikationspunkt kann man auch mit einem Briefkasten (mailbox) vergleichen. Für den Mailbox-Dienst muss das Betriebssystem allerdings zusätzlich zwei Warteschlangen einrichten: eine für die eingehenden Nachrichten und eine für die zu sendenden Nachrichten.
Kommunikationsanschlüsse
Dienst Telnet FTP SMTP rlogin rsh portmap rwhod portmap
Portnummer 23 21 25 513 514 111 513 111
75
Protokoll TCP TCP TCP TCP TCP TCP UDP UDP
Abb. 2.12 Zuordnung von Portnummern und Diensten
Außerdem muss jede Warteschlange mit einem Semaphor abgesichert sein. Die Gesamtheit dieser Prozeduren und Datenstrukturen bildet eine Schicht, die auf den Ports aufsetzt und als Endpunkte einer Interprozesskommunikation angesehen werden kann. Eine Kommunikationsverbindung zwischen zwei Prozessen A und B lässt sich also beschreiben durch ein Tupel ( Protokoll, RechnerAdresse von A, ProzessId von A, RechnerAdresse von B, ProzessId von B )
Beispiel UNIX Transport Layer Interface TLI Mit UNIX System V, Version 3 wurde 1986 das Transport Layer Interface (TLI) eingeführt, das aus einer Bibliothek libnsl.a von Funktionen (Network Service Library) besteht und auf der Transportschicht aufsetzt. Eine leicht verbesserte Form wurde von X/Open-Normierungsgremium als „Extended Transport Interface XTI“ aufgenommen. Das obige Fünftupel, das die Kommunikationsverbindung spezifiziert, dient hier dazu, um den Prozessen direkt die Kommunikation über sogenannte Transportendpunkte zu ermöglichen. In Abb. 2.13 ist das Modell gezeigt. Prozeß A
Prozeß B
Transportendpunkt
Transportendpunkt Transportschicht
Abb. 2.13 Kommunikation über Transportendpunkte
76
Netzwerkarchitektur
In TLI kann ein Prozess nach Wahl synchron oder asynchron kommunizieren; in XTI ist dies nur synchron möglich. Da die Transportschicht transparent ist, kann man verschiedene Parameter beispielsweise des TCP/IP-Protokolls nicht beeinflussen. Dies kann sich nachteilig auswirken.
2.3.2 Sockets Ein weiteres logisches Kommunikationsmodell ist das Socket-Modell, das eine punktorientierte Kommunikation mit Warteschlange organisiert. Es unterscheidet zwischen einem Server, der die Kommunikation für eine Dienstleistung anbietet und auf Kunden (Clients) wartet, und den Kunden, die eine zeitweilige Kommunikation zu ihm aufbauen. Am Anfang müssen beide, Server und Client, einen Socket mit dem Systemaufruf socket() unter einem Namen einrichten. Ein Socket ist die Kombination einer IP-Adresse und einer Portnummer, etwa in der Form 141.2.1.2:21 für den FTP-Port 21 von diokles.informatik.uni-frankfurt.de. Der Name des aufrufenden Prozesses wird mit einem bind()-Systemaufruf deklariert und im System registriert. Der Prozess tritt damit aus der Anonymität heraus und ist offiziell unter diesem Namen von außen ansprechbar. Der Serverprozess wartet nun mit listen(), bis ein Clientprozess mit connect() eine Verbindung zu ihm verlangt. Dabei ist der Name des Prozesses angegeben; seine Prozessnummer ist ja außerhalb unbekannt. Ist der Kunde akzeptiert, so bestätigt der Serverprozess die Verbindung mit accept() und die eigentliche Kommunikation kann beginnen. Client
Server
socket()
socket()
bind(„Kunde“)
Kunde
ServerDienst
bind(„ServerDienst“)
connect()
listen() accept()
send() recv()
recv() send()
close()
close()
Abb. 2.14 Kommunikationsablauf im Socket-Konzept
In Abb. 2.14 ist der Ablauf des Kommunikationsprotokolls im Socket-Konzept gezeigt, wobei die Namensgebung mit Etiketten visualisiert ist. Beim Schließen des Sockets mit dem normalen close()-Systemaufruf kann noch angegeben werden, ob die Daten in der Warteschlange gesendet oder gelöscht werden sollen.
Kommunikationsanschlüsse
77
Derartige Socket-Kommunikationsverbindungen sind auch zwischen den Prozessen auf dem lokalen Computer möglich und stellen damit eine Möglichkeit der Interprozesskommunikation dar, die im Unterschied zu pipes unabhängig von der Prozesserzeugungsgeschichte ist. 2.3.3 Named Pipes Ein weiteres wichtiges Kommunikationsmodell sind die mit einem Namen versehenen Pufferbereiche (named pipes), die wie schreib- und lesbare Dateien auch von außerhalb Prozessen einer Prozessgruppe angesprochen werden können. Ist eine solche Datei auf einem vernetzten Rechner eingerichtet, so können Prozesse auch über das Netzwerk-Dateisystem darauf zugreifen und so eine Interprozesskommunikation zwischen Rechnern durchführen. Beispiel UNIX named pipes Die Netzwerkfähigkeit von named pipes gilt allerdings nicht für das NFSDateisystem, da named pipes in UNIX als special file mit mknod() eingerichtet werden. Zu jeder named pipe gehört also ein Treiber, der anstelle des DateiMassenspeichers die Puffer des Betriebssystems zur Zwischenspeicherung und dem Aufbau der FIFO-Datenwarteschlange nutzt und ein atomares Schreiben/Lesen der named pipe ermöglicht. Named pipes lassen sich also meist nur zur Interprozesskommunikation nutzen, wenn alle beteiligten Prozesse auf demselben Rechner sind (Linux). Im Unterschied dazu erlaubt die in SYSTEM V innerhalb des Stream System eingeführte STREAM von der Art named pipe eine mit Namen versehene Kommunikationsverbindung zu einem Prozess auf einem anderen Rechner. Mit den Aufrufen socket pair() wird ein bidirektionaler TCP/IP-Kommunikationskanal eröffnet; mit bind() erhält er noch zusätzlich einen Namen, unter dem er auf dem anderen Rechner referiert werden kann. Beispiel Windows NT named pipes In Windows NT wird der Aufruf CreateNamedPipes() zum Erzeugen einer named pipe Kommunikationsverbindung ebenfalls über spezielle Treiber abgehandelt. Im Unterschied zum NFS-Dateisystem ist der Zugriff auf dieses im globalen Namensraum angesiedelte Objekt aber auch über das Netzwerk möglich, so dass Prozesse auf verschiedenen Rechnern über eine named pipe mit ReadFile() und WriteFile() kommunizieren können. Üblicherweise wird die named pipe auf dem Server erzeugt, so dass alle Clients sie danach für einen Dienst nutzen können. Eine named pipe wird wie eines Datei geöffnet, wobei allerdings ein Dateiname im UNC-Format verwendet werden muss \\ComputerName\PIPE\PipeName Lokale named pipes benutzen für ComputerName das Zeichen „ .“
78
Netzwerkarchitektur
Das named pipe-Konstrukt in Windows NT ist unabhängig vom verwendeten Übertragungsprotokoll. Es ist allerdings nur zwischen Windows NT (oder OS/2)-Rechnern möglich im Unterschied zum Socket-Konstrukt, das kompatibel zu UNIX-Sockets ist. Für eine named pipe-Verbindung zu UNIX muss dort ein besonderer Prozess, z. B. der LAN Manager for UNIX LM/U, installiert sein, der die Übertragung vermittelt.
2.3.4 Mailboxdienste Im Unterschied zu den bisher betrachteten bidirektionalen Punkt-zu-Punkt-Kommunikationsverbindungen erlauben allgemeine Mailboxdienste das Senden von Nachrichten von einem Prozess an mehrere andere (multicast und broadcast). Das Kommunikationsmodell dafür entspricht einem Senden eines Briefes an einen Empfänger: Voraussetzung für den Empfang ist die Existenz eines Briefkastens beim Empfänger; ob der Brief auch angekommen ist, bleibt dem Sender aber verborgen, wenn er nicht einen Bestätigungsbrief in den eigenen Briefkasten erhält. Dabei ist der Empfang der Nachrichten beim Server allerdings zusätzlichen Beschränkungen unterworfen, die stark vom darunter liegenden Transportprotokoll abhängen: x Die Reihenfolge der Nachrichten beim Sender muss nicht die gleiche beim Empfänger sein. x Der Empfang einer Nachricht ist nicht garantiert. Beispiel Windows NT mailslots Der Briefkasten entspricht hier einem mailslot beim Server, der mit dem CreateMailslot(MailBoxName)-Systemaufruf erzeugt wird. Das Senden von Nachrichten wird beim Client mit dem CreateFile(MailSlotName)Systemaufruf eingeleitet, jede Nachricht mit einem WriteFile()-Aufruf verschickt und die Kommunikation mit CloseFile() beendet. Der Name eines mailslot beim CreateFile()-Aufruf hat dabei das Format
MailSlotName = „\\ComputerName\mailslot\MailBoxName” Wird für ComputerName das Zeichen „.“ verwendet, so wird ein lokaler mailslot auf dem Rechner mit dem Namen MailBoxName angesprochen; wird ein echter Computername dafür gesetzt, so wird dieser Rechner kontaktiert. Ist ComputerName ein Domänenname, so werden die Nachrichten an alle Rechner der Domäne geschickt. Beim Zeichen „*“ für ComputerName wird auf das angeschlossene Netzwerk ein broadcast durchgeführt, wobei alle Rechner angesprochen werden, die einen mailslot mit dem Namen MailboxName haben. Üblicherweise wird dies der Name eines Dienstes sein; der erste freie Dienst wird sich darauf melden. Da in Windows NT nur unzuverlässige Datagrammdienste für mailslots verwendet werden, gelten die obigen Einschränkungen von Briefkästen.
Kommunikationsanschlüsse
79
In Windows NT kann außerdem die maximale Länge einer Nachricht, die jeweils mit einem SendFile() bzw. ReadFile() übermittelt wird, stark vom verwendeten Transportprotokoll abhängen. Beim NetBEUI-Protokoll beträgt beispielsweise die Länge bei Punkt-zu-Punkt-Nachrichten maximal 64 KB, bei broadcast-Nachrichten nur maximal 400 Byte. Allgemein reicht das Mailboxkonzept kaum aus, um Nachrichten strukturiert auszutauschen. Selbst wenn man eine zuverlässige, verbindungsorientierte Transportschicht und keinen ungesicherten Datagrammdienst für das darunter liegende Transportprotokoll voraussetzt, so muss man trotzdem zusätzliche Regeln (ein zusätzliches Protokoll) einführen, was die Nachrichten bedeuten, wann eine mailbox wieder gelöscht werden kann und wie mit unbeantworteten Nachrichten verfahren werden soll. Aus diesen Gründen kann man den mailbox-Dienst eher als eine weitere Zwischenschicht für höhere Dienste ansehen. 2.3.5 Remote Procedure Calls Es gibt in Rechnernetzen verschiedene, netzwerkweite Dienste wie die elektronische Post oder das Drucken von Dateien im Netzwerk. Einer der wichtigsten Dienste, auf den sich viele Programme stützen, ist der Aufruf von Prozeduren auf dem Server durch den Client. Dieser Prozedur-Fernaufruf (Remote Procedure Call (RPC), Remote Function Call (RFC)) funktioniert für den aufrufenden Prozess wie ein einfacher, lokaler Prozeduraufruf; die Realisierung durch einen Auftrag, der über ein Netzwerk an einen anderen Rechner geht, dort ausgeführt wird und dessen Ergebnisse über das Netzwerk wieder zum aufrufenden Prozess zurückkommen, bleibt dabei verborgen. Das Konzept eines Prozedurfernaufrufs ist sehr wichtig. Durch das hohe Abstraktionsniveau kann es gut geräte- und architekturunabhängig eingesetzt werden und hat sich deshalb als ein universeller Mechanismus für Client-ServerAnwendungen bewährt, der auch noch in sehr inhomogenen Netzwerken funktioniert. Die Implementierung eines RPC benutzt dazu entweder Prozeduren, die den gleichen Namen wie die Originale haben (stub procedures), aber statt dessen nur die Argumente verpacken und über eine Transportschicht zum Server schicken. Oder aber es wird nur ein RPC aufgerufen, dem der Name der gewünschten Prozedur als Parameter beigefügt wird. Beim Server werden die Argumente wieder entpackt und den eigentlichen Prozeduren mit einem normalen Prozeduraufruf übergeben. Die Ergebnisse durchlaufen in umgekehrter Richtung die gleichen Stationen. In Abb. 2.15 ist das Grundschema eines RPC gezeigt.
80
Netzwerkarchitektur
Client
Server
Anwenderprozeß
RPC- Prozeß
RPC-Prozeduren
RPC-Prozeduren
Transport
Prozeduraufruf
Transport
OriginalProzeduren
Abb. 2.15 Das Transportschema eines RPC
Wie bei normalen Prozeduren, so gibt es auch hier zwei Versionen: synchrone RPCs, die den aufrufenden Prozess so lange blockieren, bis die gewünschte Leistung erbracht wurde, und asynchrone RPCs, die den aufrufenden Prozess benachrichtigen, wenn die RPC-Ergebnisse vorliegen. In Abb. 2.16 ist der Ablauf eines synchronen RPC gezeigt.
Client Stub Prozeduraufruf
Netzwerk
Argumente packen
Stub
Server wartet ..
RPC
warten … RPC return
Argumente entpacken Prozeduraufruf OriginalRückkehr ablauf Ergebnisse packen
Ergebnisse auspacken
RETURN
Abb. 2.16 Ablauf eines synchronen RPC
Ein besonderes Problem bei einem Prozeduraufruf bilden die unterschiedlichen Hard- und Softwaredatenformate, die in den verschiedenen Rechnersystemen verwendet werden. Beginnen wir mit der Hardware. Eine einfache 32-Bit-Integerzahl, bestehend aus 4 Byte mit je 8 Bit, wird in manchen Systemen mit dem höchstwertigsten Byte auf der niedrigsten Byteadresse (Format big endian) abgelegt, in anderen auf der höchsten Byteadresse (Format little endian).
Kommunikationsanschlüsse
81
In Abb. 2.17 ist die Bytefolge auf zwei verschiedenen Prozessorarchitekturen gezeigt. Auch das Format von Fließkommazahlen (IEEE 754-1985: Mantisse 23 Bit, Exponent 8 Bit, Vorzeichen 1 Bit) kann verschieden sein, genauso wie die Kodierung von Buchstaben (ASCII-Code oder EBCDIC-Code). Motorola 680X0, IBM 370 höherwertig Byte0 Byte1
niederwertig Byte3
big endian
Byte2
Intel 80X86, VAX, NS32000 höherwertig Byte3 Byte2
niederwertig Byte0
little endian
Byte1
Abb. 2.17 Die big endian- und little endian-Bytefolgen
Die Schicht zum Einpacken und Auspacken der Argumente (data marshaling) und Ergebnisse muss allen diesen Fragen Rechnung tragen und die Daten zwischen ihrer plattformabhängigen Darstellung und der Repräsentation bei der Kommunikation hin- und hertransformieren, beispielsweise durch eine neutrale Darstellung in XML (extended markup language). Ähnliches wird auch durch den Vorgang der „Serialisierung“ in der Sprache Java erreicht. Eine weitere Aufgabe besteht in der Anpassung der Daten an die compilergenerierte Aufreihung (alignment) auf den Speicheradressen. Manche Prozessoren verweigern den Zugriff auf eine Zahl, wenn sie nicht auf einer geraden Adresse beginnt, oder benötigen zusätzliche Zyklen. Aus diesem Grund fügen die Compiler, wenn sie beispielsweise in einem RECORD nach einem Buchstaben eine Zahl speichern wollen, vorher ein Leerzeichen ein. Auch dies muss vom data marshaling beachtet werden. Beispiel UNIX Remote Procedure Calls Ein RPC in UNIX (nicht zu verwechseln mit dem remote copy-Kommando rcp) wird mit Hilfe spezieller Bibliotheken verwirklicht (SYSTEM V: in /usr/lib/librpc.a, sonst in /lib/libc.a enthalten) und besteht aus zwei Schichten: zum einen aus den RPC-Mechanismen für die Stubprozeduren und zum anderen aus dem Verpacken/Entpacken der Argumente mit Hilfe der External Data Representation XDR-Schicht. Insgesamt sind auch die RPC-Prozeduren wieder in höhere und niedere Aufrufe geschichtet. Die oberste Schicht besteht aus den Aufrufen registerrpc(), mit dem beim Server ein Dienst (eine Prozedur) angemeldet wird, der Prozedur svr_run(), mit der der Serverprozess sich blockiert und auf einen RPC wartet, und der Prozedur callrpc(), mit der der Clientprozess die gewünschte Prozedur auf dem Server aufruft.
82
Netzwerkarchitektur
Die mittlere Schicht wird von Prozeduren für Client und Server gebildet, um Parameter des Transportprotokolls, Berechtigungsausweise usw. einzustellen (Prozeduren mit dem Präfix clnt_ und svc_). Die unterste Schicht reguliert sehr tiefgehende Details des RPC/XDRProtokolls und sollte nicht ohne Not direkt angesprochen werden (Präfix pmap_, auth_, xdr_). Das RPC-System ist in UNIX beispielsweise über das Network File System (NFS) realisiert; viele Zusatzdienste zu NFS benutzen RPCs zu ihrer Implementierung. Einen anderen Zugang bietet das RPC-System im DCE-Softwarepaket, in dem die Dienste in einer Interface Definition Language (IDL) abstrakt formuliert werden. Ein Compiler übersetzt dann die Prozeduraufrufe in Stub-Aufrufe und regelt zusammen mit einer Laufzeitunterstützung die Kommunikation und die Anfrage bei einem Verzeichnisdienst aller verfügbaren Server, so dass der aufrufende Client (und damit der Programmierer) vorher nicht wissen muss, auf welchem Rechner im Netz die benötigten Dienste (Dateien, Rechenleistung usw.) zur Verfügung stehen. Beispiel Windows NT Remote Procedure Calls Windows NT bietet eine breite Palette von synchronen und asynchronen RPCs, siehe Sinha (1996). Die RPCs können sowohl auf die gleiche Maschine an einen anderen Prozess als auch auf einen bestimmten Server (verbindungsorientierter Aufruf) oder einen bestimmten Service auf einen unbestimmten Server (verbindungsloser Aufruf) zugreifen. Dabei können verschiedene low-levelMechanismen und Transportprotokolle benutzt werden, s. Abb. 2.18. Die Formatierung bzw. das Verpacken der Argumente für den RPC wird von Stubprozeduren übernommen, die die Daten in das Network Data Representation (NDR)-Format übertragen.
presentation layer
RPC named files
session layer transport layer network layer
mail WinNet slots API redirector
TCP/IP
IPX/SPX
sockets NetBEUI
...
Abb. 2.18 Die Schichtung der RPC in Windows NT
etc.
Telefonieren über Internet: Voice over IP
83
Die Stubprozeduren müssen dabei vom Programmierer nicht selbst erstellt werden; ein spezieller Compiler (Microsoft RPC IDL Compiler MIDL) erstellt aus Konfigurationsdateien zusätzlichen Code, der mit dem Client bzw. Serverprogramm kompiliert und gelinkt wird und zur Laufzeit spezielle Bibliotheken anspricht. Dadurch wird die Netzwerkverbindung für den Programmierer vollständig transparent. Besondere Protokolle können durch ein Präfix vor dem Pfadnamen des gewünschten Service gewählt werden. Beispielsweise wird mit „ncacn_ip_tcp: MyServer[2004]“ das TCP/IP-Protokoll benutzt, um beim Rechner MyServer an Port 2004 einen Dienst anzufordern.
2.4 Telefonieren über Internet: Voice over IP Netzwerke zu schaffen und zu unterhalten ist teuer. Aus diesem Grund ist es verlockend, mehrere bestehende Netze zu einem einzigen zusammenzufassen und damit Geld zu sparen. Ein typisches Beispiel dafür ist die aktuelle Tendenz, das Datennetz und das Telefonnetz zusammenzulegen. Dies wird durch die laufende Erhöhung des Datenaustauschvolumens bei (fast) konstantem Sprachaufkommen gefördert. In Abb. 2.19 ist das Verhältnis von Sprachaufkommen zu Datenaufkommen in den nächsten Jahren als Prognose gezeigt, das prinzipiell einer 1/t-Kurve entspricht.
Zeit t
Abb. 2.19 Verhältnis von Sprache zu Datenaufkommen als Funktion der Zeit (aus: BMWi: „Entwicklungstrends im Telekommunikationssektor bis 2010“)
Die Sprache zeigt sich dabei nur noch als ein immer mehr zu vernachlässigende Kommunikation, deren Extrabehandlung zu teuer werden wird. Verspricht ein solcher Ansatz der Integration von Sprach- und Datenkommunikation nur zusätzliche Vorteile wie eine bessere Sprachqualität, so scheint die Wahl klar zu sein. Leider ist die Welt aber nicht ganz so einfach. Aus diesem Grund ist diese Abschnitt in zwei Teile geteilt: einen Abschnitt, der die technischen Grundlagen klärt und einen zweiten Abschnitt, der sich mit dem Nutzungskontext befasst.
84
Netzwerkarchitektur
2.4.1 Technische Konzepte von VoiceOverIP Die Sprachkommunikation über das Internet verlangt erst eine spezielle Aufbereitung der Sprache vor dem Versenden in Form von Datenpaketen: der AnalogDigitalwandlung der Sprachsignale. In Abb. 2.20 ist eine solche zweiseitige Verbindung von Mikrofon zu Lautsprecher (im Telefonhörer) beim Empfänger und der Rückweg zum Gesprächsteilnehmer gezeigt. Das Internet oder Intranet dazwischen dient nur der Übertragung der digitalen Informationen.
Analog/ Digital wandlung
Digital/ Analog wandlung
Digital/ Analog wandlung
Analog/ Digital wandlung
Telefon
Telefon
Abb. 2.20 Wandlungen des Sprachsignals und Transport über das Netzwerk
Wie funktioniert das nun genau? In Abb. 2.21 ist das Schema gezeigt, das beim PCM (Puls Code Modulation)-Verfahren verwendet wird.
...
Zeit t
+3
Segment 3, 16 Intervalle
+2
Segment 2 16 Intervalle
+1 -1 -2
Segment 1 / 16 Int.
-3
Segment –3 16 Intervalle
Segment –1 / 16 Int. Segment –1 16 Intervalle
...
Abb. 2.21 Die Digitalisierung des Sprachsignals beim P-law
Telefonieren über Internet: Voice over IP
85
Zum einen wird das kontinuierliche (analoge) Sprachsignal in Amplitudenwerte zu diskreten Zeitpunkten umgewandelt, meist bei 8000 Punkten pro Sekunde. Dies ist notwendig, um alle Sprachfrequenzen bis 4000 Herz bei der Rückwandlung beim Empfänger wieder hörbar machen zu können (Nyquist-Theorem). Diese diskreten Signalwerte sind als kleine graue Kreise in Abb. 2.21 eingezeichnet. Zum anderen werden die so erhaltenen 8000 Signalwerte quantisiert: Sie werden in Intervalle einsortiert und es wird nur noch notiert, in welchem Intervall sie sind und nicht mehr, wo innerhalb des Intervalls. Bei der PCM-Variante „P-law“, die besonders in USA, Japan und Korea verwendet wird, ist jeder dieser 8 positiven und 8 negativen Intervalle („Segmente“) nochmals in 16 kleine Intervalle unterteilt, so dass jeder Signalwert in eines der insgesamt 16 u 16 = 256 Intervalle eingeordnet wird. Er wird damit durch eine Binärzahl von 256 Möglichkeiten kodiert – einem Byte oder 8 Bit. Da 8000 Signalwerte pro Sekunde erhoben werden mit jeweils 8 Bit, erhalten wir also einen Datenstrom von 8 u 8 = 64 Bits pro Sekunde – die Datenrate eines ISDN-Kanals. Bei dem PCM-Verfahren gibt es noch eine Besonderheit: Der subjektive Schalleindruck verdoppelt sich nur bei Vervierfachung der Signalstärke, das Schallempfinden ist nur logarithmisch (Weber-Fechnersche Gesetz). Dies macht man sich zunutze und steigert die subjektive Sprachqualität, indem man die kleineren Signalstärken fein auflöst und die größeren gröber: Die Breite der Intervalle (und damit der Segmente) verdoppelt sich bei jedem Segment. Bei dem „A-law“, einer Variante, die vor allem in Europa (und dem Rest der Welt) verwendet wird, sind im ersten und zweiten Segment die Intervallbreiten gleich, so dass nur sieben unterschiedliche Segmente existieren. Dies führt zu einer feineren Kodierung der kleinen Signalstärken, so dass die Sprachqualität etwas besser beurteilt wird. Nachdem die Daten digitalisiert sind, müssen sie über das Netz zum Empfänger geschickt werden. Dazu wird bei VoIP ein paketbasiertes Netz verwendet, das für viele andere Gespräche gleichzeitig da ist im Unterschied zu beispielsweise ISDN, bei dem eine ganze Leitung nur für ein Gespräch reserviert ist. Ein spezielles Multi-Mediaprotokoll auf Sitzungsebene, etwa H.323 der ITU (International Telecommunication Union), ermöglicht es, die Sprach- (und Video-) sitzungen kontrolliert durchzuführen, s. Abb. 2.22. Die unterste Schicht kann dabei beliebig sein; neben Ethernet kann auch FDDI (Fiberglasoptik) oder Funknetze (Voice over wireless LAN VoWLAN) diese Funktion wahrnehmen. Die normale Kommunikation über TCP/IP eignet sich allerdings nicht besonders gut für Echtzeitanwendungen: x Das TC-Protokoll garantiert die richtige Reihenfolge und den Empfang aller Pakete – dies ist aber bei Sprache nicht unbedingt notwendig. x Stattdessen wird ein Empfang der Pakete innerhalb einer Maximalzeit für verständliche Sprache verlangt – dies ist kein Kriterium für TCP.
86
Netzwerkarchitektur
7 Anwendung 6 Präsentation 5 Sitzung 4 Transport 3 Netzwerk 2 Datenverbindung 1 Phys.Verbindung
Service: Konferenz, Gebühren,..
Codec , Sicherheit H.323 RTP UDP
TCP
IP Network Access Layer
Abb. 2.22 Die Verbindungsschichten bei VoIP
Die traditionelle Datenübermittlung muss genau und fehlertolerant sein, Sprachkommunikation dagegen toleriert auch fehlende Pakete, aber die Mehrheit muss schnell da sein. Aus diesen Gründen heraus wurde ein neues Protokoll, das Realtime Transfer Protocol RTP (Clark 1990), geschaffen, das auf dem unzuverlässigen, aber schnellen UDP aufsetzt und über Zeitstempel eine Datenflusskontrolle zur Synchronisierung von Datenströmen (z.B. Video mit Sprache) enthält. Allerdings treffen wir auch bei dieser Konstruktion auf ein Problem: Verwenden wir beispielsweise einfaches PCM (ITU-Norm G.711), so haben wir einen Datenstrom von 8000 Datenpunkte (Byte) pro Sekunde. Jeder Datenpunkt wird beim Verschicken via RTP mit einem Header von 8 Byte, von UDP mit einem weiteren Header von 12 Byte und von IPv4 mit einem Header von 20 Byte, insgesamt 40 Bytes, versehen. Das Datennutzverhältnis ist also mit 40:1 extrem schlecht. Aus diesem Grund werden meist mehrere Datenpunkte zu einem Block zusammengefasst, der in größeren Abständen mit weniger Overhead verschickt werden kann. Fasst man jeweils 80 Samples in einem 10ms-Paket zu 120 Byte = 960 Bit zusammen, so drückt dies die Datenrate bei G.711 von 40u64 = 2560 kbps auf 100 Pakete pro Sekunde u 960 Bits = 96 kbps oder bei 80ms-Paketen auf 68kps. Im Gegensatz dazu können neuere, kodierende Algorithmen wie ITU-T G.723.1 mit einer fast gleich guten subjektiven Sprachqualität, aber mehr Rechenleistung die Sprache bei 90 ms-Paketen mit nur 10 kbps kodieren. Eine solche Zusammenfassung von Daten zu größeren Paketen bringt aber nicht nur Einsparungen, die dafür nötigen großen Puffer im System können auch lange Pausen oder Desynchronisation verursachen und das subjektive Sprachverständnis empfindlich beeinträchtigen. Aus diesem Grund spielt in solchen VoIP-Systemen die Qualität einer Verbindung eine wichtige Rolle. Technisch gesehen ist deshalb der Parameter Quality of Service QoS ein wichtiges Merkmal. Er äußert sich qualitätsmindernd beim Telefonserver durch x (elektrisches) Echo vom Anschluss x end-to-end Verzögerung x Abschneiden der Sprache beim Unterdrücken von Sprachpausen
Telefonieren über Internet: Voice over IP
87
und beim Server im Netzwerk durch x Paket-Verzögerungszeit x Paketverlust Eine wirksame QoS-Steuerung durch die Server (Lastverteilung, Umleiten von Verbindungen bei Verstopfung) ist deshalb eine wichtige technische Voraussetzung für VoIP. 2.4.2 Nutzungskonzepte von VoiceOverIP Bei der Nutzung des Voice-over-IP geht es nicht darum, die Sprache zu digitalisieren (dies ist in modernen ISDN-Anlagen bereits geschehen), sondern darum, die digitalen Netze der Telefonanlage und der Datennetze miteinander sinnvoll zu kombinieren. Der erste Ansatz besteht darin, innerbetrieblichen Telefonanlagen (Private Branch Exchange PBX) um IP-basierte Teile zu einer IP-PBX zu ergänzen und einen Server als Übergangspunkt zwischen der „normalen“ Telefonanlage und dem Paketnetz vorzusehen, siehe Abb. 2.23.
Abb. 2.23 Übergang von normaler Nebenstellenanlage PBX zur IP-PBX
Eine solche Ergänzung kann dann im Laufe der Zeit zu einer reinen IP-PBX migrieren. Die Netzwerk-Server können durch entsprechende Einschübe zu Telefonservern erweitert werden und die Telefonserver zu Netzwerkservern. Die Unterscheidung der Server ist dann nur noch virtuell, beide Funktionalitäten können im selben Gehäuse koexistieren. Dabei geht es nicht darum, bei der Integration
88
Netzwerkarchitektur
Geld zu sparen durch geringere Netzwerkkosten oder Einsparung der Telefontechniker; dies hat sich alles als unzutreffend herausgestellt. Ein solches Netzwerk braucht mehr Kapazität, und Telefonspezialisten sind öfters besser für die Lösung von Telefonproblemen geeignet als die Netzwerker. Der eigentliche Mehrwert einer solchen IP-basierten Lösung liegt vielmehr in den zusätzlichen organisatorischen und geschäftlichen Möglichkeiten und der Flexibilität, die eine solche Telefonanlage bietet. Beispiel
Anrufverteilsystem (Automatic Call Distribution ACD)
Bei den traditionellen Telefonanschlüssen der Filialen einer Handelsfirma besteht leicht das Problem, dass Anrufe von Kunden von Mitarbeitern beantwortet werden müssen, die mangels Informationen dem Kunden keine Auskunft geben können und den Eindruck haben, dass der Kunde nur ihren Betriebsablauf stört. Im Gegensatz dazu kann eine zentrale, qualifiziert besetzte Stelle die Kunden besser bedienen, über zufriedenen Kunden zusätzlichen Umsatz erzielen und die anderen Mitarbeiter entlasten. Ein solches Call-Center hat meist einen Zugang zu den Lagerhaltungssystemen und kann sagen, ob der Artikel vorrätig oder nicht. Ist eine VoIP-Anlage vorhanden, so ist die physische Lage des CallCenter und der Einwahlpunkt des Kunden unerheblich: Der Kunde kann sowohl über eine zentrale Rufnummer als auch über die lokale Ortsnummer der Filiale sich in das System einwählen und wird automatisch mit dem Zentrum verbunden. Ergibt sich die Notwendigkeit, dass der Kunde doch mit einem Mitarbeiter einer Filiale spricht, so lässt sich über die veränderte IP-Nummer das Gespräch nicht nur innerhalb eines Gebäudes, sondern innerhalb der ganzen Firmengruppe weitervermitteln (gestrichelte Pfeile in Abb. 2.24). Kundenanruf 0180-X-XXXX
Zentrales Callcenter
Intelligentes Netz
PostRouting
Filiale A
Filiale B
Abb. 2.24 Automatische Anrufverteilung
Telefonieren über Internet: Voice over IP
89
Eine weitere Frage ist die, ob VoIP traditionelle Telefonanlagen ersetzen kann. Hier zeichnet sich ein interessanter Effekt ab: Für traditionelle Nebenstellensystemen wurden im Laufe der Jahre Hunderte von nützlichen Funktionen geschaffen wie Konferenzschaltung, Rückruf, Gespräch halten, Mikrofon stumm schalten, automatische Umleitungen, Mailboxen usw. Diese Dienstleistungen werden von hoch entwickelten Programmen in den digitalen Nebenstellenanlagen zur Verfügung gestellt. Es ist unsinnig, eine derartige, über Jahre hinweg gewachsene Infrastruktur und Benutzeroberfläche einfach wegzuwerfen. Stattdessen gibt es die Tendenz, diese Anwenderdienstleistungen in ISO-OSISchicht 7 anzusiedeln und die PCM-Kanäle durch die unteren Schichten mit paketbasierter Vermittlungstechnik zu ersetzen. Damit merkt der Anwender nichts von der neuen Technik, die aber nun gestattet, räumlich verteilte Unternehmen in einer virtuellen Nebenstellenanlage kommunikationstechnisch zusammenzufassen. Auch projektbezogene Arbeitsgruppen lassen sich damit zu einer telefonmäßig zusammenhängenden virtuellen Firma vernetzen. 2.4.3 Sicherheitsaspekte Im Unterschied zu der gängigen Meinung ist VoiceOverIP nicht „auch nur ein Telefon“, sondern mehr. Es beginnt mit der Wahl der Parameter (IP-Adressen, MAC-Adressen) beim Starten des Systems und geht über die Frage, was bei Störungen im Datenverkehr geschieht, bis hin zur Verschlüsselung der Sprachdaten. Ein an der Firmenkommunikation interessierter Angreifer hat jede Menge Möglichkeiten, Lücken in den Protokollen zu nutzen und Daten abzuhören oder den Verkehr zu stören. Aus diesem Grund wird empfohlen (NIST), x die Sprachdaten zu verschlüsseln, etwa durch Benutzung eines VPN x die IP-Adressen für VoIP von den „normalen“ zu trennen, etwa indem man extra Adressbereiche bei separaten DHCP-Servern vorsieht x Zugriffe mittels Remote-Administration sollte möglichst vermieden oder über verschlüsselte Kommunikation (IPSec oder Secure Shell SSH) ausgeführt werden x Die Verschlüsselung/Entschlüsselung kann auch auf einem dem Telefon nächsten Router oder Gateway ausgeführt werden wenn es in den Endgeräten Leistungsprobleme gibt, aber sie sollte nicht ganz abgeschaltet werden. x Die Firewall sollte für VoIP eingerichtet sein, etwa durch spezielle Programme (application level gateway ALG), die die einzelnen Datenströme erkennen und getrennt logisch behandeln. x An der Schnittstelle zwischen Telefonnetz und „normalen“ Datennetz (VoiceGateway) sollten die Protokolle enden und keine VoIP-Verbindungen zwischen Datennetz und Telefonnetz zulassen. Man kann auch die VoIP-Software auf „gehärteten“ Betriebssystemen laufen lassen, etwa einem Linux, das von allen Modulen und Funktionen, die nicht für VoIP
90
Netzwerkarchitektur
benötigt werden, befreit wurde. Damit verhindert man alle Hintertüren durch fehlerhafte Protokolle, etwa von TCP, ftp, oder http. Literatur
D. Clark and D. Tennenhouse, Architectural considerations for a new generation of protocols, SIGCOMM'90, Philadelphia 1990 NIST: www.csrc.nist.gov/publications/drafts
3 Internet Architekturen, Web-Services und Sicherheit
Im Unterschied zu Großrechnern (main frame), die zentral bis zu 1000 Benutzer bedienen können, sind die meisten Rechner in Firmen, Universitäten und Behörden nur wenigen Benutzern vorbehalten (single user-Systeme). Um trotzdem systemweite Dienste und Daten innerhalb der Firmen oder Institutionen nutzen zu können, sind die Rechner in der Regel vernetzt.
3.1 Funktionsarchitekturen Innerhalb des Internets können verschiedene Modelle verwirklicht sein, wie Rechner miteinander umgehen. Jeder Rechner erhält eine bestimmte Funktion; die Gesamtdienstleistung wird in einzelne Leistungen aufgeteilt. 3.1.1 Client-Server Systeme Üblich ist dabei die sogenannte Client-Server-Funktionsaufteilung: Spezielle Computer enthalten Unternehmensdaten (file server, mail server), dienen als schnelle Rechner (computing server), nehmen kritische Datentransaktionen (transaction server) vor und steuern besondere Druckgeräte (print server). Dies ermöglicht zusätzliche Funktionalität für die Mitglieder einer Arbeitsgruppe: x electronic mail server Elektronische Nachrichten dienen als Notizzettel und Kommunikationsmittel, zur Terminabsprache und Projektkoordination. x file sharing Dokumente und Daten können gemeinsam erstellt und genutzt werden. Dies vermeidet Inkonsistenzen zwischen verschiedenen Kopien und Versionen und hilft dabei auch, Speicherplatz zu sparen. x device sharing Der Ausdruck von Grafik und Daten auf beliebigen Druckern im Netz (remote printing) ermöglichen die Anschaffung auch teurer Drucker für die ganze Arbeitsgruppe oder alternativ das Einsparen von Investitionen. Dies gilt allgemein
92
Internet Architekturen, Web-Services und Sicherheit
für alle Spezialhardware wie high-speed scanner, Farblaserdrucker, Plotter usw. x processor sharing Durch die Verteilung von Einzelaufgaben eines Rechenjobs auf die Rechner der anderen Gruppenmitglieder kann mit der ungenutzten Rechenzeit der anderen der eigene Job schneller bearbeitet und abgeschlossen werden oder aber Investitionen in mehr Rechengeschwindigkeit gespart werden. Ist der Funktionsunterschied zwischen dem einzelnen Computer und dem Netzwerk für den Benutzer gering, so sprechen wir von einem verteilten Computersystem. Die oben genannten Funktionen werden dabei durch ein gezieltes Zusammenspiel mehrerer Betriebssystemteile auf verschiedenen Rechnern erreicht. Die Netzwerkerweiterung kann deshalb auch als eine Erweiterung des Betriebssystems angesehen werden. Die oben geschilderten Vorteile sind in Netzen zwar möglich; sie werden aber durch die Vielfalt der eingesetzten Rechnermodelle, Betriebssysteme und Programmiersprachen stark behindert: Die Zusammenarbeit der Rechner leidet unter den unterschiedlichen Netzwerknormen, die in der Hardware und Software existieren. Deshalb wollen wir in diesem Kapitel die wichtige Rolle des Betriebssystems genauer unter die Lupe nehmen und die Aufgaben, Funktionsmodelle und Lösungen näher untersuchen, die ein Netzwerkanschluss für ein Betriebssystem mit sich bringt. Einen wichtigen Versuch, die inhomogene Landschaft zu vereinheitlichen, stellt dabei das Distributed Computing Environment (DCE) der Herstellervereinigung Open Software Foundation (OSF) (später OpenGroup) dar, das als komplexes Softwarepaket (1 Mill. Codezeilen) verschiedene Lösungen für Client-ServerArbeitsverwaltung (z.B. threads), Dateiverwaltung und Sicherheitsmechanismen enthält. 3.1.2 Verteilte Betriebssysteme Ein Betriebssystem, das mit anderen Betriebssystemen über Netzverbindungen gekoppelt wird und auf jedem Rechner vollständig vorhanden ist, wird als Netzwerkbetriebssystem bezeichnet. Man kann nun eine Aufgabe, etwa das Führen von Dateien, innerhalb eines Netzes auch unter den Rechnern aufteilen. Bezüglich dieser Aufgabe spricht man dann von einem verteilten System, in unserem Beispiel von einem verteilten Dateisystem. Die Funktionen des verteilten Systems beschränken sich im Fall eines Netzwerkbetriebssystems auf höhere Dienste und benutzerspezifizierte Programmsysteme. Im Gegensatz dazu befindet sich eine Komponente eines verteilten Betriebssystems nur einmal exklusiv auf einem der Rechner des Netzwerks. Für eine Funktionieren müssen alle Komponenten bzw. Rechner zusammenarbeiten; es gibt also nur ein Betriebssystem für das gesamte Netzwerk.
Funktionsarchitekturen
93
Ein solches Betriebssystem verwendet nur die untersten Schichten des Transportprotokolls, um seine auf verschiedene Rechner verteilten Dienste schnell ansprechen zu können. In Abb. 3.1 ist die Schichtung für den Kern eines solchen Systems gezeigt.
client
file server
print server
Benutzerprozeß
DateisystemManager
PrinterManager
Mikrokern
Mikrokern
Mikrokern
Abb. 3.1 Ein verteiltes Betriebssystem
Der Rest des Betriebssystemkerns, der noch auf jedem Rechner existiert, ist meist als Mikrokern (vgl. Kapitel 1) ausgeführt. Ein Mikrokern enthält nur die allernötigsten Dienste, um Kommunikation und schnelle Grunddienste für Speicherverwaltung und Prozesswechsel durchzuführen. Alle anderen Dienste wie Dateisystemverwaltung (file server), Drucker (print server), Namensauflösung (directory server), Job Management (process server) usw. sind auf anderen, spezialisierten Rechnern lokalisiert. Diese Art von Betriebssystem hat folgende Vorteile: x Flexibilität Weitere Dienste (computing server etc.) können nach Bedarf im laufenden Betrieb hinzugefügt oder weggenommen werden; das Computersystem kann inkrementell erweitert werden, ohne die Einzelrechner erweitern zu müssen. x Transparenz Die Dienste im Netzwerk können erbracht werden, ohne dass der Benutzer wissen muss, wo dies geschieht. x Fehlertoleranz Prinzipiell ist so auch Fehlertoleranz möglich; das System kann intern rekonfiguriert werden, ohne dass der Benutzer dies merkt. x Leistungssteigerung Da alle Dienste parallel erbracht werden, kann durch zusätzliche Rechner auch höherer Durchsatz erzielt werden. Fairerweise muss man allerdings hinzufügen, dass verteilte Betriebssysteme durch den größeren Hardwareaufwand auch Nachteile haben, beispielsweise wenn ein Service nur einmal vorhanden ist und gerade dieser Rechner ausfällt: Das ganze Netz ist dann bezüglich dieser Dienstleistung funktionsunfähig.
94
Internet Architekturen, Web-Services und Sicherheit
Ein grundsätzliches Problem verteilter Betriebssystemkerne ist auch der Zeitverlust für einen Service durch die Kommunikation. Für ein leistungsfähiges System lohnt es sich also nur, größere Arbeitspakete „außer Haus“ bearbeiten zu lassen; die Mehrzahl der kleinen Aktionen, die in einem Betriebssystemkern existieren, lassen sich schneller auf demselben Prozessorsystem ausführen. Ein weiterer Punkt sind die Netzwerkdienste. Auch bei identischen, replizierten Betriebssystemkernen, die autonom alle Betriebssystemfunktionen wahrnehmen können, gibt es spezielle höhere Dienste (wie netzweite Zugriffskontrolle usw.), deren Funktionalität verteilt ist. Da diese Grunddienste bereits teilweise zum Betriebssystem gerechnet werden und im Lieferumfang des Betriebssystems enthalten sind, handelt es sich mehr oder weniger auch um verteilte Betriebssysteme, ohne aber einen Mikrokern zu besitzen. Aus diesen Gründen ist es müßig, die Streitfrage zu entscheiden, ob ein zentrales (main frame) oder ein verteiltes (Client-Server) Betriebssystem besser sei: Die meisten Betriebssysteme sind eine Mischung aus einem reinen Netzwerkbetriebssystem, das alles selbst macht und nur lose an andere Systeme anderer Rechner angekoppelt ist, und einem reinen verteilten Betriebssystem, das alle Dienste auf spezialisierte Rechner verlagert. Der Umfang des Kerns zeigt dabei den Übergangszustand zwischen beiden Extremen an. In Abschnitt 3.4.2 ist dieses Thema nochmals am Beispiel des Netzcomputers diskutiert.
3.2 Dateisysteme im Netz Der Zugriff auf gemeinsame Dateien mit Hilfe eines Netzwerkes ist eine wichtige Arbeitsgrundlage für Gruppen und damit eine der wesentlichen Funktionen der Vernetzung. Es ist deshalb sehr sinnvoll, sich nicht nur über die Tatsache zu freuen, dass so etwas möglich ist, sondern auch einen genaueren Blick darauf zu werfen, wie dies vor sich geht. Deshalb soll in den folgenden Abschnitten ein Überblick über die Konzepte und Probleme verteilter Dateisysteme gegeben und die Verhältnisse bei UNIX und Windows NT betrachtet werden. Zusätzliche Beispiele anderer Dateisysteme findet der/die interessierte Leser/in in der Übersicht von Levy und Silberschatz (1990). 3.2.1 Zugriffssemantik Betrachten wir nur einen einzigen Prozess im gesamten Netz, so ist ein Netzdateisystem relativ einfach: Statt eine Datei auf dem lokalen Dateisystem anzulegen, zu lesen und zu schreiben, wird dies in Form von Aufträgen an ein anderes Rechnersystem weitergereicht, das diese Aufträge durchführt. So weit, so gut. Anders sieht die Lage allerdings aus, wenn wir mehrere Prozesse im System zulassen, die alle auf derselben Datei arbeiten: Was passiert dann? Für diesen Fall
Dateisysteme im Netz
95
gibt es mehrere mögliche Konzepte, die in den verschiedenen Netzdateisystemen implementiert sind und als Zugriffssemantik bezeichnet werden. Wir können dabei folgende Fälle unterscheiden: x Read Only File Die Datei ist im Netz nur lesbar. In diesem Fall erhalten alle Prozesse Kopien der Datei, die beliebig gelagert und gepuffert werden können: Es entstehen keine Probleme. x Operationssemantik Die ausgeführten Operationen der Prozesse auf der Datei verändern diese in der Reihenfolge der sukzessiv ausgeführten Operationen. Wird also ein read() von Prozess A, write() von Prozess B und folgend ein weiteres read() von A initiiert, so erfährt A beim zweiten read() die Änderungen, die B vorher auf der Datei vorgenommen hat. Da dies in UNIX-Systemen so implementiert ist, wird dies auch als UNIX-Semantik referiert. x Sitzungssemantik Arbeiten mehrere Prozesse auf einer Datei, so erhalten alle Prozesse zunächst eine Kopie der Datei, die sie verändern können. Erst wenn ein Prozess die Datei schließt, wird die gesamte Kopie zurückgeschrieben. Dies bedeutet, dass die Version des letzten Prozesses, der die Datei schließt, alle anderen überlagert und auslöscht. x Transaktionssemantik Das Konzept der unteilbaren Handlung, die entweder vollständig in der spezifizierten Reihenfolge stattfindet oder gar nicht (atomic transaction), lässt sich auch hier anwenden. Öffnen und modifizieren wir eine Datei atomar, so wird vom Netzdateisystem garantiert, dass dies unabhängig von allen anderen Prozessen in einem einzigen, gesicherten Arbeitsgang geschieht. Während dieses Vorgangs ist also die Datei für den Zugriff durch andere gesperrt. Der konkrete Arbeitsablauf in einer Gruppe hängt stark davon ab, welches der Modelle implementiert ist. Dazu kommt noch das Problem, dass die konkreten Auswirkungen des jeweiligen Modells noch zusätzlich von der Hardware, den implementierten Puffern und Kommunikationsprotokollen abhängen. Betrachten wir dazu beispielsweise die Operationssemantik. Angenommen, Prozess B schreibt etwas auf die Datei und Prozess A liest von der Datei. Beide Anfragen erreichen den Dateiserver zu einem Zeitpunkt (und damit in einer Reihenfolge), die vom verwendeten Netzwerk, vom Netzwerkprotokoll und von der Last auf den beteiligten Computern abhängt. Ob Prozess A die von B geschriebenen aktuellen Daten liest oder die alte, nichtaktualisierte Form der Datei erhält, ist also von der Implementierung abhängig, nicht von der Zugriffssemantik. Problematisch ist, wenn in einem Betriebssystem lokal die eine Semantik, im Netz aber die andere Semantik implementiert ist. Wenn die beteiligten Prozesse teilweise auf demselben Rechner und teilweise auf anderen Rechnern im Netz lokalisiert sind und auf gemeinsamen Dateien arbeiten, muss man die Zusammenarbeit der Prozesse eines Prozesssystems sorgfältig planen, um Fehlfunktionen zu vermeiden.
96
Internet Architekturen, Web-Services und Sicherheit
3.2.2 Zustandsbehaftete und zustandslose Server Bereits bei Kommunikationsverbindungen haben wir danach unterschieden, ob diese durch ein Verbindungsprotokoll eingeleitet (verbindungsorientierte Kommunikation) oder die Daten nur mit einer Adresse versehen direkt transferiert werden (verbindungslose Kommunikation). Eine solche Unterscheidung können wir auch beim Zugriff auf die Dateisysteme im Netzwerk machen. Wird eine Datei geöffnet, bevor darauf zugegriffen wird, so werden wie bei der verbindungsorientierten Kommunikation extra Datenstrukturen im Server errichtet, die Zugriffsrechte werden geprüft und der Zugriff wird registriert. Dem Kunden wird eine spezielle Dateikennung übergeben, so dass alle nachfolgenden read()- und write()Operationen mit dieser Kennung sofort bearbeitet werden können. Der Server hat damit einen bestimmten Zustand errichtet; erst ein Abschluss der Zugriffe mit close() bewirkt ein Löschen der Verwaltungsinformation und ein Freigeben des dafür belegten Speichers. Ein solches zustandsbehaftetes Vorgehen hat folgende Vorteile: x Schneller Zugriff Der nachfolgende Zugriff auf die Dateien geht sehr schnell, da zum einen die Aufträge zum Schreiben und Lesen keine Adress- und Benutzerangaben mehr benötigen, deshalb kürzer sind und schneller über das Netz transportiert werden können, und zum anderen die Angaben nicht mehr nachgeprüft werden müssen, bevor ein Zugriff erfolgen kann. x Effizienter Cache Da alle Angaben über den Zugriffszustand auf dem Server geführt werden, können effiziente Cachestrategien (read ahead etc.) für die Datei den Zugriff beschleunigen. x Vermeiden von Auftragskopien Durch Führen einer Nachrichtennummer für eine Datei können Kopien desselben Auftrags, die in Vermittlungsrechnern erzeugt werden, außortiert werden. x Dateisperrung möglich Für die Transaktionssemantik können Dateien den Zustand „gesperrt“ annehmen. Dies ist besonders bei Datenbanken wichtig. Allerdings hat ein zustandsbehafteter Server auch Probleme, die dafür sprechen, keine Zustände bei Servern zuzulassen: x Client crash Wenn ein Client „abstürzt“ und damit der Zugriffsschlüssel für die Datei verlorengeht, bleibt die Datei immer offen, und die Verwaltungsinformation belegt unnötig Platz. x Server crash Versagt ein Server, so ist der gesamte Zustand der Dateibearbeitung gelöscht. Der Client muss dann erfahren, in welchem Zustand die Datei ist und an der Stelle der Bearbeitung weitermachen. Dies erfordert verschiedene Maßnahmen von seitens des Client und zusätzlichen Aufwand.
Dateisysteme im Netz
97
x Begrenzte, gleichzeitig benutzte Dateienanzahl Die Speicherbelegung beim Server für die Zustandsinformation (Tabellen etc.) beschränkt die Anzahl gleichzeitig geöffneter Dateien. Ein zustandsloser Server ist also im Prinzip fehlertoleranter und verlangt keinen zusätzlichen Aufwand zum Aufbau einer Kommunikationsverbindung, hat aber dafür bei zustandsorientierten Diensten, wie Sperren einer Datei oder Eliminieren von Auftragskopien, mehr Probleme. 3.2.3 Die Cacheproblematik Effiziente Pufferstrategien können den Datenfluss erheblich beschleunigen, aber auch eine Menge Probleme verursachen. Dies ist bei Netzwerkdateisystemen besonders der Fall. Installieren wir beispielsweise einen Puffer auf einem Rechnersystem mit Prozess A, so können alle Anfragen von Prozess A auf diese Daten schnell beantwortet werden und belasten nicht das Netz. Allerdings werden dabei die Änderungen von Prozess B am Original auf dem anderen Rechnersystem nicht berücksichtigt. Entscheidend für das Auftreten der Cache-Konsistenzproblematik ist dabei, ob die Prozesse, die eine Datei gemeinsam bearbeiten, verschiedene Puffer oder den selben Puffer nutzen. In Abb. 3.2 ist die grundsätzliche Situation für zwei Prozesse A und B gezeigt, die auf ein Datenobjekt schreiben wollen. Nutzen sie verschiedene Puffer, so kommt es zu Konsistenzproblemen: Alles, was in Puffer A erarbeitet wird, ist nicht mit dem verträglich, was in Puffer B steht. Erst wenn die Transportwege sich vereinigen und ein gemeinsamer Weg durch einen gemeinsamen Puffer („Objektpuffer“) vorliegt, gibt es keine Konsistenzprobleme im Normalbetrieb.
B
A
Puffer A
Puffer B
Objektpuffer
Datenobjekt
Abb. 3.2 Cachekohärenz bei gleichen und unterschiedlichen Puffern
98
Internet Architekturen, Web-Services und Sicherheit
Dabei ist es unerheblich, ob der einzelne Puffer in Wirklichkeit aus vielen Puffern besteht, beispielsweise Puffer A aus dem Heap von Prozess A zuzüglich dem Betriebssystempuffer auf Rechner A und dem Treiberpuffer des Netzwerks auf Rechner A: Die Problematik bleibt die gleiche. Unkritisch sind jeweils die gemeinsamen Puffer, als meist alle Puffer, die auf der Serverseite vorhanden sind. In Abb. 3.3 ist zur Übersicht ein Bild aller beteiligten Übertragungsschichten gezeigt. Auf jeder Schicht kann ein Puffer implementiert werden; jede Schicht hat dann ihre speziellen Probleme.
Benutzerprozeß Transport Leitung Client
Netzdateisystem Transport lokaler Treiber Platte Server
Abb. 3.3 Übertragungsschichten und Pufferorte für Dateizugriffe im Netz
Man beachte, dass Leiter bei hohen Frequenzen auch Daten puffern: Bei f = 1GHz Übertragungsrate (109 Bits/sec) und einer Ausbreitungsgeschwindigkeit der elektrischen Impulse von c = 3·108 m/sec resultieren f/c = 3,3 Bit/m Information – also 10kB Speicherung in der Leitung bei einem Abstand von 3 km zwischen den Rechnern. Die Hauptstrategien, um die beschriebenen Probleme zu lösen, verwenden eine Mischung bekannter Konzepte. x Write Through Werden Kopien von einer Datei geändert, so wird der Änderungsauftrag außer an die Kopie im Cache auch an das Original auf dem Server geschickt, um der Operationssemantik zu genügen. x Delayed Write Problematisch an der write through-Lösung ist allerdings die Tatsache, dass der Cachevorteil für den Netzwerkverkehr nicht mehr vorhanden ist. Dies lässt sich etwas verbessern, wenn man alle write()-Aufträge sammelt und in einem Paket zum Server schickt (delayed write), was aber wieder die Semantik der Zusammenarbeit verändert. x Zentrale Kontrolle Um die Änderungen in den Kopien auf allen anderen Rechnern ebenfalls wirksam werden zu lassen, muss jeweils das lokale Cachesystem mit dem zentralen Server zusammenarbeiten. Dies kann sofort nach jeder Änderung des Originals geschehen, oder aber erst bei tatsächlichem Gebrauch der Daten. In letzterem Fall vergleicht bei jedem Lesen der Cachemanager seine Dateidaten (Versionsnummer, Pufferquersumme etc.) mit der des Servers. Sind sie identisch, erfolgt das Lesen aus dem Cache; wenn nicht, wird der Leseauftrag an den Server weitergegeben.
Dateisysteme im Netz
99
x Write On Close Eine Alternative zu der Operationssemantik ist die Sitzungssemantik. Dafür reicht es aus, die gesamte geänderte Kopie der Datei nach einem close() des Prozesses an den Dateiserver zurückzuschicken und das Original so zu aktualisieren. Das Problem, dass mehrere inkonsistente Kopien der einen Originaldatei auf verschiedenen Rechnern existieren können, muss dann bei der Sitzungssemantik berücksichtigt werden und nicht mehr beim Cache. Allgemein lässt sich sagen, dass die Einführung von Cache auf dem Dateiserver in jedem Fall den Durchsatz erhöht, ohne allzu viele Probleme zu verursachen. Die Bereitstellung von Cache auf dem Client-Rechner dagegen ist zwar noch effizienter, bringt aber unter Umständen bei Dateiänderungen Konsistenzschwierigkeiten mit sich. Aus diesem Grund existieren bei verteilten Datenbanken spezielle Sperrprotokolle. Beispiel UNIX NFS-Cachestrategien Zum üblichen UNIX-Puffermechanismus für die Ein- und Ausgabe von Dateien wird bei NFS noch eine zusätzliche Möglichkeit angeboten, die Ein- und Ausgabe von RPCs auch asynchron bereitzustellen. Dazu existiert eine besondere Prozessart, die basic input-output (biod)-Dämonen, die auf dem Client noch zusätzlich die jeweils nächsten Datenblöcke mit anfordert (read ahead). Verlangt der Benutzerprozess den nächsten Block, so ist er so bereits im Puffer (meist 8kB) da und kann sofort gelesen werden. Umgekehrt werden die write()-Daten zunächst in den Puffer transferiert und erst bei Ablauf einer Zeitspanne, die bei jedem Schreibpuffer extra aufgesetzt wird (3 s für Datenpuffer, 30 s für Verzeichnispuffer), bei expliziten flush() bzw. sync()-Aufrufen oder wenn der Puffer voll ist, gesammelt auf die Platte geschrieben (delayed write). Unabhängig davon werden alle modifizierten Puffer alle 30 Sekunden auf den Server zurückgeschrieben. Dies aktualisiert aber nicht die Lesepuffer. Bei einem open()-Aufruf für eine Datei im Cache wird deshalb zuerst beim Server das Datum ihrer letzten Änderung geholt und geprüft, ob es neuer als das Pufferdatum ist. Wenn ja, so werden die Puffer neu gefüllt; wenn nein, so wird der alte Puffer verwendet. Damit wird garantiert, dass nach dem Schließen einer Datei durch einen Client und dem Öffnen und Lesen der selben Datei von einem anderen Client die Datei auch wirklich aktuell ist („close-to-open consistency“). Ist eine Datei gesperrt, so werden ihre Blöcke nicht mehr gepuffert: Nach dem Beschreiben werden sie sofort zum Server geschickt (write through). Aus Leistungsgründen befindet sich auch für die biod-Dämonen der grösste Teil des Codes im Betriebssystemkern. Die Puffermechanismen auf dem Server sind transparent für den Client und entsprechen den normalen Betriebssystemmechanismen dort.
100
Internet Architekturen, Web-Services und Sicherheit
3.2.4 Implementationskonzepte Es gibt verschiedene Ideen, wie man Netzwerkdateisysteme implementieren kann. Die beiden populärsten Möglichkeiten sehen vor, entweder den Dateiservice als eigenständigen Prozess oder aber nur als speziellen Treiber im Betriebssystemkern zu installieren. In Abb. 3.4 ist die erste Konfiguration gezeigt, bei der Client
Server
Anwenderprozess
Betriebssystemaufruf Dateitreiber Gerätetreiber
Netzdateitreiber Transport Netzanschlusstreiber
Netzdateimanager
Betriebssystemaufruf Netzdateitreiber Transport Netzanschlusstreiber
Dateitreiber Gerätetreiber
Abb. 3.4 Implementierung eines Netzdateiservers auf Prozessebene
sowohl auf Client- als auch auf Serverseite Prozesse existieren. Man beachte, dass die Betriebssystemschichten symmetrisch sind; der konzeptionelle Unterschied drückt sich nur in der Art des Sender- bzw. Empfängerprozesses aus. Im Unterschied zu diesem Mechanismus, der auf Interprozesskommunikation beruht, ist in Abb. 3.5 ein Implementierungsschema auf Treiberebene gezeigt. Die Clientseite ist hier genauso gestaltet; auf der Serverseite ist dagegen kein Managerprozess vorhanden. Die Anfragen werden direkt im Betriebssystemkern verwaltet und auf das lokale Dateisystem umgeleitet. Dieser Ansatz implementiert das Netzwerkserver-Dateisystem auf Treiberebene und verhindert so das aufwendige Kopieren der Daten aus den Systempuffern in den Adressbereich eines Serverprozesses und zurück in die Systempuffer zum Schreiben im lokalen Dateisystem des Servers. Allerdings wird so die Konfiguration asymmetrisch: Das Netzdateisystem setzt nicht mehr auf einem Stellvertreterprozess und regulären Interprozesskommunikation auf, sondern auf speziellen Schichten (Treibern) des Kerns.
Dateisysteme im Netz
Client
101
Server
Anwenderprozess
Betriebssystemaufruf Dateitreiber Gerätetreiber
Netzdateitreiber Transport Netzanschlusstreiber
Netzdateimanager Netzdateitreiber Transport Netzanschlusstreiber
Dateitreiber Gerätetreiber
Abb. 3.5 Implementierung eines Netzdateiservers auf Treiberebene
Die beiden Konzepte werden unterschiedlich realisiert. Beispiel UNIX Das NFS-System Im NFS-System gibt es zwei Protokolle: eines, das das Einhängen (mount()) eines Serverdateisystems unter ein Verzeichnis im Client regelt, und eines, das den normalen Schreib-/Leseverkehr betrifft. Das erste Protokoll wird zwischen zwei Prozessen durchgeführt: dem anfragenden Clientprozess, meist beim Starten des Clientsystems, und einem speziellen Dämon mountd auf dem Server. Dazu schickt der mount-Prozess des Client eine Nachricht an den mountd-Dämon auf dem Server, der sich mit einem Systemaufruf getfh() ein file handle (Dateisystemdeskriptor) von der NFS-Serverschicht holt und an den Clientprozess zurückgibt. Dieser führt nun damit einen Systemaufruf mount() aus und verankert so das Serverdateisystem in der NFS-Clientschicht des Betriebssystemkerns. Das zweite Protokoll des Schreib-/Lesevorgangs auf Dateien wurde unter Leistungsgesichtspunkten in den Betriebssystemkern verlegt. Obwohl auf dem Server das Programm nfsd anfangs als Prozess gestartet wird, läuft es doch bald auf den Systemaufruf nfs_svc() in den kernel mode, aus dem es nicht mehr zurückkehrt. Dieser Ansatz implementiert das Netzwerkdateisystem auf Treiberebene mit den erwähnten Leistungsvorteilen. In Abb. 3.6 ist die grundsätzliche Architektur gezeigt. Eine wichtige Rolle spielt dabei die zusätzliche Schicht für ein virtuelles Dateisystem VFS, das die Dateisystemoperationen abstrahiert. Alle Operationen im VFS (System V: File System Switch FSS) sind Methoden von Objekten, den virtual i-nodes (vnode), und sind je nach Objekt dateisystemspezifisch implementiert. So ist ein Umschalten zwischen MS-DOS-Dateien, UNIXDateien und NFS-Dateien problemlos möglich.
102
Internet Architekturen, Web-Services und Sicherheit
Client
Server
Anwenderprozeß
Systemaufruf-Verteiler Virtual File System MS-DOS UNIX NFS file file Client system system Gerätetreiber
Virtual File System UNIX MS-DOS NFS file file Server system system
UDP/IP
UDP/IP
Gerätetreiber
Netz
Abb. 3.6 Die Architektur des NFS-Dateisystems
Beispiel Windows NT Netzdateisysteme Die Implementierung der Netzdateisystemdienste von Windows NT ist aus den gleichen Gründen wie bei NFS in den Betriebssystemkern gelegt worden und baut damit auf dem Treibermodell auf. In Abb. 3.7 ist eine Übersicht gezeigt. Client
Server
Anwenderprozeß
Systemaufruf-Verteiler I/O-Manager MS-DOS NT-file Redirecsystem tor file system Netztransport Gerätetreiber
Server Treiber Netztransport
I/O-Manager NT-file OS/2-file system system Gerätetreiber
Netz
Abb. 3.7 Implementierung des Netzdateisystems in Windows NT
Der Redirector-Treiber hat dabei die Aufgabe, die Dateisystemanfragen, die über das Netz gehen, auf die gleiche Art zu behandeln, wie dies bei lokalen Da-
Dateisysteme im Netz
103
teiaufträgen geschieht. Dazu muss er Netzverbindungen, die gestört sind, über die Transportschicht wieder aufbauen. Auch die gesamte Statusinformation (welche Dateien wurden geöffnet usw.) wird vom Redirector verwaltet und aktualisiert. Die Unterstützung der asynchronen Ein- und Ausgabe verlangt dabei besondere Mechanismen. Anstatt einen eigenen Prozess zu erzeugen, der nach der asynchronen Ausführung des Auftrags durch das Netz die Kontrolle erhält und aufgeweckt wird, benutzt Windows NT einen Pool von threads, der für die Treiberinitialisierung beim Systemstart verwendet wurde, um die Abschlussprozeduren für ein IRP durchzuführen. Der Redirector wickelt seine Netzwerkkommunikation über das Transport Driver Interface (TDI) ab, dessen Kanäle (virtual circuits) durch das darunter liegende Transportsystem verwirklicht werden. Die Dateiserverschicht, die ebenfalls als Treiber installiert ist, ist kompatibel zu den existierenden SMB-Protokollen der MS-Net- und LAN-ManagerSoftware. 3.2.5 Sicherheitskonzepte Es ist klar, dass auch im Netzwerk nicht mehr Sicherheit zum Schutz von Dateien möglich ist, als das lokale Dateisystem von sich aus bietet. Alle zusätzlichen Sicherheitsprüfungen müssen extra installiert und durchgeführt werden. Eine besondere Schwierigkeit besteht darin, Dateisysteme mit verschiedenen Sicherheitsmechanismen und Sicherheitsstufen konsistent miteinander zu koppeln. Beispielsweise macht es nicht nur Schwierigkeiten, Dateien mit einem langen Namen in Groß- und Kleinschreibung von UNIX- und Windows NT-Servern mittels PC-NFS auf ein MS-DOS-Dateisystem mit kurzem Namen (max. acht Buchstaben) und Großbuchstaben abzubilden, sondern die Zugriffsrechte von Access Control Lists unter Windows NT lassen sich auch schwer auf die simplen Zugriffsrechte unter UNIX abbilden, wo derartige ACL noch nicht existieren. Der Übergang von einem Sicherheitssystem in ein anderes mittels eines Netzwerkdateisystem ist deshalb meist nur mit vielen Inkonsistenzen und Widersprüchen zu bewerkstelligen; es ist fast unmöglich, alle Sicherheitslücken zu schließen. Beispiel UNIX NFS-Sicherheitssystem NIS Im NFS-Dateisystem werden alle Benutzer des Systems in einer besonderen Liste (yellow pages) geführt, ähnlich wie Handwerker in den gelben Seiten eines Telefonbuches. Diese Liste wird von einem besonderen Prozess, dem Network Information System NIS, verwaltet und enthält außer den Tupeln (Benutzer, Passwort) auch Einträge über den aktuellen Codeschlüssel für einen chiffrierten Datenaustausch zwischen Client und Server, differenzierte Benutzergruppen und ähnliche Mechanismen, die in der speziellen Variante secure NFS verwendet werden können.
104
Internet Architekturen, Web-Services und Sicherheit
Für die Anforderung von Diensten mittels eines RPC wird das NFS-System verwendet. Spezifiziert man als Sicherheitsausweis den Modus „UNIXAuthentifikation“, so hat der RPC-Sicherheitsausweis das Format der üblichen UNIX-Zugriffskennung von Benutzer- und Gruppen-Id. Auch wenn beide Systeme, Client und Server, die gleiche Art von Sicherheitsmechanismen haben, so können auch hier Sicherheitslücken existieren. Beispielsweise sollte ein Administrator (super user) mit der Benutzer-Id = 0 auf dem Client nicht automatisch auch alle Dateien des Administrators auf dem Server ändern können, auch wenn die Id auf beiden Systemen die gleiche ist. Aus diesem Grund werden alle Anfragen mit Benutzer-Id = 0 auf dem Server in Benutzer-Id = –2 („Datei für externen super user“) abgebildet, was mit den normalen Sicherheitsmechanismen weiterbehandelt werden kann. Das NIS ermöglicht darüber hinaus, generell zentral die Zuordnung von Benutzern zu den Benutzer-Ids auf dem Dateisystem des Servers zu regeln, so dass keine Überschneidungen und Inkonsistenzen von Dateien verschiedener Benutzer auftreten, die lokal auf unterschiedlichen Rechnern jeweils zufälligerweise in /etc/passwd die gleiche Benutzer-Id erhalten. Trotz verschiedener Anstrengungen enthält das Sicherheitssystem des NFS noch einige Lücken. Für weitergehende Ansprüche sei deshalb auf das Kerberos-System verwiesen, das im übernächsten Kapitel beschrieben ist. Beispiel Windows NT Sicherheit in Netzdateisystemen In Windows NT sind die Dateien, wie in Abschn. 4.3 dargelegt, mit Zugangskontrollisten ACL versehen, die für jeden Benutzer bzw. jede Benutzergruppe die Zugriffsrechte regeln. Diese Sicherheitsmechanismen werden auch auf den Dateiservern bereitgestellt. Dazu muss jeder Benutzer, bevor er eine Datei öffnen kann, in der Benutzerdatei des Security Account Managers (SAM) sowohl auf dem Client-Rechner als auch auf dem Server registriert sein. Fehlt die Registrierung oder sind die verschlüsselten Passwörter auf Client und Server nicht identisch, so wird der Zugriff auf die Dateien des Servers prinzipiell verweigert. Aber auch bei erfolgter Registrierung gelten immer noch die Zugriffsbeschränkungen der ACL wie auf dem lokalen Dateisystem. Man beachte, dass dieses Konzept keine automatische Kontrolle des ClientAdministrators über den Server mit sich bringt, so lange beide unterschiedliche Passwörter benutzen. Im Konzept von Windows 2000 ist die Sicherheitspolitik grundlegend überarbeitet worden. Hier gibt es nicht nur die Authentifikations- und Verschlüsselungsmechanismen des weiter unten behandelten Kerberos-Systems (s. Abschnitt 3.7.6), sondern auch das netzübergreifendes Adressierungssystem ADS, bei dem jedes Objekt auf einem Rechner im Netz einer Domäne im Intranetz genau bezeichnet und dessen Zugang systemweit reguliert werden kann.
Massenspeicher im Netz
105
3.3 Massenspeicher im Netz Eines der attraktivsten Vorteile eines Netzes besteht darin, einzelne Ressourcen für mehrere Rechner nutzbar zu machen. Ein wichtiges Beispiel dafür bilden die Speicherressourcen. Verbindet man alle wichtigen Massenspeicher mit schnellen Leitungen untereinander und mit den Dateiservern, so wird ein spezielles Netz nur zur Speicherung gebildet. Ein solches Netz wird Speichernetz (storage network) SN genannt. Alle Server, Netzverbindungen und Speichergeräte lassen sich zu einem Speicherbereich, dem Storage Area Network SAN zusammenfassen. In Abb. 3.8 ist ein solches Netz gezeigt. Das SAN-Konzept fasst alle Speicher im Netz (beispielsweise unternehmensweit) zu einem virtuellen Massenspeicher zusammen. Dabei werden zwei Ebenen unterschieden: Die Blockebene, bei der einzelne Speicherblöcke zu logischen, größeren Blöcken zusammengefasst werden, und der Dateiebene, bei der für jede Datei die virtuelle Speicheradresse, der Dateiname, die Zugriffsrechte usw. verwaltet werden („Metadaten“). Die für die Verwaltung, Wartung und Konfiguration dieses virtuellen Speichers nötigen Programme lassen sich als Betriebssystem des virtuellen Speichers auffassen.
Abb. 3.8 Ein Speichernetzwerk
Die verschiedenen Verwaltungsebenen können wir mit Hilfe eines Schichtenmodells in der Funktionalität besser fassen als mithilfe eines Verbindungsgraphen. In Abb. 3.9 ist eine solche SAN-Architektur gezeigt, wie sie von der Storage Network Industry Association SNIA vorgeschlagen wurde.
106
Internet Architekturen, Web-Services und Sicherheit
IV
Anwendung File / recordlayer layer File/record
III II I
Database (dbms)
Block
File system (fs)
aggregation aggregation
Storage devices
Abb. 3.9 Das SAN Schichtenmodell nach SNIA.org
Die Vorteile eines spezialisierten Speichernetzes sind klar: x Ein spezialisiertes Speichernetz stört nicht den normalen Datenverkehr x Trotzdem wird die Flexibilität eines allgemeinen Netzwerks behalten, das ermöglicht, Geräte unterschiedlicher Hersteller zu integrieren („ITKonsolidierung“) x Eine zentrale Sicherheitsverwaltung vereinheitlicht Authentifikation, Zugriffskontrolle und Verschlüsselung, etwa um Datendiebstahl im Netz unmöglich oder unwirksam zu machen. x Die Datenströme können besser der gewünschten Anforderung (Quality of Service) angepasst und ihre Topologie zentral konfiguriert werden. x Die Netzüberwachung für Ereignisbehandlung, Wartung und Diagnose wird vereinfacht. x Es ermöglicht eine zentrales Speichermanagement. Besonders der letzte Punkt ist interessant. Das Speichermanagement beinhaltet nämlich x Eine effiziente Lastverteilung und Kapazitätsmanagement, etwa durch die Regruppierung von Speicherclustern oder die Ein- und Auslagerung von Daten zwischen Bandlaufwerken und Plattenlaufwerken. x Management der Datensicherung (Kopien, Backup, Rollback und Recovery im Schadensfall) x Konfiguration und Erweiterung (upgrade) der Einzelkomponenten Die Idee, zwischen Datenquelle und Datensenke ein Netzwerk zu schalten und damit die Geräte zu virtualisieren, ist sowohl auf Datei- als auch auf Block-I/OEbene möglich. Üblicherweise sind dazu die Dateiserver, die mit virtuellen Speicheradressen, also Speicherblockadressierung (Block-I/O) auf die Speichermedien zugreifen, durch schnelle Datenleitungen verbunden, die mit einem schnellen Protokoll versehen sind, etwa durch Glasfaserleitungen (Fiber Channel FC) mit dem FC-Protokoll.
Massenspeicher im Netz
107
Analog zum FiberChannel wird beim Internet SCSI (ISCSI) jeder SCSI-Befehl als Anwenderdatenblock in ein IP-Datenpaket verpackt, über die normalen Netzwerke geschickt und beim Empfänger wieder entpackt. Das gleiche gilt auch für die Datenblöcke des Speichermediums. Außerdem erhält jedes SCSI-Gerät einen lokalen Namen. Zusammen mit der IP-Nummer und der Portnummer bildet er die globale Adresse des ISCSI-Geräts. Es gibt aber auch Nachteile des SAN: Durch die Verteilung der Funktionen auf mehrere Rechner ist das Zusammenspiel der Komponenten mehrerer Hersteller ziemlich komplex. Benutzt eine Softwarekomponente spezielle, in Hardware implementierte Funktionen, etwa file copy oder backup, so können die Bausteine anderer Hersteller dafür nicht verwendet werden und führen zu Problemen. Es ist in diesem Fall besser, bei SAN-Komponenten nur auf einfachen, einheitlichen Funktionen aufzusetzen und alle „Optimierungen“ zu vermeiden. Im Gegensatz zum SAN-Konzept kann man die Speichereinheiten auch zentral bei einem einzigen Server anordnen, etwa als RAID-System, und die FCVerbindungen des Speichernetzes nur auf diesen Server beziehen. In Abb. 3.10 ist ein solcher Network Attached Storage NAS gezeigt und seine Einordnung im Schichtenmodell.
Host
Host. with LVM
Host. with LVM and software RAID
an Ho d st. sof wit twa h re LV RAI M D
Ho st. wit h LV M
Host LAN
NAS head
Host block-aggregation NAS NAS server server
SN SN
Block layer
File/record layer
Anwendung
Device block-aggregation
Disk array
Direktanschluß
Anschluß über SN
Network block-aggregation
NAS head
NAS server
Abb. 3.10 Das NAS-Modell
In diesem Fall garantiert das Speichernetz einen schnellen Zugriff auf die Daten und der Server (host) eine Netzwerkanbindung. Ein solcher NAS enthält neben dem Speichernetz alle Funktionen des Speichermanagers wie Zugriffsverwaltung, Backup, Rollback usw. die sonst im SAS existieren. Wie in Abb. 3.10 gezeigt, lässt sich die Schicht „Blockaggregation“ aus Abb. 3.9 unterteilen in drei Unterschichten: Die Aggregation der Blöcke mehrerer Geräte auf Gerätebene, die Aggregation mehrerer Geräte auf Netzwerkebene und die Aggregation von Blöcken
108
Internet Architekturen, Web-Services und Sicherheit
im Betriebssystem auf Hostebene. Abb. 3.10 zeigt dabei mehrere mögliche Systeme nebeneinander: Ein konventionelles System, bei dem die Geräteansteuerung direkt vom Rechner ausgeht, ein System, bei dem ein Netzwerk zur flexiblen Konfiguration zwischen Rechner und Speichermedien geschaltet ist und ein NASServer, der diese unteren Schichten bereits enthält. Der NAS-Betriebsteil ohne Speicher kann auch als Extrarechner (NAS-Head) ausgelagert sein. Wird ein NAS innerhalb eines SAN als ein Speichermedium unter vielen betrieben, so kann man es als ein „SAN im SAN“ ansehen oder ein „SAN-in-a-box“.
3.4 Arbeitsmodelle im Netz Im Unterschied zu Multiprozessorsystemen, die eine leichte Arbeitsaufteilung von parallel ausführbarem Code auf Prozessoren gleichen Typs ermöglichen, sind vernetzte Computer mit verschiedenen Problemen konfrontiert. x Die Übermittlung von Daten übers Netz kostet Zeit, so dass sich nur Codestücke zur parallelen Abarbeitung lohnen, die keine oder wenig Kommunikation haben. x Auch die Verlagerung von Code „außer Haus“ auf andere Rechner ist nur sinnvoll, wenn es sich um größere Arbeitspakete handelt, also Aufwand und Ertrag in vernünftigem Verhältnis zueinander stehen. x Die Netze sind meist inhomogen aus Computern verschiedener Hersteller und verschiedenen Betriebssystemen zusammengesetzt. Ein lauffähiges Programm auf einem Rechner muss deshalb nicht auf allen Rechnern im Netz ablauffähig sein. Ein Jobmanagementsystem, das den Wahlspruch „Nicht der Einzelrechner, sondern das Netz ist der Computer“ verwirklichen will, muss deshalb auf die obigen Probleme ganz konkret eingehen. 3.4.1 Jobmanagement Es gibt verschiedene Jobmanagementsysteme, die es erlauben, ungenutzte Rechenzeit in Workstation-pools zu nutzen. Derartige Systeme werden besonders in Luftund Raumfahrt, Automobil-Entwicklungsabteilungen usw. genutzt, wo viele hochwertige Rechner versammelt sind. Die Anforderungen an ein solches Load Sharing Facility-System sind folgende: x Die Lastverteilung für alle Jobarten und Leistungsklassen soll ausgewogen sein. x Für unterschiedliche Anforderungen an Betriebsmittel wie Rechenzeit und Speicherbedarf sollen verschiedene zentrale Warteschlangen geführt werden. x Die Jobdurchlaufzeiten sollen optimiert werden.
Arbeitsmodelle im Netz
109
x Trotz der zusätzlichen Belastung soll der normale, interaktive Betrieb der Workstations störungsfrei möglich sein. x Die Workstations sollen im Lastverbund möglichst in der Nacht, an Wochenenden und im Urlaub genutzt werden können. x Die Lizenzen von Programmen sollen transparent (Workstation-unabhängig) verwaltet werden. x Die Gesamtkonfiguration soll übersichtlich sein und leicht gewartet werden können. Diese Forderungen sind nicht einfach zu erfüllen, da ein Jobmanagementsystem nicht auf alle Faktoren Einfluss hat. So kann es zwar mit einer guten Benutzeroberfläche und einfachen Konfigurationsmechanismen die Wartung des Systems vereinfachen, durch batch-Möglichkeiten die Nacht- und Feiertagsstunden nutzen und über Netzwerkkommunikation Jobs auf Rechner verschieben, aber die Zuordnung von Jobs zu Rechnern ist nicht beliebig. So sind nicht nur Speicherbedarf, Prozessortyp, Betriebssystem und lokaler Plattenbedarf für temporäre Dateien zu beachten, sondern die Lizenzen mancher Programme sind auch an ganz bestimmte Rechner gebunden (node locking) oder in der Gruppe nur auf einer bestimmten, maximalen Anzahl von Rechnern gleichzeitig ausführbar (floating licence). Auch die Priorität der verlagerten Jobs muss so gewählt sein, dass der eigentliche Benutzer des Rechners immer Vortritt hat und deshalb die zusätzliche Nutzung „seines“ Rechners nicht unangenehm auffällt. 3.4.2 Netzcomputer Ein völlig anderes Konzept für verteilte und vernetzte Systeme stellt das Netzcomputerkonzept dar, das von der Firma Oracle, Sun, IBM, Apple usw. entwickelt wurde. Enthält ein „normaler“ Rechner noch alle Betriebssystemkomponenten selbst, um autonom mit Platten, Drucker etc. arbeiten zu können, so sind in Systemen mit verteiltem Betriebssystem die verschiedenen Betriebssystemfunktionen als spezielle Server (Dateiserver, Prozessserver etc.) auf spezialisierte Rechner verlagert. Dieses Client-Server-Konzept eines verteilten Betriebssystems wird nun beim Netzcomputer NC auf die Spitze getrieben: Außer einem Hauptspeicher, einem Prozessor, einem Netzanschluss und einem Bildschirm enthält er nichts weiter. Selbst die Software des Betriebssystems (Dienstprogramme, BS-Kern usw.) ist auf einen Mikrokern, bestehend aus dem nackten BS-Kern mit den Schichten des Netzanschlusses sowie den Funktionen zum Laden von Programmen, reduziert. Alle Programme, die sonst auf der lokalen Festplatte liegen, kommen aus dem Netz; selbst das Betriebssystem kann über einen fest eingebauten ROM-bootstrap loader beim Einschalten des Geräts übers Netz geladen werden. Alle Leistungen wie Speicherung von Dateien, Druckservice usw. werden von speziellen Servern vollbracht.
110
Internet Architekturen, Web-Services und Sicherheit
Die Managementvorteile eines solchen Mikrokernkonzepts gegenüber den üblichen Netzen aus PCs in Firmen liegen auf der Hand: x Aktuelle Dateien Durch die zentrale Wartung der benutzten Daten sind diese immer aktuell und konsistent. Dies bezieht sich sowohl auf die Teile des Betriebssystems, die über das Netz geladen werden (Treiber, Dienstprogramme usw.) als auch auf die Benutzerprogramme und die verwendeten Dateien. x Billigere Hardware Der NC benötigt weniger Hardware, da bestimmte Betriebssystemteile und lokaler Plattenplatz für die Dienstprogramme entfallen. Dies macht den NC billiger in der Anschaffung. x Billigere Wartung Die Pflege der Systemsoftware, die Lizenzvergabe und die Konfiguration auf dem NC werden nur einmal zentral durchgeführt und gepflegt. Dies macht den NC billiger in der Wartung, was nicht unerheblich ist: Man rechnet für die Software- und Konfigurationspflege eines PCs den gleichen Betrag pro Jahr wie für den Kauf. Auch die Hardwarereparatur ist einfacher, da die Computerteile beliebig ausgetauscht werden können: Alle Benutzerdateien und -profile liegen auf dem Zentralrechner und gehen dabei nicht verloren. x Höhere Datensicherheit Durch die zentrale Datensicherung kann bei einem Rechnerausfall die Arbeit auch bei unzuverlässigen oder vergesslichen Mitarbeitern weitergehen. Auch der Datendiebstahl ist ohne Peripherielaufwerke nicht mehr so einfach möglich, ebenso wie das Installieren von Spielen oder unabsichtliche Verbreiten von Viren. x Bessere Ausnutzung von Ressourcen Neben der besseren Nutzung von Druckern, Fax-Anlagen usw., die allgemein durch die Anbindung an das Netzwerk möglich wird, ist speziell beim NC die bessere Ausnutzung der Massenspeicher im zentralen pool möglich. Die notwendigen Erweiterungen können sich besser am jeweiligen Bedarf orientieren. Aber auch die Nachteile eines solchen Konzepts sind deutlich: x Erhöhter Netzaufwand Laufen alle Applikationen über das Netz, so müssen das firmeninterne Netz (Intranet) und die dabei verwendeten Server in der Leistung (Durchsatz und Speicherkapazität) deutlich gesteigert werden. Dies kostet zusätzlich Geld. x Erhöhter Pufferaufwand Fügt man zur Pufferung von Daten und häufig benötigten, großen Programmen noch zusätzliche Hardware (Hauptspeicher, Massenspeicher) in den NC ein, um die Ladezeiten und die Netzbelastung klein zu halten, wie dies schon bei den UNIX-Grafiksichtgeräten (X-Terminals) nötig wurde, so werden die Kostenvorteile des NC wieder relativiert. x Bevormundung der Benutzer Durch die zentrale Wartung der Software und das Management der Hardware fühlen sich die Anwender durch die EDV-Zentrale wie früher bevormundet:
Arbeitsmodelle im Netz
111
Nur die Zentrale entscheidet, welche Programme benutzt werden können und wieviel Speicherplatz dem einzelnen zugewiesen wird. Die Akzeptanz und damit die Zukunft der Netzcomputer ist deshalb zur Zeit noch ungewiss. Eine interessante Alternative ist zweifelsohne ein Computernetzwerk, das jedem seinen individuell konfigurierten Arbeitsplatz gestattet, aber durch zentrale Wartung für die Standardprogramme die meisten Konfigurationsprobleme am Arbeitsplatz verhindert und für aktuelle Dateien sorgt, siehe Abschnitt 3.4.3. Java-Applets
Ein deutliches Problem in diesem Konzept ist die Inkompatibilität der Hardware und Software bei verschiedenen Rechnern verschiedener Hersteller. Um dieses Problem zu umgehen, wird eine einheitliche Programmiersprache benutzt: Java. Die objektorientierte Sprache Java (Sun 1997) ähnelt in der Syntax sehr C++, hat aber als Vereinfachung keine Zeiger, keine Mehrfachvererbung und ein verbessertes Schnittstellenkonzept. Jeder NC erhält als Programm die Folge von Maschinenbefehlen einer virtuellen Maschine: der Java Virtual Machine, die von der Firma Sun, den Entwicklern von Java, spezifiziert wurde. Aufgabe des NC-Prozessors (und aller anderen Maschinen, die den Java-Code ausführen sollen) ist es nun, diese virtuelle Maschine zu emulieren. Die Aufgabe eines Betriebssystems eines solchen NC besteht darin, die Ablaufumgebung für über das Netzwerk geladene Java-Programme (applets) bereitzustellen. Insbesondere ist dies x die Hauptspeicherverwaltung durch automatische Speicherreservierung für Objekte sowie Aufspüren und Beseitigen von gelöschten bzw. nicht benutzten Objekten (garbage collection) x die Isolation verschiedener, gleichzeitig ablaufender Programme voneinander und zum restlichen System. Um dies schnell und einfach zu ermöglichen, gibt es in Java keine Zeiger und Adressen, und die Aktionsmöglichkeiten der Netzprogramme und Applets auf dem NC sind sehr beschränkt. x die Interpretation des Byte-Code der Java Virtual Machine, falls sie nicht direkt emuliert wird. Insbesondere müssen die elementaren Java-Datentypen auf die Wortlänge der Zielmaschine angepasst werden. x die Bereitstellung der Standardfunktionen für Grafik, Ein- und Ausgabe, soweit dies dem Netzprogramm oder Applet überhaupt gestattet ist. Generell haben Netzprogramme keinen Zugriff auf lokale Platten, Drucker etc. Die Notwendigkeit einer einfachen, sicheren Programmiersprache für Netze ergab sich bei der explosiven Ausbreitung und Anwendungen der Hypertextsysteme im World Wide Web (WWW). Vielen Anbietern reichte die Funktionalität einer einfachen Textseite nicht mehr aus. Anstatt aber immer neue Spezialfunktionen in die Hypertext-Präsentationsprogramme (web browser) aufzunehmen, ist es besser,
112
Internet Architekturen, Web-Services und Sicherheit
den Code für eine Spezialfunktion vom Anbieter direkt zu laden und sie lokal durch einen im web browser integrierten Java-Interpreter auszuführen. Der JavaCode erfüllt diese Anforderungen durch die normierte, festgelegte Maschinenbefehlsspezifikation und die normierte, standardisierte Laufzeitbibliothek. Dies ermöglicht den web browsern auch, ihre eigentlichen Funktionen flexibler zu erfüllen – neue, unbekannte Protokolle und Funktionen können so durch Laden spezieller Java content handler installiert werden, ohne den browser neu schreiben zu müssen. Der Nachteil eines solchen Ansatzes liegt darin, dass durch die begrenzten Möglichkeiten eines Applets keine großen Aufgaben bewältigt werden können. Alle Dateizugriffe, temporäre Dateien und andere lokalen Ressourcen sind aus Sicherheitsgründen verboten – und beschränken damit die Applets auf ihre Rolle als kleine Hilfsfunktionen für Browser etc. 3.4.3 Schattenserver Eine häufige Konfiguration eines Netzdateisystems besteht aus einem zentralen Dateiserver und vielen Satellitenrechnern, also PCs, Laptop-Computer oder andere Kleincomputer wie Notebooks oder Organizer. Bei dieser Konfiguration, in der die Satellitenrechner eigene, nur lose gekoppelte Dateisysteme besitzen, ist die konsistente Aktualisierung aller Dateien und Programme im System ein ernstes Problem. Es tritt meist in zweierlei Formen auf: x Roaming Manche Benutzer, beispielsweise Außendienstmitarbeiter, Telearbeiter oder Mitarbeiter einer Firma, die zwischen Arbeitsgruppen an verschiedenen Geschäftsstellen wechseln, müssen Dokumente und Dateien auf ihrem Notebook ohne dauernde Netzverbindung zur Zentrale editieren und beim Aufbau der Netzverbindung ihre neuen Dateien überspielen sowie eigene aktualisieren. Dabei verlangen diese Benutzer überall die gleiche Arbeitsumgebung, egal wo sie mit dem Notebook andocken. x Wartung Jeder Computernutzer möchte einerseits eine Vielzahl von Programmen nutzen und Daten erzeugen, andererseits aber für Konfigurationspflege, Programmaktualisierung und Datensicherung möglichst keinen Aufwand betreiben. Die Administration von Rechnernetzen verursacht aber viele Kosten: man rechnet ungefähr den Anschaffungspreis pro Jahr pro PC. Für die Lösung der Problematik kann man zwischen zwei Konzepten unterscheiden: x Zentrale Aktualisierung Üblicherweise hat der Netzwerkmanager die Möglichkeit, aktiv die Konfiguration eines am Netz hängenden Computers zu verändern. Dies erfordert aber das Wissen um eine nötige Veränderung sowie die benötigten Daten für jeden ein-
Arbeitsmodelle im Netz
113
zelnen Computer. Aus diesem Grund sind jedem Netzwerkmanager inhomogene Systeme aus unterschiedlicher Hardware und Software ein Graus und Quelle ständigen Ärgers. x Dezentrale Aktualisierung Statt dessen kann auch jeder einzelne Arbeitsplatzcomputer neben seinen speziellen Programmen und Nichtstandard-Dienstleistungen einen vom Benutzer nicht beeinflussbaren Teil (Dateisystem) enthalten, der regelmäßig (z. B. nach dem Anschalten des Rechners oder in festen Zeitabständen) mit der Konfiguration bzw. den Datenbeständen eines oder mehrerer, benutzerspezifizierter Server automatisch durch ein besonderes Programm (Dämon, Agent usw.) abgeglichen wird. Die benötigten Dateibestände und Server (Programmserver, Objektserver) werden dazu einmal beim initialen Einrichten des Rechners festgelegt. Der Rechner arbeitet also wie ein Netzcomputer, der große Teile des Betriebssystems und der benötigten Anwenderprogramme im Cache hält, um die Zugriffszeit zu senken. Der Agentenprozess der Aktualisierung hat dabei die Funktion eines Cache-Snoopers, der den Cache aktuell hält. Der Arbeitsplatzcomputer ist also „wie ein Schatten“ des eigentlichen Dateiservers, wobei nur die tatsächlich benutzten Dateien und Verzeichnisstrukturen auf dem Arbeitsplatzcomputer vorhanden sind. In Abb. 3.11 ist ein solches Konzept gezeigt.
Arbeitsplatz-PC
Server
Abb. 3.11 Das Konzept des Schattenservers (shadow server)
Der gestrichelt umrandete Teil des Dateisystems auf dem Server wird direkt auf den PC abgebildet durch PC-Server-Kommunikation konsistent gehalten. Wird eine Datei auf einem der beiden "Spiegel" verändert, so muss sie möglichst rasch auf den anderen kopiert werden. Ebenso müssen Löschanforderungen oder Dateiumbenennungen immer bei beiden durchgeführt werden. Ein solcher Ansatz hat viele Vorteile: x Aktuelle Versionen Es sind immer alle aktuellen Versionen verfügbar, ohne dass der Benutzer sich darum kümmern muss oder der Administrator sie ihm überspielt. x Datensicherung Die Benutzerdaten werden automatisch auf dem Server gesichert.
114
Internet Architekturen, Web-Services und Sicherheit
x Netzunabhängigkeit Bei Ausfall des zentralen Servers oder bei Netzwerksstörungen kann (im Unterschied zum NC!) jeder netzunabhängig weiterarbeiten. x Adaptive Konfiguration Die sich zeitlich ändernde Konfiguration der Benutzerprogramme ist immer auf dem neuesten Stand; die auf dem Rechner existierenden Programme sind auch tatsächlich die benötigten. x Rechnerunabhängige Konfigurationen Im Unterschied zur zentralen Wartung muss beim Wechsel eines Benutzers zu einem anderen Arbeitsplatz (anderen Rechner) die neue Konfiguration nicht aufgespielt werden, sondern stellt sich automatisch ein. x Geringere Wartungskosten Sowohl die Softwarewartung, die zentral und damit kostengünstig abgewickelt werden kann, als auch die Hardwarewartung, die ein unterschiedliches Spektrum an Rechner einsetzen und ersetzen kann, werden preiswerter. Beispiel Linux Ab Version 2.2 gibt es bei der freien Unix-Variante LINUX die Möglichkeit, das Dateisystem „Coda“ zu benutzen (CODA 1999). Das CodaDateisystem (Satyanarayanan et al. 1990) ist ursprünglich für das mobile computing an der Carnegie Mellon University geschaffen worden und verwendet eine Architektur ähnlich der in Abb. 3.12.
Server
Client User process RPC Kernel system call
Cache Manager process
Server process
cache file system
Server file system
I/O Manager File filter driver Regular file driver Hard disk
Abb. 3.12 Struktur des Coda-Dateisystems
Die Implementation benutzt wie beim Netzdateiserver in Abb. 3.5 einen Betriebssystemtreiber, der alle Dateiaufrufe (CreateFile, OpenFile, CloseFile, RenameFile usw.) abfängt und umleitet. Dieser spezielle Dateisystemtreiber hängt im Kern und leite die Dateiaufrufe (File System Calls) aus dem kernel mode an einem Prozess ("Venus Cache Manager") im user mode um.
Arbeitsmodelle im Netz
115
Der Manager sieht nach, ob die gesuchte Datei im lokalen Dateisystem (CodaDateisystem, implementiert mittels eines Unix raw device special file) vorhanden ist oder nicht. Wenn ja, so wird die Information über Blocknummer usw. der lokalen Platte normal ausgelesen und wieder an den user process zurückgegeben. Dieser merkt nichts von der Umleitung; er erhält direkt die gewünschte Datei. Anders dagegen, wenn die Datei nicht vorhanden ist oder als "verändert" markiert wurde. In diesem Fall wendet der Manager sich mittels RPC an den Server und fragt nach der neuesten Version, die dann kopiert wird. Erst die Blockangaben der kopierten Datei werden sodann über den Filtertreiber an den Benutzerprozess zurückgegeben. Man beachte, dass zwar alle Veränderungen bei beiden Partnern notiert werden, alle Dateien aber erst dann kopiert werden, wenn sie wirklich gebraucht werden (lazy evaluation) Die gesamte Coda-Software enthält übrigens noch weitere Komponenten wie fehlertolerante Backup-Server. Auch hier wirkt wieder das Schatten- oder Spiegelprinzip: Ist ein Server ausgefallen und das Dateisystem auf dem reparierten Rechner per Hand völlig neu initialisiert worden, so bemerkt das CodaSystem daraufhin die Inkonsistenz beider Server und bringt den neuen Server automatisch auf den neuesten Stand. Beispiel Windows In Windows NT 5, genannt Windows 2000 gibt es verschiedene Mechanismen, die ein solches Konzept unterstützen. Neben dem Konzept des aktiven Verzeichnisses (active directory), das alle Dateiänderungen im gesamten Netz von unten nach oben propagiert und damit das gesamte hierarchische Dateisystem aktuell hält, stützen sich die Kopiemechanismen des Schattenservers auf ein „IntelliMirror“ genanntes Produkt, das durch Konsistenzmechanismen die lokalen und globalen Dateisysteme beim roaming abgleicht. In Windows NT 5.1, genannt Windows XP, ist dieses Konzept allgemein zugängig und wird als „Offline-Aktualisierung“, bezeichnet. Dazu werden alle zu synchronisierenden Dateien bzw. Ordner, die vom Server im Netz freigegeben sind, beim Client in einen speziellen Ordner „Offline-Dateien“ aufgenommen. Loggt sich der Client-Benutzer ein oder aus, so kann eine Synchronisierung aller Objekte des Offline-Ordners erfolgen. Je nach Optionen, die bei der Einrichtung der Synchronisation einmal festgelegt werden, überschreibt die neue Client-Datei die alte Datei gleichen Namens auf dem Server, oder sie wird selbst von der alten Server-Datei überschrieben, oder die neue Datei wird mit einer neuen Versionsnummer neben die alte auf dem Server gespeichert. Die jeweilige Politik kann der Benutzer entweder bei jedem Konfliktfall neu auswählen oder einmal für immer festlegen.
116
Internet Architekturen, Web-Services und Sicherheit
3.5 Standard-Dienste im Netz Im Internet gibt es einige Standarddienste, die hier kurz beschrieben werden sollen. DNS Einer der wichtigsten Dienste ist der Domain Name Service DNS, der die Übersetzung zwischen den IP-Nummern und den Internetnamen besorgt. Neben automatischen Diensten kann man ihn auch direkt über Hilfsprogramme ansprechen. Beispielsweise ergibt die Konsoleneingabe nslookup siemens.com
eine Antwort vom DNS-Server Server: styx.rbi.informatik.uni-frankfurt.de Address: 141.2.15.5 Name: siemens.com Address: 192.138.228.1
Jeder DNS-Server ist für einen Teilbereich im Namensraum (seine Domäne) zuständig und steht in einem Verbund mit den anderen DNS-Servern in der Hierarchie. Er kommuniziert mit ihnen, um unbekannte Angaben zu erfragen seine Tabellen immer auf dem neuesten Stand zu halten. TELNET Dies ist ein Programm, das eine Konsole (Terminal) auf einem anderen Rechner im Netz simuliert. Da es eines der ersten Dienste war, simuliert es nur ein „dummes“ Terminal mit schwarz-weißen ASCII-Zeichen ohne Fenster und Mauszeiger. Das Telnet-Programm verwendet ein spezielles Protokoll, was aber von anderen Rechnern im Netz abgehört werden kann. Eine solche Verbindung ist also nicht sicher. Man verwendet deshalb heute Programme mit verschlüsselter Kommunikation, etwa ein Secure Shell-Programm, z.B. SSH. FTP Der allgemeine Dateiübertragungsdienst wird durch ein Protokoll, das file transfer protocol FTP, definiert. Jeder FTP-Server muss u.a. folgende Operationen unterstützen: x Anzeige des Inhaltsverzeichnisses des Ordners im Dateisystem x Senden einer Datei x Empfangen einer Datei Zur Erfüllung seiner Aufgaben verwendet er zwei Arten von Verbindungen: zum einen Kontrollverbindungen zur Übermittlung der Wünsche, zum anderen
Standard-Dienste im Netz
117
Datenverbindungen für die eigentlichen Dateien, deren Portnummer usw. über die Kontrollverbindung übermittelt werden. WWW Die wichtigste Anwendung, die der breiten Maße der Anwender das Internet erschlossen hat („Killer Application“), ist zweifelsohne das World Wide Web WWW. Durch die Verwendung x von einheitlichen Namen im Netz, der URL x einer einfache Navigation „auf Knopfdruck“ zwischen den Seiten durch Hyperlinks x einer genormten Seitenbeschreibungssprache HTML, die es erlaubt, Bilder, Audio und Video in einem gemeinsames Dokument zu platzieren x eines verbindlichen Protokolls (z.B. HyperText Transport Protocol HTTP) zum Transport von Dokumenten zwischen Server und Client war es möglich, eine Vielzahl von Informationsangeboten mit relativ einfachen Mitteln einzurichten. Die WWW-Initiative integriert dabei auch die anderen Dienste wie FTP, news oder mailto, so dass auch andere Protokolle außer HTTP in der URL auftreten können. EMAIL Die Übermittlung von Textnachrichten ist ein wichtiger Dienst im Internet. Typische Merkmale dabei sind x Asynchrones, zeitlich entkoppeltes Senden und Empfangen von Nachrichten. Der Sender kann weiterarbeiten, ohne auf den Erhalt der Nachricht durch den Empfänger warten zu müssen. x Die Nachrichten werden zwischengespeichert, so dass keine direkte Verbindung zwischen Sender und Empfänger existieren muss Für den direkten Austausch von Nachrichten wird das Simple Mail Transfer Protocol SMTP verwendet; für Rechner, die nur ab und zu Post abholen wollen und keine Dauerverbindung haben, ist das Post Office Protocol POP sinnvoll. Für das synchrone Senden ist deshalb meist SMTP, für das Empfangen POP (Version 3: POP3) üblich. NEWS Für weltweite Diskussionen über bestimmte Themen hat sich das Usenet etabliert. Es besteht aus dezentral verwalteten Diskussionsgruppen, die thematisch strukturierte, hierarchische Namen haben. Beispielsweise wird in comp.lang.c über die Computersprache C diskutiert; in rec.games.chess über das Schachspiel. Die beliebtesten Gruppen sind sci für Wissenschaft, soc für Gesellschaft und Politik, rec für Hobbys, Essen und Trinken und comp für Computerthemen. Spitzenreiter ist aber die Kolumne für Klatsch und Tratsch – alt. Die Artikel im Usenet werden über das Network News Transfer Protocol NNTP abgewickelt. Dabei werden neu eintreffende Artikel vom Empfänger zum benach-
118
Internet Architekturen, Web-Services und Sicherheit
barten Rechner übertragen. Der Weg wird dabei im Header mitgeführt, so dass doppelte Übertragungen vermieden werden können.
3.6 Middleware In vielen Firmen hat sich im Laufe der Jahre eine bunte Mischung aus diversen Rechnerfabrikaten und -formen, Anwendungen mit verschiedenen Programmiersprachen, Kommunikationsnormen und Datenbanken verschiedener Hersteller angesammelt. Diese unterschiedlichen Komponenten haben teilweise Probleme beim Zusammenspiel, hohe Kosten bei Wartung und massive Probleme bei notwendigen Änderungen und Neukonfigurationen. Was kann man tun, um dieses kostspielige Chaos zu beseitigen?
Abb. 3.13 Heterogenität als Problem
Eine mögliche Lösung für eine IT-Konsolidierung liegt auf der Hand und wird gern von Herstellerfirmen propagiert: Alles aus einer Hand. Diese Lösung ist möglich, wenn man ganz neu anfängt, aber bei einer Firma, die im laufenden Betrieb „das Rad wechseln“ muss, schafft eine solche Monokultur neue Probleme: Neben der gefährlich starken Abhängigkeit von einem einzigen Hersteller und seiner Modell- und Lizenzpolitik ist eine solche „Rundumerneuerung“ in der Praxis auch nicht konfliktfrei, ist teuer und dauert lange, meist zu lange.
Middleware
119
3.6.1 Transparenz und IT-Konsolidierung durch Middleware Es gibt aber noch eine zweite Lösung: Statt einheitlichen Komponenten fordert man nur ein reibungsloses Zusammenspiel, die Interoperabilität. Trotz unterschiedlicher Hardware, unterschiedlicher Betriebssysteme, unterschiedlicher Kommunikationsprotokolle und unterschiedlicher Datenbanken kann ein Zusammenwirken erreicht werden. In Abb. 3.14 ist dies am Beispiel von kommunizierenden Applikationen visualisiert, die unterschiedlich auf verschiedene Datenbanken zugreifen wollen.
Middleware
Abb. 3.14 Middleware als Vermittlungsschicht
Rein technisch gesehen hilft hier wieder unser Schichtenmodell mit seinen virtuellen Maschinen: Wir ziehen eine zusätzliche Schicht zwischen Anwendungsprogramm und Datenbanken (oder Speichermedium, Peripheriegerät, etc.) ein, die Middleware. Sie verhält sich für jede der möglichen Anforderungen wie die gewünschte Datenbank und kann jedes gewünschte Netzprotokoll für den Datenbankzugriff benutzen, siehe Abb. 3.15. Die Anwendung muss auf keine Transportprotokolle oder andere Dinge Rücksicht nehmen; alle notwendigen Einstellungen werden in der Middleware vorgenommen. Umgekehrt sieht die Datenbank dadurch nur noch Standardapplikationen; alle abweichenden Zugriffsarten und –protokolle werden von der Middleware verdeckt.
120
Internet Architekturen, Web-Services und Sicherheit
Anwendungen Middleware
Abb. 3.15 Schichtung der Middleware zur Kommunikation
In Abb. 3.16 ist die dreigegliederte Architektur von SAP/R3 gezeigt, bei der Applikationen die Middleware-Aufgabe übernommen haben, zwischen Präsentationsprogrammen und den dazu nötigen Daten zu vermitteln.
Abb. 3.16 BeispielMiddleware: SAP/R3 3-tiers Architektur
3.6.2 Vermittelnde Dienste Neben proprietären Middleware-Diensten einzelner großer Herstellerfirmen gibt es auch systematische Ansätze und Architekturen, die Erstellung von Middleware zu vereinfachen und billiger zu machen. Einer der ersten Ansätze war die Konzeption einer allgemeinen, objektorientierten Dienstarchitektur durch die Object Management Group OMG 1989 mit ca. 800 Mitgliedern.
Middleware
121
Aufgabe sollte es sein, alle Anfragen an Drucker, Datenbanken usw. so zu vermitteln, dass der anfragende Prozess nicht wissen muss, welcher Drucker im Pool noch Kapazität frei hat und ausreichend Toner besitzt, sondern nur, was für eine Art von Druck er will. Der Dienstvermittler (Broker) nimmt den Auftrag entgegen, verpackt ihn mit seinen Parametern, schickt ihn an den betreffenden Service, nimmt die Antwort (Ergebnis) entgegen und gibt es an den betreffenden Auftraggeber zurück. Ein solcher Object Request Broker ORB ist sehr praktisch; allerdings ist es nicht einfach, einen solchen Dienstvermittler zu erstellen. Deshalb konzipierte das OMG eine allgemeine Architektur, die Common ORB Architecture CORBA, und führte auch eine Referenzimplementierung durch. Beispiel Java-Technologie Auch im Bereich der Programmiersprache Java wuchs die Erkenntnis, das die dedizierte, anwendungsabhängige Client-Server-Architektur, die für die Programmiersprache Java als Bibliotheken zur Verfügung gestellt wird, nicht ausreicht. Deshalb wurde von der Fa. SUN als eine anwendungsunabhängige, dienstvermittelnde Schicht die Middlewarearchitektur JINI entwickelt. In Abb. 3.17 ist ein Überblick gezeigt.
Anwend.
Abb. 3.17 Architekturschichten von Jini
Jini ist eigentlich eine Menge von Schnittstellen (API), welche die Java2-Plattform für verteilte Programmierung erweitert. Als Abstraktionsschicht ermöglicht sie dynamische Systeme mit wechselnder Anzahl von Servern und Clients, die über den naming-Service sich registrieren, suchen und finden, und auch wieder entfernen können. Die Architektur baut auf Kommunikation mittels RMI auf. Als Anwendungsbeispiel ist in Abb. 3.18 ein Thin Client/Thick Server–Modell gezeigt, das die verschiedenen vorgestellten Techniken einsetzt, um datenbankbasierte Anwendungen in Middleware-Technik zu realisieren.
122
Internet Architekturen, Web-Services und Sicherheit
Abb. 3.18 Thin clients/Thick server durch Middleware
Beispiel Windows NT Unabhängig davon konzipierte Microsoft ein System zur Kopplung verschiedener Anwendungen, das Common Object Model COM. Es definiert einen binären, proprietären, aber sprachunabhängigen Standard für den Zugriff auf Objekte durch eine Tabelle von verfügbaren Funktionen (Methoden) eines Objekts. Allerdings stellte sich bald heraus, dass in Netzen weitere Funktionalität nötig ist. Dies führte zur Entwicklung von Distributed COM DCOM, das mit der Microsoft Interface Definition Language MIDL programmiert werden muss und im Gegensatz zu CORBA nur Remote Procedure Calls zur Datenübermittlung im Netz benutzt. Für die Anwendung von DCOM bei Datenbanken ist aber noch die Unterstützung von Transaktionen nötig. Deshalb integrierte Microsoft einen Transaction Server MTS in das COM/DCOM-System. Diese Middleware, genannt COM+, nimmt dabei noch zusätzliche Aufgaben wie Cachefunktionen der Datenbank, Lastbalancierung zwischen den Objekt-Servern und Unterstützung asynchroner Aufrufe (Queued Components). Portierungen von DCOM auf andere, nicht-Windows-basierte Systeme (etwa auf Solaris und OS/390 durch EntireX) ermöglichen eine Interoperabilität auch zu anderen Betriebssystemen und Rechnern. Das COM+-Modell ist noch stark von den verwendeten Sprachen und Kommunikationsmechanismen der Komponenten abhängig. Aus diesem Grund wurde das Modell zu einem allgemeineren Rahmenwerk .NET weiterentwickelt. Das .NET-Konzept beinhaltet im Wesentlichen ein Entwicklungssystem, das VisualStudio.NET, das die Komponentenentwicklung mit einer Vielzahl von Programmiersprachen ermöglicht und die dazu benötigten Bibliotheken und Laufzeitsysteme enthält. In Abb. 3.19 ist ein Überblick der Architekturschichten gezeigt.
Sicherheitsmechanismen und Konzepte im Netz
123
Abb. 3.19 Gesamtschichtung der .NET-Architektur
Zum Datenaustausch wird ein offenes Format, die Datenbeschreibungssprache XML, verwendet. 3.6.3 Universal Plug-and-Play Im ersten Kapitel haben wir einen wirkungsvollen Mechanismus kennen gelernt, mit dem sich Geräte in einem Rechnersystem identifizieren: den „plug-and-play“Mechanismus des PCI-Busses. Hierbei ermöglichen die Geräteplatinen dem Betriebssystem, über Speicherstellen auf Informationen über das Gerät zuzugreifen und es zu identifizieren. Dies gelingt, weil die Geräteinformation ein genau festgelegtes Format hat, das ein Zerlegen der Information in Hersteller, Geräteart und Gerätenummer leicht ermöglicht. Diese Idee ist nun aufgegriffen und in dem Konzept eines „universellen plug-and-play“ Systems verwirklicht worden. Dabei wird statt eines speziellen Gerätebusses ein allgemeines Netzwerk, und statt eines passiven Geräts ein aktives Gerät angenommen, das nach dem Hochfahren sich im Netz mit seiner Bezeichnung („Drucker HP PrintJet“) meldet und außerdem noch sagt, was es kann („Druckdienst Laserdruck schwarz-weiß DIN A4“). Damit wird ein Dienstservice definiert, der einer Middleware-Schicht entspricht und leicht in andere Dienste, etwa Active Directory oder CORBA integriert werden kann.
3.7 Sicherheitsmechanismen und Konzepte im Netz Sicherheitsfragen in Computersystemen sind keine technischen Probleme, sondern menschliche Probleme. Ein Computer funktioniert auch ohne Sicherheitseinrichtungen technisch einwandfrei. Besonders aber wenn ein Computer nicht mehr isoliert, mit wenigen, vertrauten Benutzern betrieben, sondern in ein Netz mit vielen, unbekannten Benutzern eingegliedert wird, tauchen Sicherheitsprobleme
124
Internet Architekturen, Web-Services und Sicherheit
auf. Eines der wichtigsten Aufgaben eines Systemadministrators besteht darin, diese Probleme zu erkennen und Zeit und Geld dafür aufzuwenden, um sie zu beheben oder mindestens entsprechende Maßnahmen zur Prävention zu ergreifen. 3.7.1 Vorgeschichte Nachdem im November 1988 ein sich selbst replizierendes Programm einen Großteil aller ans Internet angeschlossenen UNIX-Computer an der CarnegieMellon-Universität (USA) infiziert und lahmgelegt hatte, wurde dort das Computer Emergency Response Team CERT gegründet, das systematisch versucht, Sicherheitslücken in Computersystemen zu entdecken, zu dokumentieren und zu beseitigen. Schon für 1996 rechnete das CERT mit ca. 4.000 erfolgreichen Einbruchsversuchen durch ca. 30.000–40.000 Angreifer (Hacker), die immer neue Sicherheitslücken in den Systemen entdecken oder die bekannten schneller ausnutzen, als die Systemadministratoren sie beseitigen können (oder wollen). Obwohl inzwischen „der moderne Dieb mit seinem Computer mehr stehlen kann als mit vorgehaltener Waffe“ (National Research Councel) und „der Terrorist von morgen keine Bomben mehr legt, sondern nur eine Taste drückt und damit viel mehr Schaden anrichtet“ (Wissenschaftsrat des US-Kongresses), wird bisher wenig getan, um sichere Rechnersysteme zu konstruieren. Im Zusammenhang damit ist es interessant zu wissen, dass auch das USVerteidigungsministerium (Pentagon) Arbeitsgruppen betreibt, die Kriegführung durch maximale Schädigung feindlicher Computersysteme vorbereiten. Dies schließt nicht nur den Einbruch in Computer über eine Netzwerkverbindung und das Manipulieren oder Löschen wichtiger Dateien ein, sondern auch den gezielten Hardwaredefekt von Computern am Netz. Man kann beispielsweise durch wiederholtes Ausführen bestimmter Instruktionen versuchen, bestimmte Teile eines Mikroprozessors, die dafür nicht ausgelegt sind, überhitzen und damit den Prozessor generell schädigen. Voraussetzung für die Angriffe auf Computersysteme über das Netzwerk ist das Eindringen von Daten in einen Computer über das Netz. Dies kann auf unterschiedlichen Wegen geschehen. 3.7.2 Eindringen über das Netz Eine der naheliegenden Möglichkeiten, über ein Netz einzudringen, ist die Computerbenutzung („Einloggen“) über das Netz, beispielsweise mit einem Programm wie Telnet. Das Problem, die Zugangskontrolle über ein Schlüsselwort (Passwort) zu umgehen, wird von den Einbrechern auf verschiedene Weise gelöst:
Sicherheitsmechanismen und Konzepte im Netz
125
Passwort erfragen
Ein in der Effizienz bisher unerreichte Angriffsart vermag auch gut geschützte Systeme zu durchdringen: Der logistische Angriff. Hierbei ruft der potentielle Einbrecher einfach telefonisch bei einem Mitarbeiter an, gibt sich als Mitarbeiter des Rechenzentrums aus und fragt „aus Kontrollgründen“ das Passwort ab. Viele Menschen fallen leider darauf herein. Auch die elektronische Version mittels Email hat Erfolge: So genannte Phishing-Mails ("Fishing" ausgesprochen) täuschen eine seriöse Herkunft vor - meist von Banken, Kreditkarteninstituten, OnlineAuktionshäusern- und Bezahldiensten - und fordern den Empfänger zur Eingabe persönlicher Daten, Passwörter, Kreditkartennummern und PIN-Codes auf. Dazu wird der Anwender entweder auf eine präparierte Webseite (JavaScript!) geleitet oder ein entsprechendes HTML-Formular in der Mail nimmt die Daten auf. Es ist klar, dass die eingegebenen Daten „zum Datenabgleich“ nicht bei der Bank, sondern beim Betrüger landen. Passwort erraten
Einer der typischen Fehler besteht darin, die Standardpasswörter der Herstellerfirma des Betriebssystems für Systemadministration, Service usw. nach Inbetriebnahme des Rechners nicht zu ändern, beispielsweise, weil vergessen wurde, das entsprechende Manual zu lesen. Sind die Standardpasswörter bekannt, so ist es kein Problem, von außen in ein System zu kommen. Ein weiterer, oft begangener Fehler legaler Benutzer besteht darin, nur normale Worte (Vornamen, Bäume, Tiere, Städte, Länder usw.) zu verwenden. Ist die Datei der verschlüsselten Passwörter frei lesbar (wie in UNIX-Systemen unter /etc/passwd), so kann ein harmloser Besucher wie guest (der häufig vorhandene Name für unbekannte Besucher) die Datei kopieren und mit dem bekannten Verschlüsselungsmechanismus alle Einträge eines Lexikons (bei 250.000 Einträgen und 1 ms pro Eintrag dauert dies nur 4 Minuten!) verschlüsseln und mit den Einträgen der Passwortdatei vergleichen – meist sind ein oder mehrere Benutzer dabei, die so einfache Passwörter haben, dass sie im Lexikon stehen. Auch der Name, die Zimmer- oder Telefonnummer sind als Passwörter leicht zu erraten; über das Netz werden diese Daten von den Programmen finger und who geliefert. Gegenmittel wie aufgezwungene Passwörter (z. B. Zufallszahlen) helfen auch nicht: Die Benutzer vergessen sie oder schreiben sie auf am Terminal befestigte Zettel, deutlich sichtbar für jeden zufällig Vorbeikommenden. Auch der regelmäßig erzwungene Wechsel des Passwortes hilft nicht: Benutzer wehren sich dagegen, neue Passwörter zu lernen und überlisten das System, um die alten zu verwenden. Eine Möglichkeit, zu simple Passwörter zu verhindern, besteht darin, gleich bei der Eingabe das neue Passworts daraufhin zu prüfen, ob es leicht zu erraten ist.
126
Internet Architekturen, Web-Services und Sicherheit
Passwort abhören
Hat ein Eindringling Kontrolle über einen Übermittlungsrechner des Netzwerks gewonnen, so kann er den Nachrichtenverkehr, beispielsweise eines legalen Benutzers beim Einloggen, mitverfolgen und aufzeichnen. Damit kann er sich später selbst einloggen, selbst wenn das Passwort vom Benutzer vorher verschlüsselt wurde. Abhilfe schafft hier nur entweder ein zeitabhängiges, verschlüsseltes Passwort des Benutzers (z. B. wenn der Benutzer sich über einen Satellitenrechner in den Netzwerkcomputer einloggt) oder ein Sicherheitsprotokoll, das zwischen den Computer (bzw. der „intelligenten“ Zugangskarte) des Benutzers und den Netzwerkcomputer geschaltet wird und dessen Parameter (und damit die Protokolldaten) ständig wechseln. Trojanische Pferde in Netzdiensten
Aus der griechischen Sage wissen wir, dass die Stadt Troja nicht durch Überwindung ihrer starken Mauern eingenommen wurde, sondern dadurch, dass die Einwohner ein vermeintlich harmloses, großes hölzernes Pferd in ihre Stadt rollten. Die im Innern verborgenen Angreifer bemerkten die Trojaner nicht, und wurden so in der Nacht innerhalb ihrer starken Mauern im Schlaf überrascht. Genau dieser Strategie, bekannt als „Trojanisches Pferd“, bedienen sich verschiedene Eindringlinge, um in ein System zu kommen. Dazu werden Lücken in normalen Diensten, die im Netz existieren, benutzt, um eigene Programme in einen Computer zu schmuggeln. Typische Lücken gibt es beispielsweise bei folgenden Diensten: x E-mail-Programme Eine Botschaft eines unbekannten Absenders kann alles mögliche enthalten – beispielsweise auch nicht druckbare Zeichen (Escape-Sequenzen), die beim Auflisten des Textes der Botschaft nicht ausgedruckt werden, sondern das Terminal dazu veranlassen, unter einer Funktionstaste Befehle zu speichern. Passiert dies dem Systemadministrator (super user) eines UNIX-Systems, so werden beim nächsten Betätigen der Funktionstaste die Befehle abgeschickt mit der Befehlsgewalt des super users. Löscht der letzte Befehl das Zeilenecho und die Belegung der Funktionstaste, so bemerkt der super user nichts von seiner unfreiwillig gegebenen Anweisung, beispielsweise bestimmte Zugriffsrechte auf bestimmte Dateien zu verändern, so dass der Eindringling sie später ungehindert als Gast manipulieren kann. Eine Gegenmaßnahme dafür besteht darin, in E-mail-Daten prinzipiell alle nicht druckbare Zeichen auszufiltern. x World-Wide-Web-Dienste Die Browser des Hypertextsystems WWW benutzen meist verschiedene Hilfsprogramme, um besondere Dienste auszuführen. Beispiele dafür sind Bild- und Tondisplayprogramme für verschiedene Dateiformate. Ist etwa eine Postscriptdatei im Netz vorhanden und der Benutzer möchte sie ansehen, so wird die Da-
Sicherheitsmechanismen und Konzepte im Netz
127
tei übers Netz geladen und einem speziellen Programm, meist ghostview, übergeben, das die darin enthaltenen Texte und Bilder auf dem Bildschirm darstellt. Leider ist aber Postscript nicht nur eine Seitenbeschreibungssprache, sondern auch eine Programmiersprache und kann ausführbare Programme enthalten, die von ghostview im Namen des Benutzers ohne dessen Wissen sofort ausgeführt werden. Diese Eigenschaft kann man bei der Konfiguration abstellen, wenn das Wissen um die Gefahr vorhanden ist und der Administrator die Dokumentation gut gelesen hat ... Eine andere Möglichkeit des Eindringens bietet die Active-X-Technologie der Fa. Microsoft: Die Objekte, die über das Netz geladen werden und Code enthalten, haben die Rechte des ausführenden Programms und damit prinzipiell Zugriff auf den gesamten Rechner. Dies kann dazu führen, dass unbemerkt nach Besuch einer Webseite Spionageprogramme auf dem Rechner installiert werden, die Passworte, Bankzugangsdaten u.ä. protokollieren und an Kriminelle weiterleiten. x FTP file transfer-Dienste Auf jedem vernetzten Rechner existiert i. d. R. ein Dateitransferdienst ftp, der von außen aufgerufen werden kann. Dies kann als Einstiegspunkt genommen werden. Der ftp-Prozess auf einem Rechner hat normalerweise nur sehr wenig Rechte. Sind diese aber falsch konfiguriert oder die Zugriffsrechte für Dateien von anderen Benutzern falsch gesetzt, so kann der Angreifer von außen manipulierte Dateien, beispielsweise eine Passwortdatei, in einen Computer kopieren und so den Angriff von außen vorbereiten. Aus diesem Grund sollte der ftp-Prozess praktisch keinerlei Zugriffsrechte haben und seine Aktionsmöglichkeiten sollten auf einen kleinen, genau festgelegten Dateibaum begrenzt werden. Der buffer overflow-Angriff
Einer der ältesten und bisher durch die Jahre erfolgreichster Angriffsmechanismus ist das Erzeugen eines buffer overflow. Hierbei wird beim Aufruf eines Programms über das Netzwerk ein Argument (Zeichenkette) übergeben, das viel zu groß ist für den Zeichenpuffer des Programms. Ein Beispiel dafür ist der Aufruf einer CGIFunktion (Funktion auf einem Webserver, die von jedem, der die Webseite im Internet besucht, angesprochen werden kann) oder die Eingabe bei einem Datenbankrecherche-Programm. Um zu verstehen, wieso man mit einer harmlosen Eingabe einen Rechner direkt übernehmen kann, müssen wir nur uns die typischen Programmiermechanismen vergegenwärtigen. Beim Einlesen einer Zeichenkette landet sie meist früher oder später auf dem Stack, entweder weil die Stringvariable dynamisch und nicht statisch ist, oder weil der Eingabestring als Argument einer Funktion übergeben wird. In beiden Fällen wird beim Kopieren der Eingabepuffer eine string copy-Prozedur strcpy(Quelle, Ziel) verwendet, die die gesamte Eingabekette kopiert, egal, wie lang sie ist. Auf dem Stack wird üblicherweise ein Standardplatz (ca. 80 Zeichen) pro String reserviert. Ist die Zeichenkette länger, so werden alle anderen
128
Internet Architekturen, Web-Services und Sicherheit
Daten auf dem Stack ebenfalls überschrieben, etwa die Rückkehradresse der Kopierprozedur. Sind die überschreibenden Bytes vom Angreifer so gewählt worden, dass sie den Maschinencode eines Angriffsprogramms repräsentieren, so wird statt beim Rücksprung nicht die korrekte Rücksprungadresse vom Stack gelesen, sondern die Gefälschte, die hinein in das Angriffsprogramm führt. Statt des Systemprogramms wird nun das Angriffsprogramm, ausgestattet mit den Rechten des Systemprogramms, ausgeführt. Wie kann man sich vor solchen Angriffen schützen? Der wirkungsvollste Versuch ist sicherlich, auf Betriebssystemebene das Stacksegment für ausführenden Code zu sperren, also die Prozessrechte für das Stacksegment auf Lesen und Schreiben zu beschränken. Da inzwischen diese Angriffsart auch auf das Heap (Daten) -Segment ausgedehnt wird, sollte nur noch das reine Programmcodesegment Ausführungsrechte erhalten, wobei das Setzen reiner Lese- und Ausführungsrechte auf dem Programmsegment das Ändern und Überschreiben verhindert. Versucht man, auf Programmierebene den Pufferüberlauf zu verhindern, so kommt hier zuerst die Benutzung von Programmiersprachen in Frage, die den Index bei Feldern ständig überprüfen und bei Überlauf einen Fehler melden, etwa die Sprache Java. Möchte man C verwenden, so lassen sich spezielle Funktionen benutzen, etwa der Gebrauch von strncpy() anstelle von strcpy(), bei der das Ziel des Kopierens der Zeichenkette vor Gebrauch überprüft wird. Liegen nur die Binärversionen der Programme vor, so kann man noch zwischen Programm und Bibliothek eine Zwischenschicht schalten, die die Argumentüberprüfung übernimmt. 3.7.3 Übernahme der Kontrolle auf einem Rechner Hat sich ein Eindringling erst einmal Zugang zu einem Computersystem verschafft, so kann er auf verschiedene Weise versuchen, die Administratorenrechte darauf zu erlangen. Gerade auf UNIX-Rechnern, die keine abgestuften Administratorenrechte kennen, bedeutet die Übernahme eines wichtigen Systemprogramms meist auch die Übernahme der gesamten Kontrolle über den Rechner. Wie ist es nun möglich, vom Status eines „normalen“ Benutzers zu Administratorrechten zu gelangen? Dazu gibt es verschiedene Wege, die folgende Schwachstellen nutzen: Zugriffsrechte im Dateisystem
Sind die Schreib- und Leseberechtigungen für Systemdateien falsch gesetzt, so kann ein Eindringling dies leicht ausnutzen. Beispiel Kommandopfad Üblicherweise werden Kommandos in UNIX so abgearbeitet, dass an verschiedenen Stellen im Verzeichnisbaum danach gesucht wird und die erste ausführbare Datei mit dem Kommandonamen, die gefunden wird, auch als Pro-
Sicherheitsmechanismen und Konzepte im Netz
129
gramm ausgeführt wird. Die Reihenfolge der Stellen ist in der Zeichenkette mit dem Namen PATH gespeichert. Möchte ich z. B. den Befehl cmd ausführen lassen, so wird nach /bin/cmd, /usr/bin/cmd, /usr/local/bin/cmd, und . /cmd gesucht. Hat jemand Zugriffsrechte auf eines dieser Verzeichnisse im Dateibaum, so kann er unter dem offiziellen Kommandonamen seine eigene Version des Kommandos dort hineinstellen, beispielsweise der Kommandos rlogin, su oder auch nur ls zum Anzeigen der Dateien. Führt ein Benutzer nun ein solches Programm aus, so kann es die gewünschten Ergebnisse liefern – und zusätzlich (bei rlogin und su) die Passwörter in einer Datei des Eindringlings speichern (Trojanische Pferde). Auch ein loginProgramm ist denkbar, das ein Standardpasswort des Eindringlings kennt und ihn ohne Kennung einlässt. Zugriffsrechte von Systemprogrammen
Viele Dienstprogramme für Arbeiten am Betriebssystem haben umfangreiche Rechte. Beispielsweise muss ein Editor auf alle Dateien eines beliebigen Benutzers schreiben und lesen können; ein E-Mail-Programm muss die elektronischen Briefdateien allen Benutzern in ihren Briefkasten schreiben dürfen und ein Prozessstatusmonitor (z. B. ps in UNIX) muss Zugriff auf Tabellen des Betriebssystemkerns haben. Gelingt es, die Aktionen des Systemprogramms so auszunutzen, dass es andere als vorgesehene Dinge tut, so hat der Angreifer sich im System eingenistet. Beispiel Emacs-Fehler Im GNU-Emacs-Editor hatte man die Möglichkeit, mit movemail eine EMail in ein Verzeichnis zu schreiben. Allerdings prüfte der Editor (obwohl Systemprogramm) nicht nach, ob das Zielverzeichnis auch dem aktuellen Benutzer gehört und ermöglichte so, andere Systemdateien zu überschreiben. Dies war eine der Lücken, die deutsche Hacker 1988 nutzten, um im Auftrag des KGB in militärische Computer der USA einzudringen und auf Militärgeheimnisse hin zu untersuchen. Meist sind von diesen Problemen neue Versionen oder inkorrekt installierte Systemprogramme betroffen. Einen Sicherheitstest auf Zugriffsrechte und andere Schwachstellen wie zu einfache, ungültige oder fehlerhafte Passwörter bietet das Softwarepaket COPS von CERT für UNIX, das kostenlos bezogen werden kann (COPS 1997). Weitere Informationen sind auch z. B. in (SAT 1997) über das Werkzeug Satan sowie über neuere Sicherheitspatches in (DFN 1997) zu finden. Erzeugen eines Virus
Ein anderer Ansatz besteht darin, den Angriff gegen das System nicht per Hand auszuführen, sondern als Programm ablaufen zu lassen. Ist darin noch zusätzlich eine Möglichkeit zum Kopieren (Vervielfältigen) des Einbruchprogramms enthalten, so kann sich dieses Programm wie eine Infektion von Computer zu Computer
130
Internet Architekturen, Web-Services und Sicherheit
weiterverbreiten. Allerdings benötigt es dazu meist andere Programme (z. B. Netzwerk- oder Systemprogramme) als Überträger, so dass diese Art von Programmen Viren genannt werden. Es gibt verschiedene Arten von Viren. Die auf MS-DOS-Systemen meistverbreiteten Viren (80%) sind die im Betriebssystem-Startprogramm (bootstrap) angesiedelten Viren, die beim Systemstart aktiviert werden (bootstrap-Viren). In der Regel werden sie durch Disketten übertragen, deren bootstrap-Blöcke (Block 0 bei Massenspeichern) infiziert sind und die sich auf die Festplatte übertragen. Aus diesem Grund ermöglichen neuere bootstrap-Konfigurationen (BIOSEPROM) dem Eigentümer, das Beschreiben seines Festplattensektors 0 zu sperren. Die allerdings heutzutage auf Windows und Unix-Systemen am meisten verbreitete Form von Viren kommt aus dem Internet und existiert auf Rechnern, die nicht mit Disketten gestartet werden (z. B. UNIX-Rechnern), und siedelt sich in den ausführbaren Dateien der Benutzer an. Dazu fügt der Virus einen zusätzlichen Codeteil an das Programmende an, der eine Viruskopie enthält. In Abb. 3.20 ist das Prinzip gezeigt.
Start
Infektion Start
Sprung
user program code
user program code
Sprung
Virus Programmausführung
start
Abb. 3.20 Die Infektion eines Programms
Der Virus klinkt sich in die Startsequenz ein, so dass er zuerst ausgeführt wird, sein dunkles Tagewerk vollbringt und erst danach die Kontrolle an das Originalprogramm weitergibt. Das läuft ab, ohne dass der Benutzer dabei etwas von den anderen Dingen ahnt, die er mit der Aktivierung des Programms verursacht hat. Infiziert der Virus nur langsam und unregelmäßig die ausführbaren Dateien des Dateisystems, so kann man sicher sein, dass keiner etwas davon bemerkt. Näheres darüber ist in (Spafford, Heaphy, Ferbrache 1990) zu finden. Da Anti-Virenprogramme die Viren durch typische Befehlssequenzen am Programmende erkennen, gibt es mittlerweile Viren, die ihren Code nicht mehr direkt an die Programme anhängen, sondern nur aus dem verschlüsselten Viruscode sowie einer Entschlüsselungsprozedur bestehen. Erst zur Laufzeit wird der Code zu dem eigentlichen Programm expandiert und ausgeführt. Da auch diese Spielart an typischen (codierten) Befehlssequenzen erkannt werden können, versuchen die polymorphen Viren durch dauernde Mutation des Schlüssels ihren Befehlscode bei jeder Infektion zu verändern.
Sicherheitsmechanismen und Konzepte im Netz
131
Die dritte Art von Viren besteht aus Prozeduren, die einem Objekt beigefügt werden, ohne dass der Benutzer dies merkt. Ein typisches Beispiel dafür sind Textdateien, die mit entsprechenden benutzerdefinierten Funktionen zusammen abgespeichert werden, beispielsweise die in einer hardwareunabhängigen Hochsprache (Makrosprache) geschriebenen Funktionen für den Microsoft-WordEditor. Bezieht man eine Word-Datei von einem fremden Rechner, so kann ein in der Makrosprache geschriebener Virus darin enthalten sein (Makrovirus), der im Dokument nicht als Text sichtbar ist. Im Unterschied zu einem herkömmlichen Virus wird er nicht direkt mit dem Wirtsprogramm gestartet, sondern erst beim Laden der Daten. Durch seine Unabhängigkeit von der Betriebssystem- und Compilerumgebung ist ein Makrovirus extrem portabel und ansteckend. Unbekannte Textdateien sollten also zuerst mit einem Virenscanner auf unbekannte Funktionen (Makros) abgesucht und dann erst geladen werden, wobei die Makroausführungsmöglichkeit vorher abgeschaltet werden sollte. Ein wichtiger Schutz ist deshalb die Typisierung von Dateien und die Registrierung der Typen im Betriebssystem, und die Regelung, welche Programme auf welchen Typ mit welchen Rechten zugreifen dürfen. Ist beispielsweise registriert, dass ausführbare Dateien nur vom Linker geschrieben werden können, so kann ein Virus nicht Programme beschreiben und damit sich nicht in andere Programme einschleusen. Leider ist die Ausdehnung dieser Systematik auf die Makros ausführenden Programme wie Word oder Excel nicht möglich, da die Ausführungseigenschaften vom Makros nirgendwo spezifiziert werden und deshalb auch nicht beachtet werden können. Hier besteht Verbesserungsbedarf beim Hersteller. Wie kann man einen eingeschleppten Virus entdecken?
Wie entdeckt man, dass ein Virus sich im System angesiedelt hat? Eine Möglichkeit ist, von allen ausführbaren Dateien die Quersumme (Prüfsumme) zu bilden und sie in extra Dateien abzuspeichern. Ist das Format dieser Prüfsummendatei geheim, so kann der Virus sie nicht fälschen, und eine regelmäßige Überprüfung der Quersummen kann zur Entdeckung führen. Eine andere Idee besteht darin, eine genau definierte Testdatei (Lockvogel) zu installieren und regelmäßig zu überprüfen. Ist sie vom Virus verändert worden, so kann man damit nicht nur die Anwesenheit des Virus feststellen, sondern auch den Code extrahieren und den Virus im ganzen Rechnersystem gezielt suchen und löschen. Was kann man dagegen tun, wenn man einen Virus im System entdeckt hat?
Das bewährte Mittel dazu heißt: Erst einmal Ruhe bewahren (Don’t panic). Haben wir ein kleines System (z. B. einen PC), so ist die Strategie klar:
132
Internet Architekturen, Web-Services und Sicherheit
Gibt es auf einem separaten Datenträger eine saubere Kopie des Betriebssystemkerns (z.B. die Original-Systemdiskette) sowie ein vertrauenswürdiges und aktuelles Antivirusprogramm, das den fraglichen Virus „kennt“ (neuere Datenbasis!), dann reicht es, den Rechner auszuschalten (Löschen aller Viren im Hauptspeicher). Nach dem Einschalten und einem Systemstart kann das Virensuchprogramm laufen und die Massenspeicher „reinigen“. Findet das Virensuchprogramm den Virus nicht oder haben wir gar kein solches Programm, so müssen wir anders vorgehen. Zuerst müssen alle ausführbaren Benutzerprogramme gelöscht werden. Dann müssen wir in einer „logischen Kette von vertrauenswürdigen Maßnahmen“ alle Systemprogramme ersetzen. Dazu booten wir das System von einer sauberen BS-Kopie (die wir hoffentlich noch haben) und installieren alle wichtigen Systemprogramme neu. Dabei verwenden wir nur die Originalprogramme der Systemdiskette. Dann werden alle weiteren Hilfsprogramme und Systeme erneut installiert (bzw. von den Originalmedien kopiert), bis alles wieder wie vorher war. Zum Schluss werden alle Benutzerprogramme erneut installiert. Bei einem großen Computersystem (z.B. main frames) kann man aber leider nicht so vorgehen, da der Betrieb weiterlaufen soll. Hier hilft ein Scannerprogramm, das alle in Frage kommenden Dateien systematisch auf den Virus hin prüft und sie gegebenenfalls „reinigt“. Allerdings ist der Erfolg eines solchen Ansatzes nicht garantiert: Ist das Reinigungsprogramm langsamer als der Virus oder ebenfalls infiziert, so ist dem kein Erfolg beschieden. Man kann zwar analog zu der biologischen Abwehr spezielle Antiviren schreiben („Antikörper“), aber ihr Erfolg und der Zeitpunkt, wann sie sich selbst abschalten und vernichten sollen, sind höchst unklar. Das einzige und probate Mittel in einem solchen Fall (und bei anderen Störfällen!) ist und bleibt die logische Vertrauenskette. Man geht von einem ganz sicher funktionierenden Minimalsystem aus und erweitert es mit vertrauenswürdigen Schritten solange, bis das Gesamtsystem wieder vertrauenswürdig ist. Im folgenden sollen nun die Sicherheitsmechanismen in UNIX und Windows NT beschrieben werden. Für eine genauere Beschreibung der notwendigen UNIXSicherheitskonfiguration siehe z. B. Groll (1991) und Farroes (1991). Beispiel UNIX Authentifizierung (Benutzeridentifizierung) Die grundsätzliche Personenzugangskontrolle erfolgt in UNIX mit einem Benutzernamen, der allgemein bekannt sein kann, und einem Passwort, das nur der Benutzer persönlich kennt. Bevor eine Sitzung eröffnet wird, prüft das login-Programm, ob der Benutzername und das dazu eingetippte Passwort registriert sind. Ist dies der Fall, so wird für den Benutzer ein besonderer Prozess (die Shell) erzeugt, der als Kommandointerpreter fungiert und die Zugriffsrechte und Identifikationen des Benutzers hat. Alle weiteren, vom Benutzer erzeugten Prozesse zum Ablaufen seiner Programme erben die Rechte der shell und haben damit die Rechte des Benutzers. In Abb. 3.21 ist der login-Ablauf gezeigt.
Sicherheitsmechanismen und Konzepte im Netz
init Prozess
getty Prozess
login Prozess
sh Prozess
133
Kommando 1 Kommando 2 ...
Abb. 3.21 Ablauf beim login
Jedem ist Benutzer eine Benutzerkennung uid und eine Gruppenkennung gid zugeordnet, für die bei jeder Datei Zugriffsrechte definiert werden. Diese Kennungen stehen mit dem verschlüsselten Passwort und dem Namen des Benutzers (getrennt durch „:“) in der Datei /etc/passwd, dessen Einträge beispielsweise lauten root:Igl6derBr45Tc:0:0:The Superuser:/:/bin/sh brause:ntkyb1ioøkk3j:105:12:&Brause:/user/user2/NIPS:/bin/csh
Der erste Eintrag ist der Benutzername, der zweite das verschlüsselte Passwort, der dritte die Benutzerkennung uid, dann die Gruppenkennung gid, der Name des Benutzers, sein Startverzeichnis und der Name des Startprozesses (der Shell). Die Gruppennamen sind in der Datei /etc/group definiert, beispielsweise staff:*:12:boris,peter,brause
wobei das erste der Gruppenname ist, dann folgt das verschlüsselte Gruppenpasswort (hier mit „*“ leer) dann die Gruppenkennung gid und dann die Benutzernamen, die zusammen die Gruppe bilden. Ein Benutzer kann dabei in verschiedenen Gruppen gleichzeitig sein. Beispiel UNIX Autorisierung (Vergeben von Rechten) Der initiale shell-Prozess richtet alle vom Benutzer aufgerufenen Programme ein und gibt ihnen als Identität die des Benutzers mit. Die "reelle Identität" eines Prozesses, die Zahlen ruid und rgid, werden mit der uid und gid des Benutzers initialisiert. Wirksam bei der Abfrage, ob ein Prozess eine Datei öffnen oder verändern darf sind allerdings zwei weitere Zahlen: die "effektive Identität" euid und egid. Wird ein Programm ausgeführt, so werden die bei der Datei vermerkten Angaben über uid und gid nicht beachtet, und es wird euid : = ruid sowie egid : = rgid gesetzt. Auch wenn das Programm vom super user erzeugt wurde und ihm gehört, so hat es doch nur die Zugriffsrechte des aktuell das Programm benutzenden Benutzers, die Rechte für euid und egid. Manche Programme (wie E-Mail-Programme etc.) müssen allerdings auch auf Dateien (z. B. mailbox) anderer Benutzer schreiben. Um dem Programm die stärkeren Rechte von root zu geben, kann man nun zusätzlich bei einer Datei vermerken, dass ihre Zugriffsrechte (uid bzw. gid) auf den Prozess (auf euid und egid) übertragen werden (set user id- bzw. set group id) und dabei ruid und rgid nicht beachtet werden. Beim nächsten Programm ohne diese
134
Internet Architekturen, Web-Services und Sicherheit
Attribute werden aber die effektiven auf die reellen Rechte wieder zurückgesetzt. Beispiel UNIX Authentifikation in Netzen In lokalen UNIX-Netzen kann man sich mit einem remote login (rlogin)oder remote shell (rsh)-Kommando in andere Rechner des Netzes eintragen. Um zu verhindern, dass man sich immer neu mit einem Passwort ausweisen muss, ist auf jedem Rechner eine Datei etc/hosts.equiv vorhanden, in der alle Rechner vermerkt sind, denen vertraut wird. Loggt sich ein Benutzer eines solchen „befreundeten“ Systems ein, so wird darauf vertraut, dass das Passwort bereits abgefragt wurde, und der Benutzer unbesehen mit seinen Zugriffsrechten hineingelassen. Dies ist eine von allen Eindringlingen bevorzugte Schwachstelle, mit der sie bei Übernahme nur eines Rechners im Dominoeffekt alle anderen Rechner eines LAN übernehmen können. Beispiel Windows NT Benutzerauthentifikation Für die Funktion als Server wurden bei Windows NT besondere Konzepte implementiert. Über die lokalen Access Control Lists ACL der lokalen Dateisysteme hinaus wurde die Kontrolle von verschiedenen, sich global und lokal überschneidenden Benutzergruppen (domain control) eingerichtet. Damit gibt es also insgesamt drei verschiedene Möglichkeiten für einen Benutzer, sich im Rechnersystem anzumelden: x eine lokale Anmeldung, bei der sich der Benutzer mit seinem lokalen Benutzernamen auf seinem Computer anmeldet x eine Netzwerk-Anmeldung, um Zugriff auf die Dateien eines Dateiservers zu erhalten x eine Anmeldung in einer Domäne, mit der der Zugriff auf die Rechner und die Dienste der Domäne geregelt wird. Obwohl jede Windows NT Workstation NTW als kleiner Server fungieren kann, hat jede Domäne einen besonderen Server: den Windows NT Server NTS, der als domain controller DC fungiert. Um Verfügbarkeit und Leistung zu erhöhen, kann es in einer größeren Domäne nicht nur einen, sondern mehrere DCs geben. Von diesen fungiert aber immer einer als Hauptcontroller (primary domain controller PDC), die anderen als Nebencontroller (backup domain controller BDC), die vom Hauptcontroller aktualisiert werden. So ist die Benutzerauthentifizierung auf einem BDC möglich, aber nicht die direkte Modifizierung der Account-Daten. Dies kann nur indirekt von den Systemadministratoren erreicht werden, die die Daten der Domäne verwalten. Dabei ist die Abbildung auf die PDC und BDC transparent. Der Austausch zwischen Benutzern und Daten verschiedener Domänen kann durch Einrichtung von Vertrauensrelationen (trust relationship) zwischen den Domänen erleichtert werden, so dass ein Benutzer von einer Domäne in einer anderen automatisch bestimmte Zugriffsrechte erhält. Welche Rechte dies sind, muss explizit beim Einrichten der Vertrauensrelationen festgelegt werden. Die-
Sicherheitsmechanismen und Konzepte im Netz
135
se Konstruktion erleichtert beispielsweise die Zusammenarbeit zwischen verschiedenen Firmen oder den Abteilungen einer Firma, ohne zu große Privilegien und zu großen Datenzugriff gewähren zu müssen. 3.7.4 Fire-wall-Konfigurationen Ein wichtiges Konzept, um die Ausbreitung von Feuer auf mehrere Häuser zu verhindern, ist die Isolierung der Häuser oder Hausabschnitte durch spezielle Brandschutzmauern und Brandschutztüren. Dieses Konzept stand Pate bei den Bestrebungen, den Schaden bei einem Systemeinbruch in einem Computernetz auf ein Gebiet zu begrenzen. Die Brandschutzmauer (fire wall) besteht in diesem Fall aus einem speziellen Vermittlungsrechner (router), der als Bindeglied zwischen einem lokalen Netz und der feindlichen Außenwelt fungiert, beispielsweise dem Internet, siehe Kienle (1994). Dieser fire wall router hat die Aufgabe, alle Datenpakete zu untersuchen (screening) und Pakete mit unerwünschter Internetadresse oder Portadresse auszusondern und zu vernichten. Dazu verwendet er eine Positivliste und eine Negativliste von Portnummern, die entweder ausdrücklich erlaubt oder verboten sind. Weiterhin kann er Datenpakete von Rechnern des internen Netzes (Intranet) mit IP-Adressen, die nach außen unbekannt sind, in den "offiziellen" IP-Adressraum abbilden. Dies erlaubt nicht nur, alte Netze problemlos ans Internet anzubinden, sondern unterbindet auch den direkten Durchgriff von Hackern des Internets auf das interne Netzwerk. Da all dies sehr schnell gehen muss, um den Datenverkehr nicht unnötig zu behindern, wird das screening meist als Kombination von Software- und Hardwaremaßnahmen verwirklicht. Allerdings haben wir dabei ein Problem: Woher soll der fire wall router wissen, welche Internetadressen (Rechner) und Portadressen (Dienste) problematisch sind? Meist ist dies benutzer- und kontextabhängig: Portnummern wechseln, dem System unbekannte Benutzer antworten aus dem Internet, eine Folge von Portbenutzungen ist erlaubt, aber nicht die Einzelbenutzung. Dies führt bei starren Listen bald zu Problemen. Aus diesem Grund wird dem fire wall router noch ein zweiter Rechner zur Seite gestellt: der relay host. Er befindet sich außerhalb des LAN und ist beispielsweise über einen allgemeinen Vermittlungsrechner (external router) mit dem Internet verbunden. In Abb. 3.22 ist eine solche klassische fire wallKonfiguration gezeigt. Auf dem relay host befinden sich nun alle wichtigen Programme (application relay software) und Netzwerkdienste. Ihre Ausführung ist dabei bestimmten Restriktionen unterworfen, die mit programmabhängigen Zugangskontrolllisten (ACL) überprüft werden. In diesen Listen sind nicht nur die Benutzer und ihre Zugriffsrechte verzeichnet, sondern auch wer welche Funktionen des Programms benutzen darf. Zusätzliche Programme (wrapper) können Kommunikationsverbindungen überprüfen und Datenpakete auch auf dem relay host unterdrücken.
136
Internet Architekturen, Web-Services und Sicherheit
andere Netze fire wall router
external router
Internet
LAN
relay host Abb. 3.22 Die klassische fire-wall-Konfiguration
Die Sicherheitskriterien, die der relay host mit seinen ausgewählten, gesicherten Programmen und Internetverbindungen erfüllt, kann man nun leicht auf den fire wall router übertragen. Dazu erlaubt man dem fire wall router nur noch, Datenpakete zwischen den Rechnern des LAN und dem relay host durchzulassen; alle anderen Daten werden ausgefiltert und gelöscht. Alle Kommunikationsverbindungen laufen damit zwangsweise über den relay host; vom Internet gibt es für Eindringlinge in dieser Konfiguration zwei Sicherheitsbarrieren: den external router und den relay host. Sparen wir uns den externen Router und kombinieren ihn mit dem relay host, so sparen wir zwar Geld, senken aber die Sicherheitsschranke. Eine weitere Möglichkeit besteht darin, alle drei Funktionen in einem einzigen fire wall- Rechner zu integrieren. In Abb. 3.23 ist diese dual homed host genannte Sparversion gezeigt.
Internet
fire wall router
LAN
Abb. 3.23 Die dual-homed-host-Konfiguration
Auch hier leidet die Sicherheit: Ist es einem Angreifer gelungen, in den dual homed host einzubrechen, so ist die Brandschutzmauer vollständig durchbrochen. Aus diesem Grunde gibt es Systeme, die ausgefeiltere Mechanismen zur Benutzerkontrolle anbieten. Eines der bekanntesten ist das Kerberos-System. 3.7.5 Zugriffslisten und Fähigkeiten Bisher war das Beste, was wir an Sicherheitsmechanismen betrachtete hatten, die Zugriffslisten, kurz ACL (Access Control Lists) genannt. In diesen Listen werden für ein Objekt alle Nutzer und ihre Rechte aufgeführt, sowohl als Positivliste,
Sicherheitsmechanismen und Konzepte im Netz
137
was sie dürfen, als auch als Negativliste, wer etwas nicht darf (obwohl alle Welt es sonst darf). Es gibt nun einen zweite Sicht auf die Dinge: die Sicht der Benutzer. Anstatt jedem Objekt einen Liste mitzugeben kann man auch jedem Nutzer einen Liste zuordnen, wo alle drin steht, was er darf und was nicht, entsprechend seinen Fähigkeiten (Capability Oriented Approach). Die beiden Sichten sind zwei Seiten der selben Medaille: einer Zugriffsmatrix aus Nutzern und Objekten, in der für jedes Paar aus (Nutzer, Objekt) die Zugriffsmöglichkeiten vermerkt sind. In Abb. 3.24 ist ein Beispiel dafür gezeigt.
Nutzer 1 Nutzer 2 Nutzer 3
Objekt 1 rw
Objekt 2 r r
x
Objekt 3 rwx rx rx
Objekt 4 rx
Abb. 3.24 Die Matrix der Zugriffsrechte
Dabei bildet die Liste der Fähigkeiten jeweils eine Zeile der Matrix, die Liste der ACL jeweils eine Spalte. Betrachten wir allerdings ein großes System etwa ein Netz aus 1000 Computern und 1000 Nutzern, so kommen wir schnell an die Grenze einer solchen Matrix. Anstelle für jeden Nutzer jede Zugriffsart für jedes existierende (und neu erstellte!) Objekt zu spezifizieren, ist es sinnvoll, die Nutzer in Kategorien wie „Administrator“, „Benutzer“, „Gast“ usw. einzuteilen und nur jeder dieser Kategorien oder Rollen ein differenziertes Profil zuzuordnen. Damit reduziert sich die zu spezifizierenden Situationen erheblich und damit auch die Arbeit des Administrators. Ein wichtiges Beispiel für ein solches Capability-oriented System ist das Authentifizierungssystem Kerberos. 3.7.6 Die Kerberos-Authentifizierung Hat man ein sehr großes Netz von Rechnern, etwa 1000 Workstations wie sie beim US-Forschungsinstitut MIT bereits 1988 vorhanden waren, so ist die Strategie von Unix oder Windows NT, sich bei jedem Dienst (Email, Drucker, remote login, Dateiserver, etc.) mit Passwort und Nutzernamen anmelden zu müssen, sehr hinderlich. Kann man den Dienst ohne Anmeldung nutzen, so ist das auch nicht besonders attraktiv: Niemand mag es, wenn seine Email von anderen gelesen wird oder seinen Dateien „aus Versehen“ gelöscht werden. Den Kompromiss zwischen beiden Vorgehensweisen stellt beispielsweise ein zentraler Vermittler dar, der alle Nutzer und ihre Passworte sowie alle Dienste und ihre Passworte kennt. Möchte man einen Dienst nutzen, so wendet man sich an ihn und bezieht von ihm die nötigen Passworte und Ausweise gemäss der über einen selbst dort gespeicherten Fä-
138
Internet Architekturen, Web-Services und Sicherheit
higkeiten und Rollen-Berechtigungen. Eines der am weitesten verbreiteten Systeme ist das Kerberos-Authentifizierungssystem. Das Kerberos-Sicherheitssystem (Steiner et al., 1988) ist Teil des Athena-Projekts, das für das Netz am MIT eine systematische Verwaltung entwickelte. In einem solchen Netz tritt die Frage auf, ob der anfragende Prozess, der eine Dienstleistung haben will, auch der ist, der er zu sein vorgibt. Das Problem der Authentizität ist also nicht nur auf das Einloggen eines Benutzers beschränkt, sondern gilt für alle Dienstleistungen im Netz. Da es sehr problematisch ist, ein offenes, nicht verschlüsseltes Passwort über mehrere (eventuell angezapfte) Rechner zu schicken, wurden im Athena-Projekt drei verschiedene Protokolle spezifiziert, um das Problem der Sicherheit zu lösen. Die zentrale Idee besteht darin, alle Anfragen zu verschlüsseln und den Inhalt (Absender usw.) an die Kenntnis des involvierten Schlüssels zu binden. Bei der Verschlüsselung wird im allgemeinen eine Einwegfunktion f (trap-function) verwendet, die nicht einfach umgekehrt werden kann. Der eigentliche Inhalt von y = f (Nachricht, Schlüssel s1) ist deshalb nicht einfach mit der Kenntnis der Verschlüsselung f und der verschlüsselten Daten y zu ermitteln. Üblicherweise gibt es dazu eine Dekodierungsfunktion g(.), die mit Hilfe eines zweiten Schlüssels s2 die ursprüngliche Nachricht wiederherstellen kann: Nachricht
= =
g(y, s2) = g ( f(Nachricht, s1), s2 ) gs2( fs1(Nachricht) )
Gilt für die Schlüssel s1 = s2 und die Kodierung/Dekodierung f = g, so spricht man von symmetrischer Kodierung, ansonsten von asymmetrischer Kodierung. Für einen Überblick über solche Verfahren siehe Preneel et al. (1993). Im KerberosSystem wird mit dem DES (Data Encryption Standard) ein symmetrisches Verschlüsselungsverfahren verwendet. Im Folgenden ist der Einsatz und die Abfolge der drei Protokolle beschrieben. Benutzerauthentifizierung (Single Sign on Protocol)
Beim Einloggen des Benutzers mit dem Kerberossystem läuft alles zunächst wie gewohnt ab: Der kinit()-Prozess wird gestartet und der Benutzer tippt sein geheimes Passwort ein. Dies wird in einen DES-Schlüssel S0 umgewandelt und zunächst lokal gespeichert. Dann schickt kinit() den Benutzernamen im Klartext an einen speziellen Prozess, den Authentication Server AS. Dieser sucht in seiner zentralen Datenbank unter allen registrierten Benutzern nach und ermittelt das Passwort des Benutzers und damit auch den dazu gehörenden DES-Schlüssel S0. An dieser Stelle verfügen beide Kommunikationspartner über den selben geheimen Schlüssel S0, mit dem eine Nachricht verschlüsselt bzw. entschlüsselt werden kann. Sodann stellt er ihm einen Sitzungsausweis TG (session ticket) aus, bestehend aus Zeitpunkt, NutzerName etc., verschlüsselt mit seinem geheimen Schlüssel ST.
Sicherheitsmechanismen und Konzepte im Netz
139
Dann erwürfelt er einen Zufallsschlüssel S1 für alle weitere Kommunikation mit dem Benutzer, den er aber mit Hilfe von S0 „verpackt“ (verschlüsselt). Beides wird zusammen als Nachricht an den Benutzer (bzw. den lokalen kinit()-Prozess) geschickt. In Abb. 3.25 ist dies verdeutlicht. Die Rahmenschattierung eines Objekts entspricht der Schlüsselschattierung, mit der das Objekt verschlüsselt wurde. Dabei steht „User“ stellvertretend für alle Benutzerdaten wie Benutzername, IPAdresse, usw. Die Nachricht besteht also zwei Teilen [User, S1]0 , TG = [User, Zeit]T wobei eine Verschlüsselung jeweils mit Klammern und dem Schlüsselindex notiert sei. Mit S0 dekodiert kinit() den ersten Teil der Nachricht und speichert den Schlüssel S1 ab; der Schlüssel S0 wird nicht mehr benutzt und kann sofort vernichtet werden. Antwort
Anfrage S0
S0
User
AS
User
S1
Ausweis TG
User
ST
S1 S1 Authenticator
User
Ausweis TT
Service User
TGS
Service S2
User S2
SS
Abb. 3.25 Sitzungsausweis und Transaktionsausweis
Wozu enthält der erste Teil redundante Information? Nun, zum einen weiß der Benutzer, wenn er seinen Namen richtig herausbekommt, dass er richtig dekodiert hat, zum anderen, dass der Schlüssel valide ist, da nur der echte AS den Namen mit S0 verpacken kann. Der zweite Teil, der Sitzungsausweis TG, kann nicht entschlüsselt werden; so wie er ist wird er einfach nur lokal abgespeichert. Damit ist der Benutzer so lange zentral registriert wie der Sitzungsausweis gilt. Schlüsselverteilung (Key Distribution Protocol)
Möchte nun der Benutzer einen Dienst, etwa remote login, nutzen, so setzt er den Befehl „rlogin“ oder ähnliches ab. Der so entstandene Benutzerprozess schickt kein „rlogin“ im Klartext zum Server; einen solchen Dienst gibt es nicht mehr. Stattdessen müssen alle Anfragen fälschungssicher gemacht werden. Allerdings schickt der Prozess auch nicht sofort eine verschlüsselte Anfrage zum Server:
140
Internet Architekturen, Web-Services und Sicherheit
Dieser weiß ja gar nicht, mit welchem Schlüssel die ankommende Nachricht verschlüsselt ist. Statt dessen fragt der Benutzerprozess zuerst bei einer neutralen Instanz, dem Transaction Grant Server TGS (dem Bruder vom AS) nach einer Erlaubnis bzw. dem Passwort des Dienstes. Dazu wird eine Anfrage Service, [User, Zeit]1 , TG zum TGS geschickt. Dieser entschlüsselt zuerst den Ausweis TG mit seinem Standardgeheimschlüssel und sieht so, dass ein „User“ etwas von ihm will. Der User hat vom AS in der mit dem TGS gemeinsamen Datenbank den Vermerk „Sitzungsschlüssel S1“ bekommen, so dass der TGS auch den Abschnitt davor, den sog. „Authentikator“ entschlüsseln kann. Er vergleicht den darin befindlichen Benutzernamen mit dem in Frage kommenden aus TG; stimmen sie überein, so hat sich der Benutzer ausgewiesen. Ist der verlangte Service auch für ihn freigegeben, so stellt der TGS eine Antwort der Form [Service, S2]1 , TT = [User, S2, Zeit]S aus. Die Antwort enthält wieder in verschlüsselter Form den Namen des verlangten Dienstes zur Kontrolle, den Schlüssel für die Kommunikation sowie ein Transaktions-Ticket TT, das in unfälschbarer Form den Kontext enthält. Transaktion durchführen (Authentication Protocol)
In dem durch die kurze Lebensdauer der Transaktion (ca. 5 Minuten) spezifizierten Zeitraum kann nun der Client eine Transaktion mit dem Ticket TT einleiten : Anfrage, [User, Zeit]2 , TT Der Server entschlüsselt mit seinem geheimen Serverschlüssel SS das Ticket TT, kann daraufhin mit dem daraus entnommenen Schlüssel S2 den Authentikator des Benutzers entschlüsseln und beide Namen „User“ miteinander vergleichen. Nun weiß er, dass die Anfrage wirklich von „User“ stammt und zum Service berechtigt (Authentifikation). In Abb. 3.26 ist dies visualisiert.
SS
User S2
ServiceAnfrage
User S2
Service
Ergebnisse S3
S2
Abb. 3.26 Transaktions (Service-)durchführung
Sicherheitsmechanismen und Konzepte im Netz
141
Seine Antwort mit den Daten verschlüsselt er mit dem gleichen Transaktionsschlüssel: [Ergebnis]2 Eventuell legt er auch noch einen weiteren Schlüssel S3 bei, mit dem die darauf folgende Kommunikation, etwa alle Kommandos für das rlogin, verschlüsselt werden. Die Authentifikation in obiger Form hat verschiedene Vorteile: ¾Eine Kopie der Ausweise oder Tickets ist zwar möglich, aber sie lässt sich nicht zur Täuschung einsetzen, da für jede Anfrage der Authentikator fehlt. Dieser kann aber nur gefälscht werden, wenn der entsprechende Schlüssel bekannt ist, was aber ohne den ursprünglichen Schlüssel, also das Passwort, nicht einfach ist. ¾Eine Fälschung der Ausweise oder Tickets ist nicht möglich, da die Schlüssel von TGS und Dienst unbekannt sind. ¾Alle Tickets und Authentifikatoren lassen sich auch vom Benutzer selbst nicht beliebig aufbewahren und verwenden – durch die Zeitvalidierung kann man die Benutzerrechte einschränken, ohne dass ihm alte Ausweiskopien weiterhelfen. ¾Da nur das Benutzerpasswort einmal eingegeben werden muss, bemerkt der Benutzer von dem ganzen Authentifikationsprotokoll nichts weiter – eine wichtige Voraussetzung für die Akzeptanz. Die oben skizzierten drei Protokolle werden in mehreren gekoppelten LANs (Domänen) nun durch ein viertes ergänzt: Möchte ein Client eine Leistung eines Servers nutzen, der in einem anderen LAN arbeitet, so schickt er zuerst eine Anfrage an den TSG „seines“ LANs für eine geschützte Transaktion mit dem TSG des Nachbar-LANs, dem vertraut wird. Ist dies erfolgreich, so wird in der Transaktion nach einem Transaktionsticket für den weiter benachbarten TSG gefragt, und so fort, bis der Client schließlich ein Ticket für den TSG des LANs mit dem gewünschten Server hat. Nun kann er die gewünschte Leistung direkt beim Server mit dem erhaltenen Ticket und Schlüssel durchführen, als ob dieser direkt im LAN wäre. Das Kerberos-System ist zwar ziemlich sicher; es gibt aber einige Schwierigkeiten dabei zu überwinden: x Der Dienst funktioniert nur, wenn die Uhren aller Rechner synchron gehen und damit die Zeitschranken überprüft werden können. Dies erfordert in Gegenden ohne Funkzentraluhr ein Zeitauthentifizierungssystem, um eine Manipulation der Uhren zu verhindern – ein Unding, da ja auch dieses auf einem solchen Dienst aufgebaut sein müsste. x Die eingebauten festen Zeitschranken für die Transaktionsschlüssel und Sitzungsschlüssel (z. B. 5 Minuten und 8 Stunden) erzeugen Probleme, wenn
142
Internet Architekturen, Web-Services und Sicherheit
Transaktionen länger dauern oder Benutzer länger arbeiten. Hier wird eine Ticket-Eigenschaft „verlängerbar“ wichtig, die aber schon existiert. x Transaktionsschlüssel und -tickets werden in Dateien abgelegt, die in Multiuser-Umgebungen von anderen evtl. gelesen werden könnten. Lokale Zugriffsbeschränkungen sind hier wichtig. Es ist deshalb noch ein langer Weg zu einem sicheren System, besonders wenn mehrere Systeme mit unterschiedlichen Sicherheitsmechanismen zusammengekoppelt werden. Aus diesem Grund gibt es Bestrebungen der X/Open-Gruppe, mit einheitlichen Sicherheitsrichtlinien XBSS (X/Open Baseline Security Specifications) lückenlose Sicherheit in Netzen zu erzielen. Literatur Levy E., Silberschatz A.: Distributed File Systems: Concepts and Examples. ACM Computing Surveys 22(4), 322–374 (1990) Spafford E., Heaphy K., Ferbrache D.: A Computer Virus Primer. In: P. Denning (ed.): Computers Under Attack. Addison-Wesley, Reading, MA 1990 JAVA: siehe http://java.sun.com im World Wide Web CODA: siehe http://www.coda.cs.cmu.edu im World Wide Web Satyanarayanan M., Kistler J.J., Kumar P., Okasaki M.E., Siegel E.H., Steere D.C.: Coda: A Highly Available File System for a Distributed Workstation Environment. IEEE Trans. On Computers 39(4), 1990 COPS: siehe ftp://ftp.cert.org/pub/cops im World Wide Web DFN: siehe http://www.cert.dfn.de im World Wide Web SAT: siehe http://www.trouble.org im World Wide Web Spafford E., Heaphy K., Ferbrache D.: A Computer Virus Primer. In: P. Denning (ed.): Computers Under Attack. Addison-Wesley, Reading, MA 1990 Groll M.: Das UNIX Sicherheitshandbuch. Vogel Verlag, Würzburg 1991 Farroes R.: UNIX System Security. Addison-Wesley, Reading, MA 1991 Kienle M.: Firewall Konzepte: Sicherheit für lokale Netze ohne Diensteeinschränkung. iX, Verlag Heinz Heise, Hannover, Juli 1994 Steiner J. G., Neumann B. C., Schiller I.: Kerberos: An Authentication Service for Open Network Systems. Proc. Winter USENIX Conference, Dallas, TX, pp. 191–202, 1988 Preneel B., Govaerts R., Vandewalle I. (eds): Computer Security and Industrial Cryptography. LNCS 741, Springer-Verlag Berlin, 1993
4 Datenbanksysteme
Kernaufgaben von Datenbanksystemen ist die Speicherung und Verwaltung von großen Datenbeständen. Wichtig für diese Kernaufgabe sind die Organisation der Daten (Wie effizient ist die Speicherorganisation der Daten?) sowie die angebotene Benutzerschnittstellen (Wie greife ich auf die gespeicherten Daten zu? Wie kann ich gespeicherte Daten ändern?). Betrachten wir dazu das Beispiel einer Fluganzeigetafel. Beispiel 1: Fluganzeigetafel Flüge FlugNr von
nach
Abflug
Ankunft
83
JFK
O‘Hare
11:30am
1:43pm
84
O‘Hare
JFK
3:00pm
5:55pm
109
JFK
Los Angeles
9:50pm
2:25am
213
JFK
Boston
214
Boston
JFK
11:43am 12:45pm 2:20pm
3:12pm
Die obige Tabelle besteht aus Zeilen und Spalten. Jede Zeile zeigt einen Flug an und besteht aus Attributswerten wie „von“ = “JFK“ und „Abflug“ = “11:43 am“. Die obige Tabelle ist eine kleine Datenbank. Um sie zu verstehen, benötigen wir nicht nur die eigentlichen Zeilen mit ihren Attributswerten, sondern auch die (konstanten) Überschriften, die Attributsbezeichnungen. Normalerweise werden diese als Metadaten bezeichnet und extra gespeichert. Möchten wir einen Flug aus der Tabelle auswählen und ihn genau bezeichnen, so benötigen wir ein eindeutiges Merkmal, einen Schlüssel. Das Attribut „FlugNr“ wäre hier ein solcher Schlüssel, oder die Attributskombination „von“-„nach“. Wichtig ist dabei, dass der Schlüssel eindeutig ist und für jede Zeile einen anderen Attributswert annimmt. Alle Attribute bilden zusammen ein Datentupel. Ein solches Tupel wird als Relation bezeichnet; wir haben also eine relationale Datenbank. Beispiele relationaler Datenbanken sind MySQL, DB2 , Oracle. Die Anzahl möglicher Attributskombinationen (Ausprägung der Attribute) ist damit die Anzahl aller möglichen Relationen. Möchte man eine solche Datenbank abfragen, etwa „Wann geht ein Flug nach Los Angeles?“, so müssen wir die Relationen entsprechend der Frage
144
Datenbanksysteme
auslesen und beurteilen. Bei großen Datenbanken wird dazu eine Abfragesprache (data query language) verwendet, meist SQL. Die Abfragen lassen sich damit wie ein Programm aufschreiben und somit automatisieren. Beispiel 2: Bankkonten Ein komplexeres Beispiel sind die Finanzdaten einer Bank. Die Datenbank enthält dann Daten über x Die Kunden (Namen, Anschrift, Alter, ...) x Das Personal (Name, Anschrift, Tätigkeit, Gehalt, ...) x Die Konten (Kontonummer, Buchungen, Stand, ...) x Die Zuordnung von Kunden und Konten x ... und vieles mehr! Verschiedene Anwendungen müssen auf die Bank-Daten zugreifen und sie ändern können: 1. Ein Geldautomat muss von Konten Abbuchungen vornehmen. 2. Ein Überweisungsterminal muss Abbuchungen und auch Zugänge verarbeiten können. 3. Ein Bankangestellter muss neue Kunden und Konten anlegen können. 4. Ein Kontoauszugsdrucker muss den Kontostand und die Buchungen auslesen können. Wozu benötigt man für solche Aufgaben eine Datenbank?
4.1
Wozu Datenbanken?
Als es noch kein Datenbanksystem gab, wurden die Daten für jede Anwendung in separaten Dateien permanent gespeichert (Einzellösung). Anwendung 1
Anwendung 2
Anwendung 3
Elementare Zugriffsoperationen
Elementare Zugriffsoperationen
Elementare Zugriffsoperationen
Daten (Files)
Daten (Files)
Daten (Files)
Abb. 4.1 Datenzugriff bei Einzellösungen
Wozu Datenbanken?
145
Eine solche Einzellösung hat aber verschiedene Nachteile und Probleme: x
Redundanz und Inkonsistenz: Daten müssen mehrfach gespeichert werden. Die Adresse und Telefonnummer eines Kunden ist in einer Datei, welche zum Speichern von Kontobewegungen dient, als auch in einem (anderen) File, welches zur Überprüfung von Kontobewegungen dient, gespeichert. Diese Daten sind also mehrfach gespeichert (Redundanz), was einen höheren Bedarf an Speicher bedeutet. Zusätzlich birgt dies immer die Gefahr der Inkonsistenz, falls Daten nur an einer Stelle geändert werden und vergessen wird, sie an allen anderen Stellen ebenfalls zu ändern.
x
Beschränkte Zugriffsmöglichkeiten: Keine Ad-Hoc-Abfrage, keine Abfragesprache. Angenommen ein Bankangestellter benötigt die Namen aller Kunden, die in Frankfurt wohnen. Der Bankangestellte kann nun entweder manuell versuchen die Namen aus dem entsprechenden File herauszusuchen oder einen Programmierer beauftragen ein entsprechendes Programm zu schreiben. Sollte sich die Fragestellung nur leicht ändern, z.B. nur die Namen mit einem Kontostand über 10.000 €, besteht das gleiche Problem jedoch wieder für die neue Abfrage und ein erneutes Programm müsste geschrieben werden.
x
Probleme des Mehrbenutzerbetriebs (Concurrency): Keine Überwachung gleichzeitiger Zugriffe. Um die Geschwindigkeit zu steigern, erlauben viele Systeme, insbesondere Banksysteme mit vielen Benutzern, den Mehrbenutzerbetrieb. Mehrere Benutzer arbeiten also gleichzeitig auf und mit den Daten. Beispiel Betrachten wir Abb. 4.2. Falls gleichzeitig zwei Benutzer auf das gleiche Konto (A mit 300 €) verschiedene Beträge einzahlen möchten (z.B. 100 € und 50 €), so kann es sein, dass der Kontostand hinterher einen anderen Betrag als 450 € aufweist. In diesem Fall könnte Konto A hinterher auch 350€ oder 400 € aufweisen. Ein solcher Fehler wird von keinem Kunden toleriert! Parallele Zugriffe müssen also genau geregelt werden, um richtige Ergebnisse zu erzielen. Die inhaltliche Regelung wird als Zugriffssemantik bezeichnet. x Integritätsverletzungen. Bei Konten möchte man den Kreditrahmen gerne begrenzen. Eine Bedingung wie ‚Kontostand darf nicht negativ werden’ ist mit Dateien nur sehr schwierig bzw. aufwendig zu realisieren.
146
Datenbanksysteme
15:00 Uhr Konto A= 300 €
50 € einzahlen B := read(A)
100 € einzahlen 300 €
B := B + 50 B
write(A)
15:01 15:02
350 €
300 €
C := B + 100
15:03 15:04
C := read(A)
400 €
C
write(A)
Konto A = 400 € Zeit Abb. 4.2 Problem paralleler Datenzugriffe: Beispiel Geld einzahlen
x Sicherheitsprobleme: Nicht alle Benutzer sollten die gleichen Zugriffsmöglichkeiten auf die gespeicherten Daten haben. Ein Bankangestellter zuständig für die Gehaltsabrechnungen sollte keine Einsicht in die Kundenkonten und Kundendaten haben. Bei der Einbindung neuer Anwendungen müssen diese Sicherheitsüberprüfungen jeweils wieder überprüft und entsprechend implementiert werden.
4.2
Übersicht Datenbanksysteme
Datenbanksysteme (DBS) wurden entwickelt (seit ca. 1975), um diese Probleme zu überwinden. Ein Datenbanksystem sollte daher folgende Funktionalitäten bieten: x Dauerhafte Speicherung von großen Datenbeständen, x Zugriffsmöglichkeit durch verschiedene Benutzer und Anwendungen, ohne inkonsistente Zustände zu erhalten, x Bereitstellung einer Anfragesprache zum einfachen Umgang mit der Datenbank, x Überwachung von Integritätsbedingungen, x Sicherheit gegenüber Hard- und Software ausfällen (backup and recovery) x Sicherheit gegenüber nicht autorisierten Datenzugriffen (views)
Datenabstraktion (Data Abstraction)
147
x ... und natürlich effizient, möglichst schnell unter Benutzung weniger Ressourcen In Abb. 4.3 ist die resultierende Architektur gezeigt.
Anwendung 1
Anwendung 2
Anwendung 3
Elementare Zugriffsoperationen
Elementare Zugriffsoperationen
Elementare Zugriffsoperationen
DatenbankManagementsystem (DBMS) DBS Datenbank (DB)
Abb. 4.3 Organisation paralleler Datenbankzugriffe
Die Kernidee eines Datenbanksystems ist die zentrale Datenverwaltung und Datenhaltung. Diese Kernidee des DBS bedarf einer neuen Vorgehensweise beim Erstellen von Anwendungen: x Erst Design der benötigten Datenstrukturen, x danach können die Anwendungen auf den gemeinsamen Daten (shared data) entwickelt und angewendet werden.
148
Datenbanksysteme
4.3
Datenabstraktion (Data Abstraction)
Sehr grob unterscheidet man drei Abstraktionsebenen in einem Datenbanksystem: x Die physische Ebene (physical level) Auf dieser Ebene ist beschrieben, wie die Daten auf dem Sekundärspeicher abgelegt sind. Ein Kunde könnte in Modula-2 z.B. wie folgt deklariert werden: TYPE Kunde = RECORD Name: String; Strasse: String; Ort: String; END;
Der Record „Kunde“ hat hier drei Felder. Jedem Feld wird ein Name und ein Type zugewiesen. Auf der physikalischen Ebene kann die Speicherung eines Kunden als Block (von Words oder Bytes) im Speicher beschrieben werden x Die logische/konzeptionelle Ebene (conceptual level) Auf dieser Ebene wird mittels eines Datenbankschemas festgelegt, welche Daten abgespeichert sind. Die konzeptionelle Ebene beschreibt die TypenDeklaration wie oben beschrieben und die Beziehungen zwischen einzelnen Typen. x Die Sichten (view level) In den Sichten werden Teilmengen der Daten dargestellt; die einzelnen Sichten zeigen nur Teile der vorhandenen Daten. Die Sichten sind auf die Aufgaben und Bedürfnisse der Benutzer zugeschnitten. Ein Kassierer am Bankschalter zum Beispiel sieht nur die für ihn relevanten Daten und nicht auch Gehaltsinformationen von Kollegen. Das gesamte Schema ist in Abb. 4.4 dargestellt. Die Möglichkeit eine dieser Ebenen zu ändern, ohne dabei Auswirkungen auf die anderen Ebenen zu haben, wird als Datenunabhängigkeit bezeichnet. Es wird dabei wie folgt unterschieden: x Physische Datenunabhängigkeit = Stabilität der Benutzerschnittstelle gegen Änderungen der Datenorganisation (z.B. Datendarstellung, Feldlänge, Satzformate, Zugriffspfade), d.h. nach Änderungen auf der physikalischen Ebene müssen die Anwendungen nicht umgeschrieben werden.
Die Architektur eines DBMS Sicht 1
Sicht 2
.....
149
Sicht n
logische Ebene
physikalische Ebene
Abb. 4.4 Die drei Schichten der Datenbanksicht
x Logische Datenunabhängigkeit = Stabilität gegen Änderungen in der logischen Ebene (z.B. die Einführung neuer oder abgeleiteter Merkmale wie Ausbildung bzw. Alter für Kunden) Bemerkungen: Das Erreichen der logischen Datenunabhängigkeit ist deutlich schwerer, da Anwendungen stark auf den logischen Strukturen der benötigten Daten aufbauen. Die Idee der Datenunabhängigkeit ist vergleichbar mit der Idee der abstrakten Datentypen in modernen Programmiersprachen. Mit beiden Ideen soll ein Information Hiding erreicht werden, damit Anwender sich mehr auf das eigentliche Problem konzentrieren können, anstatt sich mit den genauen Implementierungsdetails beschäftigen zu müssen.
4.4
Die Architektur eines DBMS
Ein Datenbanksystem ist unterteilt in Module, die einzelne Aufgaben des Systems übernehmen. In der folgenden Abb. 4.5 ist eine Übersicht darüber gezeigt: Zu diesen Modulen gehören: x Datenbank-Manager Der Datenbank-Manager ist ein Modul, welches die Schnittstellen zwischen den Low-Level Daten auf dem Massenspeicher und den Programmen und Abfragen von Benutzern zur Verfügung stellt. Der Datenbank-Manager muss dabei folgende Aufgaben erfüllen: -
Interaktion mit dem Dateimanager des Betriebssystems Zusicherung von Integritätsbedingungen Sicherheitskontrollen
150
Datenbanksysteme
Backup
und
Schemamodifikationen
Anfragen
DDL Compiler
Recovery
Modifikationen AnfrageProzessor
Transaktions Manager Datenbank Manager
DBMS
Daten Metadaten
Abb. 4.5 Architektur eines Datenbank-Managementsystems
x Anfrage Prozessor übersetzt die Ausdrücke der Anfragesprache in Low-Level Anweisungen, die der Datenbank-Manager versteht; führt Optimierung von Anfragen durch x Transaktions-Manager überwacht den gleichzeitigen Zugriff auf Daten (concurrency control). Probleme von “gleichzeitigem“ Zugriff auf gemeinsame Daten gibt es auch bei Interruptsystemen und Mehrprozessoranlagen. Die Lösung dafür besteht allgemein darin, die Daten während eines Zugriffs (Transaktion = Lesen + Schreiben) exklusiv für einen Benutzer zu reservieren und dies durch Signale (Semaphoren) anzuzeigen. Jeder andere kann seine Transaktion erst durchführen, wenn die vorherige zuende gegangen ist und die Semaphoren einen Zugriff gestatten. Eine solche Aktion, die entweder vollständig oder gar nicht durchgeführt wurde, also nicht unterbrechbar ist, heißt atomare Aktion, in unserem Fall also atomare Transaktion und wird vom Transaktionsmanager überwacht. x Data Definition Language (DDL) Compiler Die Art der Tabellenorganisation, das Datenbankschema, wird mit Hilfe einer speziellen Definitionssprache aufgeschrieben, der Data Definition Language (DDL). Der Complier übersetzt die DDL Ausdrücke. Die Schemainformationen nach der Übersetzung von DDL-Aussagen werden in einem besonderen File, dem Data Dictionary gespeichert. Die Daten im Data Dictonary beschreiben also nicht einen Kunden und dessen Eigenschaften, sondern die
Die Architektur eines DBMS
151
Struktur welche Informationen zu einem Kunden gespeichert werden. Diese ‚Daten über Daten’ werden als Metadaten bezeichnet. x Data Manipulation Language (DML) Eine Data Manipulation Language DML ist eine Sprache, die es Benutzern erlaubt, auf Daten zuzugreifen (Anfragen) und diese zu modifizieren (Einfügen, Löschen, Ändern). Ein typisches Beispiel ist dafür SQL. Die Personen, die auf eine Datenbank zugreifen, sind im wesentlichen in vier Typen eingeteilt. Sie sind in Abb. 4.6 eingezeichnet. Benutzergruppen
Datenbank Administrator
Fortgeschrittener Benutzer Programmierer
Einfacher Benutzer Programme
Schemamodifikationen DDL
DML
DDL Compiler
AnfrageProzessor Transaktions Manager Datenbank Manager
DBMS
Daten Metadaten
Abb. 4.6 Zugriffsarchitektur eines Datenbanksystems
Dabei lassen sie sich entsprechend ihrer Aufgaben wie folgt unterteilen: (1) Datenbank-Administrator Einer der Hauptgründe für die Erstellung einer Datenbank ist es, eine zentrale Kontrolle über die Daten und Programme zu bekommen. Die Person mit diesen Kontrollmöglichkeiten nennt man „Datenbank-Administrator“. Ihre Aufgaben sind: x x x x
Definieren von Schemata Festlegen von Speicherstrukturen und Zugriffsmethoden Vergabe von Rechten Spezifikation von Integritätsbedingungen
(2) Datenbank-Benutzer Ziel einer Datenbank ist es, die Informationen den Benutzern zur Verfügung zu stellen. Es werden drei Typen von Benutzern unterschieden:
152
Datenbanksysteme
x Einfacher Benutzer Interagiert mit der Datenbank durch fertige Programme. x Fortgeschrittene Benutzer Benutzen keine fertigen Programme, sondern erstellen selbst ihre Anfragen in einer Anfragesprache. x Programmierer Computer-Experten, die mit der Datenbank durch DML-Aufrufe eingebettet in Programmiersprachen kommunizieren (Anwendungsprogrammierer).
4.5
Data Warehouse
Für Analysezwecke ist es sinnvoll, alle interessierenden Daten aus verschiedenen Datenbanken, etwa aus dem Einkauf, der Produktion und dem Verkauf, zusammen in eine extra Datenbank zu übernehmen. Diese Datenkollektion wird als Data Warehouse bezeichnet. Die Vorteile sind klar: x Ein einheitlicher Blick auf das Unternehmen wird möglich. x Redundante Datenerfassung und –auswertung sowie Inkonsistenzen in den Daten werden vermieden. x Durch die Vereinheitlichung (Konsolidierung der IT) werden Kosten gespart.
Operative Systeme
Data Warehouse
Data Marts und Datenanalyse
Abb. 4.7 Erstellen eines Data Warehouses und seiner Ableger
Data Warehouse
153
Die Herausforderung besteht in diesem Fall darin, die unterschiedlichen Formate der Datenquellen zu vereinheitlichen, sie in der Qualität zu prüfen (Konsistenz, Plausibilität) und die Daten in der gemeinsamen Datenbank unterzubringen. Für Einzelanalysen (Anwendungen) unter verschiedenen Fragestellungen benötigt man aber nicht die ganze, umfangreiche Datenkollektion, sondern nur eine genau abgegrenzte, auf die Fragestellung bezogene Teilmenge davon. Ein solches „kleines“, problemspezifisches Data Warehouse wird als Data Mart bezeichnet. Die wichtigsten tendenziellen Unterschiede zwischen beiden sind in der nachfolgenden Tabelle aufgelistet.
Einsatz Daten Design Historie Menge Ursprung Anforderungen Datenbank
Data Warehouse Unternehmen Granular allgemein lang bis 10 J TeraByte datenmodell-orientiert vielseitig allgemein
Data Mart Abteilung Aggregiert speziell optimiert begrenzt bis 1 J GigaByte aufgabenorientiert spezifisch multidimensional
Tabelle 1 Die typischsten Unterschiede zwischen Data Warehouse und Data Mart
Es gibt dabei verschiedene Data Warehouse-Architekturen: x einstufige Architektur Bei dieser Variante ist das Data Warehouse virtuell; die Analysen werden direkt aus den Quellen versorgt. x zweistufige Architektur Neben den Quellen gibt es hier problemspezifische, kleine Datenbanken, die Data Marts. x dreistufige Architektur Erst in der vollen Ausbaustufe sind Datenaufbereitung und Problemlösung voll entkoppelt. Sie besteht aus der Architektur, wie sie in Abb. 4.7 gezeigt wurde.
5 Programmiersprachen und Paradigmen
Ein wichtiger Schritt bei der Informationsverarbeitung besteht darin, den Verarbeitungsprozess so zu beschreiben, dass er auch von anderen Menschen oder Maschinen durchgeführt werden kann. Historisches Beispiel dafür ist das Rechenbuch von Adam Riese, das eine wichtige Vorbedingung für die Entwicklung der Rechenmaschinen darstellt. Quiche Lorraine Teig:
200g Mehl 1 Ei 1 Eßl. kaltes Wasser Salz 100g Butter
Für die Form: Butter
Belag: 200g Frühstücksspeck 4 Eier weißer Pfeffer edelsüßes Paprikapulver 1 Tasse Sahne 200g geriebener Emmentaler Käse Salz
Zubereitungszeit: 50 Min Ruhezeit: 30 Min. Backzeit: etwa 30 Min. i i i i i
Das Mehl auf die Arbeitsfläche sieben, in der Mitte eine Mulde drücken. Das Ei, das Wasser und das Salz in die Vertiefung geben, die Butter in Flöckchen auf den Mehlrand schneiden. Alles mit kühlen Händen zu einem glatten Teig kneten. Den Teig in Alufolie wickeln und 30 Min. im Kühlschrank ruhen lassen.
i Den Frühstücksspeck in dünne Scheiben schneiden. i In einer Pfanne bei mittlerer Hitze knusprig braten und auf Küchenkrepp abtropfen lassen i Die Springform einfetten, den Backofen auf 200° vorheizen. i Die Arbeitsfläche mit Mehl bestäuben und den Teig darauf rund ausrollen. i Den Boden der Form damit auslegen und einen zweifingerbreiten Rand formen. i Den Teig mehrmals mit einer Gabel einstechen. i Die Speckscheiben darauf verteilen. i Die Eier mit Pfeffer, Paprikapulver und der Sahne verquirlen, den geriebenen Käse unterrühren und die Mischung eventuell mit etwas Salz abschmecken. i Die Käsesahne auf den Tortenboden gießen. i Die Specktorte auf der zweiten Schiene von unten in etwa 30 Min. goldgelb backen. i Die Quiche mit einem Messer vom Formrand lösen, die Springform öffnen und die Torte noch heiß in 8 Portionsstücke schneiden und sogleich servieren.
Abb. 5.1 Zubereitungsvorschrift einer quiche lorraine
Formulieren wir uns eine solche Beschreibung abstrakt genug, so wird sie Algorithmus genannt:
156
Programmiersprachen und Paradigmen
„Ein Algorithmus ist ein allgemeines Verfahren zur Lösung gleichartiger Probleme, das durch einen endlichen Text präzise beschrieben ist“. Betrachten wir dazu als Beispiel die Vorschrift, einen Speckkuchen (Quiche Lorraine) zuzubereiten wie sie in Abb. 5.1 abgebildet ist. Wir sehen, dass es genaue Mengenangaben und Backzeiten gibt; das Rezept sieht nur eine endliche Zahl von Bearbeitungsschritten vor. Eine alternative Definition wäre somit auch „Ein Algorithmus ist eine endliche, präzise Verarbeitungsvorschrift, die so formuliert ist, dass sie von einer mechanisch oder elektronisch arbeitenden Maschine in endlicher Zeit bearbeitet werden kann“ Ein Algorithmus ist also gekennzeichnet durch x
Endlichkeit der Beschreibung
x
Vollständigkeit der Beschreibung
x
Eindeutigkeit der Beschreibung
x
Universalität (Lösung für eine ganze Klasse von Problemen)
x
Endlichkeit der Ausführung
Ein Algorithmus besteht meist aus Anweisungen oder Aktionen („das Mehl auf die Arbeitsfläche sieben“) für die zu verarbeitenden Daten („200g Mehl“), die von einem „Prozessor“ , z.B. einer Maschine oder einem Menschen, ausgeführt werden. Jeder Algorithmus muss in einer formal festgelegten Sprache formuliert werden, um für Rechner ausführbar aufbereitet werden zu können. Im Unterschied zu den ausführenden Rechnern selbst, die durch Technikentwicklung um ein Vielfaches schneller und billiger geworden sind, dümpelt der Fortschritt bei der Programmerstellung immer noch vor sich hin. Es gibt den Missstand, dass die Computerleistung in wenigen Jahren um Größenordnungen gewachsen ist, dagegen die Produktivität der Programmierer ungefähr gleich bleibt. Damit hat sich die Softwareentwicklung zum größten Kostenfaktor gemausert. Es liegt deshalb auf der Hand, die herkömmlichen Programmiersprachen und ihre kostengünstigen Alternativen näher zu betrachten. Es gibt sehr viele Programmiersprachen; in Abb. 5.2 ist nur ein unvollkommener Überblick über die existierende Fülle gegeben.
Natürliche Sprachen
157
JAVA
Abb. 5.2 Der Babylonische Turm der Programmiersprachen
Deshalb wollen wir nur die wichtigsten Typen und Charakteristiken der Sprachen untersuchen, um auch neue Sprachen gleich in ihren Eigenschaften einordnen zu können.
5.1 Natürliche Sprachen In der langen Entwicklung der menschlichen Rasse hat sich zur Koordination der Menschen untereinander ein sehr wirkungsvolles Werkzeug herausgebildet-die natürliche Sprache. Sie ist gänzlich anders als mancher ProgrammiersprachenDesigner es wahrhaben will. So verwendet sie im Unterschied zu den konventionellen Programmiersprachen meist mehrere, oft auch parallel benutzte Kanäle, um Informationen zu übermitteln
158
Programmiersprachen und Paradigmen
x sprachliche Lautäußerungen (Worte) x tonale Modulation (Tonfall) x non-verbale Kommunikation (Gesten, Mimik) x Situationskontext (Wozu wird das gesagt? Wer erzählt das?..) Man sieht: ein normales Buch ist bereits eine ziemliche Einschränkung der Kommunikationsvielfalt. Dazu kommt noch die Fähigkeit der Menschen, unvollständige oder falsche Satzteile (Semantik, Syntax) im sprachlichen, semantischen und Situationskontext zu korrigieren und zu ergänzen. Beispiele x Syntax Nimmt man mit einem Tonbandgerät versteckt eine natürliche Unterhaltung zwischen zwei Menschen auf, so findet man jede Menge von Fehlern darin. Beispiel: Na, geht´s? [Na, wie geht es Dir?] So la la. [ Einigermaßen gut. ] Is was? [ Ist etwas [passiert]?] Hmmm. [ Ja, frage weiter. ] Die grundsätzlichen Probleme solcher Sprachäußerungen zeigen sich genau dann, wenn man versucht, sie korrekt in eine andere Sprache (z.B. als Handlungsanweisungen) maschinell zu übersetzen. x Situationskontext „Er wechselte die Mine in seinem Bleistift“. Für den Menschen ist assoziativ klar, dass hier keine Kohlegrube, Sprengmittel oder Gesichtsausdruck gemeint ist; nicht aber für einen Computer. x Semantik „Er kaufte ein Klavier und ein Xylophon und trug es nach Hause“. Auf die Frage: „Was trug er nach Hause?“ kann ein Mensch sofort antworten: das Xylophon, da das Klavier zu schwer ist; für einen Computer aber ist die Antwort unbestimmt. Aus den obigen Beispielen ist ersichtlich, dass der sprachlichen Formulierung ein komplexes Netz von Assoziationen zugrunde liegt, mit dem unser Wissen in unseren Köpfen gespeichert ist. Die sprachliche Formulierung selbst ist nur ein flacher Abklatsch dieser komplexen Strukturen. Aus diesem Grunde kann man eine gute Übersetzung von einer natürlichen Sprache in eine Andere nur in der ersten, groben Näherung durch automatische Termersetzungssysteme durchführen; die Feinarbeit der Korrektur (Entscheidung von zweideutigen Worten, Anwendung von Ausdrücken je nach Situationskontext, etc.) bleibt dem menschlichen Übersetzer vorbehalten.
Imperative Sprachen
159
Für unser Thema „Programmiersprachen“ bedeutet dies, dass es keine SuperProgrammiersprache geben kann, die alle Aspekte natürlicher Sprache direkt auf Maschinencode abbilden kann. Statt dessen suchen wir für jeden Anwendungsbereich diejenige Sprache, die es am Besten gestattet, die Anwenderproblematik zu formulieren. Aus diesem Grund gibt es sehr viele, verschiedene Programmiersprachen, die alle in ihrer Nische ihre Existenzberechtigung haben. Allerdings kann man trotzdem die Programmiersprachen anhand einiger typischer Elemente in verschiedene Gruppen unterteilen, die wir im Folgenden näher kennen lernen wollen.
5.2 Imperative Sprachen Die Entwicklung der klassischen Programmiersprachen wie FORTRAN, PASCAL, C und Java ist durch viele Bestrebungen geprägt, die Programmierung der Computer zu vereinfachen. Im Folgenden wollen wir deshalb die wichtigsten Prinzipien der bisher erfolgreichsten Art von Programmiersprachen zusammenstellen. Die wichtigsten Elemente der imperativen Sprachen sind x Aneinanderreihung von Deklarationen und Befehlen x Kontrollflusskonstrukte wie Selektion (IF..THEN, CASE, Ausnahmebehandlung..), Iteration (FOR ..DO,..) und Rekursion x Datenzusammenfassung mit Blöcken, Feldern und Records x Abstraktion durch Daten und Prozeduren x Nebenläufigkeit durch Synchronisierung und Nicht-Determinismus Dies wird durch Algorithmen und Strukturen von Programmiersprachen erreicht, die im wesentlichen x Variablen verwenden, um Zustände im Programm zu speichern x Befehle verwenden, um diese Variablen zu verändern x das Programm übersichtlicher und (meist) kürzer machen, indem sie die Menge aller Befehle in Unterprogramme oder Prozeduren untergliedern Aus diesen Gründen nennt man diese Gruppe auch imperative oder prozedurale Programmiersprachen. Das grundsätzliche Funktionsidee dieser Sprachen besteht darin, die Zustände eines Problems durch Zustandsvariable und die Übergänge zwischen den Zuständen als Operationen oder Prozeduren auf den Variablen zu modellieren.
160
Programmiersprachen und Paradigmen
Beispiel Beim Kuchenbacken haben wir die Zustandsvariablen „Mehl“, „Eier“, „Kuchen“ und die Operationen „Zutaten_vermengen“, „Teig_ausrollen“, „Kuchen_backen(Zeit, Temperatur)“ usw. Dies entspricht auch unserem Maschinenmodell, bei dem wir den Zustand des Programms durch den Zustand von Daten auf bestimmten Speicherplätzen charakterisieren und steuern. Die feste (imperative) Zuweisung von Zuständen zu Speicherplätzen ist charakteristisch für diese Art der Programmierung. Allerdings ist es dabei sinnvoll, zwischen notwendigen, modellierungsbedingten Zustandsvariablen und nicht unbedingt notwendigen, nur temporären, implementierungsbedingten Variablen zu unterscheiden. Beispiel Die n-te Potenz einer Zahl b lässt sich sowohl iterativ als auch rekursiv in PASCAL errechnen. (a) FUNCTION power (B:REAL; n:INTEGER): REAL; VAR p: REAL; i:INTEGER; BEGIN p:=1.0; FOR i:=1 TO abs(n) DO p:=p*b; IF n>=0 THEN power:=p ELSE power:=1.0/p; END; (b) FUNCTION power (B:REAL; n:INTEGER): REAL; BEGIN IF n=0 THEN power:=1.0 ELSE IF n>0 THEN power:=b*power(b,n-1) ELSE power:=1.0/power(b,-n) END;
Wie man sieht, benutzt Version (a) die lokalen Variablen p und i, die in Version (b) nicht nötig sind. Also sind sie nicht essentiell für die Modellierung der Potenzberechnung. Literatur D. Watt: Programming Language Concepts and Paradigms; Prentice Hall, New York 1990 E. Horowitz: Fundamentals of Programming Languages; Springer Verlag, Berlin 1983 N.Wirth, C.Hoare: The Pascal Programming language; Acta Informatica, Vol.2, S. 335, (1972)
Objektorientierte Sprachen
161
5.3 Objektorientierte Sprachen Die objektorientierte Programmierung beruht auf Ideen, die im Xerox Palo Research Center (PARC) Ende der 70ger Jahre aufkamen. Damals wollte man eine bessere, menschengerechtere Programmiertechnik entwickeln durch die Integration der Programmiersprache mit der Benutzeroberfläche. Tatsächlich wurden die wichtigsten Elemente der Oberfläche, die damals entwickelt wurden, wie Fenster, Ikone und Zeigeinstrument (Maus), auf anderen Systemen übernommen (Apple: Lisa-System; heute: Presentation Manager, Windows, OpenLook, Motif,..), allerdings ohne auch gleich die dazugehörende Philosophie der objektorientierten Sprache mit zu übernehmen. Statt dessen blieb bis heute die dafür entworfene Sprache Smalltalk-80 relativ isoliert, obwohl sie auch heute noch einen der besten Ansätze darstellt, Sprache und Oberfläche zu integrieren. Die Gruppe der objekt-orientierten Sprachen bilden gegenüber den imperativen Sprachen nicht unbedingt einen Gegensatz, sondern können eher als Weiterentwicklung angesehen werden. Sie enthalten zusätzlich zu den Möglichkeiten des letzten Abschnitts aber weitere, wichtige Elemente. Eine Erweiterung der herkömmlichen Sprachen um objekt-orientierte Elemente ist leicht möglich, siehe z.B. Pascal auf Turbo-Pascal oder C auf C++. Man kann dies auch über die Denkdisziplin erreichen, ähnlich wie man in Assembler strukturiert und mit Pascal sehr unstrukturiert programmieren kann. Das objektorientierte Paradigma, bei dem die Welt auf Objekte und den Nachrichtenaustausch zwischen Objekten abgebildet wird, ist also mehr eine Denkdisziplin, die mit objektorientierten Sprachen verwirklicht werden kann, aber nicht automatisch durch den Gebrauch einer bestimmten Sprache verwirklicht wird. In diesen Abschnitten wollen wir einige Eigenschaften von objektorientierten Sprachen kennen lernen. 5.3.1 Das Kapseln von Programmteilen Eine wichtige Möglichkeit zur Strukturierung von Daten und Anweisungen besteht darin, sie zu Einheiten (Paketen, Blöcken) zusammenzufassen. Die Bezeichnung „Kapseln“ drückt dabei die zusätzliche, bewusste Isolierung des Paketinhalts von dem übrigen Programm aus: Der Inhalt soll von außen nicht sichtbar sein, sondern nur kontrolliert über eine Schnittstelle benutzt werden können (information hiding). Fassen wir nun noch Operationen (Prozeduren und Funktionen) und Daten bzw. Datentypen in einem Paket zusammen, so erhalten wir einen abstrakten Datentyp. Betrachten wir als Beispiel unseren Speckkuchen aus Abb. 5.1. Die Klasse aller Speckkuchen lässt sich in der Art formulieren wie in Java
162
Programmiersprachen und Paradigmen class Speckkuchen { float Mehl = 200; float Butter = 100; int Eier = 5; ... void Zutaten_vermengen(){...}; void Kuchen_backen(float Zeit, float Temperatur) ... }
Die Variablen (Mehl, Butter usw.) werden dabei als Attribute bezeichnet; die Prozeduren und Funktionen (Zutaten_vermengen(), Kuchen_backen()) heißen Methoden. Allerdings ist mit der Kenntnis der Datenstruktur des Typs Speckkuchen auch hier wieder die Versuchung für jeden Programmierer groß, direkt auf die Daten zuzugreifen und sie zu verändern. Ist die Mengenangabe auf der Einkaufsliste nicht ebenfalls verändert und werden die anderen Mengen darauf abgestimmt, erhalten wir damit eine schlechte quiche lorraine. Dies bedeutet allgemein, dass jede Änderung, die bei einem zentralen Datentyp gemacht werden muss, eine Vielzahl von Änderungen in allen Programmen erfordert, die ihn benutzen. Dies ist ein großes Hindernis für eine fehlerfreie Änderung (Wartung, Pflege) von Programmen und verursacht unverhältnismäßig große Kosten an Zeit und Geld (und Nerven!). Die Lösung für diese Problematik liegt darin, die Realisierung des Datentyps geheimzuhalten und damit dem unliebsamen Zugriff „tricksiger“ Programmierer zu entziehen. Definition und Implementierung sowie alle Zugriffsoperationen (Zuweisung, Vergleich, Addition etc.) werden so gekapselt, dass „von außen“ nur die Tatsache der Existenz des Datentyps und die Syntax seiner Zugriffsoperationen bekannt ist. Für unser Beispiel bedeutet dies, den Zugriff auf den Speckkuchen nur durch wenige, genau spezifizierte Methoden zuzulassen, etwa nur durch die Hauptmethode, den Kuchen zu erzeugen (public) und alle anderen Zugriffe auf die internen Attribute und internen Methoden zu sperren (private): public class Speckkuchen { public mache_quiche_lorraine(Einkaufsliste){…} ; private float Mehl; private float Butter; private int Eier; ... private void Zutaten_vermengen(){...}; private void Kuchen_backen(float Zeit, float Temperatur) ... }
Die so entstehende Datenstruktur heißt abstrakte Datenstruktur (ADS); der entsprechende Datentyp ein abstrakter Datentyp (ADT). In neuerer Zeit hat sich
Objektorientierte Sprachen
163
für eine ADS der Name Objekt eingebürgert. Die Erzeugung des Objekts eines Typs wird Instantiierung genannt; das Objekt selbst ist eine Instanz eines Typs. Eine abstrakte Datenstruktur lässt sich also wie ein Dienstleistungsbetrieb, etwa ein Installationsbetrieb, ansehen: für den Kunden gibt ein Schaufenster, in dem alle Leistungen aufgeführt sind, und im Hinterzimmer ist die Werkstatt, in die der Kunde normalerweise nicht hineinkommt und in der die versprochene Leistung erbracht wird. Dabei kann der Meister seinen Betrieb organisieren (z. B. auf EDV umstellen) wie er will: der Kunde braucht keinen Gedanken daran zu verschwenden, solange er die gleiche Leistung erhält. Diese Situation ist in der folgenden Abbildung verdeutlicht: Objekt
PROGRAM ...
public
Schnittstelle
Aufruf ...
END.
private
Implementierung Daten Abb. 5.3 Schnittstelle (Schaufenster) und Implementierung (Werkstatt) eines abstrakten Datentyps
Die beschriebene Datenkapselung hat verschiedene Vorteile: x Die Daten werden nur lokal verändert, so dass Seiteneffekte und damit Fehler und Wartungsaufwand reduziert werden. x Die Daten werden unabhängig voneinander, so dass sie leichter getestet bzw. vorhandene Fehler leichter lokalisiert werden können. x Durch die Datenunabhängigkeit kann leichter ein parallele Verarbeitung auf Multiprozessorsystemen erreicht werden. x Die Modularisierung der Daten erhöht die Übertragbarkeit (Portabilität) auf andere Maschinen und Compiler. 5.3.2 Objekte und Vererbung Bei der Kapselung wurden Prozeduren und Daten in einem Objekt vereint. Dies hat zwar verschiedene Vorteile, bringt aber auch Nachteile mit sich. Einer der bedeutendsten Nachteile des bisher entwickelten ADT-Modells besteht darin, dass man bei jedem Datum obigen Typs den gesamten Code der Prozeduren ebenfalls speichern müsste. Um dem abzuhelfen, werden intern die Prozeduren nur einmal
164
Programmiersprachen und Paradigmen
eingerichtet und jeder Aufruf von ihnen bei einem Objekt wird zu dieser Zentralstelle umgeleitet. Diese Idee kann man nun noch weiter verfeinern. Definiert man einen neuen Datentyp, der zusätzlich zu den Methoden des alten Typs noch neue enthält, so reicht es, nur die neuen anzugeben; die alten werden automatisch mitbenutzt. Hat eine neue Methode den gleichen Namen wie eine alte, so wird die Neue anstelle der Alten verwendet. Mit diesem Konzept kann man nicht nur eine Menge Schreibarbeit sparen, sondern auch die Übersichtlichkeit erhöhen, wenn man für ein Problem sehr viele ähnliche Datentypen definieren muss. Da jeder neu definierte abstrakte Datentyp alle Methoden und Attribute seines „Vorgängers“ erhält und seine eigenen, zusätzlich „erworbenen“ Eigenschaften an die von ihm abgeleiteten, abstrakten Datentypen (seine „Nachfolger“) weitergibt, vergleicht man den Vorgang mit dem biologischen Mechanismus der Vererbung. Der objektorientierte Ansatz ist also besonders für Probleme geeignet, bei denen die Datenstrukturen (Objekte) eine natürliche Verallgemeinerungshierarchie bilden. In der folgenden Abbildung ist dies für das Beispiel „Kraftfahrzeuge“ durchgeführt.
Kraftfahrzeug ist_ein
LKW Cabrio
ist_ein
ist_ein
ist_ein
Omnibus
PKW ist_ein
Limousine Abb. 5.4 Eine Vererbungshierarchie
Die Relation „ist ein“ ist dabei gleichbedeutend mit der objektorientierten Sprechweise „erbt von“. In Java wird eine Vererbung dadurch angegeben, dass bei der Objektdeklaration der Vorgängertyp genannt wird: class tPKW extends tKraftfahrzeug { ... }
Die entstehenden Typen sind alle aufwärtskompatibel, d.h. sie vertragen sich im Typ mit allen ihren Vorgängern, von denen sie alles geerbt haben, aber natürlich nicht mit ihren Nachkommen. Die Vererbung bedeutet also eine inkrementelle Erweiterung des Basistyps. Da Objekttypen eine Menge von Datenobjekten beschreiben, die alle bestimmte Eigenschaften gemeinsam haben, spricht man auch von Objektklassen. Die Vererbungsmechanismen werden intern ziemlich komplex implementiert. Deshalb ist die saubere und systematische Benutzung der Konstruktoren und De-
Objektorientierte Sprachen
165
struktoren sehr wichtig, um „Speicherleichen“ (nicht referenzierte Objekte) zu vermeiden. In Java wird dies durch ein automatisches Speichermanagement (garbage collector) sehr vereinfacht, in C++ muss dies von Hand richtig gemacht werden und verursacht deshalb viele Fehler. Mit dieser Erweiterung der abstrakten Datenstrukturen und Datentypen möchten wir dieses Thema zunächst abschließen. Es gibt eine Menge mehr Mechanismen in modernen, objektorientierten Sprachen wie Mehrfachvererbung, Polymorphie etc., die aber an dieser Stelle nicht weiter diskutiert werden sollen. Statt dessen möchten wir noch kurz im folgenden Abschnitt auf die Implikationen für die Programmierphilosophie eingehen. 5.3.3 Die Philosophie objektorientierter Klassen Die obige Hierarchie der Datenbegriffe (Klassenhierarchie) kann man nun dazu verwenden, die gesamte Philosophie der Datengestaltung neu zu gestalten. Ausgehend von einem universellen, sehr allgemeinen Datentyp, in unserem Beispiel zur Beschreibung und Manipulation von Kraftfahrzeugen, stellt man eine Hierarchie auf, die die allgemeinen Eigenschaften immer mehr spezialisiert und durch diese konkretisierenden Anreicherungen Stufe für Stufe alle Spezialfälle abdeckt. In Abb. 5.5 ist eine zu Abb. 5.4 analoge Struktur aufgestellt. Die Hierarchie kann hier durch den Mechanismus der „Vererbung“ erstellt werden.
Kraftfahrzeug
Ladefläche anhängen()
LKW
Omnibus
PKW
Cabrio Verdeckart öffnen() schließen()
ZahlStehplätze kassieren()
Limousine Stufenheck Kofferraum_öffnen()
Kombi Rückbank_umklappen()
Abb. 5.5 Objekthierarchie von Fahrzeugen
Dieses Vorgehen hat verschiedene Vorteile: x Durch die einheitliche Problemstrukturierung wird der Einsatz automatischer Werkzeuge dafür auf allen Ebenen der Problemlösung erleichtert (z.B. die automatische Erzeugung von Klassen aus einem Spezifikationsmodell) x Durch die bessere Problemstrukturierung werden Übersicht und damit die Dokumentation und Wartbarkeit verbessert.
166
Programmiersprachen und Paradigmen
x Die daraus entstehende Klassenbibliothek ist leicht zu pflegen und Fehler können gezielt gesucht werden. x Ist die Hierarchie der Klassenbibliothek nach menschlichem Gliederungsverständnis aufgebaut, so verstehen auch andere Programmierer (oder derselbe ein Jahr danach) das Programmsystem und können Teile der Bibliothek für ihre Zwecke in reiner oder abgewandelter Form verwenden. Damit wird die Problematik der Wiederverwendbarkeit von Software entscheidend verbessert. x Durch die entstehende Typenvielfalt und einen streng prüfenden Compiler ist der Programmierer gezwungen, sehr sauber zu programmieren, so dass Fehler vermieden oder schneller entdeckt werden. x Durch die Kapselung erhalten wir die oben beschriebenen Vorteile für Korrektheit, Unabhängigkeit, Wartbarkeit und Portabilität. Allerdings bedingt dies alles, dass das Problem sehr sauber vorher durchdacht worden ist. Eine gute objektorientierte Programmierung zeichnet sich also nicht unbedingt durch den entstehenden Code aus, sondern durch den Aufwand, der bei der Modellierung und Spezifikation des zu lösenden Problems getrieben wird. Es ist also mehr eine Programmierdisziplin und -philosophie als eine Programmiermethode. 5.3.4 Objektorientierte Programmentwicklung Wie soll man konkret vorgehen, um ein Anwendungsproblem objektorientiert zu lösen? Die Problemanalyse
Der allgemeine Analyseansatz beschreibt das Problem als ein zeitlich sich veränderndes Gebilde, einen Prozess. Dieser Arbeitsprozess lässt sich weiter untergliedern in aktive und passive Elemente sowie Daten, die ausgetauscht werden. Ziel der Prozessmodellierung und des daraus folgenden Systementwurfs ist es, den Arbeitsprozess auf ein System interagierender Objekte abzubilden. Dazu kann man verschieden vorgehen. In einem ersten Ansatz ist es sinnvoll, zusammen mit dem Benutzer die Fachsprache zu rekonstruieren, also Begriffe und Abläufe zu beschreiben. Dies fördert nicht nur das Vertrauen der Benutzer in die Problemanalyse, sondern etabliert auch eine gemeinsame Fachsprache. Beispiel (Begriffe und Eigenschaften in Schrägschrift) „Der Berater holt dann seinen Beratungsordner, sucht das Produkt über ein Register und öffnet den Ordner an der entsprechenden Stelle. Zum Beratungsordner gibt es noch einen Formularordner, in dem Standardformulare (z.B.
Objektorientierte Sprachen
167
Vertrag zugunsten Dritter) abgelegt sind, und einen Musterordner, in dem Ausfüllhilfen für Verträge und Schlüsselblätter abgelegt sind.“ Aus den so entstehenden Listen von Fachbegriffen wird in enger Zusammenarbeit mit dem Anwender ein Glossar (data dictionary) gebildet, das die grundlegenden Begriffe enthält. Beispiel Glossar Beratungsordner
enthält Konditionstabellen Gebührentabellen Kontenrahmen Produktinformation ...
Produkt
alle Leistungen, die im Rahmen der Passivberatung angeboten werden. Produkte können bankspezifische Namen haben. Sie sind gekennzeichnet durch ...
Aus diesem Glossar lassen sich nun Szenarien erstellen, also Systementwürfe (Systemvisionen), die ein Handlungsgerüst für maskierte und typisierte Einheiten (z.B. Charaktere, Personen etc.) darstellen, ähnlich wie in der italienischen Commedia dell´ Arte, und dabei Arbeitsabläufe oder Handlungsstudien darstellen. Beispiel Bankszenario „Der Berater öffnet dann seinen Beratungsordner mit einem Doppelklick, wählt zunächst das Produkt über das sichtbare Inhaltsverzeichnis, wählt anschließend im zweiten Verzeichnis die gewünschte Variante mit einem Doppelklick und lässt sich dadurch gleich die zugehörige Verkaufshilfe anzeigen.“ Objektorientierte Modellierung
Ein gutes Hilfsmittel, um bei der Modellierung die Arbeitsprozesse auf statische und dynamische Objektstrukturen abzubilden, ist ein Graph [Rumbaugh]. Hierbei bildet man die Hauptworte (Gegenstände, Personen etc.) auf Objekte (im Graph: Knoten) ab, die Relationen und Assoziationen auf die Methoden und Attribute der Objekte (im Graph: Kanten). Die eigentliche objektorientierte Modellierung besteht aus verschiedenen Aspekten: statische Modellierung, dynamische Modellierung und funktionale Modellierung.
168
Programmiersprachen und Paradigmen
1) Die statische Modellierung kennt folgende Schritte: x Identifiziere Klassen x Erstelle ein Wörterbuch (Glossar) der Klassenbedeutung x Identifizierung von Klassenbeziehungen (Assoziationen, Aggregationen, Vererbung) x Identifizierung von Attributen x Verfeinerung der Modellierung durch Vererbung x Validierung durch Testen x Zusammenfassung von Klassen in Modulen Resultierende Dokumente: Klassendiagramme (Hierarchien), Objektdiagramme (gerichtete Graphen mit Knoten als Klassen und Pfeilen als Beziehungen dazwischen) Beispiel Autos Die Schlagwörter eines LKW bzw. PKW-Systems seien: LKW-Reifen, Dieselmotor, Gaspedal, Räder, Tacho, Bremse, PKW-Reifen, Benzinmotor, Drehzahlmesser. Das Objektdiagramm ist dann beispielsweise Drehzahl messer
Tacho Klasse
zeigt_Drehzahl
zeigt_Tempo
Reifen
sitzen_auf
Räder treibt_an Motor
betätigt
Gaspedal
bremst
LKWReifen
PKWReifen
Bremse betätigt
Bremspedal
Benzinmotor
Dieselmotor
Klasse
Abb. 5.6 Klassendiagramm eines Autos
2) Die dynamische Modellierung dagegen formuliert das Anwendersystem mit dem typischen Ansatz der imperativen Sprachen als einen Fluss von Ereignissen (Ein/Ausgabe) und Zuständen der Objekte. Dabei kennt sie folgende Schritte: x Erstelle Szenarien von typischen Interaktionen x Identifiziere Ereignisse (events) zwischen Objekten x Erstelle einen Ereignispfad für jedes Szenario x Erstelle ein Zustands-Übergangsdiagramm
Objektorientierte Sprachen
169
x Prüfe die Vollständigkeit und Konsistenz der Zustandsdiagramme der Klassen zueinander: Jedes Ereignis soll Sender und Empfänger haben sowie Zustände einen Vorgänger und Nachfolger. Resultierende Dokumente: Szenariotabellen, Diagramme der Ereignisverläufe, Zustandsdiagramme (gerichtete Graphen mit Knoten als Systemzustände und Pfeilen als Übergänge dazwischen, die durch Ereignisse ausgelöst werden) Ein mögliches Zustandsdiagramm für obiges Beispiel ist Bremspedal_treten
Motor zieht an
Auto Halt Gaspedal_treten
Räder_drehen
Räder_langsamer
Bremse zieht an
Auto fährt Bremspedal_treten
Abb. 5.7 Zustandsdiagramm eines Autos
3) Die funktionale Modellierung schließlich verfolgt schließlich den Weg, mit der Daten erstellt und bearbeitet werden: x Identifiziere die Ein- und Ausgabedaten x Erstelle Kontroll- und Datenflussdiagramme, die funktionale Abhängigkeiten aufzeigen x Beschreibe die verwendeten Funktionen x Identifiziere bestehende Nebenbedingungen x Spezifiziere Optimierungskriterien Resultierende Dokumente: Kontrollflussdiagramme, Datenflussdiagramme (gerichtete Graphen mit Knoten als Verarbeitungsprozessen und Pfeilen als Datenfluss dazwischen) Ein Datenflussdiagramm von obigem Beispiel kann also folgendermaßen aussehen:
170
Programmiersprachen und Paradigmen Drehzahl
Seilzug
Motor
Gaspedal
Drehzahlmesser
Drehung Druck
Seilzug
Bremspedal
Bremse n
Räder
Drehung
Tacho Traktion
Reifen Abb. 5.8 Datenflussdiagramm eines Autos
Die anschließenden Schritte wie Implementierung und Wartung sowie die Problematik von Test und Dokumentation vervollständigen den Weg der Softwareerstellung. Sie sollen aber an dieser Stelle nicht weiter verfolgt werden, sondern in Kapitel 8 in dem größeren Zusammenhang des Software-Engineering dargestellt und erläutert werden. Zusammenfassung: Vor- und Nachteile
Der Entwurf von Programmen mit Hilfe objektorientierter Sprachen bietet verschiedene Vorteile, die bei traditionellen Systemen stark vermißt werden: x Die Komplexität des Systems und seiner Komponenten kann durch stufenweise Kapselung verborgen werden x Das System kann inkrementell erweitert werden, ohne dass Probleme durch unbekannte Neben- und Wechselwirkungen auftreten x Mit der Einführung einer Hierarchie ist die Wiederverwendung der Software stark erleichtert; Modifikationen der vorgegebenen Lösungen entsprechend einer Anwendungsproblematik können durch Vererbung auf jeder Stufe der Hierarchie (der Abstraktion) vorgenommen werden. Am wichtigsten aber schlägt sich der Vorteil zu Buche, dass nun die Analyse-, Entwurfs- und Programmiermethodik einheitlich von der Problemmodellierung über den Systementwurf bis zur Codeerstellung mit Hilfe eines einheitlichen Konzepts, des Objektkonzepts, möglich wird und damit eine sprachliche Basis für Anwender, Projektmanagement und Programmierer gefunden werden kann. Allerdings sollen auch die Nachteile nicht verschwiegen werden: x Die Identifikation geeigneter Objekte ist schwierig
Funktionale Sprachen
171
x Das Design erfordert andere Denkstrukturen im Vergleich zum funktionalen bzw. prozeduralen Entwurf x Der Rechner wird zusätzlich belastet, da der Aufruf von einem Objekt durch das andere durch Zeiger etc. mehr Zeit braucht als ein direkter Prozeduraufruf. x Durch Einarbeitung und Untersuchung von Wiederverwendbarkeit entstehen höhere Anfangskosten. Diese Nachteile lassen sich teilweise wieder kompensieren: x Durch Verwendung optimierter Compiler und neuerer Prozessoren spielt die Zusatzbelastung während der Lebenserwartung des Programms keine große Rolle x Die Wiederverwendung bestehender, fehlerfreier Module beschleunigt die Kodierungs- und Testphase
Literatur G. Booch: Object-Oriented Design. Redwood City, CA: Benjamin/Cummings 1991 U. Claussen: Objektorientiertes Programmieren. Mit Beispielen und Übungen in C++. Springer Verlag 1993 P. Coad, E. Yourdon: Data Abstraction and object-oriented Programming in C++. B.G. Teubner, John Wiley & Son 1990 G.Gryczan, H. Züllighoven: Objektorientierte Systementwicklung; InformatikSpektrum (1992) 15: pp.264-272 B. Meyer: Object-oriented Software construction. Prentice Hall 1991 J.Rumbaugh et al.: Object-Oriented Modeling and Design. Prentice Hall 1991
5.4 Funktionale Sprachen Bei der Suche nach einer guten Programmiersprache, die kompakte, überprüfbare und leicht zu wartende Programme fördert, bietet der Ansatz der funktionalen Programmiersprachen interessante Aspekte. 5.4.1 Nebenwirkungsfreie Funktionen Der inhaltliche Kern, mit dem sich diese Sprachenfamilie von den bisher behandelten imperativen Programmiersprachen unterscheidet, liegt in der Schwäche der imperativen Sprachen, bei allen Prozeduren und Funktionen auch Nebeneffek-
172
Programmiersprachen und Paradigmen
te zuzulassen, etwa durch eine verdeckte, nicht dokumentierte Veränderung einer globalen Variablen. Benutzen wir etwa bei einer Prozedur oder Funktion nur Referenzen auf aktuelle Parameter und nicht ihre Werte, so muss die Funktion trotz lokaler Berechnungen auch dauerhaft im Hauptspeicher den Wert mindestens einer (globalen) Variable verändern, um die Ergebnisse zurückzugeben. Prinzipiell lassen sich drei Arten der Funktionsaufrufe unterscheiden: x call-by reference (call-by-name): Die Funktion bekommt n Zeiger zu Speicherplätzen als Argumente. Die Werte der Argumente sind in den Speicherplätzen enthalten. In diesem Fall sind Seiteneffekte nicht nötig, aber möglich. x call-by-value + globale Variable: Die Funktion bekommt n Argumentwerte. Auch hier sind Seiteneffekte möglich und bei Benutzung von globalen Variablen auch nötig. x call-by-value: Die Funktion erhält nur die Argumentwerte und gibt einen Wert zurück. Hier gibt es keine Seiteneffekte. Nebenwirkungsfreie Funktionen führen ihre Berechnungen ohne Wechselwirkung mit globalen Variablen durch und geben die Ergebnisse als Wert (z.B. auf dem Stack) zurück. Dieses Vorgehen erleichtert die Parallelisierung der Funktionen für parallele Hardwaresysteme ungemein, da das Ergebnis einer Funktion nun nicht mehr von der Aufrufreihenfolge, sondern nur noch von den Argumenten abhängt. Imperative Programmiersprachen, die Ausdrücke und Anweisungen sequentiell abarbeiten, haben große Schwierigkeiten bei Parallelrechnern: Sie lösen Probleme durch das schrittweise Abarbeiten, indem sie sequentiell den Zustand der globalen Variablen im Zustandsraum verändern. Dabei betrachten sie die Variablen nur als Behälter, dessen Inhalt beliebig, auch in Ausdrücken, verändert werden kann. Beispiel Abarbeitungsreihenfolge in Ausdrücken In C und Java bedeutet „x++“, dass zuerst der Wert der Variable zu nehmen und dann die Variable um eine Einheit zu erhöhen ist. Analog bedeutet „--x“, dass zuerst die Variable um eins zu erniedrigen ist und dann mit diesem Wert weiterzuarbeiten ist. Betrachten wir nun die Zuweisung mit dem Ausdruck y = (x++)*(--x);
Was ist das Ergebnis der Evaluierung des Ausdrucks auf der rechten Seite? Nehmen wir an, x=1 sei vorher gesetzt worden. Dann ist, wenn wir den Ausdruck von rechts nach links abarbeiten, y = (0)*(0);
/*
= 0
*/
und wenn wir ihn von links nach rechts abarbeiten y = (1)*(1);
/*
= 1
*/
Funktionale Sprachen
173
Es ergeben sich also zwei verschiedene Ergebnisse, je nach Evaluationsrichtung. Auch die Abarbeitungsreihenfolge der Anweisungen in imperativen Programmiersprachen kann bei einer parallelen Ausführung ernsthafte Schwierigkeiten bereiten. Beispiel Abarbeitungsreihenfolge in Zuweisungen Eine parallele Abarbeitung der drei Zuweisungen tmp := x, x := y und y := tmp, die einen Austausch der Werte der Variablen x und y bewirken sollen, kann leicht zu falschen Ergebnissen führen.
Ablauf 1
Ablauf 2 Ergebnis:
Ergebnis: tmp := x x := y y := tmp
xoy yox
Ablauf 3
x := y tmp := x y := tmp
yox yoy
Ergebnis: tmp := x y := tmp x := y
xoy xox
Das Ergebnis der Berechnungen ist also von der Reihenfolge der Abarbeitung der Anweisungen abhängig und deshalb nicht einfach so parallelisierbar. Bei den funktionalen Programmiersprachen aber ist immer garantiert, dass der Wert einer Funktion unabhängig von der Evaluationsreihenfolge ist. Teilausdrücke können durch andere mit dem gleichen Wert ersetzt werden, ohne dass Seiteneffekte zu befürchten sind. Das Funktionsergebnis ist dabei nur von den Parametern abhängig und nicht von der Vereinfachung (Resolution) oder Abarbeitungsreihenfolge. Als Beispiel sei der Evaluationsgraph des Ausdrucks 3*4 -1 +7*6 in Abb. 5.9 gezeigt. 3 * 4 –1 + 7 * 6 3 * 4 –1 + 42
12 –1 + 7 * 6 11 + 7 * 6
12 – 1 + 42
3 * 4 + 41 12 + 41
11 + 42 53
Abb. 5.9 Evaluationsgraph eines Ausdrucks
174
Programmiersprachen und Paradigmen
In einer funktionalen Programmiersprache ergibt also jeder der verschiedenen Pfade durch den Graph den selben Wert. Damit wird nicht nur eine Parallelisierung des Programms, sondern auch die logische Argumentation im Programm bzw. der Beweis der Programmkorrektheit und die Wiederverwendung von Programmteilen wesentlich erleichtert Ein wesentliches Merkmal der funktionalen Programmiersprachen ist die feste Zuordnung eines Namens zu einem Wert (referentielle Transparenz), ähnlich wie in der mathematischen Kurznotation, so dass keine Nebenwirkungen auftreten können. Beispielsweise ist in square(x) = x*x die Referenz zu „x“ identisch zu dem einem festen Argumentwert, für den x nur als Name steht. Ist x ein Ausdruck, so muss sein Wert unabhängig von der Implementierung sein, mit der der Ausdruck evaluiert wird. Im funktionalen Fall ist also mit „x“ im obigen Beispiel keine „Variable“ gemeint, sondern ein fester Wert: wenn ein formaler Parameter einmal mit einem Wert fest assoziiert wurde, kann dies nicht mehr geändert werden, da es keine expliziten Zuweisungen gibt. 5.4.2 Sprachkonstrukte In den funktionalen Programmiersprachen gibt es also keine sequentiellen Anweisungen. Statt dessen ist das Hauptprogramm als Funktionsaufruf formuliert; ebenfalls die Definition aller dazu benutzten Funktionen, so dass die Ausführungsreihenfolge (Sequenz) nicht von der Reihenfolge der sprachlichen Definitionen im Programmtext abhängt, sondern von der gegenseitigen funktionalen Abhängigkeit. Eine solche Definition kann z.B. in der Sprache Miranda lauten square x = x*x
oder f x y = x + a, if x>10 = x - a, otherwise where a = square (y+1)
Im ersten Fall fällt auf, dass eine formelle function Deklaration fehlt; da es nur Funktionen gibt, wäre dies redundant. Des weiteren fehlen die Klammern und die Parameter sind nur durch Leerzeichen getrennt (s. zweites Beispiel). Am zweiten Beispiel fällt die logische Bedingung mit dem Schlüsselwort if auf, die bestimmt, ob die Definitionszeile gültig ist oder nicht, genauso wie dies in der mathematischen Schreibweise notiert wird. Eine solche Bool´sche Bedingung heißt Wächter (guard), wobei der spezielle Wächter otherwise genau dann den Wert True besitzt, wenn alle anderen Wächter (im Beispiel: einer) auf der rechten Seite den Wert False liefern. Die Menge aller Funktionsdefinitionen bildet das Skript oder den Kontext (Umgebung, Scope), in dem ein Ausdruck abgearbeitet (evaluiert, reduziert, simplifiziert) wird. Die Reduktion eines Ausdrucks heißt kanonisch, wenn er nicht mehr weiter simplifiziert werden kann. (Minimalausdruck). Beispielsweise ist die
Funktionale Sprachen
175
kanonische Repräsentation des Ausdrucks 3*4 -1 +7*6 der Wert 53. Ist ein kanonischer Ausdruck nicht definiert, so wird ihm das Symbol A zugeordnet. Genauso, wie in Pascal zur kompakteren Schreibweise innerhalb einer Funktion eine lokale Funktionsdefinition möglich ist, gibt es dies auch in Miranda. Syntaktisch wird dies durch das Schlüsselwort where auf der rechten Seite eingeführt, wie dies am obigen Beispiel gezeigt ist. In eine solche Funktionsdefinition kann auch eine weitere Definition eingeschachtelt sein. Beispiel Fakultätsberechnung fak n = mult 1 n where mult k r
= 1, = k, = mult k m*mult (m+1) r, where m = (k+r) div
if k>r if k=r if k
Abb. 5.10 Fakultätsberechnung in Miranda
Die Funktion fak wird in Abb. 5.10 rekursiv berechnet, indem lokal mit where eine rekursive Multiplikation definiert wird. Die Definition ist dabei zeilenweise gültig; die Bedingung (Wächter) steht rechts in der Zeile. In funktionalen Programmiersprachen gibt es keine Variablen; um in Schleifen einen Zähler zu erhöhen, wird eine Funktionsrekursion definiert und so die Zahl der Stackaufrufe als Zähler benutzt. Durch das Fehlen der Zuweisungen können Datenstrukturen nicht als „statische“ Datenfelder oder Datenrecords repräsentiert werden, sondern nur als rekursive Datenstrukturen, deren Struktur durch dazu gehörende Funktionen definiert werden. Beispiele In Miranda gibt es explizite Listen, wie z.B. Farben = [“rot“, “grün“, “blau“]
Eine Liste der ersten 100 Quadratzahlen in aufsteigender Reihenfolge lässt sich formulieren als squares = [n*n | n<-[1..100]]
Hierbei ist n eine lokale Variable, die, rekursiv benutzt, über alle Elemente 1..100 der Liste hinweggeht. Der rechte Teil der Listendefinition wird Generator genannt; durch geeignete Nebenbedingungen (Filter, z.B. 100 mod n = 0) lassen sich zusätzliche Forderungen an die Listenelemente stellen. Eine sequentielle Ausgabe der Listenelemente (z.B. zum Ausdruck) lässt sich erreichen durch take´0 x = [] take´(n+1)[] = [] take´(n+1)(a:x) = a: take´n x
176
Programmiersprachen und Paradigmen
Die Länge einer Liste ergibt sich mit den parallelen Definitionen length [] = 0 length (a:x) = 1 + length x
Beim Abspeichern der Datenstrukturen werden deshalb nicht nur die Daten, sondern auch die Funktionen selbst mitgespeichert. Es gibt in Miranda aber auch Tupel, wie z.B. Vektor = (1.0, 6.24, 3.7) In diesem Fall ist eine Funktionsdefinition f (x,y,z) = ... von der Funktionsdefinition f x y z = ... zu unterscheiden: im ersten Fall gilt es ein Argument (ein vollständiges Tupel), im zweiten Fall gibt es drei Argumente. Dies macht genau dann einen Unterschied, wenn man die Argumente sequentiell anwendet: f x y z = ((f x) y) z Beispiel Die Funktion f x y = x + y stellt für jeden Wert von x eine andere Funktion dar. So ist für x = 1 die Funktion (f 1)(y) = 1 + y eine andere Funktion als (f 2)(y) = 2 + y. Im Unterschied dazu ist die Funktion f (x,y) eine einfache Funktion f des Tupelarguments (x,y) und wird von den Werten von x und y zur Ausführungszeit bestimmt. Die schrittweise Anwendung einer Funktion auf ihre Argumente wurde von H.B. Curry eingeführt und wird deshalb currying genannt. 5.4.3 Sprachenübersicht Die Vielfalt der funktionalen Programmiersprachen können wir nach verschiedenen Kriterien ordnen. Ein wichtiges Merkmal ist dabei das Verhalten bei Datentypen. Ein anderes Problem ist die Unterscheidung von Datentypen. Eine Programmiersprache, die dies erlaubt, hilft mit, Fehler und Inkonsistenzen in den Programmen bereits bei der Übersetzung (Compilezeit) zu vermeiden. Auch bei den funktionalen Programmiersprachen gibt es Typendefinitionen oder auch nicht, je nach Programmiersprache. In Miranda beispielsweise lässt sich einen Definition der Quadratfunktion square x = x*x
durch die Deklaration
Funktionale Sprachen
177
square :: num onum
ergänzen, wobei der Datentyp num ganze Zahlen und Fließkommazahlen erfasst. Die Identitätsfunktion id´ x = x
kann mit der amorphen Deklaration id´ :: *o*
abgedeckt werden, wobei das Symbol * alle Datentypen, also num o num, char o char etc. im Anwendungssinn der Polytypen erlaubt. Eine gute Übersicht der Unterscheidungskriterien für Typen gibt uns dabei folgendes Schema: Typsysteme statisch: dynamisch: stark (strong): monomorph: polymorph: erster Ordnung: höherer Ordnung: strikt: nicht-strikt (lazy, verzögert): Speicherverwaltung
zur Compilezeit Test zur Laufzeit Typfehler werden vom Compiler nicht toleriert Funktionen haben einen festen Typ Funktionen haben schematischen Typ (Typvariable) Argumente/Werte können nur Datenobjekte sein Funktionen sind als Objekte zugelassen, können Argument/Wert von Funktionen sein Argumente werden bei Übergabe ausgewertet nicht ausgewertete Argumente möglich automatisch / "garbage collector"
Mit diesen Kriterien lassen die funktionalen Programmiersprachen sich folgendermaßen charakterisieren:
Sprache
Typisierung
strikt
Funktionsaufrufe in C, Pascal Lisp Common-Lisp Standard Allegro-Common-Lisp CLOS: Common Lisp Object System Scheme: Lisp-Variante ML, Standard ML LML, Lazy-ML ID Miranda Gofer / Haskell CLEAN
monomorph NEIN
JA JA
NEIN polymorph polymorph NEIN polymorph polymorph polymorph
JA JA NEIN JA NEIN NEIN NEIN
Funktionsordnung 1. Ordn. höhere Ord.
höhere Ord. höhere Ord. höhere Ord. 1. Ordn. höhere Ord. höhere Ord. höhere Ord.
Die Vorteile der funktionalen Programmiersprachen liegen
Seiteneffekte möglich JA
JA JA ?? JA NEIN NEIN NEIN
178
Programmiersprachen und Paradigmen
x in der Kürze, Kompaktheit des entstehenden fehlerfreien Programms. Geht man davon aus, dass die Anzahl der korrekten Codezeilen pro Programmierer unabhängig von der verwendeten Sprache ist, so bedeutet die Benutzung einer kompakten, sich auf das Wesentliche (die Spezifikation) beschränkende Programmiersprache eine Produktivitätssteigerung des Programmierers. x in der Nebenwirkungsfreiheit des Codes. Man kann deshalb davon ausgehen, dass ein funktionales Programm auf allen (sequentiellen) Rechnern problemlos läuft. x in der Parallelisierbarkeit des Codes. Da keine Variablen zugewiesen werden, sind alle globale Daten read-only und können nicht zu Inkonsistenzen führen. Unterfunktionen können unabhängig von den anderen Funktionen auf getrennten Rechnern abgearbeitet werden, ohne die Struktur der Rechner zu beachten. Allerdings sollte man beachten, dass dieser Ansatz auch Probleme beinhaltet: x vom „Human engineering“ Standpunkt aus kann eine kompakte, mathematische Beschreibung durchaus problematisch sein kann. Zum einen muss die formale Spezifikation nicht unbedingt unseren informellen Intentionen vom Sinn des Programms entsprechen und zum anderen kann der Beweis, dass die Implementation der Spezifikation entspricht zu lang oder zu kompliziert sein, dass er nicht als fehlerfrei garantiert werden kann. x Die lazy evaluation erfordert bei jeder Variable die Überprüfung, ob sie schon evaluiert ist oder nicht. Dieses und andere Speichertechniken verlangsamen die Programmabarbeitung um den Faktor 3-5 gegenüber einem imperativen Programm. Aus diesen Gründen sollte man eine funktionale Programmiersprache verwenden, wenn dies durch die Art des Problems möglich ist, aber dies nicht unbedingt forcieren. Literatur Abelson, H. Sussmann,G.J. Structure and Interpretation of Programs, MIT Press, (1985) Bird, R., Wadler, Ph., Introduction to Functional Programming, Prentice Hall, (1988) Davie, J. An introduction into functional programming using Haskell, Cambride University Press, (1992) Hinze, R., Einführung in die funktionale Programmierung mit Miranda, Teubner Verlag, (1992) Jeuring, J., Meijer. E., eds. Advanced functional programming, Lecture Notes in Compter Science 925, Springer-Verlag (1995) Plasmeijer, R., von Eekelen, M., Functional Programming and Parallel Graph Rewriting, Addison Wesley, (1993)
Datenflusssprachen
179
5.5 Datenflusssprachen Der Ablauf von Programmen wird durch die Ablaufprinzipien Kontrollfluss und Datenfluss bestimmt. Beide Prinzipien bestimmen für jede Operation in einem Algorithmus den Zeitpunkt, zu dem die Operation ausgeführt werden darf. Während das Kontrollflussprinzip die Bearbeitungsreihenfolge der einzelnen Operationen streng (imperativisch) vorgibt, verlangt das Datenflussprinzip lediglich, dass bei der Abarbeitung der Operationen die Datenabhängigkeiten zwischen den einzelnen Operationen nicht verletzt werden. Das Datenflussprinzip eröffnet dadurch Möglichkeiten, die Effizienz von Programmen z.B. durch Permutation von Operationen oder durch Parallelarbeit zu erhöhen. 5.5.1 Das Kontrollflussprinzip Das Kontrollflussprinzip basiert auf dem Grundsatz, dass eine Operation genau dann zur Ausführung kommt, wenn die vorhergehende beendet wurde. Der Kontrollfluss eines Programmes bestimmt die Bearbeitungsreihenfolge der Instruktionen. Dieses Prinzip wird lediglich durch die sogenannten Kontrollkonstrukte unterbrochen. Statt die Ausführung bei der nächsten Operation im Speicher fortzusetzen, wird durch eine Kontrolloperation (Maschinenbefehl „Jump“) eine beliebige andere Operation als Fortsetzung des Programms ermittelt. Das Kontrollflussprinzip wird in imperativen Programmiersprachen durch die Kontrollkonstrukte implementiert. Moderne imperative Programmiersprachen stellen die folgenden Kontrollkonstrukte zur Verfügung: x Schleife mit Vorbedingung
: while (Bedingung) do {Anweisungen}
x Schleife mit Nachbedingung : do {Anweisungen} while (Bedingung) x Unbedingter Sprung x Verzweigung
: goto (Label)
: if(Bedingung) {Anweisungen} else {Anweisungen}
x Mehrfachverzweigung
: switch bzw. case
x Sequentialisierungsoperator : ; Die Semantik des Sequentialisierungsoperators ; wird bei der Beschreibung von Programmiersprachen in der Literatur häufig vernachlässigt. Von Programmierern wird er häufig als eine Art Zeilenende interpretiert. Seine Bedeutung als Trennungssymbol streng sequentiell auszuführender Operationen wird üblicherweise nicht vermittelt. Der Sequentialisierungsoperator hat die in frühen Programmiersprachen üblichen Zeilennummern ersetzt. Das Kontrollflussprinzip wird auf der Hardwareebene durch die Architekturen implementiert, die einen Befehlszähler (program counter PC) haben.
180
Programmiersprachen und Paradigmen
5.5.2 Das Datenflussprinzip Im Unterschied dazu kann man auch ein Programm ganz anders, nämlich als Abarbeitungsreihenfolge von Daten und nicht von Befehlen, ansehen. Der Datenfluss in Algorithmen wird durch die Datenabhängigkeiten zwischen den Instruktionen bestimmt. Das Datenflussprinzip basiert auf den Datenabhängigkeiten der Operationen in Algorithmen. Was ist eine „Datenabhängigkeit“? Man sagt, dass zwischen zwei beliebigen Operationen genau dann eine Datenabhängigkeit besteht, wenn eine Operation das Ergebnis der anderen weiterverarbeitet. Die Operationen sind dann datenabhängig. Besteht diese Art der Datenabhängigkeit nicht, dann sind die Operationen datenunabhängig Beispiel Reihenfolge von Operationen Seien drei Operationen gegeben: 1: C=B+A; 2: D=E*F; 3: G=C/D;
Die ersten beiden Operationen im Beispiel sind datenunabhängig, denn keine der beiden Operationen benötigt das Ergebnis der anderen. Die Operation 3 hängt von 1 und 2 ab, denn sie verwendet deren Ergebnisse. Instruktion 3 muss also in jedem Fall nach 1 und 2 ausgeführt werden. Die Operationen 1 und 2 können beliebig vertauscht werden oder sogar parallel berechnet werden, aber Operation 3 muss immer danach erfolgen. Die Möglichkeit der automatischen Ermittlung parallel ausführbarer Operationen motiviert die wissenschaftliche Untersuchung des Datenflussprinzips. Aus den Datenabhängigkeiten der Operationen lässt sich das nun folgendes Abarbeitungsprinzip ableiten, das als Datenflussprinzip bezeichnet wird: Eine Operation gilt als ausführbar, sobald alle ihre Operanden verfügbar sind (nicht erst dann, wenn der Befehlszähler auf sie zeigt). Ein Berechnungszyklus nach dem Datenflussprinzip besteht dann aus: x der Berechnung aller ausführbarer Operationen x der Ermittlung aller Operationen, die durch die neuen Ergebnisse ausführbar werden. Datenflussprogramme laufen üblicherweise so ab, dass eine Teilmenge aller Befehle als ausführbar markiert ist. Diese Befehle werden nach der Produktion der Ergebnisse als ausgeführt gekennzeichnet und in die weitere Bearbeitung nicht mit einbezogen. Durch die neuen Ergebnisse können neue Operationen als „ausführbar“ ermittelt werden. Wichtig ist dabei, dass die Reihenfolge der Abarbeitung bei den ausführbaren Operationen keine Rolle mehr spielt. Sie dürfen in einer beliebigen Reihenfolge oder sogar parallel berechnet werden.
Datenflusssprachen
181
Das Datenflussprinzip findet seine Anwendung in den Datenflusssprachen und in den Datenflussrechnern, die im folgenden Abschnitt behandelt werden. Eine weitere wichtige Anwendung dieses Prinzip stellt die Optimierung der Codegeneratoren von Compilern dar. Gerade bei der RISC-Architektur moderner Mikroprozessoren hat sich gezeigt, das unterschiedliche Permutationen einer Befehlssequenz unterschiedlich effizient abgearbeitet werden. Dabei wird Permutierbarkeit datenunabhängiger Operationen ausgenutzt, um die günstigste Anordnung zu finden. 5.5.3 Datenflussrechner und Datenflusssprachen Datenflussrechner stellen die Hardwareimplementierung des Datenflussprinzips dar. Sie nutzen die Tatsache, dass in den meisten Berechnungszyklen mehrere Operationen ausführbar sind, zu Parallelarbeit. Das Datenflussprinzip garantiert durch die Berücksichtigung der Datenabhängigkeiten eine folgerichtige Abarbeitung der Operationen. Datenflusssprachen stellen das Programmierparadigma dieser Rechner dar. Datenflussrechner
Datenflussrechner sind entsprechend des datenflussgetriebenen Bearbeitungsprinzips strukturiert. Sie weisen eine sogenannte Ringstruktur auf. Die einzelnen Komponenten arbeiten wie in einer Pipeline parallel und sind datenabhängig. Nachdem jede Einheit ihre Ergebnisse produziert hat, arbeitet die jeweilige Folgeeinheit auf den Ergebnissen weiter. Diese Pipeline ist für die wiederholten Berechnungszyklen in Datenflussrechnern ringförmig geschlossen. In Abb. 5.11 ist die Struktur eines Datenflussrechners dargestellt. Verbindungsnetzwerk
Rechenwerke
Speicher mit Suchfunktion Ein- und Ausgabe
Abb. 5.11 Die Architektur eines Datenflussrechners
182
Programmiersprachen und Paradigmen
Die Speichereinheit enthält alle Operationen und alle bekannten Operanden. Ausführbare Operationen (alle Operationen deren Operanden bekannt sind) werden über das Verbindungsnetzwerk an die Rechenwerke geschickt. Die Rechenwerke berechnen die Operationen und erzeugen neue Operanden. Diese Operanden werden dem Speicher zugeführt, der daraufhin neue ausführbare Operationen ermittelt. Die Ein- Ausgabeeinheit ermöglicht die Eingabe von Operanden, die Beobachtung neu erzeugter Operanden sowie die Ausgabe der Endergebnisse. Datenflusssprachen
Datenflusssprachen ermöglichen idealerweise die Codierung von Algorithmen unter Berücksichtigung des Datenflussprinzips. Diese akademische Anforderung an Datenflusssprachen wurde in den tatsächlich implementierten Sprachen jedoch nur teilweise verwirklicht. Sie sind eher durch die architekturellen Merkmale der Rechner geprägt, für die sie geschrieben wurden. Das heißt, sie enthalten Konstrukte, die spezielle Eigenschaften des Rechners unterstützen. Diese Tatsache hat zusammen mit der eher prozedural strukturierten Denkweise des Menschen dazu geführt, dass das Datenflussprinzip und die Datenflusssprachen als allgemeines Programmierparadigma kaum Anwendung gefunden haben. Die Basis der Datenflusssprachen bilden die Datenflussgraphen, die zur Darstellung von Algorithmen zusammen mit ihren Datenabhängigkeiten verwendet werden. Compiler für Datenflusssprachen übersetzen die Programme in Datenflussgraphen, die dann direkt auf Datenflussrechnern abgebildet werden können. Die Datenflussgraphen stellen in diesem Sinne eine Art Assembler der Datenflussrechner dar. In Abb. 5.12 ist ein Datenflussgraph zur Berechnung des Polynoms ax3+bx2+cx+d dargestellt. Aus dem Datenflussgraphen sind die Datenabhängigkeiten direkt abzulesen. Operationen, die nach diesem Schema in keiner Datenabhängigkeit zueinander stehen, sind parallel berechenbar.
x
a
b
u
c
d
u
u
u u ax3
cx
bx2
+
+ +
cx + d
ax3 + bx2 + cx + d Abb. 5.12 Datenflussgraph zur Berechnung eines Polynoms
Datenflusssprachen
183
Die wesentlichen Merkmale der Datenflusssprachen sind: x Datengetriebene Auswertungsreihenfolge Wenige Kontrollstrukturen Durch das datengetriebene Ausführungsprinzip erübrigen sich in vielen Fällen explizite Kontrollkonstrukte. Im besonderen kann auf einen Sequentialisierungsoperator (;) und auf Parallelisierungsoperatoren verzichtet werden. x Single Assignment Principle Dieses Prinzip besagt, dass einer Variablen nur zu Beginn ihrer Gültigkeit ein einziges mal ein Wert zugewiesen werden darf. Der Grund für dieses Prinzip wird in den Beispielen zur Datenflusssprache Id erläutert. x Kein Zugriff auf externe Variablen Durch den Ausschluss externer Variablenzugriffe wird die Seiteneffektfreiheit der Datenflusssprachen erreicht. Die folgenden Sprachen stellen die wichtigsten und bekanntesten Datenflusssprachen dar: x Id (Irvine dataflow) x Val (Value oriented algorithmic language) x Sisal (Streams and iterations in a single assignment language) Anhand der Sprache Id werden im folgenden einige Merkmale der Datenflusssprachen erörtert. Die wichtigste Struktur von Id ist der Block. Ein Block ähnelt den Blöcken der prozeduralen Programmiersprachen mit dem wesentlichen Unterschied, dass die Reihenfolge, in der die Operationen berechnet werden, nicht durch die Auflistung im Programmtext gegeben ist, sondern erst bei der Ausführung des Programms durch den Datenflussrechner festgelegt wird. Ein Block in Id wird wie folgt notiert: { Anweisung; Anweisung; Anweisung; finally x; }
Das Semikolon ; ist zwar zulässig, hat aber in Id keine Bedeutung als Sequentialisierungsoperator. Die Ausführungsreihenfolge der Anweisungen wird durch den Datenflussrechner anhand der Datenabhängigkeiten bestimmt. Der Block stellt einen Teilgraphen des gesamten Datenflussgraphen dar. Die Anweisung finally dient der Bestimmung der Variablen, die an nachfolgende (abhängige) Blöcke weitergegeben wird. Durch die automatische Ermittlung der Ausführungsreihen-
184
Programmiersprachen und Paradigmen
folge nach dem Datenflussprinzip wird an dieser Stelle auch die Notwendigkeit des Single Assignment Principle innerhalb eines Blocks deutlich. Beispiel Ein Block enthalte die Anweisungen a = b + c und a = x2 Beide Operationen seien datenunabhängig. Das Ergebnis beider Operationen, dass jeweils in die Variable einzutragen ist, ist dann ohne weitere Annahmen nicht eindeutig bestimmt, da keine Ausführungsreihenfolge vorgegeben ist. Die parallele Ausführung in einem geeigneten Rechner würde sogar zu einem Konflikt führen, da einer Variablen gleichzeitig unterschiedliche Werte zugewiesen werden können. Neben dem unbedingt auszuführenden Block enthält Id Schleifenblöcke und bedingt auszuführende Blöcke. Sie werden durch die Schlüsselwörter if, for oder while gekennzeichnet: { x=1 y=1 In { for j<-1 to n do next x = y next y = x + y finally x } }
Neben den Datenflusssprachen besteht die Möglichkeit der Anwendung der weitverbreiteten prozeduralen Sprachen wie C/C++ oder Pascal zur Programmierung von Datenflussrechnern. Dabei wurde prinzipiell davon ausgegangen, dass die Datenabhängigkeiten, die zur Erzeugung eines Datenflussgraphen notwendig sind, auch aus einer prozeduralen Beschreibung extrahiert werden können. Probleme bereitet dabei, dass weder von der Einhaltung des Single Assignment Prinzips, noch von der Seiteneffektfreiheit solcher Programme ausgegangen werden darf. Die Konflikte, die dadurch entstehen, können aber durch den Compiler erkannt und aufgelöst werden. Dieser Ansatz hat trotz des Mehraufwandes in der Konfliktauflösung verschiedene Vorteile: x Akzeptanz Durch die weite Verbreitung der Sprache im Bereich der Anwendungs- und Systemprogrammierung ist die Akzeptanz von Programmentwicklern gegeben.
Datenflusssprachen
185
x Vorhandene getestete Anwendungen Es existieren bereits zahllose Applikationen, die ohne die Notwendigkeit der Neuprogrammierung auf dem Zielrechner (in diesem Fall ein Datenflussrechner) zur Ausführung gebracht werden können. Beispiel Das folgende C-Programm { input double x; output double cosx; /* cosinus approximation */ cosx = 1 - x*x / 2 + (x*x)*(x*x) / 24 - (x*x)*(x*x)*(x*x) / 720 }
wird in den folgenden Datenflussgraphen übersetzt
x * * 2 /
* 24
/
720 /
1 – +
– out cosx
Abb. 5.13 Datenflussgraph eines C-Programms
Dieses Beispiel zeigt Möglichkeiten, durch geschickte Verwendung bereits durchgeführter Operationen (der Quadrierung von x) die Zahl der Rechenschritte zu reduzieren. Allerdings gibt es dabei ein wichtiges Problem: Der mit Datenflussarchitekturen nutzbare Parallelitätsgrad eines Programms hängt entscheidend von der implizit oder explizit parallel formulierten Algorithmus ab. Es ist deshalb wichtig, dass bereits bei der Spezifikation des Anwenderproblems auf unnötige Abhängigkeiten verzichtet wird und der Algorithmus dazu möglichst parallel formuliert wird. Die automatische Umformulierung von der sequentiellen zu einer parallelen Version
186
Programmiersprachen und Paradigmen
des Algorithmus kann dagegen heutzutage auch der beste Compiler noch nicht leisten. Die Verwendung einer graphischen Datenflusssprache kann diese Aufgabe entscheidend erleichtern. Literatur T. Ungerer: Datenflussrechner, Teubner Verlag, Stuttgart 1993 W.K. Giloi: Rechnerarchitektur, Springer Verlag, Berlin 1993, Kapitel 9 B. Rau: Data Flow and Dependence Analysis for Instruction Level Parallelism. In U.Naerjee, D. Gelernter, A. Nicolau, D. Padua (eds.), Languages and Compilers for Parallel Computing, Lecture Notes in Computer Science 589, S. 236, Springer Verlag, Berlin 1992
5.6 Logische Sprachen Eine der neueren Entwicklungen bei den Programmiersprachen und ihre Anwendung bei Expertensystemen und „künstlicher Intelligenz“ beruht auf den Entwicklungen in der Mathematik auf dem Gebiet der formalen Logik und Beweistheorie. In diesem kurzen Abschnitt soll keinesfalls versucht werden, eine vollständige Einführung zu geben; dies ist aus Platzgründen hier nicht möglich. Statt dessen wollen wir uns nur mit einer Übersicht begnügen, die uns einige grundlegende Begriffe näher bringen soll und das Verständnis für die Grundlagen logischer Programmierung, wie sie durch die weit verbreitete Sprache PROLOG möglich wurde, etwas verbessern. 5.6.1 Aussagenlogik Der erste Schritt für eine logische Programmierung besteht darin, die Gegebenheiten des betrachteten Gegenstandes in die Form logischer Aussagen zu bringen. Allerdings muss man an dieser Stelle besonders aufpassen. Beispielsweise sind die Aussagen A1=„Ich werde krank“ und A2=„Der Arzt verschreibt mir eine Medizin“ zwar richtig, aber die Zusammensetzungen A1 und A2 sowie A2 und A1 meinen in der natürlichen Sprache nicht das gleiche, obwohl sie logisch formal keinen Unterschied bedeuten. Die Ursache dafür liegt in einer impliziten zeitlich-kausalen Struktur, die wir in der Umgangssprache assoziieren und die nicht durch diese Abbildung auf logische Zusammenhänge erfasst wird. Die Verknüpfung von logischen Aussagen zu einem logischen Ausdruck oder Formel F lässt sich auch formal fassen. Bezeichnen wir die elementaren Aussagen als atomare Formel oder Literal im Unterschied zu den aus elementaren Aussagen (Propositionen) zusammengesetzten Formeln, so lassen sich Verknüpfungen folgendermaßen definieren: Seien F und G Formeln, so ist
Logische Sprachen
x UND (Konjunktion) FG x ODER (Disjunktion) FG x NICHT (Negation) F
187
ist eine Formel ist eine Formel ist eine Formel
Teilausdrücke einer Formel werden als Teilformeln bezeichnet. Beispielsweise sind alle Teilformeln der Formel (AB)C die Formeln A, B, C und AB. Die Bedeutung der Formeln (Semantik) erhalten wir, indem wir auf die Bedeutung, in diesem Fall den Wahrheitswert, der Einzelkomponenten zurückgehen. Bezeichnen wir mit {0,1} die Menge der Wahrheitswerte, so ist eine Belegung W: Do{0,1} eine Funktion, die jeder atomaren Formel (aus einer Teilmenge D aller atomaren Formeln) jeweils einen Wahrheitswert zuordnet. Damit haben die Formelsymbole auch einen Inhalt, den Wahrheitswert, bekommen. Die Belegung W+: Eo{0,1} der Formeln aus der Gesamtmenge E (mit DE) entspricht dann den bekannten, elementaren logischen Verknüpfungen von zwei Wahrheitswerten
W+ (FG) = ®1 ¯0
falls W + (F) 1 und W + (G ) 1 sonst
W+ (FG) = ®1 ¯0
falls W + (F) 1 oder W + (G ) 1 sonst
W+ (F) = ®1 ¯0
falls W + (F) sonst
0
Definieren wir noch die Belegung W+ einer Formel für eine atomare Formel A mit W+(A)´= W(A), so sind die Wahrheitswerte auch von nicht-atomaren Formeln definiert. Einige Definitionen und Bezeichnungen: x Enthält eine Belegung W einer Formel F Aussagen für alle Literale, so wird sie als „passend“ bezeichnet. Beispiel: F = AB, { W(A) = 1, W(B) = 0 } ist passend, { W(A = 1) } aber nicht. x Ist W passend und W(F) = 1, so ist „F gültig unter W“ oder „W ist ein Modell von F“. x Existiert mindestens ein Modell von F, so ist F „erfüllbar“, sonst „unerfüllbar“. x Sind alle möglichen passenden Belegungen auch Modelle, so ist F (immer) „gültig“ oder eine „Tautologie“. Beispiel: FF ist Tautologie. x Ist ein Modell von F auch immer ein Modell von G, so „folgt G aus F“.
188
Programmiersprachen und Paradigmen
Damit wird sogleich klar: F ist eine Tautologie jede passende Belegung ist ein Modell für F jede passende Belegung ist kein Modell für F es gibt kein Modell für F F ist unerfüllbar. Und damit: Folgt G aus F (FoG) ist Tautologie (FG) = (FG) ist unerfüllbar. Ein Test für die Richtigkeit der Folgerung G aus F ist also äquvalent zu einem Test nach der Unerfüllbarkeit von (FG). Aus der Frage: „Folgt G aus (F1..Fn)?“ wird also die Frage: „Ist (F1..FnG) unerfüllbar?“ Natürlich gelten hier auch die bekannten Vereinfachungsregeln der Propositionalen Logik sowie die Tatsache, dass wir logische Aussagen (und damit auch die Formeln) in konjunktiven (KNF) und disjunktiven Normalformen (DNF) darstellen können. 5.6.2 Hornformeln Es gibt eine wichtige Gruppe von Formeln, die nach dem Logiker Alfred Horn in Hornformeln benannt wurde. Bezeichnen wir eine nicht-negierte, atomare Formel als positives Literal und im Unterschied dazu die Negation als negatives Literal, so lässt sich die Gruppe der Hornformeln dadurch kennzeichnen, dass die Formel in KNF maximal ein positives Literal pro Disjunktionsglied enthält. Beispiele x Die Formel F = (AB) ist eine Hornformel. Sie ist auch als Implikation in der Schreibweise (AoB) bekannt: „Aus A folgt B“. Ihre Wahrheitstafel ist A B AB 0 0 1 Also: (0oB) ist Tautologie 0 1 1 (Ao1) ist Tautologie 1 0 0 (1o0) unerfüllbar 1 1 1 x Die konjunktive Normalform (KNF) F = (AB) (CAD) (AB) D E ist eine Hornformel. Mit der obigen Abkürzung lautet sie auch F = (BoA) (CAoD) (ABo0) (1oD) (Eo0) Wie kann man nun herausfinden, ob eine Formel erfüllbar ist? Ein Weg dazu besteht darin, für alle möglichen Belegungen (Wahrheitswerte der atomaren Formeln) ihren Wert herauszufinden. Jede Zeile einer solchen Wahrheitstafel enthält eine Belegung der Literale. Diese Methode ist allerdings für eine große Zahl von Literalen nicht zu empfehlen, da eine Wahrheitstafel mit n Literalen genau 2n Zeilen enthält und das reine Durchprobieren deshalb nur mit exponentieller Laufzeit (NP-vollständiges Problem!) durchgeführt werden kann. Statt dessen reicht es, solange die Teilformeln der konjunktiven Normalform zu vereinfachen, bis eine Teilformel gefunden wird, die unerfüllbar ist, beispielsweise der Form (1o0). Hier kann man getrost abbrechen, da dann, egal welchen Wahr-
Logische Sprachen
189
heitswert die anderen Teilformeln haben, die Gesamtformel unerfüllbar ist. Ein solcher einfacher Algorithmus ist der Hornformel-Erfüllbarkeitstest 1.) Wenn es die Teilformel (1oA) in F gibt, so markiere jedes Vorkommen von A 2.) WHILE Es ex. in F eine Teilformel G der Form1 (A1...AnoB) oder Form2 (A1...Ano0), n>1 mit A1...An ist markiert, aber B ist nicht markiert. DO IF G hat_Form1 THEN markiere jedes Vorkommen von B ELSE Ausgabe „unerfüllbar“, da (1o0). STOP. END IF END WHILE 3.) Ausgabe „erfüllbar“, STOP. Die erfüllende Belegung ist mit allen markierten Ai gegeben; hier ist W(Ai)=1. Wie man leicht sieht, sind mit n Literalen auch maximal n Markierungsschritte nötig, um den Algorithmus zu beenden, wobei die erfüllende Belegung durch markierte atomare Formeln gegeben ist. Auf den eigentlichen Beweis für die Korrektheit des obigen Algorithmus (s. Schöning) wird hier aus Platzgründen verzichtet. Die Frage, ob eine Formel erfüllbar ist, wird besser gelöst, indem man logisch äquivalent danach fragt, ob die negierte Formel unerfüllbar ist. Dies ist dann der Fall, wenn es zum einen keine Belegung gibt, die die Formel erfüllt (Korrektheit) und zum anderen, wenn jede Belegung die Formel nicht erfüllt (Vollständigkeit). 5.6.3 Klauseln Formeln in der Konjunktiven Normalform mit den Literalen Lij im i-ten Term F = (L11..L1n )..(Lk1..Lkn ) 1 k lassen sich auch anders schreiben. Die Tatsache, dass bei der „“ Verknüpfung keine serielle Ordnung vorgeschrieben ist und mehrfache Terme nur einmal geschrieben werden (Assoziativität, Kommutativität und Idempotenz) bleibt uns erhalten, wenn wir die Disjunktionsglieder als Mengen schreiben und die „“ Verknüpfung durch Kommata andeuten F = {{L11,..,L1n },...,{Lk1,..,Lkn }} 1 k Die Elemente einer solchen Familie heißen Klauseln.
190
Programmiersprachen und Paradigmen
Beispiel Die Formeln F1 = ((A1A2)(A3A3)) F2 = (A3(A2A1)) F3 = (A3((A2A2)A1)) besitzen alle die selbe Klauselmenge {{ A3},{A1,A2}} 5.6.4 Resolution Aus der Boolschen Algebra wissen wir, dass die Form (ab) (cb) zu a (bei b = 0) und zu c (bei b = 1) vereinfacht werden kann. Dies gilt auch entsprechend für die Formel F = (HA) (GA) der Aussagenlogik. Der Beweis dafür ist einfach: Sei A = 1, so ist A = 0 und mit G ist auch F erfüllbar. Ist andererseits A = 0, so ist A = 1 und mit H auch F erfüllbar. Also gilt insgesamt: F = (HA) (GA) erfüllbar (HG) erfüllbar. Dies lässt sich auch ausführlich mit der entsprechenden Wahrheitstafel zeigen Diese Vereinfachung der Formeln für die Erfüllbarkeit kann man auch auf Klauseln übertragen. Ist L ein Literal aus der Klausel K1 und das negierte Literal aus Klausel K2, so ist mit R = (K1-{L}) (K2-{L}) die um das Literal L reduzierte Klauselmenge gegeben; sie wird Resolvent genannt, der Vorgang selbst eine Reduktion. Man beachte übrigens: ist das Literal L in der obigen Formel eine negierte atomare Formel A, so ist L = A definiert. Bekanntlich ist die Formel F = (AA) unerfüllbar. Der Unerfüllbarkeit der Formel entspricht in der neuen Mengennotation mit Klauseln der Tatsache, dass die Klauseln {L} und {L} und damit über den Resolvent eine leere Menge ={} als Klausel vorhanden ist. Eine Klauselmenge, die also enthält, wird als „unerfüllbar“ definiert. Der Resolutionssatz der Aussagenlogik besagt nun inhaltlich, dass eine Klauselmenge F genau dann unerfüllbar ist, wenn als Element der Resolutionen auftritt, die auf F (auch hintereinander ausgeführt) möglich sind. Übersetzen wir Hornformeln in die Klauselschreibweise, so erhalten wir Hornklauseln. Es lässt sich zeigen, dass für diese Klasse ein eingeschränkter Resolutionskalkül immer noch vollständige Lösungen liefert. Hierbei wird nur dann ein Resolvent aus zwei Klauseln gebildet, wenn mindestens eine der beiden Klauseln nur aus einem einzigen Literal besteht (Einheitsresolution). Bei einer solchen Resolution entstehen immer nur kürzere Klauseln, so dass dadurch ein effizienter Resolutionsalgorithmus entsteht, der analog zum Markierungsalgorithmus abläuft.
Logische Sprachen
191
5.6.5 Prädikatenlogik Bisher haben wir nur von einfachen Wahrheitswerten gesprochen. In der Mathematik gibt es aber noch wichtige Beziehungen über die Anzahl von Einheiten, die in unsere Überlegungen miteinbezogen werden sollten. Beispiel: Für alle H > 0 existiert ein n0, so dass für alle n> n0 gilt |f(n)-a| < H Dies lässt sich auch folgendermaßen formulieren: (H mit P(H,0) = WAHR) und (n0 mit P(n,n0) = WAHR P(H,g(n)) = WAHR), wobei P(a,b) = (a > b) und g(n) = |f(n)-a|. Vergleichen wir diese Schreibweise mit unserer bekannten Aussagenlogik, so fällt uns auf, dass es neue Elemente bei diesen Aussagen gibt. Zum einen gibt es nun numerische Funktionen, z.B. g(n), zum anderen hängen die logischen Aussagen, z.B. P(a,b), von Funktionen und Variablen ab; sie machen Aussagen über diese. Aus diesem Grund werden sie Prädikate genannt; zusammen mit den syntaktischen und semantischen Regeln formen sie die Prädikatenlogik. Ein wichtiges neues Element fehlt aber noch. Betrachten wir nochmals das obige Beispiel. Allgemein können wir dort ausdrücken, dass „eine Beziehung F(x) für alle x“ wahr ist oder „es existiert mindestens ein x“ für die F(x) wahr ist. Abgekürzt wird dies mit „xF(x)“ und mit „xF(x)“. Dies lässt sich auch für unsere aussagenlogischen Formeln einführen. Die Symbole werden mit Allquantor und Existenzquantor bezeichnet. Formeln, die von solchen quantisierten Argumenten abhängig sind, nennen wir Prädikate 1. Ordnung und die syntaktische Umformung solcher Formeln das Prädikatenkalkül 1.Ordnung. Lassen wir die Quantisierung weg, so wird F(x) zu einer einfachen Formel unserer Aussagenlogik: diese ist kann man also auch als Prädikatenlogik 0. Stufe ansehen. Die Prädikatenlogik 2. Stufe bezieht noch zusätzlich die Prädikate selbst mit in die Quantisierung ein, z.B. bei der Form „xF F(x) = 0“ In der Prädikatenlogik haben wir also einfache Variable (z.B. x), Funktionen (z.B. f(x)) und logische Prädikate (z.B. P(f(x))). Damit lassen sich die Terme der Prädikatenlogik definieren x Jede Variable ist ein Term x Seien t1,..,tk Terme und f ein Funktionssymbol, so ist f(t1,..,tk) ein Term. sowie die Formeln der Prädikatenlogik x Seien t1,..,tk Terme und P ein Prädikatensymbol, so ist P(t1,..,tk) eine (atomare) Formel x F ist Formel F ist Formel x F,G seien Formeln (FG) und (FG) sind Formeln x x ist Variable, F ist Formel xF und xF sind Formeln Beispiel: F = ( x1P5(x1,f2(x2))x2P4(x2,f7(f4,f5(x3))) ) ist eine Formel. Da Algorithmen für die Erfüllbarkeit und Gültigkeit von allgemeinen prädikatenlogischen Formeln nicht existieren (können) und die Prädikatenlogik der 1.Stufe für die meisten mathematischen Probleme ausreichend ist, wollen wir auf die noch
192
Programmiersprachen und Paradigmen
nicht vollständig erforschten weiteren Stufen verzichten und uns auf die 1.Stufe beschränken. Aber auch hier gibt es noch jede Menge Probleme. Im Unterschied zu der Aussagenlogik sind nicht endlich viele Belegungen, sondern Strukturen Gegenstand unserer Betrachtung. Beispielsweise kann man für rekursiv unendliche Strukturen nur unendliche Modelle als Lösungen angeben, die prinzipiell nicht durch Durchprobieren aller Zustände herleitbar sind. Der Resolutionssatz der Prädikatenlogik besagt ähnlich dem der Aussagenlogik, dass eine Klauselmenge F genau dann unerfüllbar ist, wenn als Element der Resolutionen auftritt, die auf F* (auch hintereinander ausgeführt) möglich sind. Dabei ist allerdings F eine Aussage in „Skolemform“ (enthält nur Allquantoren, keine Existenzquantoren; lässt sich mit mehreren Schritten herstellen) und F* die „Matrix“ von F (F ohne Quantoren). Dies ist etwas umständlich. Enthält die Klausel mehr als ein positives Literal, so kann die Antwort nicht eindeutig sein, sondern nur mehrere mit „oder“ verbunden vorliegen. Um aber für eine Frage eine klare Antwort zu bekommen, beschränken wir uns aus diesem Grund auf eine einfache, leicht zu behandelnde Klasse von Prädikaten: den Hornschen Klauseln. 5.6.6 Beispiel: Affe-Banane-Problem Ein einfaches Beispiel einer Resolution mit solchen Klauseln ist das „AffeBanane-Problem“, das hier modernisiert als „Roboter-Glühlampe-Problem“ beschrieben sein soll. Hierbei haben wir einen Roboter, der eine Glühlampe auswechseln soll und dazu auf eine Kiste steigen muss. Aus dem Wissen über die Tatsachen soll nun logisch die Handlungsreihenfolge des Roboters erschlossen werden. Mit dem Prädikat P(Roboterposition, Glühlampenposition, Kastenposition, Zustand) werden die Tatsachen dargestellt, wobei Folgerungen (Implikationen) durch die Form „G folgt aus F“ bzw. (FoG) bzw. (FG) bzw. {F,G} dargestellt werden. 1. {P(a,b,c,d)}
a,b,c,d sind die konstanten Startpositionen.
2. {P(x,y,z,s), P(w,y,z,walk(x,w,s))} Ist er an der Position x im Zustand s, so kann der Roboter sich mit walk() nach Position w bewegen. 3. {P(x,y,x,s), P(w,y,w,push(x,w,s))} Ist er an der Position x im Zustand s, so kann der Roboter den Kasten mit push() nach Position w bewegen. 4. {P(x,y,x,s), P(x,y,x,climb(s))} Ist er an der Position x im Zustand s, so kann der Roboter auf den Kasten steigen.
Logische Sprachen
193
5. {P(x,x,x, climb(s)), Reach(grasp(climb(s)))} Ist er an der Position x auf dem Kasten, so kann der Roboter durch Zufassen die Glühbirne erreichen. Die Frage ist nun: Wie erreicht er die Glühbirne? In der Syntax des Prädikats Reach(z) ist dies z Reach(z) ? 6. oder: {Reach(z), Antwort(z)} Die Antwort folgt aus dem Zustand z, für den Reach(z) = WAHR ist. Die Resolution erfolgt wie folgt: Resolvent aus 6. und 5. {P(x,x,x,climb(s)), Reach(grasp(climb(s))) }, {Reach(grasp(climb(s))),Antwort(grasp(climb(s))) } mit Variablenbenennung (Unifikation): zo grasp(climb(s)) F1 = { P(x,x,x, climb(s)), Antwort(grasp(climb(s))) } Resolvent aus F1 und 4. { P(x,x,x, climb(s)), Antwort(grasp(climb(s))) P(x,y,x,climb(s))} mit Unifikation yox F2 = { P(x,x,x, s), Antwort(grasp(climb(s)))}
},
{P(x,y,x,s),
Resolvent aus 3. und F2 {P(x,y,x,s), P(y,y,y,push(x,y,s))}, { P(y,y,y, s), Antwort (grasp(climb(s))} mit Unifikation w o y bzw. x o y F3 = { P(x,y,x, s), Antwort(grasp(climb(push(x,y,s))) } Resolvent aus 2. und F3 { P(z,y,z, walk(x,z,s)), Antwort(grasp(climb(push(z,y, walk(x,z,s)))) }, {P(x,y,z,s), P(z,y,z,walk(x,z,s))} mit Unifikation xoz bzw. woz und so walk(x,z,s) F4 = { P(x,y,z, s), Antwort( grasp( climb( push(z,y,walk(x,z,s)))) } Resolvent aus 1. und F4 { P(a,b,c,d), Antwort( grasp( climb( push(c,b,walk(a,c,d)))) }, {P(a,b,c,d)} mit Unifikation xoa, yob, zoc, sod F5 = {Antwort(grasp(climb(push(c,b,walk(a,c,d)))) } Damit ist die Antwort klar: Um die Glühbirne zu erreichen, muss der Roboter aus der Startsituation zuerst von a nach c gehen, dann die Kiste von c nach b schieben, auf die Kiste steigen und die Glühbirne fassen.
194
Programmiersprachen und Paradigmen
5.6.7 Logikprogramme Im obigen Beispiel beim Roboter sahen wir, dass eine Antwort mit einem einfachen Resolutionsmechanismus zu erreichen war. Dies ist der Grundmechanismus, der bei logischen Programmen, insbesondere der Programmiersprache PROLOG, verwendet wird. Hier gibt es nur Klauseln der Form {P, Q1, ..,Qn} (Hornklauseln), die als Prozedurklauseln bezeichnet werden. Ein komplettes Logikprogramm besteht zum einen aus Tatsachenklauseln oder Fakten, die positive Atomformeln darstellen, und zum anderen aus den Prozedurklauseln. Die Klauseln werden solange der Resolution unterworfen, bis entweder die leere Menge oder aber die Antwort (Klausel erfüllt) erscheint. Syntaktisch wird die Implikation als Klauselmenge {P, Q1, ..,Qn} oder in aussagenlogischer Schreibweise (P(Q1..Qn)) bzw. (PmQ) mit Q = (Q1..Qn) in PROLOG durch P:- Q1,..,Qn geschrieben, wobei die Kommata UND Verknüpfungen sind und das Symbol „:-“ einen Pfeil m bedeutet. Die Abarbeitung der Frage nach allen Variablenwerten, so dass P folgt, wird im interaktiven Dialog des Interpreters als Zielklausel ?- Q1,..,Qn formuliert und kann auch als Prozeduroder Funktionsaufruf aufgefasst werden. Hier ergibt sich eine interessante Parallele zur funktionalen Programmierung: da das Programm als Menge von Hornklauseln nicht sequentiell strukturiert ist, kann wie bei der funktionalen Programmierung die Abarbeitung auch parallel erfolgen. Sequentielle Abfolgen resultieren nur aus der inhaltlich-funktionalen Abhängigkeit und nicht aus dem zugrunde liegenden von-Neumann Rechner. Damit ist auch die logische Programmierung ein guter Ansatz für eine parallele Programmiersprache. Dabei gilt aber die Einschränkung, dass dem Programmierer trotzdem die Kontrolle über die Ausführung möglich sein muss. Betrachten wir zum Beispiel die Zielklausel ? - read(X), bearbeite(X,Y), write(Y). so erwarten wir, dass zuerst die Eingabe X eingelesen, dann bearbeitet und dann als Y ausgegeben wird und nicht alles durcheinander. Gerade die Bearbeitung von Text (Zeichenketten) ist ziemlich sensibel in dieser Hinsicht. Aus diesem Grund werden die Klauseln auf dem sequentiellen Rechner auch von links nach rechts sequentiell abgearbeitet. Spezielle Konstrukte wie der cut Operator können so in bestimmten Fällen bei rekursiven Aufrufen die sequentielle Bearbeitung der Argumentliste an einer Stelle abbrechen, einen Rücksprung bewirken und dabei unendliche Rekursionen verhindern. Allerdings sind für die logische Programmierung in der Anwendung auf breiter Front die gleichen Zweifel angebracht wie bei den funktionalen Programmiersprachen: Obwohl eine Spezifikation in PROLOG den Vorzug hat, auch ausführbar zu sein und somit der Konsistenzbeweis gleich mitgeliefert wird, ist damit noch lange nicht gesagt, dass die logische Spezifikation, die der Programmierer gemacht hat, auch den Absichten des Benutzers entspricht. Es ist nicht einfach, eine gute logi-
Visuelle Sprachen
195
sche Programmierung eines Problems durchzuführen und die dazu gehörende Datenbasis konsistent zu halten. Aus diesem Grund hat sich die logische Programmierung auch nicht in der Anwendungspraxis durchgesetzt. Nur als „Assembler“ in Expertensystemen, die die Programmierebene vom Benutzer durch Kommunikations- und Wissenserwerbsschichten verbergen, ist ein Zugang unproblematisch. Literatur U. Schöning: Logik für Informatiker; BI Wissenschaftsverlag, Mannheim 1987 W.F.Clocksin, C.S. Mellish: Programming in Prolog; Springer Verlag Berlin 1981 A. Thayse (Hrsg.): From Standard Logic to Logic Programming; J.Wiley&sons, 1988 M. Genesereth, N. Nilsson: Logical Foundations of Artificial Intelligence; Morgan Kaufmann Publ. Inc. 1987 D. Hofbauer, R. Kutsche: Grundlagen des maschinellen Beweisens; Vieweg Verlag 1989 W. Bibel: Automated Theorem Proving, Vieweg Verlag 5.7 Visuelle Sprachen Mit dem Aufkommen preiswerter, graphischer Bildschirme und der zunehmenden Komplexität der Anwendungen und damit auch der Programmierung kam immer mehr die Erkenntnis auf, dass man komplizierte Sachverhalte nicht unbedingt textuell beschreiben sollte, sondern durch graphische Repräsentationen, die am Bildschirm manipuliert werden, überschaubar und verfügbar gemacht werden können. Die visuelle Repräsentation eines Sachverhalts durch Ikone, beispielsweise die Schreibtischmetapher, ist ein wichtiger Schritt hin zur visuellen Programmierung. Box1:
Box2:
Höhe 30 Breite 40 Tiefe 40
Datenrepräsentation
Höhe 20 Breite 30 Tiefe 30
Datenveränderung
Graph. Editor
...
Datenobjekt
graph. Repräsentation
Abb. 5.14 Die Rückkopplung graphischer Programmierung
Das entscheidende Merkmal für visuelle Programmierung kommt aber erst hinzu, wenn man auch eine Rückkopplung zulässt, also eine Veränderung der graphi-
196
Programmiersprachen und Paradigmen
schen Repräsentation eines Objekts mithilfe von graphischen Editorfunktionen und damit eine entsprechend definierte Veränderung des Datenobjekts, siehe Abb. 5.14. In den folgenden Abschnitten soll dies näher dargestellt werden, ausgehend von der historischen Situation der Datenvisualisierung. 5.7.1 Visualisierung von Daten Eines der wichtigsten Mittel, um das Verständnis für die Ergebnisse von Computerrechnungen zu fördern, ist die Visualisierung der Daten. In der folgenden Abb. 5.15 sind links eine Zahlentabelle und rechts ihre Visualisierungen gezeigt.
Ost West Nord
1. Qrt 2. Qrt 3. Qrt 4. Qrt 20,4 27,4 90 20,4 30,6 38,6 34,6 31,6 45,9 46,9 45 43,9
100 80 60 40 20 0
Ost West Nord
4. Qrt 1. Qrt
3. Qrt 2. Qrt
1. 2. 3. 4. Qrt Qrt Qrt Qrt
Abb. 5.15 Visualisierung mit Torten- und Balkendiagrammen
Es gibt noch viele weiteren Mittel, Zahlengrößen zu visualisieren. Funktionsdarstellungen, Skalierung von Gegenständen (z.B. große und kleine Autos für PKW-Umsätze) und Zahlen (große Zahl = großer Wert) und dergleichen sind inzwischen oft benutztes Mittel. Wichtig ist diese Technik besonders bei der Darstellung wissenschaftlicher Ergebnisse und ihrer Zusammenhänge. Von Wetterkarten über die Belastung von Tragflächen zur Visualisierung von Molekülen: die visuelle Darstellung erleichtert nicht nur das Verständnis der Zahlenkolonnen, sondern ermöglicht auch über die visuelle Abstraktion neue Ansätze für Modelle und Experimente. Ein weiterer wichtiger Aspekt besteht darin, wenn man über die reine Darstellung der Daten auch eine Rückkopplung (Beeinflussung der Ausgangsdaten) zulässt. So lassen sich dann durch solche spread sheet-Arbeitsmethoden die inneren Abhängigkeiten von Daten bei einem Modell visualisieren. 5.7.2 Visualisierung von Programmen Die ersten Versuche, Programminhalt und Visualisierung miteinander zu verbinden, kann man in den Formatierungsprogrammen für Programmtext (pretty printer) sehen. Das visuelle Einrücken von Blöcken in Pascal beispielsweise ist ein einfacher Versuch, Visualisierungsmittel zur besseren Übersichtlichkeit einzusetzen. Weitere Mittel bestehen darin, Schlüsselworte (IF, WHILE,..) groß oder fett,
Visuelle Sprachen
197
Kommentare in einem anderen Zeichenfont darzustellen, Funktionen und Prozeduren durch Linien visuell abzutrennen, u.d.g.l. mehr. Andere wichtige Visualisierungsmöglichkeiten bei Programmen ist die Visualisierung der Syntax (Syntaxdiagramme), des Gültigkeitsbereichs der Variablen sowie des Daten und Kontrollflusses durch Flussdiagramme und NassiShneiderman Diagramme. Aber auch die optische Kapselung von Modulen (Blockstrukturen), die Abhängigkeitsbeziehungen von Modulen (IMPORT bzw. use-Abhängigkeiten), die Aufrufgraphen von Prozeduren und Funktionen sowie die Schichtendarstellungen von Basisprozeduren und darauf aufbauende, „höhere“ Prozeduren (beispielsweise bei Betriebssystemen) sind wichtige Programmvisualisierungen. 5.7.3 Visuelle Spezifikation Bei der Programmerstellung gilt eine wichtige Regel: je früher im Erstellungsprozess ein Fehler gefunden wird, um so weniger kostet es, ihn zu beheben. Deshalb sind die Anstrengungen der Programmiersprachen-Designer darauf gerichtet, alle möglichen Fehler durch Konsistenzprüfungen bereits vom Compiler finden zu lassen, also dem Benutzer erst gar keine Fehler zuzulassen. Für diese Philosophie ist ein wichtiger Schritt bei Programmdesign besonders interessant: die Spezifikation. Gelingt es, bei der Programmierung einen Unterschied zwischen der Spezifikation eines Programmpakets und seiner Implementierung nachzuweisen, so kann dies rechtzeitig vorher korrigiert werden. Werden spezielle Spezifikationssprachen, wie z.B. PROLOG verwendet, so kann mit einer ausführbaren Spezifikation bereits die innere Konsistenz der Spezifikation überprüft werden. Eine gute, der Wirklichkeit angepasste Spezifikation zu schreiben ist nicht einfach. Dies ist besonders schwierig, wenn parallele, konkurrente Aktionen zu einem Gesamtsystem zusammengebunden werden müssen. Hier hilft eine visuelle Spezifikation. Gegenüber der textuellen Spezifikation hat sie den Vorteil, dass die zweidimensionale Bildschirmfläche parallele Aktionen auch visuell parallel nebeneinander darstellen kann. Visuelle Spezifikationen lassen sich vor allem einsetzen bei x Definition von Grunddatentypen und Modellen Hier ist die Darstellung von Integer, Strings, stack, Bäume, Listen etc. gefragt. x Informationssystemen Der Zugriff auf Datenbanken kann so besser dargestellt werden. Dies wird vor allem genutzt, um die Integrität der Datenbasis zu garantieren und die Relationen zwischen den Mengen bereitzustellen. x Kontrolle von Prozessen Müssen der Ort und die Zeit von verschiedenen Daten koordiniert werden, beispielsweise um Realzeitprobleme zu lösen, so bewährt sich die visuelle
198
Programmiersprachen und Paradigmen
Darstellung der parallelen Transaktionen und der bedingten Ereignisse. Ein wichtiges Beispiel dafür ist die visuelle Spezifikation mit Petrinetzen. x Transformation von Daten Werden Daten als Datenstrom aufgefasst, wie dies beispielsweise bei den Datenflusssprachen üblich ist, so ist es sinnvoll, den Weg der Daten von der Eingabe zur Ausgabe zur Synchronisation der internen Datenströme zu visualisieren. Beispiele dafür bilden die Funktionen von Textformatierungsprogrammen, Parsern, Sortierprogramme, Rechtschreibprogramme etc. Aber auch eine Mischung aller Darstellungsformen ist möglich! Als Beispiel für eine visuelle Spezifikation ist in der folgenden Abbildung eine Spezifikation der Fakultätsberechnung mit Hilfe eines Petrinetzes gezeigt.
P1
f:=1 T1
P2
f:=f*k T2
k:=n T3
P3 P4
P5
n:=n-1 T4 P6
n>0 T5
T6 n=0
P7
P8
Abb. 5.16 Visuelle Spezifikation der Fakultätsberechnung
Hierbei sind die Zustände Pi als Kreise, die Übergänge (Transitionen Tj) als Pfeile mit Barrieren gezeichnet. Die Bedingung, unter der eine Markierung (Schwarzer Punkt) entlang einem Pfeil wandert und über eine Transition in den nächsten Zustand übergeht, ist jeweils daneben geschrieben. Eine Transition kann nur dann schalten, wenn an jedem Pfeil eine Marke zur Verfügung steht. Gehen mehrere Pfeile von einer Transition, so duplizieren sich die Marken. Der Mechanismus beginnt mit einer Marke im Startzustand P1 und endet im Endzustand P8. 5.7.4 Visuelle Programmierung In der Einleitung wurde schon angedeutet, dass die eigentliche visuelle Programmierung durch eine Einflussnahme der graphischen Repräsentation auf das Programmverhalten gekennzeichnet ist. Formal kann man dies definieren als „Benutzung bedeutungsvoller graphischer Repräsentationen im Prozess der Programmierung“ (Shu, 1988). Tatsächlich handelt es sich dabei um die Methode, graphische Objekte, z.B. Ikone oder andere selbsterklärende Symbole, auf dem Bildschirm zu platzieren. Diese graphische Anordnung, also die Nähe der Objekte
Visuelle Sprachen
199
zueinander, die Art der Objekte in gerade dieser Konfiguration usw., muss nun von einem graphischen Compiler in Programmcode übersetzt werden. Dies stellt große Anforderungen an den Compiler, um Mehrdeutigkeiten sicher aufzulösen und Fehler in der Eingabe zu tolerieren. Eine geeignete Definition der graphischen Programmiersprache, die einfach und eindeutig sein sollte, kann dabei helfen, diese Probleme zu vermeiden. Eine der möglichen Lösungen dafür ist das Programmierparadigma der Programmierung in Puzzleteilen. In der folgenden Abbildung ist ein solches Beispiel gezeigt, in dem ein PASCALProgramm aus hierarchischen Puzzleteilen angefertigt wird.
Abb. 5.17 Ein Pascal Programm in hierarchischen Puzzleteilen
Typisch dafür ist: x Alle Elemente (Blöcke) werden miteinander explizit verbunden, beispielsweise in Form eines Puzzles x Jedes System von Blöcken (Zusammenhängende Puzzleteile) kann selbst eigener Superblock werden x Es existiert eine zusätzliche Funktionalität der Verbindungen bzw. Standardblöcke, beispielsweise in Form von Pfeilen, Linien etc. Bei dem obigen Beispiel wird klar, dass eine direkte Repräsentation einer textuellen Programmiersprache im Verhältnis 1:1 nicht ratsam ist: Der Programmcode wird in visueller Repräsentation auch nicht klarer und übersichtlicher, da die Komplexität des Codes damit nicht verborgen ist. Im obigen Beispiel kann man die Zusammenfassung in Superblöcke auch im textuellen Fall durch spezielle Eigenschaften des Texteditors (z.B. Repräsentation von ganzen Prozedurdefinitionen nur mit einem speziellen Prozedurkopf etc.) erreichen.
200
Programmiersprachen und Paradigmen
Es ist deshalb besser, bei der visuellen Programmierung bewusst graphische Repräsentationen für komplexe, inhaltlich gerechtfertigte Modellgebilde, die als Einheit angesehen werden unabhängig von dem Programmcode, zu verwenden. Ein Beispiel dafür ist die Modellierung einer Autofabrik durch das IGS-System (Frauenhofer Institut IITB). In Abb. 5.18 ist ein graphisches Modul einer Karassorieweiche zu sehen. Dabei sind die Eingabepunkte als transparente Quadrate, die Ausgabepunkte als schwarze Quadrate gezeichnet. Im visuellen Editor werden die Module definiert (Beschriftet etc.) und dann miteinander verbunden.
Abb. 5.18 IGS-Modell „Autofabrik“: Weiche
Mehrere Module zusammen können wieder ein „Supermodul“ ergeben. Als Beispiel ist das obige Modul der Weiche innerhalb einer Lackierstrasse in der folgenden Abb. 5.19 zu sehen.
Abb. 5.19 IGS-Modell „Autofabrik“: Lackhalle
Das gesamte System ist so hierarchisch in Form einer Abstraktionspyramide strukturiert. Ein spezielles, dahinter liegendes Programmiersystem nimmt diese graphische Spezifikation/Implementation auf und setzt es in PEARL-
Visuelle Sprachen
201
Programmmodule um, die auf einem Simulationssystem auf korrektes Zusammenwirken getestet werden. Ein entsprechendes Laufzeitsystem sorgt dafür, dass die selben Programmmodule auch im „echten“ Industriekontext eingesetzt werden können. 5.7.5 Visuelles Training Die visuelle Programmierung war bisher nur eine statische Beschreibung eines Programms. Die Sprachprimitive oder Elemente (Ikone, Linien, Kästen, etc.) wurden mit Regeln darauf, der graphischen Syntax und Semantik, interpretiert oder von dem graphischen Compiler kompiliert. Diesen Ansatz versucht das visuelle Training (visual coaching) zu überwinden. Dabei zeigt der Benutzer mit Übungsdaten dem System, „wie es geht“, also welche Sequenzen graphischer Elemente welchen Operationen entsprechen. Das System wiederholt dann diese Operationen mit anderen Daten genauso. Allerdings gibt es ein großes Problem bei diesem Ansatz: das System ist meist nicht „intelligent“ genug, zwischen Beispielsdaten und Generalisierung zu unterscheiden. Beispiel
concat((a,b,c),(d,e,f)) = (a,b,c,d,e,f)
Aus diesem Beispiel soll das System schließen, wie man zwei Listen konkateniert. Allerdings ist dies nicht eindeutig: geben wir x,y,z und u,v,w ein, so kann auch nur {a,b,c,d,e,f } herauskommen - die Generalisierung erfordert zusätzliches Wissen Beim visuellen Training verwendet man meist die Methode, Ein-/Ausgabepaare anzugeben. Programmsequenzen werden nur für spezielle Einzeldaten angegeben, sollen aber allgemein gelten (generische Programmpfade). Problematisch an diesen Methoden ist die Notwendigkeit, unzweideutige Beispiele zu finden, die außerdem noch vollständig für alle Situationen gelten sollen. Die Ansätze, derartige Probleme zu lösen, stützen sich beispielsweise auf induktive Inferenz (automatisches Programmieren durch AI). Der ursprüngliche, intuitive Ansatz des visuellen Trainings, der ohne Methoden der künstlichen Intelligenz auskommt, ist deshalb auf einen kleinen, überschaubaren Problemkreis beschränkt. Er besteht meist aus den Schritten x Einfaches Abspeichern der Befehle (Start/Befehl 1,..,Befehl n/Stop) z.B. durch Speichern der Tasten- und Menücodes x Zusätzliche Abfrage bei Unklarheiten Abgespeichert werden Befehls- und Entscheidungsdaten x Schachtelung von Sequenzen
202
Programmiersprachen und Paradigmen
Ein Beispiel für ein derartiges System ist das „Programming by Rehearsal“, bei dem der Computerbildschirm als Theaterbühne aufgefasst wird. Die „Schauspieler“, die dabei auftreten, sind vorher definierte Einheiten, die bestimmte Eigenschaften haben. Das gesamte Schaustück (Programm) wird als „Rollenspiel“ festgelegt, bei dem sich die Schauspieler bestimmte „Stichworte“ zurufen und das aufgezeichnet wird. Diese Sichtweise entspricht der nachrichtenbasierten Prozesskommunikation. Zusätzlich zu der „graphischen Mannschaft“ (GraphicTroup), die Bildhintergrund, Richtungen, Positionen etc. bereitstellen, und den „Grundschauspielern“ (BasicTroup) wie Text, Zahlen und Zähler, auch die Regiekontrolle (ControlTroup), die Zeiger, Listen, Repeater, Wahr/Falsch Entscheidungen zur Verfügung stellt. Interrupts und Zeit werden von der Zeitgruppe (TimeTroup) sowie die Debug-Funktionen von der DebugTroup (Step/Run) ermöglicht. 5.7.6 Diskussion Die bisherige Darstellung und Diskussion visueller Programmierung hat gezeigt, dass dieses Programmparadigma nur eine logische Erweiterung bestehender Ansätze darstellt. Vorteilhaft bei diesem Ansatz ist, dass vor allem die parallele Programmierung sehr intuitiv und benutzerfreundlich gestaltet werden kann. Allerdings lässt sich damit eine bestehende Komplexität des Problems nicht lösen, sondern nur vor dem Benutzer verbergen. Man kann deshalb die Tendenz beobachten, dass die visuelle Programmierung immer interessanter, potenter und überzeugender wird, je komplexer - und damit je spezieller - die hinter den graphischen Repräsentationen verborgene Anwendung ist. Versucht man aber, die graphische Programmiersprache so allgemein wie möglich zu machen (s. das PASCAL-Beispiel), so gewinnt man keine besonderen Vorteile gegenüber der textuellen Programmierung. Die grundsätzlichen Schwierigkeiten, mit denen der Designer einer visuellen Programmiersprache zu kämpfen hat, sind folgende: x Für das gegebene Problem muss eine adäquate Programmiermetapher gefunden werden. x Die graphischen Objekte sollen algorithmische Objekte repräsentieren. x Die graphische Sprache muss definiert werden. x Die Implementierung muss dies unterstützen. Hierbei müssen wichtige Details gut graphisch modelliert werden können (Bildschirmauflösung!), die animierten Programmteile müssen speicherbar sein und die verwendete Grafikmaschine muss für das Problem schnell genug sein, um die Programmiermetapher zu unterstützen. Dabei kann man aber auch auf Lösungsmethoden aufbauen:
Grundbegriffe zur Übersetzung von Programmiersprachen
203
x Wenn der zentrale Lösungsmechanismus in analoger Form in der Wirklichkeit bekannt ist, so kann man ihn graphisch danach modellieren. Beispiel: Für die Verwaltung von Daten kann man als Programmiermetapher die Vorgänge in einem Lagerhaus verwenden. Hier ist das Liefern, Einsortieren von Paketen, Lagerung, Aussondern etc. gut bekannt und als Referenz bei dem Benutzer vorhanden.
Objekte Textdateien Unterverzeichnisse vis. Modell (Ikon)
Behälter mit Büchern Regale mit Behältern Behälter mit Bild
Operationen Verzeichnis untersuchen Datei im Dateisystem abspeichern Datei löschen Datei kopieren
Durch das Lager gehen Behälter in einem Regal ablegen Behälter in den Müll werfen Behälter in eine Kopiermaschine legen
Abb. 5.20 Datenobjekte und visuelle Entsprechungen
Es gibt einfache, konsistente Beziehungen zwischen dem Modell und dem Algorithmus. In unserem Beispiel entspricht dies x Einfache Visualisierung, Vieldeutigkeit vermeiden z.B. durch Benutzung von Ikonen statt Symbolen x Aufbauendes Lernen (Komplexität verstecken) x Einbindung in vorhandene optimierte Grafikumgebungen (GKS, Windows, Motif..) Im Rahmen von graphischen Benutzerschnittstellen werden auch dialogorientierte, regelgesteuerte Zwischenschichten angeboten (User Interface Management System UIMS). Der visuellen Programmierung entspricht dabei die graphische Konfiguration fertiger Modulen der Anwenderlogik. Der Vorteil eines solchen UIMS liegt erfahrungsgemäß bei einer um den Faktor 5-10 schnelleren Programmentwicklung, besonders bei der Entwicklung von graphischen Benutzerschnittstellen selber. Die Methode der visuellen Programmierung ist verhältnismäßig jung und verspricht noch eine umfassende, weitere Entwicklung. Literatur S-K. Chang: Principles of Visual Programming Systems, Prentice-Hall Int., Englewood Cliffs, 1990 N-C. Shu: Visual Programming, Van Nostrand Reinhold Co, New York 1988
204
Programmiersprachen und Paradigmen
5.8 Grundbegriffe zur Übersetzung von Programmiersprachen Alle Programmiersprachen haben einige Dinge gemeinsam: eine Grammatik (Syntax) und eine Semantik. Die Grammatik gibt an, wie Sätze der formalen Sprache (Programmiersprache) gebaut sein können. Liegt nun ein Programm in einer Programmiersprache formuliert vor, so muss der Prozess der Spracherzeugung wieder rückgängig gemacht werden, um den Inhalt des Programms zu „verstehen“. Dazu identifiziert ein spezielles Programm, der Übersetzer, in einem ersten Durchgang (lexikalische Analyse) alle Namen (Bezeichner) von Daten und alle Schlüsselwörter wie „Program“, „Integer“ usw. Die so erkannten Terminalsymbole (token) werden nun mit den bekannten Produktionsregeln der Sprache in der syntaktischen Analyse zurück in NichtTerminalsymbole verwandelt. Mit dieser Darstellung wird nun in der semantischen Analyse die notwendigen Aktionen bestimmt, z.B. in der Sprache eines abstrakten Rechnermodells (Zwischencode), und diese dann in Sequenzen der Zielsprache (z.B. Maschinensprache) übersetzt (Codegenerierung). Dieser Code kann dann nochmals mit einem Optimierer so verbessert werden, dass er schneller läuft, beispielsweise indem man vorhandene Spezialprozessoren (Coprozessoren) nutzt oder Kombinationen von einfachen Anweisungen durch ebenfalls mögliche, aber schnellere, komplexere Anweisungen ersetzt, oder aber die Daten besser zusammenpackt um den Speicherbedarf des Programms zu verringern. Ist der Unterschied zwischen der Programmiersprache (Quellsprache) und der erzeugten Sprache (Zielsprache) groß, beispielsweise wie zwischen PASCAL und Maschinensprache, so wird der Übersetzer als Compiler bezeichnet; andernfalls spricht man von einem Translator oder Crosscompiler. Führt das Übersetzungsprogramm beim Einlesen das Programm bereits Zeile für Zeile aus, so spricht man von einem Interpreter. Da ein Interpreter bei Schleifen jede Anweisung immer wieder neu übersetzen muss, ist er meist langsamer als ein Compiler; dafür biete er aber meist die Möglichkeit, die Werte aller Variablen bei der Programmausführung anzuzeigen und interaktiv zu verändern, was für Zwecke der Fehlersuche sehr praktisch ist. Um die Ausführung zu beschleunigen, kann man auch jede Zeile, die interaktiv eingegeben wird, zu einem bereits compilierten Programm hinzufügen. Übersetzer, die solche kleinen Veränderungen zulassen, heißen inkrementelle Compiler. Übersetzung und Abarbeitung der Programme benötigen unterschiedliche Zeiten auf einem Rechner. Um sie zu unterscheiden, spricht man deshalb von Compilezeit für die Übersetzung und Laufzeit für die Abarbeitung. Alle Programme haben Abschnitte (wie z.B. für Fehlerbehandlung, Maschineninitalisierung etc.), die grundsätzlich vom Compiler zu jedem Programm (vom Programmierer unbemerkt) hinzugefügt werden. Diese Abschnitte werden als Laufzeitsystem bezeichnet. Darüber hinaus gibt es Unterprogramme für Ein- und Ausgabe, Zeitabfrage etc., die standardmäßig vom Compilerhersteller dem Programmierer zur Verfügung
Grundbegriffe zur Übersetzung von Programmiersprachen
205
gestellt werden und Laufzeitbibliothek genannt werden. Ihre Funktionen müssen im Unterschied zum Laufzeitsystem explizit vom Programmierer aufgeführt werden.
6 Softwareentwicklung
Für die rechnergestützte Automatisierung kann man drei verschiedene Begriffswelten unterscheiden: x Die Begriffe des Anwenders (Kunden, Auftraggeber) aus ihrem Problembereich, wie z.B. Adressen, Kunden, Konten, Bilanzen, Waren ... x Die Begriffe der Projektentwickler aus der Datentechnik, wie z.B. Stack, Datenbanken, Suchalgorithmen, Ausnahmevektoren .... x Die Begriffe des Projektmanagements der Projektorganisation, wie z.B. Meilensteine, Phasen, Mannjahre, Schulung In den vorigen Abschnitten lernten wir vorzugsweise die technischen Aspekte der Programmentwicklung kennen. In den folgenden Abschnitten sollen nun auch die Begriffe und Probleme des Managements und der damit verbundenen Softwaretechniken näher dargestellt werden. Nicht selten entscheiden sie in der Praxis über Wohl und Wehe eines Programms und nicht die Programmiermodelle oder Algorithmen.
6.1 Das klassische Phasenmodell In der Sicht des klassischen Projektmanagements [Zehnder86] lässt sich ein Projekt in Funktionsabschnitte (Phasen) unterteilen, deren zeitliche Abschnittsmarkierungen (Meilensteine) in Form von Reports und Entscheidungen über den weiteren Projektverlauf besondere Besinnungs- und Koordinationsmomente darstellen. In Abb. 6.1 ist dies schematisch dargestellt; die Meilensteine sind als dunkle Vierecke eingezeichnet. Dieses Modell geht davon aus, dass aus einer Idee nach einer losen Ideenskizze ein Projekt entsteht. Die eigentliche Entscheidung, ob das Projekt auch verwirklicht wird, muss erst nach einer detaillierten Vorstudie, die verschiedene Alternativen und eine Kosten/Nutzenabwägung enthält, getroffen werden. Nach einem positiven Entscheid wird eine Spezifikation ausgearbeitet, eine Programmierung festgelegt (z.B. ob Eigen- oder Fremdprogrammierung). Die Methoden, aus einer Anforderung über Spezifikation und Programmstrukturierung eine Anwendung zu erstellen, wird als „software engineering“ bezeichnet.
208
Softwareentwicklung
Ideen, Vorabklärungen
Projektumriß, Konzepte, Varianten
Spezifikation Programmierung SystemFestlegen d.Schnittstellen test Rahmenorganisation
Dokumentation, Projektbeginn
Betrieb Einführung
Wartung
Qualitätssicherung
Projektentscheid
Projektübergabe
Zeit
Abb. 6.1 Das Phasendiagramm
Parallel dazu wird die Organisation zur Bereitstellung der nötigen Testdaten und der Umstellung der zu automatisierenden Arbeitsabläufe auf EDV eingeleitet. Dies erfordert nicht nur Überzeugungsarbeit bei den betroffenen Mitarbeitern, sondern auch teure Umschulungsmaßnahmen. Nach einem Systemtest wird das System vom Auftraggeber abgenommen, d.h. der Auftraggeber bestätigt, dass das erstellte Softwaresystem seinen Anforderungen entspricht. Mit einer Einführung der daran arbeitenden Mitarbeiter wird das Projekt dem Auftraggeber übergeben und für die Entwickler damit vorerst abgeschlossen. Eine Wartung währende des Betriebs hilft, auftretende Fehler zu beseitigen und kleineren Änderungswünschen zu genügen. Die Dokumentation während des Gesamtprojekts soll es erleichtern, einmal getroffene Entscheidungen nachzuvollziehen und bei Fehlentscheidungen den richtigen Rücksetzpunkt zu finden. Die Qualitätskontrolle hilft, das entstehende Produkt benutzerfreundlich , betriebssicher und dauerhaft funktionieren zu lassen. Man beachte, dass die eigentliche Programmierarbeit nur einen geringen Teil der Gesamtkosten (ca. 5-15%) verursacht. Die Hauptaufgaben des Projektmanagements sind dabei x Kalkulation der Projektkosten sowie der Projektdauer in der Planung und Angebotserstellung x Aufstellung des Projektplans einschließlich der Meilensteine x Vertretung des Projekts gegenüber dem Auftraggeber, z.B. Aushandeln und Fortschreiben der Projektziele und Vereinbarungen x Auswahl, Ausbildung und Einweisung der für das Projekt einzusetzenden Mitarbeiter x Auswahl, Beschaffung und Betreuung aller für das Projekt benötigten Werkzeuge und Hilfsmittel x Aufteilen der Projektziele und -verantwortungsbereiche unter den Projektmitarbeiter: Aufbau einer dafür geeigneten, internen Projektorganisation
Das klassische Phasenmodell
209
x Festlegung, Einführung und Anpassung der für die Projektdurchführung anzuwendenden Prozesse und Methoden x Erfassung der aufgelaufenen Kosten, Ermittlung des Kosten/Nutzen Verhältnisses, Kontrolle des Projektfortschritts, Erstellen von Berichten x Anerkennung, Entlohnung und Förderung der Mitarbeiter Diese Sicht der Projektentwicklung geht von einem einfachen, klaren Weltbild aus: alle (mindestens die Leitung) weiß, was sie wollen und müssen es nur noch vernünftig strukturiert durchführen.
Auftraggeber Projektleitung Anwender- Entwicklerleitung leitung Projektteam Abb. 6.2 Organigramm der Projektorganisation
Dies ist in der Realität aber meist nicht gegeben. In dem Phasenmodell wird deshalb die hierarchische Struktur Auftraggeber o Projektleitung o Projektteam um eine Anwenderkomponente erweitert. Weitergehende Maßnahmen verlangen allerdings eine Korrektur oder Wechsel des Entwicklungsparadigmas, wie wir später sehen werden. 6.1.1 Der Zeitplan Die Hauptphase des Projekts wird in einem Zeitplan in Unterphasen unterteilt. Die nötige Arbeitsleistung der Phasen wird traditionell mit Manntagen, Mannwochen etc. bezeichnet. Die Planung und Koordination der parallelen Aktivitäten wird bei großen Projekten mit Hilfe der Netzplantechnik, bei kleineren Projekten mit Balkendiagrammen visualisiert, s. Beispiel Projektinitialisierung in Abb. 6.3
210
Softwareentwicklung
Ideen abklären Konzepte & Varianten Detailspezifikation Literaturstudium Zeit
Projektentscheid
Abb. 6.3 Balkendiagramme
Eine Unterteilung in parallel ausführbare Arbeitseinheiten ermöglicht eine Entkopplung der Arbeiten und verhindert eine Blockierung von Arbeiten durch Warten auf den Abschluss von anderen. Allerdings ist dann ein zusätzlicher Synchronisierungsaufwand der parallelen Aktivitäten notwendig, so dass man die Vor- und Nachteile bei der Phasenplanung und Arbeitsaufteilung gut austarieren muss. Ein wichtiger Punkt, der oft vernachlässigt wird, ist die Ausarbeitung von mehreren (2-3) Varianten des Lösungskonzepts, die sich durch unterschiedlichen, geschätzten Aufwand von einander abheben. Dabei sollte die automatische Lösung, um in der Praxis bestehen zu können, mit der besten, konventionellmanuellen Lösung konkurrieren können. Es ist eine gute Übung, als eine der Lösungsvarianten eine mögliche optimierte, manuelle Lösung selbst zu entwerfen. 6.1.2 Phasen und Netzplantechnik Das Phasenmodell kann man noch durch zusätzliche Nebenbedingungen verfeinern, etwa durch die Notwendigkeit, parallele Phasen zu koordinieren. Hierbei muss man zunächst eine Liste aller Arbeiten und ihrer Zeitdauer aufstellen sowie als Nebenbedingungen die Nummer der Aktivitäten, die vorher abgeschlossen sein müssen, sowie die damit verbundnen Meilensteine davor als Abschlusspunkte solcher Abhängigkeiten eintragen. In Tabelle 1 ist dies an einem Beispiel aus 12 Aktivitäten (Phasen) gezeigt. Aktivität A1 Dauer 8 Abhängigkeiten Meilenstein
A2 15
A3 15 A1 M1
A4 10
A5 10 A2, A4 M2
A6 5 A1, A2 M3
A7 20 A1
A8 25 A4
M1
M4
A9 15 A3, A6 M5
Tabelle 1 Aktivitäten und ihre Abhängigkeiten
A10 15 A5, A7 M7
A11 7 A9
A12 10 A11
M6
M8
Das klassische Phasenmodell
211
Ein Meilenstein ist dann erreicht, wenn alle damit verbundenen Aktivitäten abgeschlossen sind. Aus einer solchen Aufstellung lässt sich ein Graph, genannt „Netzplan der Aktivitäten“, aufstellen. Dazu starten wir mit einem START-Zustand und fügen alle Aktivitäten an, die keine Voraussetzungen haben, im Beispiel A1, A2 und A4. Im zweiten Schritt folgen dann die Meilensteine, von denen die weiteren Aktivitäten abhängig sind sowie die Aktivitäten, die ausgeführt werden können, nachdem die Aktivitäten des ersten Schritts beendet wurden.
A1
Start
Start
A2
A4
A1
M1
A3
A2
M3
A7
A4
M2
A5
M4
A8
A6
Abb. 6.4 Erster und zweiter Schritt des Netzplanaufbaus.
In den weiteren Schritten wird fortgefahren, bis wir zum Ende kommen. Man beachte, dass dieser Graph keine in sich geschlossenen Wege (Zyklen) enthalten darf, da die Arbeiten nicht ausgeführt werden können, wenn sie (über mehrere Abhängigkeiten hinweg) von dem eigenen Abschluss abhängig sind. In Abb. 6.5 ist der vollständige Graph für das obige Beispiel gezeigt. Zusätzlich sind als Bewertung die Längen der Aktivitäten eingetragen.
14.7.03
Start
8 Tage
24.7.03
15 Tage
14.8.03
15 Tage
A1
M1
A3
M5
A9
15 Tage
A2 10 Tage
A4
4.8.03
M3 4.8.03
M2
M4 28.7.03
20 Tage
A7 10 Tage
A5
4.9.03
M6
5 Tage
M7
A11 15. 9.03
A6 18.8.03
7 Tage
M8 15 Tage
A10
A8 25 Tage
10
Tage
A12
Ende 29.9.03
Abb. 6.5 Der vollständige Netzplan der Aktivitätenliste
Mit der Kenntnis des Startdatums des Projekts (hier: 14.7.2003) und der Dauer der Einzelschritte können wir nun über jeden Meilenstein das Datum schreiben, an
212
Softwareentwicklung
dem er erreicht werden soll. Dabei müssen wir aber beachten, dass die Aktivitätsdauern in „Arbeitstagen“ angegeben sind und deshalb Sonn- und Feiertage zusätzlich einbezogen werden müssen. Erreichen wir einen Meilenstein oder das Ende auf mehreren Wegen, so erhält der Meilenstein das späteste Datum aller Wege dahin. Endet eine Aktivität bereits vor dem spätesten Datum, so kann man die Differenz als Toleranz interpretieren – es macht nichts für das Enddatum aus, wenn die Aktivität später endet. Unter allen Pfaden, die im Netzplan vom Start zum Ende führen, gibt es einen, der am Längsten dauert. Er wird als „kritischer Pfad“ bezeichnet. In unserem Beispiel besteht er aus der Folge Start-A1-M1-A3M5-A9-M6-A11-M8-A12-Ende. Dieser kritische Pfad legt die Länge des gesamten Projekts fest und ist besonders zu beachten. Wir können nun alle parallel ausführbaren, überlappenden Aktivitätsphasen übereinander in einem Balkendiagramm auftragen, siehe Abb. 6.6. Die Phasen starten an den frühesten ausführbaren Zeitpunkten, den Meilensteinen. Ihr spätestes Ende ist durch den zusätzlichen Toleranzzeitraum nach hinten erweitert. In Abb. 6.6 ist er hell schraffiert.
Abb. 6.6 Aktivitäts-Balkendiagramm des Beispiels
Die Überlappung der Aktivitätsphasen wird hier graphisch deutlich. Wollen wir nun mehrere Bearbeiter (Entwickler) den Softwareaktivitäten zuordnen, so sollten wir zuerst den schnellsten und zuverlässigsten heraussuchen und ihn oder sie den Aktivitäten des kritischen Pfades zuordnen. Alle anderen können dann derart den Aktivitätsphasen zugeordnet werden, dass zwischen ihren Meilensteinen möglichst viel Toleranzzeitraum existiert. Sie können dann den Beginn der Folgeaktivitäten passend innerhalb des Toleranzzeitraums nach hinten verschieben, um für sich eine lückenlose Bearbeitungsfolge zu erreichen.
Das klassische Phasenmodell
213
6.1.3 Randbedingungen Im Unterschied zu mathematisch klaren, exakt formulierten Nebenbedingungen bei numerischen Lösungen sind bei der Projektplanung die Randbedingungen nicht immer eindeutig. Beispiel Kauf eines Taschenrechners Der Wunsch nach einem preiswerten und leistungsfähigen Rechner kann verschieden entsprochen werden x ein Rechner, der maximal X Euro kostet und möglichst viele Funktionen hat x ein Rechner, der möglichst preiswert ist und mindestens eine Zahl Y von Funktionen hat Für jede der beiden, sehr ähnlichen Nebenbedingungen ergibt sich eine andere Lösung, siehe Abb. 6.7. Zahl der Funktionen
Y Lsg 1 Lsg 2
min Y
Preis max X Abb. 6.7 Optimierung der Kaufentscheidung
Für welchen Rechner wird man sich entscheiden? Betrachtet man nur die erste Variante, so ist die Entscheidung mit Lösung 1 klar. Vergleicht man sie aber mit der Lösung der zweiten Variante, so sieht man, dass eine nur geringfügige Formulierungsänderung der Nebenbedingungen einen Rechner ergibt, der bei einer Funktion weniger nur die Hälfte kostet. Manch einer wird sich deshalb die Neuformulierung der Zielvorstellung überlegen. Wie man am Beispiel sieht, sind deshalb „harte“ Nebenbedingung manchmal sehr „weich“, da sie einer direkten Interpretation unterliegen, und können durch ihre Kostenkonsequenzen geändert werden. Der hauptsächliche Unterschied der beiden Versionen im Beispiel liegt in der Unterscheidung, was unbedingt notwendig und
214
Softwareentwicklung
was nur wünschenswert ist. Dies ist sehr subjektiv und kann sich auch im Laufe des Projekts ändern, was dann eine kostenintensive Neuentwicklung auslösen kann. Man sollte deshalb auch von den Nebenbedingungen am Anfang mehrere Versionen berücksichtigen. 6.1.4 Das Pflichtenheft Eine wichtige Rolle bei der Softwareentwicklung spielen die Anforderungen der Auftraggeber an das Softwaresystem (Programmspezifikation). Sie werden meist in einem Pflichtenheft zu Beginn der Auftragserteilung festgelegt. Ein Pflichtenheft enthält x eine Beschreibung der Realität (Ist-Zustand) bezüglich der verwendeten Einund Ausgabedaten und Operationen (Mengengerüst). Dies enthält den Umfang der Daten mit - Name und Sinn des Datensatzes - Anzahl der Zeichen pro Datensatz - Anzahl der Datensätze (heute und max.) sowie die Häufigkeit der Operationen und Datenbewegungen (Transaktionen) - Name und Sinn der Operation - Art der Operation - Anzahl der Operationen pro Zeiteinheit (normal und max.) Eine gute Faustregel besagt, dass zur vollständigen Automatisierung einer Anwendung die komplizierten, aber wenigen Fälle (ca. 20%) den meisten Aufwand (80%) benötigen. Es ist umgekehrt deshalb besser, nicht alles perfekt automatisieren zu wollen, sondern sich auf die Massendaten (die 80%) zu beschränken, um mit wenig Aufwand (20%) ein akzeptables Aufwands/Ergebnisverhältnis zu erreichen. Dies entspricht auch der RISCMotivation aus Kapitel 1. Gutes Material für das Mengengerüst lässt sich meist aus existierenden Jahresberichten, Formularen, Bescheiden, Verzeichnissen etc. erschließen. x eine Darstellung der Zielvorstellungen und Anforderungen sowie der Randbedingungen und Bewertungskriterien möglicher Lösungsvarianten. x die organisatorischen Vereinbarungen zur Darstellung und Ablieferung der Lösungsvorschläge Es sollten grundsätzlich nur die wichtigsten Aspekte beschrieben und unwichtige Details weggelassen werden. Dabei muss man sowohl der Gefahr vorbeugen, wichtige Fragen offen zu lassen und auf später zu verschieben als auch zu viele Möglichkeiten („interessante Zusatzfunktion“) mit einzubeziehen und damit die Entscheidungsfindung und Entwicklung zu erschweren.
Das klassische Phasenmodell
215
6.1.5 Der Programmentwurf Bei der Programmkonzeption und -erstellung gehen wir grundsätzlich von den gewünschten Ergebnissen, also der Programmspezifikation, aus und unterteilen diese in unserem Implementierungskonzept in Teilfunktionen bzw. Unterprobleme. In Abb. 6.8 ist dies gezeigt. Die Unterteilung (Top-down Strukturierung) wird nach funktionellen und logischen Gesichtspunkten durchgeführt, wobei sowohl statische Gesichtspunkte (Unterfunktionen) als auch dynamische Kriterien (Teilabläufe) verwendet werden können. Für jede Funktion bzw. Programmteil (Modul) muss auch eine Schnittstelle spezifiziert werden. Gibt es bereits Datenstrukturen und Funktionen, die man verwenden kann und sich damit Arbeit erspart, so richtet sich die Unterteilung wesentlich nach diesen vorhandenen Programmmodulen und beeinflusst wiederum die oberen Gliederungsschichten. In diesem Fall wird die Unterteilung mit einem Prozess der Zusammenfassung zu höheren Funktionen (Bottom-up Entwurf) ergänzt.
Konzept Spezifikation Pflichtenheft Untergliederung Top-down
Rahmenbedingungen der Implementation
Funktion und Daten der Anwendung
Zusammenfassender Aufbau Bottom-up
Abb. 6.8 Untergliederung und Aufbau der Funktionen
Erst ein mehrmaliger Wechsel zwischen Top-down und Bottom-up Entwurf harmonisiert den Programmentwurf und korrigiert Inkonsistenzen und Spezifikationsabweichungen. Als Richtwert für die Anzahl der Funktionen einer Ebene lässt sich die Zahl der gleichzeitig von Menschen überschaubaren Fakten von 7-10 angeben. Die untersten Module sollten nicht mehr als 100 Codezeilen (ohne Kommentar) enthalten. Dabei wird der Entwurf auch wesentlich von den Rahmenbedingungen bestimmt. Hierzu zählen nicht nur die Notwendigkeiten der Funktionen und Kosten, sondern auch die vorgesehene Computerplattform mit ihrem Speicherausbau und Prozessorleistung.
216
Softwareentwicklung
Data Dictionary
Eine der einfachsten Techniken der Spezifikation besteht in der strukturierten Aufstellung aller involvierten Daten in einem Datenlexikon (data dictionary). Beispiel Kundendatei = {Kundeneintrag} Kundeneintrag = KundenNr + Name + Adresse + (Telefon) + (Arbeitgeber) KundenNr = 1..99999 * Datenelement* Name = Nachname + Vorname1 + (Vorname2) Adresse = [Straße + Hausnummer | Postfachnummer] + (Länderkennzeichen) + PLZ + Ort + (Zustellbereich) Notation: {a} = Wiederholung von a, 1{a}n = FOR 1 TO n DO a end; (a) =Option = 0{a}1 a + b = ungeordnete Sequenz, [a|b] = a oder b, *..* = Kommentar Entity-Relationship Diagramme
Die graphische Spezifikation der Zusammenhänge durch Objekte und Beziehungen zwischen Objekten kann als erster Ansatz einer formalen Spezifikation angesehen werden. In den Diagrammen existieren Einheiten (Objekte, Entitäten), zwischen denen Beziehungen (Relationen) existieren. Im erweiterten ER-Diagramm wird noch die maximale Anzahl der Entitäten pro Beziehung vermerkt. Beispiel Jeder Mitarbeiter kann 1 und maximal m Weiterbildungskurse besuchen (1:m Relation) und jeder Kurs kann von 1 und maximal n Mitarbeitern besucht werden (1:n Relation). Dabei ist ein Kurs eine Weiterbildung, aber nicht jede Weiterbildung ein Kurs. Dies ist in Abb. 6.9 vermerkt.
n Mitarbeiter
m besucht
Kurs 0
1 Anmeldedaten
zugeordnet 1 Weiterbildung
Abb. 6.9 Entity-Relationship Diagramm der Weiterbildung
Das klassische Phasenmodell
217
Dabei wird jeder Entität und Relation Attribute (Daten) zugeordnet, wobei ein Schlüsselattribut (z.B. MitarbeiterNr., SchulungsNr.) ausgezeichnet wird. Dies ist eine wichtige Vorstufe zur Datenmodellierung und Aufstellung einer Datenbank, s. (Flavin 1981 und Vetter 1987). Strukturierte Analyse
Es gibt verschiedene Arten von Entwurfstechniken für ein strukturiertes Programm. Die drei wichtigsten, kommerziell vertretenen sind die Strukturierte Analyse (SA), die Realzeitanalyse (RT) und die objekt-orientierte Analyse (OOA) um die Benutzeranforderungen strukturell zu gliedern und in Funktionen aufzuteilen. x Bei der strukturierten Analyse SA (McMenamin 88) werden Datenflusspläne hierarchisch gegliedert, s. Abb. 6.10 . Man geht von der Ebene der Ein- und Ausgaben an die Umwelt aus (Rechtecke im Kontextdiagramm) und unterteilt den darin enthaltenen Prozess bzw. Funktion (runder Kreis) der ersten Ebene in weitere Prozesse auf der zweiten Ebene, der Diagrammebene 0. Dies wird wiederholt bis für einen Prozess nur noch eine kleine Spezifikation (MiniSpec) als Pseudocode oder Entscheidungsbäumen bzw. -tabellen textuell eingesetzt wird. Kontextdiagramm
0 1
Speicher
4 2
Diagramm 0
3 2.3 2.2 2.1
Diagramm 2
MiniSpec 2.2
Abb. 6.10 Strukturierte Datenflussanalyse
Alle Daten werden dabei in Datenspeichern (Notation: parallele Linien oben und unten) abgelegt und dort ausgelesen. Alle Datenflüsse werden als Bezeichnungen in einem Lexikon (data dictionary) gesammelt und abgelegt. x Die Realzeitanalyse RT (Hatley87) erweitert die SA noch zusätzlich um Kontrollflussdiagramme. Im Unterschied zum Datenfluss bestehen die Kontrollsignale immer aus diskreten, bekannten Werten. Daten- und Kontrollflussdiagramme können zu einem allgemeinen Flussdiagramm kombiniert werden. x Bei der objektorientierten Analyse werden die Probleme auf ein Geflecht auf Objekte und ihre Beziehungen (entity-relationship Diagramm) abgebildet, wie wir dies bereits kennen gelernt haben (Coad90).
218
Softwareentwicklung
Jackson-Diagramme
Eine Visualisierung der Datenkonzepte kann auch mit Jackson-Diagrammen (Jackson 1983) erfolgen. Hierbei wird die Daten- bzw. Kontrollstruktur von oben nach unten und von links nach rechts ausgebreitet. In Abb. 6.11 ist dies für das data dictionary von oben gezeigt. Der gesamte Baum wird von oben nach unten und auf einer Ebene von links nach rechts als Sequenz von Anweisungen oder Sequenz von Daten durchlaufen. Repetitive Anweisungen, z.B. Schleifen, und Daten, z.B. Felder, sind mit einem * gekennzeichnet und bedeuten „beliebig oft“. Ein Kreis ° bezeichnet Alternativen, also CASE-Befehle für Anweisungen und in Datenstrukturen Kunden datei
Kunden * eintrag
Kunden nummer
Name
Adresse
Telefon
Telefon ° nummer
_
Firma
°
Arbeitgeber
_
°
Abb. 6.11 Jackson-Diagramm einer Datenstruktur
Entscheidungstabellen
Eine weitere wichtige Spezifikations- und Analysetechnik besteht in der Aufstellung von Entscheidungstabellen und -bäumen. Dies sei an einem Beispiel näher erläutert. Beispiel
Überlaststeuerung eines Roboterarmes
Zuerst formulieren wir uns die Bedingungen und Aktionen in normaler Sprache: x Wenn die Belastung normal ist, so soll weiterbewegt werden x Wenn eine Überlast existiert, aber diese kleiner als 1kp ist, so soll weiterbewegt werden
Das klassische Phasenmodell
219
x Wenn eine Überlast existiert und diese größer als 1kp ist, so soll nur noch langsam weiterbewegt werden x Wenn einen Bruchgrenze überschritten wird, so soll angehalten werden Dies kann man in einer Entscheidungstabelle aus Regeln niederlegen, wobei die Bedingungen mit „B“ und die Aktionen mit „A“ bezeichnet sind. In den rechten Spalten steht bei den Bedingungen jeweils ein „J“ für JA als Annahme, ein „N“ für NEIN (Bedingungszeiger); bei den Aktionen ein X für „erfüllt“ und nichts bei „nicht erfüllt“ (Aktionszeiger). R1 R2 R3 R4 B1 B2 B3 A1 A2 A3 A4
Last OK ? Überlast < 1kp ? Last > Limit ? weiterbewegen langsam bewegen stop unlogisch
J J J
J J N X
J N J
J N N
R5
R6 R7 R8
N J J
N J N X
N N J
N N N X
X X
X
X
X
Bei n Bedingungen gibt es also 2n Spalten, in diesem Fall 8 Spalten. Dies sind die Regeln. Diese Regeln lassen sich vereinfachen: Betrachten wir sie als Boolsche Ausdrücke, so lauten sie für die vorkommenden, also logischen Fälle als disjunktive Normalform B1B2B3 + B1B2B3 = A1 B1B2B3
= A2
B1B2B3
= A3
Der erste Ausdruck lässt sich zu B2B3 = A1 vereinfachen, so dass die vereinfachte Tabelle geschrieben werden kann als
B1 B2 B3 A1 A2 A3 A4
Last OK ? Überlast < 1kp ? Last > Limit ? weiterbewegen langsam bewegen stop Fehlermeldung
R1
R2
R3
N N J
J N X
N N N
sonst
X X X
Eine Entscheidungstabelle kann man aber auch anders schreiben:
220
Softwareentwicklung Last OK ? Überlast < 1kp ? J J N J N N
Last > Limit ? J N J N J N J N
weiter langsam bewegen
stop
Unlogisch X
X X X X X X X
Wie man sieht, ist dies nur die transponierte Version der ersten Tabelle, bei der zusätzlich die „J“ und „N“ Felder zusammengefasst sind. Durchläuft man diese Version von links nach rechts, so lässt sie sich auch als Entscheidungsbaum betrachten, dessen Knoten von den Bedingungen und dessen Blätter von den Aktionen gebildet werden. Die graphische Form sieht dann folgendermaßen aus: Überlast < 1kp
Last > Limit
unlogisch
Last d Limit
weiter
Überlast t 1kp
Last > Limit
unlogisch
Last d Limit
unlogisch
Überlast < 1kp
Last > Limit
unlogisch
Last d Limit
weiter
Überlast t 1kp
Last > Limit
stop
Last d Limit
langsam bewegen
Last OK
LastTest
Last OK
Abb. 6.12 Entscheidungsbaum zur Robotersteuerung
Für weitere Formen sei auf die Literatur (z.B. Strunz 1977) verwiesen. Programmformen
Prinzipiell lassen sich zwei Arten von Systemen für die Programmkonzeption unterscheiden: geschlossene, deterministische Systeme, die nur repetitive Tätigkeiten vorsehen und deshalb streng kanonisch komponierbar sind sowie offene Organisationen, bei denen nur Tätigkeiten vorkommen, die sich weder in der Art noch in der Reihenfolge wiederholen. Die Wirklichkeit liegt aber meist zwischen den beiden Extremen, so dass es kein Patentrezept für die Softwareerstellung gibt, sondern man nur je nach Anwendungsfall mehr oder weniger formale Methoden einsetzen kann.
Das klassische Phasenmodell
221
Ein wichtiger Spezialfall ist gegeben, wenn zwar die Arbeit aus stets wechselnden Reihenfolgen von Tätigkeiten besteht, aber jede Tätigkeit gut abgrenzbar ist und öfters als Einheit benötigt wird. In diesem Fall spricht man von einem reaktiven System. Dies bietet dem Benutzer als Assistent eine Palette von Möglichkeiten an, wobei die Reihenfolge und die Parameter allerdings dem Menschen überlassen werden. Jede Möglichkeit kann man als Werkzeug ansehen, mit dem der Benutzer seine Aufgaben löst, und wird separat als Einzelaufgabe programmiert. Bei der Programmkonzeption gibt es grundsätzlich drei verschiedene Möglichkeiten: x vollständige Übernahme eines speziellen Systems x teilweise Übernahme von Programmteilen x vollständige, freie Neuprogrammierung Im Regelfall ist weder die sehr teure Neuprogrammierung noch die sehr unflexible Übernahme einer speziellen HW/SW Kombination sinnvoll, sondern eine pragmatische Mischung aus Standardelementen und selbstprogrammierten Zusätzen. Beispielsweise gibt es als Bausteine Bürosysteme, die um Zusatzfunktionen erweitert werden können (z.B. dynamic link library dll zum Überladen in MS-Windows, Zusatzprozesse mit Kommunikation in Unix, etc.) und Funktionen aus Textverarbeitung, Tabellenkalkulation, Datenbankdienste, Statistikberechnungen, Netzdienste etc. ermöglichen. Dies gilt besonders für kleine und verteilte (PC) Anendungen. Konzipiert man die Programme so, dass sie fast nur aus Bausteinen akzeptierter Normen (Standardbausteinen größerer Hersteller) besteht, so vereinfacht (und verbilligt!) man nicht nur die Programmerstellung, sondern auch die Einarbeitung und die Wartung und damit letztendlich die Akzeptanz beim Benutzer und Auftraggeber. Literatur M. Flavin: Fundamental Concepts of Information Modeling, Yourdon Press, Englewood Cliffs 1981 C. Ghezzi, M. Jazayeri, D. Mandrioli: Fundamentals of Software Engineering; Prentice-Hall 1991 H. u. W. Heilmann:Strukturierte Systemplanung und Systementwicklung; ForkelVerlag, Wiesbaden 1979 M.A. Jackson: System Development; Prentice Hall, Englewood Cliffs, 1983 C. Jones: Applied Software Management, McGraw-Hill, 1991 Kimm, Koch, Simonsmeier, Tontsch: Einführung in Software Engineering, de Gruyter, Berlin 1979 S. McMenamin, J. Palmer: Strukturierte Systemanalyse, Hanser Verlag 1988 H. Strunz: Entscheidungstabellentechnik, Hanser Verlag, München-Wien 1977 M. Zelkowitz, A. Shaw, J. Gannon: Principles of Software Engineering and Design, Prentice-Hall, Englewood Cliffs, 1979 A. Zehnder: Informatik-Projektentwicklung; Teubner Verlag, Stuttgart 1986
222
Softwareentwicklung
M. Vetter: Aufbau betrieblicher Informationssysteme mittels konzeptioneller Datenmodellierung; Teubner Verlag, Stuttgart 1987 6.1.6 Der Systemtest Beim Testen des Gesamtsystems sollen möglichst alle Funktionen des Systems geprüft werden. In einigermaßen komplexen Systemen ist es aber fast unmöglich, die korrekte Funktion des Systems für alle möglichen Belegungen der Parameter zu prüfen. Die Aufgabe wird dadurch einigermaßen handhabbar, indem man anstelle eines großen Programms jedes Modul einzeln testet und von der Funktionsfähigkeit der Einzelteile auf das Gesamtprogramm schließt. Haben wir z.B. 100 binäre Zustandsvariable in 10 Moduln, so testen wir anstelle von 2100 = 1,21030 Zuständen nur 10*210 | 104 Zustände, also nur ein 1026-tel davon. Die grundsätzliche Vorgehensweise beim Testen ist also von unten nach oben (bottom-up) im Gegensatz zur Konstruktion und Problemzerlegung. Die Daten, mit denen dazu das Modul arbeitet (Testdaten) sollten speziell ausgewählt werden. Außer einer repräsentativen Zufallsauswahl aus den tatsächlichen Daten ist es für Eingaben sinnvoll, fehlerhafte Eingabedaten zu erzeugen, um auch die Reaktion des Moduls auf unsachgemäße Eingaben zu prüfen. Die Auswahl der Testdaten entscheidet dabei über die Grenzen des Programms (was muss passieren, was darf nicht passieren). Eine dritte Datenquelle bildet die von einem Datengenerator (z.B. Zufallszahlengenerator, Simulationsprogramm etc.) erstellten Testdaten. Das automatische Testen eines komplexen Programms ist eine schwierige Aufgabe. Es gibt verschiedene Ansätze, die man in einer sinnvollen Reihenfolge abarbeiten kann (Balzert 91) x Mit Hilfe der Modulspezifikationen werden Äquivalenzklassen von Testfällen gebildet und das Verhalten bei Grenzwerten bestimmt. Dies ist die Grundlage für Testdaten, mit denen das Modul getestet wird. x Es wird mit einem Werkzeug versucht, in dem zu testenden Modul durch passende Testdaten jeden Zweig des Programms mindestens einmal zu durchlaufen (Zweigüberdeckung). Dazu werden automatisch an allen Verzweigungen Zähler eingefügt. x Das so präparierte Modul wird übersetzt und mit den Testdaten betrieben. Die Ergebnisse werden zusammen mit den Testdaten protokolliert und gespeichert. Damit lassen sich nach einer Änderung des Moduls die Ergebnisse vergleichen (Regressionstest). x Mit den Testergebnissen werden nun die fehlenden Testdaten bestimmt, um eine volle Zweigüberdeckung zu erreichen (white box test), und die Tests durchgeführt x Mit einem Analysewerkzeug kann eine Quellcode-Analyse auf nicht benutzte Variable, laufzeitintensive Programmteile, etc. vorgenommen werden.
Das klassische Phasenmodell
223
In Abb. 6.13 ist der Datenfluss schematisch gezeigt. ausgewählte Daten
Testdaten
generierte Daten Handeingabe
SollErgebnisse
Programm
Ist-Ergebnis Vergleich
Urteil, Protokoll
Abb. 6.13 Datenfluss beim Test
x Eine andere, wirkungsvolle Strategie besteht darin, ein Kontrollprogramm zu erstellen, das wesentlich einfacher ist und nur die Spezifikation umfasst. Es testet auch nicht das Ergebnis exakt, sondern simuliert nur grob das Verhalten des Programms (Äquivalenzklassen) und prüft ob die Ausgabe mit der Spezifikation konsistent ist, z.B. über den Wertebereich der Ausgabe in bestimmten Situationen (Grenzwertanalyse). Im Unterschied zu den vorher generierten Testfällen kann es ständig parallel zum Hauptprogramm laufen und als Fehlerindikator bei kritischen Anwendungen im Normalbetrieb dienen. In der Abb. 6.14 ist dies illustriert.
ausgewählte Daten generierte Daten Handeingabe
Programm
Ist-Ergebnis
Testdaten Simulation
Vergleich
Urteil
Abb. 6.14 Online black box Test
Falls möglich, sollte der Systemtest nicht von dem Verfasser des Programmsystems selbst, sondern von einer unabhängig davon arbeitenden Instanz, beispielsweise der Qualitätssicherung, durchgeführt werden. Ihr Auftrag lautet: „Finde Fehler im Programm“ und nicht: „Arbeitet das Programm korrekt?“. Die Funktion eines Testers kann auch von Kollegen im Programmierteam wahrgenommen werden, die das betreffende Modul nicht selbst geschrieben haben. Um Animositäten unter den Mitarbeitern zu vermeiden, sollten allen klar sein, dass dies wichtige und notwendige Rollen sind, die sie für den Projekterfolg ausüben, und nicht persönliche Besserwisserei, Konkurrenzneid oder Racheakte. Grundlage der Modultests ist die Schnittstellenspezifikation, wie sie als Parameterliste und als Abfolge (Protokoll) im Programmkommentar bzw. in der Dokumentation bei der Funktionsaufteilung und Detaillierung festgelegt wurde. Haben wir einen Fehler entdeckt oder aber den Eindruck, dass kein Fehler zu erwarten ist, so können wir den Test abbrechen. Ist ein Fehler vorhanden, so muss er nicht nur korrigiert werden, sondern alle Tests, die die Funktion des betreffenden Moduls voraussetzen, müssen wiederholt werden, um eine Fehlkorrektur aus-
224
Softwareentwicklung
zuschließen. Je früher ein vorhandener Fehler gefunden wird, desto weniger kostet die Fehlerbehebung. Setzen wir die Kosten bei der Programmentwicklung auf 1, so kostet es 10 mal mehr, ihn später beim fertigen Gesamtprodukt nach dem Systemtest zu finden und zu beheben, und den Faktor 100, ihn erst beim Kunden bei der Wartung auszumerzen. Umfangreiche Tests lohnen sich deshalb immer, obwohl sie viel Arbeit machen und von Programmier“künstlern“ nicht gern gesehen werden. Gerade dieser Aspekt der rationalen, kalkulierten Softwareerstellung ermöglicht aber das Funktionieren großer Softwaresysteme und damit auch ihre Akzeptanz beim Benutzer. Allerdings müssen wir auch die Grenzen des Testens beachten: ein tadelloser Testdurchlauf bescheinigt uns nur, dass das Programm bei den getesteten Fällen funktioniert, nicht aber bei allen anderen. Meist ist eine solche Validierung des Programms für die Hauptanwendungen ausreichend; auftretende Fehler müssen eben nachgebessert werden. Das Softwareprodukt wird dabei als „Bananenprodukt“ betrachtet: es reift beim Kunden. In sicherheitskritischen Anwendungen (Flugzeugsteuerung, Bank-Transferverschlüsselung) ist aber ein solches Vorgehen nicht mehr akzeptabel: hier muss man beweisen (verifizieren) können, dass das Programm auch korrekt ist. Dies ist nicht einfach, sehr arbeitsintensiv und damit kostspielig und nicht immer möglich, z.B. wenn die Quellen des Betriebssystems nicht verfügbar sind. Die Fehler, ihre Ursachen und Abhilfen müssen nicht nur in der Dokumentation vermerkt werden, sondern können auch Auswirkungen nach oben bis ins Pflichtenheft (inkonsistente oder ungenaue Spezifikation) haben. 6.1.7 Die Einführung der Software Die Akzeptanz der neuen Softwarelösung und damit ihre Nutzung im betrieblichen Alltag hängt davon ab, wie sie dem Endbenutzer (Bedienungspersonal, Sachbearbeiter, etc.) vorgestellt wird. Wichtig dabei ist nicht nur, dass er/sie die Computerleistung als Hilfe und nicht als Arbeitsplatzbedrohung empfindet, sonder auch, wie der Endbenutzer die Funktion der Software erlernt: x Einführungskurse sind teuer, aber bei komplizierten, völlig neuen Arbeitsabläufen unumgänglich. Dabei sollte zuerst nur der Regelfall (S.80/20 Regel) erlernt werden. Später erst die wenigen Spezialfälle. x Schriftliche Gebrauchsanweisungen (Manuals) sind meist nur als Nachschlagwerke brauchbar. Bei umfangreichen Systemen muss noch zusätzlich ein Handbuch zum Gebrauch der Manuals erstellt werden. x Computergestützte, situationsabhängige Hilfen sind meist sehr brauchbar: Über den "Hilfe"-Knopf lässt sich genau diese spezielle Situation im Benutzerdialog sagen, welche Eingaben und Fehler möglich sind. Das Auffinden der adäquaten Situation bzw. der Stelle im Handbuch wird so entscheidend erleichtert. Allerdings muss dies auch mühsam detailliert bei der Programm-
Das klassische Phasenmodell
225
erstellung berücksichtigt werden. In der Regel vermittelt eine solche Hilfe aber keinen Überblick, so dass eine Konstruktion von situationsgerechter Hilfe und elektronischem Manual angebracht ist. x Computergestützte Simulation. Dies ist eine Mischung aus einem elektronischen Einführungskurs, bei dem die Eingabe von Beispieldaten am Programm simuliert wird, und dem computergestützten Hilfesystem. Der Vorteil eines solchen Simulators oder Trainers ist die Schulung auch an ungewöhnlichen Daten oder problematischen Situationen. Dies wird besonders bei Flugzeugbedingungen (Flugsimulator) oder Kernkraftzentren (Prozesssimulator) intensiv eingesetzt. Eine besondere Rolle spielt die Einführung bei der Reaktion in Katastrophensituationen ("System stürzt ab", "Virus im System", "Dateien defekt", "nichts geht mehr", ...). Hier ist es sinnvoll, dem Benutzer mittels eines Katastrophenhandbuches klare Verhaltensregeln und Vorgehensweisen an Hand zu geben, die nebenbei auch den Sinn einer systematischen Datensicherung (Back-up) vor Augen führen. 6.1.8 Dokumentation Eine Dokumentation soll den Zugang zu einer Sache ermöglichen und erleichtern. Sie sollte dazu nach dem Top/Down-Prinzip aufgebaut sein und je nach Interesse und Vorwissen des Nutzers unterschiedliche Einstiegspunkte enthalten. Die Dokumentation muss während der Systementwicklung erstellt werden, um möglichst alle wichtigen Aspekte zu umfassen: x Benutzerdokumentation Sie beschreibt die Funktionalität eines Systems aus der Sicht des Benutzers. Sie zeigt gewissermaßen die Syntax und Semantik des Systems und beschreibt dabei das WAS: a) Was leistet das Programm? b) Was leistet es nicht? c) Welche Wahlmöglichkeiten bietet es? d) Welche Benutzerumgebung (Definition von logischen Namen) bracht es? e) Bei Systemteilen: Welche Funktionen werden durch die Schnittstelle angeboten? x Implementierungsdokumentation Die Implementierungsdokumentation unterstützt die Fehlersuche und die Verwendung von Programmteilen in anderen Programmen, indem sie die aktuelle, gegebene Funktionalität des Systems aus der Sicht des Wartungstechnikers beschreibt. Sie beschreibt deshalb das WIE: a) Wie wird die Funktion XY auf dem gemeinsamen Speicherbereich realisiert?
226
Softwareentwicklung
b) Wie und in welchem Kontext wird sie aufgerufen, usw. ? x Entwicklungsdokumentation Die Entwicklungsdokumentation wird aus der Sicht des Entwicklers geschrieben. Sie ist das Tagebuch des Projekts. Sie beschreibt die Grundlagen des Systems und wie sie zustande gekommen sind. Außerdem enthält sie Alternativen und Lösungen der gestellten Aufgabe. Theoretische Überlegungen, Designentscheidungen, Anpassungskompromisse, kurz: Entwicklungsprobleme und Lösungen finden sich darin. Die Entwicklungsdokumentation soll dem Systementwickler alle zur Weiterentwicklung nötigen Informationen bieten und helfen Doppel- und Fehlentwicklungen zu vermeiden. Sie beschreibt das WARUM: a) Warum wurde gerade diese Art der Implementierung gewählt? b) Welche Alternativen wurden erwogen? c) Weshalb wurden sie verworfen oder zurückgestellt? Im Unterschied zur Benutzerdokumentation, die die Funktionalität eines Systems oder einer Komponente beschreibt, und zur Implementierungsdokumentation, die die Realisierung dieser Funktionalität beschreibt, soll die Entwicklungsdokumentation die Entwicklung gerade dieser Funktionalität begründen und gegen Alternativen abwägen. x Programminterne Dokumentation Sie erleichtert das Lesen und Verstehen eines Programms indem sie die Aufgabe des Programms spezifiziert und Verweise auf die oben genannten Dokumentationen enthält. Typische Programminterne Dokumentationen bestehen aus Abschnitten (header) zu Anfang der Module und Schnittstellen, die die Funktion, die Ein- und Ausgabewerte sowie Referenzen zur Dokumentation (Algorithmen etc.) enthalten. Innerhalb des Programmcodes sind es die Programmkommentare, die ein Verständnis des Codes erleichtern. Man sollte sich klarmachen, dass nicht nur Text zu der Systemdokumentation gehört, sondern auch Bilder (z. B. Bildschirmmasken, verwendete Grafikelemente, Flussdiagramme) und die Testdaten und Protokolle. Aus diesem Grund sind nur hochwertige Textverarbeitungssysteme, die auch eine Gliederung und Verwaltung der Daten (z. B. Suchen nach Begriffen und Funktionen) erlauben, für ein Dokumentationssystem geeignet. 6.1.9 Qualitätsmanagement Obwohl bei der Hardware ein stetiger Leistungszuwachs von ca. 75% pro Jahr zu verzeichnen ist, können wir bei der Softwareproduktivität (Codezeilen pro Mannjahr) einen Zuwachs von 4% bemerken. Dies macht sich besonders bei großen Projekten bemerkbar, wo bei ca. 100000 bis 1 Million Codezeilen nicht nur die Anzahl, sondern auch die Qualität (Fehlerfreiheit) sich stark bemerkbar macht. Es wird deshalb versucht, auch die Softwareproduktion rationaler zu gestalten und die
Das klassische Phasenmodell
227
„normale“ Fehlerrate von 0,1%-0,3% des getesteten Codes mit geeigneten Maßnahmen zu verkleinern (Jones 91). Qualitätszertifikate
Da eine normale Produkthaftung bei Software nicht üblich ist, muss der Käufer Vertrauen in die Firma oder das Produkt haben. Bei unbekannten, neuen Produkten oder Firmen ist dies schwer. Aus diesem Grund hat sich in letzter Zeit als industrieller Standard als „Norm und Siegel für Qualitätsarbeit“ die ISO 9000 Normfamilie durchgesetzt. Dabei wird das Qualitätsmanagement QM des Herstellungsprozesses geprüft, also die Planung, Lenkung (Entwicklungsmethodik, Programmierrichtlinien), Sicherung (Analyse, Test, Review) und Verbesserung der Qualität. Im Unterschied dazu liefert DIN ISO 8402 Kriterien für die Beurteilung des Produkts durch eine systematische und unabhängige Untersuchung. Zwar existieren auch ca. ein Dutzend IEEE Normen für Projektmanagement, Software Design, Verifikation und Testdokumentation, aber diese sind unverbindliche Hilfen und als Zertifikat nicht geeignet. Auch die Qualitätsauszeichnungen wie der amerikanische „Malcolm Baldridge National Quality Award“ und der europäische „European Quality Award“ ersetzen nicht die oben angeführten Normzertifikate. Qualitätsmodelle
Es gibt im wesentlichen zwei wichtige Qualitätsmodelle, die historisch aufeinander aufbauen: das Reifemodell und das Bootstrap-Modell. Beide Modelle beruhen auf Fragebögen, aus deren Beantwortung auf den Zustand der Firma geschlossen wird. Das Reifemodell (Capability Maturing Model CMM) wurde zunächst 1986 vom Software Engineering Institut SEI der Carnon Mellon University für Firmen des amerikanischen Verteidigungsministeriums konzipiert und dann weiterentwickelt (Paulk93). Aus den Fragen, die mit JA/NEIN beantwortet werden, kann dann auf einen von fünf verschiedenen Zuständen der Projektorganisation geschlossen werden. Der initiale, erste Zustand deutet auf einen unreife, chaotische Organisation hin: x x x x
die Softwareprozesse werden unsystematisch durchgeführt die Organisation ist im wesentlichen nur reaktiv am „Feuer löschen“ Zeit- und Geldpläne werden überzogen, Termine nicht eingehalten es gibt keine festen Qm-Maßnahmen bzw. bei Zeitnot werden sie eingeschränkt oder gestrichen
Im Gegensatz dazu ist die reife Organisation (5. Stufe) gekennzeichnet dadurch, dass x ein präzises Management existiert, das die Ziele neuen Mitarbeitern vermitteln kann und eine klar definierte Aufgabenteilung besitzt (Stufe 2) x die Pläne (Termine, Budget) eingehalten werden, da bei der Planung auf eine Prozessdatenbank mit realistischen Referenzdaten zugegriffen wurde (Stufe 2)
228
Softwareentwicklung
x die Erstellungsprozesse konsistent sind und immer aktualisierte Daten benutzen x kontrollierte Pilotversuche existieren x eine Kosten/Nutzen Analyse vorgenommen wird x die Qualität durch das Management überwacht wird (Stufe 4) x eine quantitative Bewertung der Qualität durch Anwendung von Metriken möglich ist (Stufe 4) Von der ersten Stufe kann man durch Einführung verschiedener Techniken auf die höchste, „reifste“ Stufe 5 gelangen, wie dies in Abb. 6.15 gezeigt ist.
standardisierter, konsistenter Prozeß disziplinierter Prozeß
kontinuierlich verbesserter Prozeß optimized
vorhersagbarer Prozeß
managed
defined
repeatable
initial
Abb. 6.15 Stufen im Reifemodell
Betrachten wir qualitativ die Häufigkeit, mit der Zeit-, Kosten- und Restfehlerpläne eingehalten werden, so können wir eine Abnahme der Varianz von den Zielvorstellungen bei fortschreitender Reifung konstantieren, siehe dunklere Kurven in Abb. 6.16.
Stufe 1
... Stufe 5 Ziel
Zeit, Kosten, Restfehler
Abb. 6.16 Einhaltung der Pläne im Reifeprozess
Das klassische Phasenmodell
229
Aufbauend auf dem CMM Reifemodell wurde im Rahmen eines ESPRIT Projektes eine Anpassung an zivile europäische Verhältnisse geschaffen: die Bootstrap-Methode (Members .93). Hierbei wurden die ISO 9000 Normen sowie das Qualitätsmodell der European Space Agency ESA einbezogen und ein detaillierteres Modell geschaffen. Der Fragebogen ist umfangreicher (Organisation der Arbeit und des QM, verwendete Methoden, Einführung von Technologien und Transfer) und die Beantwortung wird mit „nicht/schwach/stark vorhanden/ nicht anwendbar“ stärker differenziert. Das ganze Methodenpaket besteht aus x x x x x
Unterlagen zur Vorgehensweise den Fragebögen und dem Auswertungsalgorithmus den Richtlinien zur Verbesserung der Prozesse den Richtlinien zur Ausbildung der Gutachter einer Datenbank mit Vergleichswerten
Die Reifestufen, die bei der Untersuchung durch einen externen(!) Gutachter ermittelt werden, sind kompatibel zu denen des CMM, aber noch zusätzlich unterteilt. Ergebnis der Untersuchung sind Fähigkeitsprofile sowohl der Organisation als auch des konkreten Projekts. Die absoluten Profile erlauben dabei gezielte Verbesserungen der eigenen Organisation; die relativen Profile bezüglich aller untersuchten Einheiten geben Auskunft über den Stand relativ zur Konkurrenz. Das Bootstrap Verfahren wird laufend weiter entwickelt im Rahmen des Technologietransfer Programms European Software Initiative (ESSI). 6.1.10 Der menschliche Faktor Die bisherigen Ausführungen zeigten, wie eine Softwareentwicklung idealerweise durchgeführt werden kann. Dies berücksichtigt aber verschiedene, typisch menschliche Probleme nicht: x Die Endanwender kennen zwar ihr Fachproblem genau, aber die Computermöglichkeit nicht: Interne Fachleute sind „zu nah dran“; sie haben eingefahrene Gewohnheiten etc. und nur implizites Wissen („Handfertigkeiten“). x Die Projektentwickler dagegen kennen meist die Fachprobleme nur aus Erzählungen und übersehen deshalb wichtige Details und Zusammenhänge: Externe Entwickler wissen wenig über die Arbeitsaufgaben und -abläufe der Firmenprozesse, die sie modellieren sollen x Die Sprache der Anwender und Entwickler ist verschieden, auch wenn Sie die gleichen Begriffe benutzen. Beispielsweise ist der Begriff "Benutzerliste" vieldeutig: Von einer einfachen Liste weniger Vornamen bis zur kompletten Adressliste mit komplexen Attributen ist alles denkbar. Auch gibt es vielfach betriebliche Rahmen und Nebenbedingungen, die nicht bewusst ausgesprochen sind, sondern als "selbstverständlich" gelten.
230
Softwareentwicklung
x Die Wünsche der Anwender (Vorstand und Sachbearbeiter) sind unterschiedlich und ändern sich laufend. Meist kommt mit der Realisierung eines Systems "der Appetit" auf mehr Funktionen. x Das Softwaresystem (z. B. ein Informatiksystem) greift in die gewachsenen Beziehungen in den Betrieben ein. Es beeinflusst z. B. die Beziehung zwischen Vorgesetzten und Untergebenen ("wer weiß was") oder zwischen Betriebsleitung und Gewerkschaft (Leistungskontrolle, Datenschutz, Arbeitsplatzüberwachung). Außerdem ist die Modellierung und Optimierung von Geschäftsprozessen durch Software nur ein Aspekt der Geschäftsrestrukturierung (business reengineering). Das Gesamtkonzept sieht normalerweise die Schritte vor x Unternehmensstrategie entwerfen (Visionen, Kunden, Märkte..) x Nutzen für den Kunden festlegen x Analyse der Geschäftsprozesse (Restrukturierung, Kontrolle und Informationswege mit Betroffenen bestimmen) x Reorganisation beispielhaft implementieren (Betroffene einbeziehen, Testläufe organisieren) x Allgemeine Planung von Wissenstransfer und Kommunikation (Veränderung fördern, Ängste abbauen, neues Verhalten trainieren) x Neuen Prozessablauf verankern (Kontrolle der Prozesse regeln und neue Unternehmenskultur festigen) um gemeinsam mit den Betroffenen die Restrukturierung durchzuführen. Hier gibt es jede Menge Stolpersteine für die Einführung einer Automatisierung. Deshalb sind nach verschiedenen Umfrageergebnissen ein Großteil der Firmen unzufrieden mit den Ergebnissen. So gaben beispielsweise bei einer Umfrage (Trend Research,´95) 228 Unternehmen an, zwar aus schlechter wirtschaftlicher Situation (79%) oder wegen organisatorischer Schwachstellen (86%) ein Reengineering-Projekt durchgeführt zu haben, aber 40% führten es nur teilweise zu Ende und 19% brachen es vollständig oder überwiegend ab. Selbst bei denen, die es zu Ende führten, fanden nur 22% ihre Erwartungen bestätigt, aber 40% waren damit unzufrieden. Ob dies daran liegt, dass sie sich überhaupt nicht (17%) oder nur teilweise (44%) von außen helfen ließen, ist unklar. Aus diesen Gründen heraus wurden verschiedene Modelle entwickelt, die stärker mit dem Benutzer zusammen ein Anwendersystem interaktiv entwickeln (Benutzerpartizipative Programmentwicklung). Literatur P. Coad, E. Yourdon: Object-Oriented Analysis, Prentice-Hall, Englewood Cliffs 1990
Modifizierte Phasenmodelle
231
D.J. Hatley, I.A. Pirhai: Strategies for Real-Time System Specification; Dorset House Publ., New York 1987 M. Paulk, B. Curtis, M. Chrissis, C. Weber: Capability Maturity Model, Version 1.1, IEEE Software 10/4 (1993), pp.18-27 M. Shooman: Software Engineering; Mc Graw Hill 1983 Members of the Bootstrap Project: BOOTSTRAP-Europe´s Assessment Method, IEEE Software 10/3 (1993), pp.93-95
6.2 Modifizierte Phasenmodelle Es gibt eine Reihe von Varianten des Phasenmodells, die hier vorgestellt werden sollen. Sie haben alle gemeinsam, dass die Einteilung in Phasen beibehalten wird, die Erfüllung der Funktionen aber durch verschiedene Maßnahmen erleichtert wird. 6.2.1 Das Wasserfallmodell Eines der populärsten Modelle sieht den Softwarerstellungsprozess als eine Folge von Stufen, auf denen das Projekt ablaufen soll. Das Überwechseln der Projektarbeiten von einer Stufe auf die nächste kann man dann wie einen Wasserfall ansehen, der in Stufen den „Höhenunterschied der Schwierigkeiten“ überwindet. Ein solches, mit einer Rückkopplung auf jeder Stufe versehenes Modell ist in Abb. 6.17 zu sehen.
Definition
Entwurf Pflichtenheft Implementierung Spezifikation
Anforderungs prüfung
Test Entwurfsinspektion
Moduln Testfälle Installation System Codeinspektion Testinspektion Testauswertung
Anwendung
Installationsprüfung
Abb. 6.17 Projektphasen und Prüfschritte
Betrieb Wartung
232
Softwareentwicklung
Die einzelnen Stufen der Projektabwicklung lassen sich folgendermaßen charakterisieren: x Erstellung einer Anforderungsanalyse Die Ziele, die gewünschten Funktionen sowie die Randbedingungen werden ermittelt und im Pflichtenheft niedergelegt. x Design des Gesamtssystems und der Komponenten Die Anforderungen werden in Hard- und Softwareleistungen getrennt und eine entsprechende Systemarchitektur mit ihren formalen Schnittstellen festgelegt. Damit sind auch die Schnittstellen der Funktionen und ihrer Module bestimmt. x Implementierung und Test der Module Die Spezifikationen werden auf Modulebene implementiert und getestet. x Integration der Module und Systemtest Das Zusammenwirken der einzelnen Module auf den verschiedenen Ebenen wird eingerichtet und getestet. Dies wird durch einen Systemtest beendet. x Installation, Test und Wartung des Systems Die Installation des erstellten Systems wird durch fortlaufende Wartung, bei der neben der Fehlerbehebung zusätzlich Leistung und Effizienz verbessert werden, ergänzt. Dabei sollten aber jeweils eine Rückkopplung nicht nur in der selben Phase, sondern auch inhaltlich von unten nach oben erfolgen. Dies bedeutet beispielsweise, dass auch aus der Wartung nicht nur ein Wunsch nach besseren Moduln, sondern auch nach veränderten Anforderungen (Pflichten) folgen kann, was ein Neuprojekt bewirken würde. Generell ist aber die Wirkung von einer Stufe nach oben auf eine andere durch das Phasenmodell äußerst problematisch; der Benutzer geht das Risiko ein, etwas anderes zu erhalten als er sich gewünscht hat. 6.2.2 Objektorientierte Entwicklung Das Modell der objekt-orientierten Entwicklung geht von einer Analyse aus, beschreibt das Design der Funktionen und ihre Evolution und geht über die Anwendung wieder zum Design zurück. Dies kann man auch als Wasserfallmodell zeichnen, siehe Abb. 6.18.
Modifizierte Phasenmodelle
233
Analyse
Design
Evolution
Anwendung Evaluation
Abb. 6.18 Der objektorientierte Entwicklungszyklus
Die einzelnen Phasen sind dabei folgende: x Analyse Das Ziel der Analyse ist eine Beschreibung des Problems, die nicht nur vollständig und konsistent sein soll, sondern auch lesbar und damit für mögliche Interessenten nachvollziehbar und überprüfbar gegenüber der Realität sein soll. Die Modellierung des Problems wird durch eine Identifikation mit Klassen und Objekten sowie deren Interaktionen durchgeführt. x Design Hier werden Abstraktionen und Mechanismen entworfen, die das geforderte Verhalten des Modells erbringen. Dies kann schon während der Analysephase beginnen, da zu detaillierte Analyse ohne Designwissen zu ineffizienten Festlegungen führen kann (siehe Abschnitt Nebenbedingungen). Es ist deshalb besser, sich nach dem Erwerb von etwas Problemverständnis zum Design überzugehen und dann wieder weiter zu analysieren. Dies fordert einen direkten Rückkopplungskreislauf zur Analyse wie dies in Abb. 6.18 zu sehen ist. x Evolution Bei dem Kodieren, Testen und Integration der Teile werden verschiedene Teilprototypen entwickelt, die zusammen das System bilden. x Anwendung und Evaluation Bei der Anwendung der Prototypen zeigen sich dem Benutzer bereits die Probleme und Vorteile verschiedener Varianten. Da hier die Ausdifferenzierung und damit auch die Kosten noch nicht so hoch sind, kann man im Unterschied zum klassischen Phasenmodell relativ einfach auf die Änderungswünsche eingehen. Ein solcher Entwicklungszyklus vereinigt ein versuchsweises, erkundendes Vorgehen (explorative Entwicklung), bei der ein System so schnell wie möglich entwi-
234
Softwareentwicklung
ckelt wird und dann solange angepasst wird, bis es den Anforderungen entspricht, mit einer evolutionären Programmierentwicklung, bei der die erste Programmversion bereits darauf angelegt ist, durch Änderungen zu einer weiteren Version wechselnden und wachsenden Anforderungen zu entsprechen. Die Vorteile des objekt-orientierten Entwicklungszyklus liegen auf der Hand: x die wichtigen Rückkopplungen zum Benutzer werden besser berücksichtigt x der Übergang vom alten zum neuen System ist gestaffelt und damit sanfter x die Benutzerschnittstellen können früher überprüft werden und sind so qualitativ besser x die Arbeitsmoral der Entwickler und der Benutzer wird verbessert, da frühzeitig Ergebnisse sichtbar sind x die Kodierung und das Testen können bereits früher (vor Ende des Design) beginnen und können durch die Parallelität eine kürzere Projektdauer bewirken x die Bewertung des Entwurfs und mögliche Alternativen werden wirkungsvoller unterstützt Grundsätzlich ergeben sich die Vorteile des rapid prototyping, ergänzt durch den besseren Benutzerkontakt, durch die einheitliche Terminologie bei Entwicklern und Auftraggebern. 6.2.3 Rapid Prototyping und Spiralenmodell Die objekt-orientierte Entwicklungsweise ermöglicht uns eine stärker benutzerorientierte Entwicklung. Dazu starten wir zunächst mit einer stark reduzierten Version des Phasenmodells, die nur sehr grob und allgemein Teile der gewünschten Funktionen enthält und realisiert, beispielsweise nur als simulierende Benutzeroberfläche. Im Laufe des Projekts wird nun zum einen die allgemein spezifizierten Funktionen mit Leben erfüllt, zum anderen kommen neue Funktionen hinzu, die man anfangs nicht bedacht hatte.
Modifizierte Phasenmodelle
235
Ein Phasenlauf dieses Modells sieht vor:
x Festlegung von Ziel und Zweck des Prototyps x Auswahl der Funktionen, die auch realisiert werden x Erstellung des Prototyps x Bewertung des Prototyps (Erfüllung der Benutzeranforderungen, Brauchbarkeit für Weiterentwicklungen) zusammen mit dem Benutzer Bei jedem Durchlaufen eines solchen Phasensequenz wächst der Umfang des Prototyps und seine Kosten. Visualisiert man die Kosten durch die Fläche, die der Kreiszyklus auf dem Papier bedeckt, so wird aus dem Kreis eine Spirale: in Polarkoordinaten ist die Zeit proportional dem Winkel in Uhrzeigerrichtung und der Radius proportional den bisherigen Gesamtkosten. Ein Winkelabschnitt bedeutet im Spiralenmodell also eine Phase, wobei nach (Boehm 1986) die vier Quadranten vage den vier Abschnitten Analyse, Design, Kodierung, Test und Bewertung zugeordnet werden. In Abb. 6.19 ist dies schematisch gezeigt.
Alternativen
Analyse
Design
Prototype1
Prototyp2
Analyse Validierung
Analyse
Evaluation Planung Validierung Test
Planung
Test
Abb. 6.19 Das Spiralenmodell
Die Vorteile von „rapid prototyping“ liegen auf der Hand: x Die Programme werden frühzeitig an den Benutzeranforderungen geprüft (validiert)
236
Softwareentwicklung
x Missverständnisse zwischen Entwickler und Benutzer können frühzeitig erkannt werden x Die Anforderungen (Spezifikationen) werden auf Konsistenz und Vollständigkeit (fehlende Funktionen !) vom Benutzer selbst überprüft x Der Benutzer übt automatisch, das System kennenzulernen und zu bedienen. Dieses Vorgehen entspricht eher einer „benutzerpartizipativen Programmentwicklung“. Dies lässt sich auch in einem Quadrantenfeld darstellen, in dem eine 360Grad-Spiralenumrundung gerade eine Phase im Phasenmodell darstellt. Der Abstand vom Koordinatennullpunkt (Radius der Spirale) ist dabei proportional zu den Kosten des Projekts. In Abb. 6.20 Ist das Schema gezeigt.
Zielsetzung
Planung
Risikoabschätzung
Entwicklung und Verfikation
Abb. 6.20 Die Quadranten des Spiralenmodells
Das Spiralenmodell kann man auch als Metamodell ansehen, das für die Evolution aller Arten von Software-Entwicklungsmodellen geeignet ist. Die Werkzeuge (Programme), die dafür benötigt werden, lassen sich kennzeichnen durch x Schnelle Entwicklung: explorative Programmerstellung durch „vorgefertigte“ Funktionalität ermöglicht schnelle und billige Konstruktion eines Prototyps und damit frühzeitige praktische Erfahrungen x Evaluierungsunterstützung: Bewertung des Prototyps möglich durch Visualisierung von Struktur und Funktionalität sowie probeweises Ändern und Erweitern der Funktionalität x Inkrementelle Entwicklung: Es werden Teile-Bibliotheken unterstützt, die neben der Aufnahme von neuen Teilen auch die Komposition des Prototyps aus Bibliotheksteilen und damit die Wiederverwendung der alten Teile ermöglichen.
Das V-Modell
237
Außerdem sollen auch die Werkzeuge selbst wiederum an neue Anforderungen anpaßbar sein, um eine integrierte Arbeitsumgebung zu erhalten. Es gibt verschiedene Werkzeuge für eine solche Prototyping-Arbeitsumgebung. Sie lassen sich als „sehr hohe Programmiersprachen“ (Very High Level Programming VHLP) bezeichnen und durch vordefinierte Sprachkonstrukte zur Programmerstellung, graphische Schnittstellen zur Programmentwicklung etc. realisieren. Beispiele dafür sind x Sprachen der 4. Generation Verwendung vorwiegend in relationalen Datenbankanwendungen und enthalten Generatoren für Bildschirmmasken, Ablaufsteuerungen, Funktionsschablonen etc. für datenorientierte Anwendungen (z.B. Dateneingabe, Abfrage etc.) und sind mit den Funktionen der Datenbanken eng verzahnt. x Visuelle Programmierung Hier wird der Entwurf der Metapher durch die Bereitstellung von graphischen Symbolen und Ikonen (und der Editoren) sowie der Ablaufmechanik im Hintergrund (Regiediagramme, Funktionsanbindung etc.) stark vereinfacht. Diese Art der Programmierung eignet sich besonders für parallele Aufgaben und bei wenigen, aber hochkomplexen Funktionen in reaktiven Systemen. x Deklarative Sprachen Diese Art der Programmierung beschränkt sich auf die Angabe des Ziels und der Nebenbedingungen (was soll getan werden, und nicht wie soll es getan werden). Bei all diesen Ansätzen lassen sich auch objekt-orientierte Schnittstellen und Bibliotheken verwenden. Trotzdem verdient das eigentliche Entwicklungsmodell einen gesonderten Abschnitt.
6.3 Das V-Modell Das V-Modell wurde ursprünglich im Auftrag des Bundesministeriums für Verteidigung (BMVg) entwickelt mit der Idee, einen verbindlichen, herstellerunabhängigen Begriffs- und Vorgehensrahmen für alle Projekte zu schaffen. Erste Entwicklungen zum V-Modell begannen 1986. In der Folge wurde es zunächst (1991) im wehrtechnischen Bereich als verbindlicher Standard und später im zivilen Behördenbereich einsetzt. Es wurde ständig unter Einbeziehung aller Beteiligten weiterverbessert. Diese Verbesserungen „konvergieren“, d. h. man kann das V-Modell mit gutem Recht als ausgereift ansehen. Im Sommer 1992 wurde es vom Bundesministerium des Innern (BMI) für den zivilen Verwaltungsbereich der Bundesbe-
238
Softwareentwicklung
hörden übernommen. Damit existiert ein einheitlicher Standard für den gesamten öffentlichen Bereich. 6.3.1 Die drei Schichten der Standardisierung Das V-Modell hat den Anspruch, alle Aktivitäten eines Projekts normiert zu beschreiben, ohne dabei zu viel vorzuschreiben. Zu jeder Aktivität gibt es eine Beschreibung in der Art einer Arbeitsanleitung; zu jedem Entwicklungsdokument gibt es eine Dokumentenbeschreibung, welche die Inhalte des Dokuments festlegt. Das V-Modell regelt die Systementwicklung auf drei Ebenen oder Schichten: 1. Vorgehensweise, 2. anzuwendende Methoden, 3. funktionale Anforderungen an einzusetzende Werkzeuge. Im wehrtechnischen Bereich sind alle drei Standardisierungsebenen verbindlich. Im Bereich der Bundesbehörden ist nur die Vorgehensebene verbindlich. Jede der drei Schichten ist in funktionelle Abschnitte, sogenannte Submodelle gegliedert. Diese umfassen x Systemerstellung (SE) Im Submodell SE sind alle unmittelbar der System-Erstellung dienenden Aktivitäten und die jeweiligen Entwicklungsdokumente zusammengefasst. Das Submodell Systementwicklung (SE) ist das wichtigste Submodell des VModells. x Qualitätssicherung (QS), Das Submodell QS regelt die Aufgaben und Funktionen der Qualitätssicherung innerhalb des Softwareentwicklungsprozesses. Die Regelungen berühren jedoch wie bei den anderen Submodellen in keiner Weise organisatorische Festlegungen. x Konfigurationsmanagement (KM) Das Submodell KM stellt sicher, dass Produkte eindeutig identifizierbar sind, Zusammenhänge und Unterschiede von verschiedenen Versionen einer Konfiguration erkennbar bleiben und Produktänderungen nur kontrolliert durchgeführt werden können. x Projektmanagement (PM) Die im Submodell PM festgelegten Aufgaben umfassen Planung, Kontrolle und Steuerung projektinterner Tätigkeiten, Schnittstellen zu projekt-externen Einheiten und projekt-internen Rollen, Projektrepräsentanz und informationszentrum. In Abb. 6.21 sind die Unterteilungen übersichtlich dargestellt.
Das V-Modell
239
Projektmanagement PM 1 Projektinitialisierung
PM 4 Feinplanung
PM 7 Risikomanagement
PM 10 Schulung/Einarbeitung
PM 2 Vergabe/Beschaffung
PM 5 Kosten-/Nutzenanalyse
PM 8 P.-Kontrolle/Steuerung
PM 11/12/13 diverses
PM 6 Durchführungsentsch.
PM 9 Informationsdienst
PM 14 Projektabschluß
PM 3 Auftragnehmer-Mgmt
Konfigurationsmanagement
Systemerstellung
Qualitätssicherung
SE 1 Systemanforderungsanalyse
QS 1 QS-Initialisierung
KM 1 KM-Initialisierung
SE 2 Systementwurf
QS 2 Prüfungsvorbereitung
KM 2 Produkt- und Konfigurationsverwaltung
SE 3 SW-/HW-Anforderungsanalyse
QS 3 Prozeßprüfung QS 4 Produktprüfung
SE 4-SW bis SE 7-SW
SW-Entwicklung
QS 5 QS-Berichtswesen
KM 3 Änderungsmanagement
SE 4-HW bis SE 7-HW
HW-Entwicklung
KM 4 KM-Dienste
SE 8 Systemintegration SE 9 Überleitung in die Nutzung
Abb. 6.21 Interaktion der Submodelle
Die vier Submodelle sind eng miteinander vernetzt und beeinflussen sich über den Austausch von Produkten und Ergebnissen gegenseitig. Dies soll an einem Projektbeispiel dargestellt werden.
Systemerstellung (SE)
A2
Qualitätssicherung (QS)
P2
A5
A3
P3
A7
P6
P6 Konfigurationsmanagement (KM) Projektmanagement (PM)
A1
P1
A4
P4 P6
A6
P5
Abb. 6.22 Beispiel: Aktivitäten- und Produktfluss zwischen den vier Submodellen
6.3.2 Anwendung des V-Modells Das V-Modell wurde unter mehreren Anwendungsaspekten entwickelt. Einsatzschwerpunkte sind:
240
Softwareentwicklung
x Vertragsgrundlage Hierbei definiert es eindeutig den Lieferumfang der Software und die Vollständigkeit der Softwaredokumentation. x Arbeitsanleitung Durch die detaillierten Beschreibungen der Aktivitäten und Entwicklungsdokumente dient das V-Modell bei der Softwareentwicklung als Leitfaden und konkrete Arbeitsanleitung. x Kommunikationsbasis Durch die Beschreibung der Vorgehensweise, der Entwicklungsdokumente und der Bereitstellung eines Glossars dient es als Basis wechselseitigen Verständnisses und reduziert Reibungsverluste zwischen Auftraggeber, Nutzer, Auftragnehmer und Entwicklern. Der Regelungsgehalt des V-Modells ist streng organisationsneutral gehalten. Er beschränkt sich ausschließlich auf den technischen Entwicklungsgang. Deshalb ist das V-Modell nicht nur für die öffentliche Verwaltung als Entwicklungsstandard geeignet, sondern auch für Industrieunternehmen. Das Vorgehensmodell zeichnet sich durch Allgemeingültigkeit und durch Firmen - und Projektunabhängigkeit aus. Es ist damit unabhängig vom Einsatzbereich. Um es für ein konkretes Projekt einzusetzen, muss individuell entschieden werden, welche Aktivitäten und Entwicklungsdokumente für das Projekt aus sachlichen Gründen erforderlich sind: Es muss an die Projekt- und Vorhabenseigenschaften angepasst werden (Tailoring). Damit eine Vergleichbarkeit aller Angebote hinsichtlich der Art der Durchführung und der Dokumentation umfassend gewährleistet ist, erfolgt die projektspezifische Anpassung des V-Modells bei externer Auftragsvergabe bereits vor der Ausschreibung. Das dabei erstellte Projekthandbuch definiert den vom Auftragnehmer (und dessen Unterauftragnehmern) zu erbringenden Leistungsumfang. Damit wird das Projekthandbuch zur einheitlichen Handlungsgrundlage für alle Projektbeteiligten. 6.3.3 Vorteile des V-Modells Welche Vorteile hat die Vorgehensstandardisierung nach dem V-Modell? x Alle Projektbeteiligten (Nutzer, Entwickler, Auftraggeber, Auftragnehmer etc.) können besser miteinander kommunizieren, da das V-Modell ein gemeinsames Verständnis und eine Bezugsbasis schafft. x Durch eine einheitliche und abgestimmte Vorgehensweise reduzieren sich Einarbeitungs- und Schulungszeiten. Das einmal gelernte kann immer wieder angewendet werden (Investitionssicherung).
Das V-Modell
241
x Neue Projekte können besser kalkuliert werden, wenn Erfahrungswerte zur Dauer und Art der Projektausführung vorliegen. Dies geht jedoch nur bei standardisierten Aktivitäten, so wie sie das V-Modell anbietet. x Die klare Definition der Aktivitäten erleichtert dem Projektmanagement die Planung. Projekte werden planbarer. Auch die Projektmitarbeiter schätzten den „roten Faden“, den die Abarbeitung der Aktivitätenfolge vorgibt. Dies führt per Saldo zu einer besseren Termintreue, da einfacher erkennbar ist, an welchem Punkt des Projektablaufs man sich gerade befindet. x Die Abhängigkeit von den Eigenheiten der Projektdurchführung von einzelnen Personen wird geringer, da die Vorgehensweise und die Art der Dokumentation durch das V-Modell bestimmt wird. x Die Abhängigkeit von beauftragten Firmen wird verringert, da ein erster Projektabschnitt von Firma A (mit Prozessbeschreibung und Dokumentation nach V-Modell) durchgeführt werden kann. Der nächste Projektabschnitt kann dann von Firma B durchgeführt werden, die die Ergebnisse von Firma A nahtlos (weil durch V-Modell standardisiert) übernehmen kann. x Durch ein klares Rollenkonzept und den zugehörigen Aktivitäten weiß jeder zu jedem Zeitpunkt, was zu tun ist und wer verantwortlich zeichnet. x Es zeigt sich, dass die Anzahl der Wartungsfälle bei einem strukturierten Vorgehen nach V-Modell vermindert werden. Dies führt zu einer verbesserten Produktqualität. x Der Wartungsaufwand verringert sich, da durch das V-Modell ein Mindestmaß an Dokumentation erzwungen wird. Dies hilft später im Fall von Pflege und Änderung. Das V-Modell übt damit einen gewissen Zwang zu mehr Aufwand in den frühen Projekt- und Planungsphasen aus. Die Devise ist: „Erst denken, dann handeln und Programmieren“. x Der einheitliche Aufbau der Dokumentation und die Überschneidungsfreiheit (Redundanzfreiheit) der enthaltenen Informationen erleichtern die Wartung. Wichtige Informationen werden sofort gefunden. x Bei Ausschreibungen sind die Angebote besser vergleichbar, da das VModell einen gewissen Rahmen erzwingt. x Bei einer ISO 9000-Zertifizierung ist die Nennung des eingesetzten Prozessmodells zwingend erforderlich. Hier füllt das V-Modell eine Lücke, denn es kann unmittelbar eingesetzt werden. Durch den breiten Einsatz des V-Modells in der Wehrtechnik und der zivilen Industrie sowie der öffentlichen Verwaltung rentiert es sich für die CASE-ToolHersteller das V-Modell zu unterstützen. Dies hat dazu geführt, dass heute schon der überwiegende Teil der CASE-Tool-Hersteller das V-Modell unterstützt.
242
Softwareentwicklung
Durch die exakte Beschreibung der Aktivitäten, durch zusätzliche Empfehlungen und Erläuterungen liefert das VM eine wertvolle und konkrete Hilfestellung bei der Projektdurchführung. Mit der Beteiligung der Industrie und der öffentlichen Hand an der Entwicklung und Fortschreibung wurde eine ausgewogene Sichtweise erreicht und keine firmenspezifische Vorgehensweise verankert. Dies wird verstärkt durch die öffentlich kontrollierte Fortschreibung durch eine jährliche Änderungskonferenz mit Industrie- und Behördenvertretern, bei der der erforderliche Einfluss der Anwender auf den Pflege- und Änderungsprozess des Vorgehensmodells gewährleistet werden soll. Die Rechte an einer Anwendung des V-Modells wurden von der öffentlichen Hand freigegeben. Das Modell ist somit Public Domain. Jeder kann es ohne Lizenzgebühren einsetzen. Literatur B. Boehm: A spiral model of software development and enhancement; IEEE Computer, Vol.21/5, S. 61-72 (1988) R. Budde, K. Kuhlenkampf, L. Mathiassen, H. Zullinghoven: Approaches to Prototyping; Springer Verlag Berlin 1984 GI Fachgruppe WI-VM : Vorgehensmodelle für die betriebliche Anwendungsentwicklung, http://www.vorgehensmodelle.de/
6.4 Selbstorganisierende Projektentwicklung Das klassische Phasenmodell ging davon aus, dass in dem Entwicklungsprozess jeder Beteiligte eine klare Aufgabe hat und dies, in mehreren Schichten hierarchisch gegliedert, auch organisatorisch durchgeführt werden kann. Dies ist aber in der Regel nicht so, weder im Umfeld (s. Abschnitt 6.1.10), noch bei dem Projektteam selbst. Um den vielen möglichen Missverständnissen und Gruppenkonflikten zu begegnen und ein Gegeneinanderarbeiten zum Nachteil des Projekts zu verhindern wurde ein kooperatives, selbstorganisierendes Organisationsmodell aufgestellt (Floyd 89) und weiterentwickelt (Pasch 94). In diesem Abschnitt sollen kurz die wichtigsten Aspekte dazu dargelegt werden. 6.4.1 Gruppenstrukturen Grundlage der selbstorganisierenden Prozesse ist die Gruppe. In ihr werden auf Basis gleichberechtigter, interaktiver Organisation ein Konsens in allen wichtigen Fragen ausgehandelt:
Selbstorganisierende Projektentwicklung
x x x x x x
243
die Arbeitsmethodik Denk- und Sprachschemata Art und Form der Beteiligung der Mitglieder akzeptables und nichtakzeptables Verhalten Art der zu entwickelnden Interaktion Rollenzuweisung der Mitglieder
Die Diskussion in der Gruppe muss überzeugend sein, sonst wirkt die persönliche Arbeit des Einzelnen automatisch gegen des Entwurf. Eine gute zwischenmenschliche Kommunikation Projektleitung
Plenum
übergeordnete Gruppen Projektgruppen
Abb. 6.23 Das „magische Dreieck“ der Projektgruppen
und Kooperation ist eine wichtige Grundlage dafür. Die dahinterstehende psychologische Theorie ist die „Themenzentrierte Interaktion“ (Cohn 79). Die Gruppen organisieren sich auf verschiedenen Ebenen. Ausgehend von den Projektgruppen (3-8 Mitglieder) bilden sich übergeordnete Gruppen für gemeinsame Themen sowie eine Projektleitungsgruppe. Eine personelle Überschneidung der Gruppen sorgt für einen Informationsaustausch und das Einbringen unterschiedlicher Perspektiven. Alle Gruppenmitglieder tagen auch als Projektgruppe in einem Plenum. In Abb. 6.23 ist dies dargestellt. Man beachte, dass in der Abbildung individuelle Mitglieder in verschiedenen Interessenlagen und damit in verschiedenen Rollen agieren können. Allgemein gibt es in diesem Modell folgende Rollen: x Der Projektleiter Seine Rolle, die Anbahnung von Lösungen, ist fest, seine Kompetenzen (Vetorecht etc.) müssen deutlich formuliert werden. x Der Supervisor Die zweite feste Rolle, ausgefüllt von einer Person außerhalb des Projektteams. Sie dient zur grundsätzlichen Aufarbeitung und Moderierung gruppendynamischer Prozesse.
244
Softwareentwicklung
x funktionelle Rollen Die Einzelrollen oder Gruppenrollen (Designgruppe, Testgruppe) sollten rotiert werden. Es gibt die Rollen des Plenum-Moderators, des Entwerfers, Programmierers, Testers, Qualitätssicherers, des Advocatus Diaboli, des Konfigurationsverwalters und des Beauftragten für Gruppendynamik. x Der Benutzer Diese funktionelle Rolle ist besonders hervorgehoben, da sehr wichtig. Der Benutzer spricht nur die Benutzersprache und versteht keine Entwicklersprache. Ist ein echter Benutzer nicht verfügbar, so muss ein Teammitglied als „virtueller Benutzer“ die Interessen und Anliegen der Benutzer vertreten. Im konkreten Fall fungiert er damit als interne Vertrauensperson der externen Benutzer. x Facilitator Dies ist ähnlich dem Supervisor, aber weniger gruppendynamisch. Der Facilitator überwacht formal die Diskussion und vermittelt als neutrale Person in Konfliktsituationen die Diskussionsart, aber nicht das Ergebnis. Bei allen Diskussionen in einer Gruppe ist klar, dass jeder eine bestimmte Perspektive als Diskussionskontext hat: x seine persönliche, erfahrungsbedingte Problemsicht x eine rollenspezifische Sicht (Leiter, Testgruppe, etc.) Gegenseitiges Verständnis wird durch Rollenwechsel erreicht. x eine Leitmetapher (z.B. „Werkzeug“, „Schreibtisch“, „Kommunizierende Prozesse“ etc.) x ein Gestaltungsanliegen (Entwicklungsziele, Qualitätskriterien,..) 6.4.2 Projektentwicklung im Plenum Zu Anfang eines Projekts formt sich die Selbstorganisation in drei Phasen: x lose Bildung der Gruppen Dies lässt sich am Besten durch eine „warming-up“ Sitzung beim Biertrinken, von sich erzählen etc. bewerkstelligen. Auch gruppendynamische Übungen durch ausgebildete Psychologen sind sinnvoll. x Etablieren der Projektgruppen Die Regeln der Zusammenarbeit und Kooperation, der Projektgestaltung, Wissensaneignung und Weitergabe sowie der nötigen Schulung werden hier geklärt. Das Ziel ist das schriftliche Festhalten von Regeln für den Entwurf, Entscheidungsfindung, Problemlösung und Konflikte in einem Projektvertrag. x Etablieren der Projektumgebung Diese Phase wird im Plenum durchgeführt. Sie klärt Fragen wie - Ist der Auftrag vom Auftraggeber klar?
Selbstorganisierende Projektentwicklung -
245
Welche Verpflichtungen und Erwartungen hat der Auftraggeber? Ist eine Durchführungsstudie nötig? Wie ist die Rolle des virtuellen Benutzers? Welche Rahmenbedingungen für Produktmuster und Standards gibt es? Welche Ressourcen sind nötig, vorhanden und möglich? In welchem Umfang?
Dabei müssen auch Konventionen festgelegt werden: - Terminplanung - wie? - eine gemeinsame Sprachbasis (Begriffe der Anwendung) muss gefunden und alle Definitionen, Erklärungen und Bedeutungsfestlegungen im Projektordner eingetragen werden - Prototypen: Warum und wann erstellt; wie ausgewertet? - Welche Aufgaben haben die übergeordneten Themengruppen und wie koordinieren sie sich ? - Welche Konventionen existieren für den Entwurf, für die Schnittstellen und die Programmierung? - Wie und welche Testdaten werden erstellt, welche Teststrategien verwendet? - Wie wird die Versionsverwaltung organisiert? Nach der Festlegung der Arbeitsumgebung kann man sich nun auf das eigentliche Projekt konzentrieren. In einer ersten Planungssitzung müssen im Plenum x die Teilaufgaben im Team verteilt werden. Dabei gilt auch das Geheimnisprinzip (information hiding), um sich auf die Schnittstellen zu konzentrieren. Die ganzheitliche Sicht des Projekts wird durch Austausch und Überprüfung von Arbeitsdokumenten, übergeordnete Themengruppen und Rotation der Rollen und Aufgaben erreicht. x das projektspezifische Prototyping festgelegt werden. Anzahl und Funktionsumfang (Vollständigkeit) müssen auf das Zielsystem und die Lebensdauer des Gesamtsystems abgestimmt werden. x die Bewertungsmerkmale der konstruktiven Kritik an der Arbeit der anderen festgelegt werden. Dies sind meist die Art der Qualitätsmerkmale, die Art der Kritikschwerpunkte sowie die typischen Arbeitsabläufe, um die Angemessenheit der Lösungen und der Testdaten festzulegen. Dabei ist besonders wichtig, das Geben und Nehmen bei konstruktiver Kritik bewußt auszugleichen und Kritik an Personen und Sachen zu trennen. Voraussetzungen solcher Gruppenprozesse sind dabei vor allem persönliche Kontakte und ein positives Gruppenerlebnis als Vertrauensgrundlagen. Die Plenumssitzungen sollten dazu eine Tagesordnung haben, die nicht durch den Projektleiter, sondern die gesamte Plenumsgruppe festgelegt wird. Das Plenum ist dabei zuständig für
246
Softwareentwicklung
x Lösung und Entscheidung der Probleme der Teilnehmer x Koordination und Delegation der Aufgaben x Abarbeitung der Aufgaben mit einer Alternative:
Lösung des Problems Delegation an eine Gruppe zur Lösung Delegation an eine Gruppe zur Analyse und Lösungsvorschlag Neuformulierung des Problems Verschiebung an einen anderen Termin
Dazu muss ein Protokoll von einem Protokollführer („Editor“) geführt werden. Die Plenumssitzungen schaffen bei den Beteiligten ein Verantwortungsbewußtsein und führen zur Weiterentwicklung der Projektmitglieder. Die Selbstorganisation des Projektteams wird dadurch erst möglich. Literatur R. Cohn: Von der Psychoanalyse zur themenzentrierten Interaktion; Klett-Cotta, Stuttgart 1979 C. Floyd, F.-M. Reisin, G. Schmidt: STEPS to Software development with users. In: Ghezzi, McDermid (Eds.)ESEC´89 2nd European Software Engineering Conference; Lectur Notes in Comp. Sc. 387, Springer Verlag Berlin 1989, pp. 48-64 J. Pasch: Software-Entwicklung im Team, Springer Verlag Berlin, 1994
6.5 CASE Werkzeuge zur Projektentwicklung Der Prozess des technischen Managements der Softwareproduktion und erstellung kann durch verschiedene Werkzeuge wie Organisationspläne, Programmier- und Dokumentationshilfen unterstützt werden. Dabei spielen die computerunterstützten Werkzeuge (Computer Aided Software Engineering CASE) eine immer größere Rolle für eine qualitativ hochwertige Software. In Abb. 6.24 ist eine solche systematische Architektur gezeigt.
CASE Werkzeuge zur Projektentwicklung
247
Benutzerschnittstelle
Planung
Projekt führung und Überwa chung
Definition
Import Export Schnitt stellen
Entwickl.
Implem.
Datenhaltung
Abb. 6.24 CASE-Basisfunktionalität
6.5.1 CASE Grundtechniken Gute CASE Werkzeuge sollten verschiedene Entwicklungsmodelle, sowohl das einfache Phasenmodell als auch das Wasserfallmodell, das Prototypingmodell und Spiralenmodell sowie das Objekt-Orientierte Entwicklungsmodell unterstützen um der konkreten Situation des Anwenders möglichst gerecht werden zu können. Innerhalb der Modelle werden verschiedene Basistechniken zur Strukturierung der Programmentwicklung verwendet, von denen wir bereits einige kennen gelernt haben. Ein gutes CASE System sollte möglichst viele davon unterstützen. Es sind dies x Funktionale Werkzeuge - Funktionsbäume: Aufgliedern der Funktionalität in Hierarchien (Welche komplexe Funktion benutzt welche einfache Funktionen) - Datenflussdiagramme x Datenorientierte Werkzeuge - data dictionary: Anlegen und Verwalten von Listen, in denen die Daten erklärt werden - Entity-Relationship Diagramme: In den Diagrammen existieren Einheiten (Objekte, Entitäten), zwischen denen Beziehungen (Relationen) exis-
248
Softwareentwicklung
tieren. Im erweiterten ER-Diagramm wird noch die maximale Anzahl der Entitäten pro Beziehung vermerkt - Jackson Diagramme: Dieser Ansatz gliedert die Daten und Kontrollkonstrukte baumartig auf.. x Kontrollorientierte Werkzeuge - Programmablaufpläne: Die Darstellung mit Ablaufplänen und Flussdiagrammen ist nicht so günstig, da damit wilde GOTO-Konstrukte möglich sind, die Verständnis und Wartung der Software nicht erleichtern. - Struktogramme: Die strukturierte Darstellung von Blockstrukturen mit strukturierten Diagrammen (z.B. Nassi-Shneiderman Diagramme) ermöglicht eine bessere Übersicht, falls dies durch Hierarchien in der Darstellung unterstützt wird (information hiding). - Pseudocode: Dies ist ebenfalls eine wichtige Form, sprachliche Aussagen zu formalisieren und leitet kontinuierlich in Spezifikationssprachen über. - Jackson-Diagramme: Auch für den Kontrollfluss ist die graphische Notation geeignet. x Zustandsorientierte Werkzeuge - Petri-Netze: Die Petri-Netze ermöglichen es, parallel stattfindende Aktionen und Bedingungen zu modellieren. - Zustandsautomaten: Manchmal hilft es auch, das Problem als System von Zuständen und Übergängen zwischen den Zuständen zu beschreiben. Dies führt zu einer formalen Beschreibung als Zustandsautomat, der direkt mittels eines Treibers in Programmcode (oder Pseudocode) umgesetzt werden kann. - Entscheidungstabellen und -bäume: Hat man zuerst das Wissen über viele konkrete Entscheidungen, aber keine Struktur, so ist es sinnvoll, zuerst dieses Wissen systematisch zusammenzustellen. Dies lässt sich in Form einer Tabelle durchführen, in der alle möglichen Aktionen sowie ihre Bedingungen aufgelistet und durchnumeriert werden (DIN66241). 6.5.2 CASE Architektur Eine sinnvolle CASE Architektur sollte den folgenden Forderungen genügen (s.Balzert 91) x Vollständigkeit Die wichtigsten Basistechniken sowie Funktionen zur Softwareerstellung, Softwaremanagement und allgemeine Bürofunktionen sollten unterstützt werden, je mehr, desto besser. Dabei müssen die Werkzeuge und Techniken sich aber inhaltsmäßig ergänzen (semantische Integration). x Integration aller Werkzeuge für Basistechniken Die Werkzeuge sollten zusammen funktionieren, also Daten austauschen können (syntaktische Integration:Datenformate!) und funktionsmäßig sich er-
CASE Werkzeuge zur Projektentwicklung
249
gänzen. Hier helfen Pipeline-, OLE, CORBA und Datenbankkonzepte. Eine Skizze zeigt die gewünschte Funktionalität. x Einheitliche Benutzeroberfläche Eine einheitliche Bildschirmdarstellung und Interaktion (Fenster, Mausaktionen, Rollbalken, Hilfesystem, Dialogführung etc.) ermöglichen eine schnelle Eingewöhnung und leichteres Arbeiten. x Benutzungsfreundlichkeit Dies schließt nicht nur eine einheitliche Benutzeroberfläche ein, sondern auch Forderungen wie Adaptierbarkeit der Oberfläche, Konfigurierbarkeit der Werkzeuge, ein einheitliches Hilfesystem, Unterscheidung der Benutzer nach Erfahrung, Makro-Fähigkeit für Funktionstasten sowie interaktives, nicht-batch orientiertes Arbeiten. x Erweiterbarkeit und Offenheit Es ist sehr vorteilhaft, wenn am Anfang nicht alle Werkzeuge vorliegen müssen, neue Versionen direkt eingesetzt und verwendet werden können oder Produkte von anderen Anbietern (Textverarbeitungssysteme!) und Eigenentwicklungen integriert werden können. Auch eine Zusammenfassung von mehreren kleinen zu einem großen Werkzeug ist möglich. x Teamfähigkeit Für die gemeinsame Arbeit sind Netzwerkfähigkeiten (Dateisperrung, Versionskontrolle im Netz etc.) und Unterstützung von Kommunikation (mailDienste, Kalenderfunktionen etc.) sinnvoll. x Portabilität, Anpaßbarkeit Die Entscheidung für ein CASE-System und die Einarbeitung ist eine langfristige Investition die kurzlebige Hardwaregenerationen weit überdauert. Die Projektarbeit darf bei einem Wechsel der Hardwaregeneration oder des Herstellers nicht beeinträchtigt werden. Aus diesem Grund sollte die CASE Software nicht zu stark auf einen Computerhersteller und seine Produktlinie zugeschnitten sein, um leicht auf neue Hardware und Betriebssysteme übertragen werden zu können; Maschinenabhängigkeiten sauber dokumentiert und modularisiert sein. Eine typische Architektur ist in Abb. 6.25 zu sehen.
250
Softwareentwicklung
CASE System
Benutzeroberfläche User Interface Management System
Werkzeug 1
Werkzeug 2
xxx
Werkzeug n
CASE Datenbank Verwaltung aller Daten
CASE System Schnittstelle Anpassung ans Betriebssystem
Betriebssystem Hardware Abb. 6.25 Basisarchitektur eines CASE-Systems
Literatur H. Balzert (Hrsg.): CASE-Systeme und Werkzeuge, BI Wissenschaftsverlag, Mannheim 1991
6.6 Risikomanagement Ein wichtige, prozessbegleitende Aktivität des Projektmanagements ist die Analyse und Überwachung der verschiedenen Risiken während des Projektablaufs. Man kann auch hier einige Phasen unterscheiden: 1.
Risikoidentifikation: Alle möglichen, denkbaren Risiken werden in einer Liste erfasst.
2.
Risikoanalyse: Die Auftrittswahrscheinlichkeit der Risiken wird abgeschätzt und die möglichen Konsequenzen erfasst. Dazu werden die Listeneinträge mit einer Wichtigkeit (Priorität) versehen. Dies stützt sich größtenteils auf Erfah-
Risikomanagement
251
rungen mit früheren Projekten (Datenbank!) und sollte nur mit groben Kategorien durchgeführt werden. Ein Beispiel ist in Abb. 6.26 zu sehen.
Risiko Finanzielle Probleme verursachen eine Budgetkürzung des Projekts Es ist unmöglich, alle nötigen Positionen des geplanten Entwicklerteams zu besetzen. Der Teamleiter oder Entwickler mit Schlüsselposition wird krank Benutzte Bibliotheken enthalten versteckte Fehler Die Produktanforderungen werden nachträglich verändert, so dass ein komplettes Re-Design nötig ist. Umstrukturierungen innerhalb der Firma verändern das Management, das für das Projekt verantwortlich ist Die für das Produkt vorgesehene Datenbank ist nicht performant genug Der zeitliche Aufwand, um die Vorgaben zu erfüllen, wurde unterschätzt. Die verfügbaren CASE-Tools können nicht in das Projekt integriert werden. Die Kunden haben ein falsche Bild von den technologischen Folgen ihrer veränderten Anforderungen. Die für das Projekt benötigten Schulungen sind nicht verfügbar. Die Größe des zu entwickelnden Projekts wurde unterschätzt. Der Personalaufwand, um das Produkt fehlerfrei ausliefern zu können, wird unterschätzt. Der vom CASE-Tool generierte Code ist ineffizient.
Auftrittswahrscheinlichkeit
Auswirkung
niedrig
katastrophal
hoch
katastrophal
mittel
ernsthaft
mittel mittel
ernsthaft ernsthaft
hoch
ernsthaft
mittel
ernsthaft
hoch
ernsthaft
hoch
tolerierbar
mittel
tolerierbar
mittel
tolerierbar
mittel
tolerierbar
hoch
tolerierbar
mittel
unwichtig
Abb. 6.26 Mögliches Ergebnis einer Risikoanalyse
3.
Risikoplanung: Für das Entschärfen bzw. Minimieren der Risiken werden Pläne und Alternativen gesucht und notiert. Hier ist das Gefühl und die Erfahrung der Projektleitung ausschlaggebend. In Abb. 6.27 sind als Beispiele einige Probleme und ihre möglichen LösungsAlternativstrategien aufgelistet.
252
Softwareentwicklung Risiko
Alternativstrategie
Finanzielle Probleme
Ein Treffen mit dem Management wird vorbereitete, bei dem die Wichtigkeit des Projekts für die Firma erläutert wird; alternative Geldgeber werden gesucht. Der Kunde wird auf Schwierigkeiten und Verzögerungen vorbereitet; Erwägung des Einkaufs fertiger Komponenten. Umorganisation des Entwicklerteams derart, dass die Überlappung der Kompetenzen vergrößert wird. Die Bibliotheken durch stabile, hinzugekaufte , fertige Komponenten ersetzen. Mehrschichtige Abstraktionsebenen vorsehen, modulares Design und Information-Hiding (verbindliche Schnittstellendefinition!) anstreben. Ein Briefing-Dokument für das neue Management vorbereiten, in dem die Wichtigkeit des Projekts beschrieben wird. Eine Marktanalyse leistungsfähigerer DatenbankBackends vorbereiten Fertige Komponenten einkaufen; bessere Werkzeuge,
Unzureichend großes Entwicklerteam Krankheit im Entwicklerteam Fehlerhafte Bibliotheken Veränderte Produktanforderungen ManagementUmstrukturierung Datenbankperformanz Unterschätzte
Abb. 6.27 Risiken und mögliche Minimisierungsstrategien
4.
Risikoüberwachung: Dieser Risikoplan wird ständig überwacht und aktualisiert.
Treten unerwartet neue Risiken auf, so muss man erneut bei Phase 2 beginnen.
Abbildungsverzeichnis
Abb. 1.1 Abb. 1.2 Abb. 1.3 Abb. 1.4 Abb. 1.5 Abb. 1.6 Abb. 1.7 Abb. 1.8 Abb. 1.9 Abb. 1.10 Abb. 1.11 Abb. 1.12 Abb. 1.13 Abb. 1.14 Abb. 1.15 Abb. 1.16 Abb. 1.17 Abb. 1.18 Abb. 1.19 Abb. 1.20 Abb. 1.21 Abb. 1.22 Abb. 1.23 Abb. 1.24 Abb. 1.25 Abb. 1.26 Abb. 1.27 Abb. 1.28 Abb. 1.29 Abb. 1.30 Abb. 1.31 Abb. 1.32 Abb. 1.33 Abb. 1.34 Abb. 1.35 Abb. 1.36 Abb. 1.37 Abb. 1.38 Abb. 1.39 Abb. 1.40 Abb. 1.41
Gliederung einer Computeranlage .....................................................................1 Einige ASCII-Buchstabenkodierungen ...............................................................2 Die ASCII-Buchstabenkodierung........................................................................3 Auslegung des Unicodes .....................................................................................4 Die Pixelkoordinaten der Rastergrafik ...............................................................6 Ein Buchstabe (a) im 5×7-Raster und (b) als Umrandung .................................7 Grobe funktionelle Aufgliederung der Recheneinheit .........................................8 Direkte Verbundarchitektur eines Rechners .......................................................9 Busarchitektur eines Rechners............................................................................9 Typische Architektur eines modernen PCs........................................................12 Hochsprache, Assembler und Kommentar der Fakultätsberechnung ..............14 Die Struktur eines von-Neumann-Prozessors ...................................................15 CISC-Grundstruktur .........................................................................................17 Schema eines Computersystems ........................................................................21 Zusammensetzung der Prozessdaten.................................................................22 Prozesszustände und Übergänge ......................................................................23 Preemptives Scheduling ....................................................................................24 Benutzungsrelationen von Programmteilen ......................................................25 Schichtenmodell und Zwiebelschalenmodell.....................................................25 Überblick über die Rechnersoftwarestruktur ....................................................26 UNIX-Schichten ................................................................................................27 Schichtung und Aufrufe bei Windows NT .........................................................29 Echte und virtuelle Maschinen..........................................................................30 Hierarchie virtueller Maschinen.......................................................................31 Virtuelle, logische und physikalische Geräte....................................................33 Software-Hardware-Migration des Java-Codes ...............................................34 Grundschichten der Geräteverwaltung.............................................................35 Die Schichtung und Schnittstellen des UDI-Treibers........................................36 Die Dateisystemschichtung unter Linux............................................................37 Das Stream-System in UNIX .............................................................................38 Einfache und multiple Schichtung in Windows NT ...........................................38 Adressraum, Controller und Gerät ...................................................................39 Adressreservierungen der Standardperipherie eines PC ..................................40 Plattenspeicheraufbau ......................................................................................42 Neugruppierung der Plattenbereiche in Streifen ..............................................46 Spiegelplattenkonfiguration zweier Platten ......................................................47 Datenzuweisung beim RAID-2-System..............................................................49 Wechselseitige Abspeicherung der Fehlertoleranzinformation ........................50 Block interleaving .............................................................................................52 Überblick über die ACPI Energieverwaltung ...................................................56 Die Zustände des ACPI-Systems und ihre Übergänge ......................................57
254
Abbildungsverzeichnis
Abb. 2.1 Abb. 2.2 Abb. 2.3 Abb. 2.4 Abb. 2.5 Abb. 2.6 Abb. 2.7 Abb. 2.8 Abb. 2.9 Abb. 2.10 Abb. 2.11 Abb. 2.12 Abb. 2.13 Abb. 2.14 Abb. 2.15 Abb. 2.16 Abb. 2.17 Abb. 2.18 Abb. 2.19 Abb. 2.20 Abb. 2.21 Abb. 2.22 Abb. 2.23 Abb. 2.24 Abb. 3.1 Abb. 3.2 Abb. 3.3 Abb. 3.4 Abb. 3.5 Abb. 3.6 Abb. 3.7 Abb. 3.8 Abb. 3.9 Abb. 3.10 Abb. 3.11 Abb. 3.12 Abb. 3.13 Abb. 3.14 Abb. 3.15 Abb. 3.16 Abb. 3.17 Abb. 3.18 Abb. 3.19 Abb. 3.20 Abb. 3.21 Abb. 3.22 Abb. 3.23 Abb. 3.24 Abb. 3.25 Abb. 3.26
Ein typisches lokales Netzwerk .........................................................................59 Subnetze und Backbone eines Intranets ............................................................60 Das Open System Interconnect (OSI)-Modell ...................................................62 Die Kapselung der Daten und Kontrollinformationen......................................64 Oft benutzte Protokollschichten in UNIX..........................................................65 OSI-Modell und Windows NT-Netzwerkkomponenten ......................................66 Virtual Private Network ....................................................................................66 Mögliche Aufteilungen der IP-Adresse (Version 4) ..........................................69 Ein homogener Dateibaum im lokalen Netz......................................................72 Lokale, inhomogene Sicht des Dateibaums.......................................................72 Auflösung von Netzwerk-Dateianfragen ...........................................................73 Zuordnung von Portnummern und Diensten.....................................................75 Kommunikation über Transportendpunkte .......................................................75 Kommunikationsablauf im Socket-Konzept.......................................................76 Das Transportschema eines RPC......................................................................80 Ablauf eines synchronen RPC...........................................................................80 Die big endian- und little endian-Bytefolgen....................................................81 Die Schichtung der RPC in Windows NT..........................................................82 Verhältnis von Sprache zu Datenaufkommen als Funktion der Zeit .................83 Wandlungen des Sprachsignals und Transport über das Netzwerk ..................84 Die Digitalisierung des Sprachsignals beim P-law...........................................84 Die Verbindungsschichten bei VoIP .................................................................86 Übergang von normaler Nebenstellenanlage PBX zur IP-PBX.......................87 Automatische Anrufverteilung...........................................................................88 Ein verteiltes Betriebssystem.............................................................................93 Cachekohärenz bei gleichen und unterschiedlichen Puffern ............................97 Übertragungsschichten und Pufferorte für Dateizugriffe im Netz ...................98 Implementierung eines Netzdateiservers auf Prozessebene ............................100 Implementierung eines Netzdateiservers auf Treiberebene.............................101 Die Architektur des NFS-Dateisystems ...........................................................102 Implementierung des Netzdateisystems in Windows NT .................................102 Ein Speichernetzwerk......................................................................................105 Das SAN Schichtenmodell nach SNIA.org ......................................................106 Das NAS-Modell .............................................................................................107 Das Konzept des Schattenservers (shadow server) .........................................113 Struktur des Coda-Dateisystems .....................................................................114 Heterogenität als Problem ..............................................................................118 Middleware als Vermittlungsschicht...............................................................119 Schichtung der Middleware zur Kommunikation............................................120 BeispielMiddleware: SAP/R3 3-tiers Architektur ..........................................120 Architekturschichten von Jini..........................................................................121 Thin clients/Thick server durch Middleware...................................................122 Gesamtschichtung der .NET-Architektur ........................................................123 Die Infektion eines Programms.......................................................................130 Ablauf beim login............................................................................................133 Die klassische fire-wall-Konfiguration ...........................................................136 Die dual-homed-host-Konfiguration ............................................................136 Die Matrix der Zugriffsrechte .........................................................................137 Sitzungsausweis und Transaktionsausweis .....................................................139 Transaktions (Service-)durchführung .............................................................140
Abbildungsverzeichnis Abb. 4.1 Abb. 4.2 Abb. 4.3 Abb. 4.4 Abb. 4.5 Abb. 4.6 Abb. 4.7 Abb. 5.1 Abb. 5.2 Abb. 5.3 Abb. 5.4 Abb. 5.5 Abb. 5.6 Abb. 5.7 Abb. 5.8 Abb. 5.9 Abb. 5.10 Abb. 5.11 Abb. 5.13 Abb. 5.14 Abb. 5.15 Abb. 5.16 Abb. 5.17 Abb. 5.18 Abb. 5.19 Abb. 5.20 Abb. 6.1 Abb. 6.2 Abb. 6.3 Abb. 6.4 Abb. 6.5 Abb. 6.6 Abb. 6.7 Abb. 6.8 Abb. 6.9 Abb. 6.10 Abb. 6.11 Abb. 6.12 Abb. 6.13 Abb. 6.14 Abb. 6.15 Abb. 6.16 Abb. 6.17 Abb. 6.18 Abb. 6.19 Abb. 6.20 Abb. 6.21 Abb. 6.22 Abb. 6.23 Abb. 6.24 Abb. 6.25
255
Datenzugriff bei Einzellösungen .....................................................................144 Problem paralleler Datenzugriffe: Beispiel Geld einzahlen ...........................146 Organisation paralleler Datenbankzugriffe....................................................147 Die drei Schichten der Datenbanksicht...........................................................149 Architektur eines Datenbank-Managementsystems.........................................150 Zugriffsarchitektur eines Datenbanksystems...................................................151 Erstellen eines Data Warehouses und seiner Ableger ....................................152 Zubereitungsvorschrift einer quiche lorraine..................................................155 Der Babylonische Turm der Programmiersprachen.......................................157 Schnittstelle und Implementierung eines abstrakten Datentyps......................163 Eine Vererbungshierarchie .............................................................................164 Objekthierarchie von Fahrzeugen...................................................................165 Klassendiagramm eines Autos ........................................................................168 Zustandsdiagramm eines Autos ......................................................................169 Datenflussdiagramm eines Autos....................................................................170 Evaluationsgraph eines Ausdrucks .................................................................173 Fakultätsberechnung in Miranda ...................................................................175 Datenflussgraph zur Berechnung eines Polynoms..........................................182 Datenflussgraph eines C-Programms .............................................................185 Die Rückkopplung graphischer Programmierung ..........................................195 Visualisierung mit Torten- und Balkendiagrammen .......................................196 Visuelle Spezifikation der Fakultätsberechnung .............................................198 Ein Pascal Programm in hierarchischen Puzzleteilen....................................199 IGS-Modell „Autofabrik“: Weiche .................................................................200 IGS-Modell „Autofabrik“: Lackhalle .............................................................200 Datenobjekte und visuelle Entsprechungen ....................................................203 Das Phasendiagramm.....................................................................................208 Organigramm der Projektorganisation ..........................................................209 Balkendiagramme ...........................................................................................210 Erster und zweiter Schritt des Netzplanaufbaus. ............................................211 Der vollständige Netzplan der Aktivitätenliste................................................211 Aktivitäts-Balkendiagramm des Beispiels .......................................................212 Optimierung der Kaufentscheidung ................................................................213 Untergliederung und Aufbau der Funktionen.................................................215 Entity-Relationship Diagramm der Weiterbildung .........................................216 Strukturierte Datenflussanalyse ......................................................................217 Jackson-Diagramm einer Datenstruktur.........................................................218 Entscheidungsbaum zur Robotersteuerung.....................................................220 Datenfluss beim Test .......................................................................................223 Online black box Test......................................................................................223 Stufen im Reifemodell .....................................................................................228 Einhaltung der Pläne im Reifeprozess ...........................................................228 Projektphasen und Prüfschritte ......................................................................231 Der objektorientierte Entwicklungszyklus.......................................................233 Das Spiralenmodell.........................................................................................235 Die Quadranten des Spiralenmodells .............................................................236 Interaktion der Submodelle.............................................................................239 Beispiel: Aktivitäten- und Produktfluss zwischen den vier Submodellen ........239 Das „magische Dreieck“ der Projektgruppen................................................243 CASE-Basisfunktionalität ...............................................................................247 Basisarchitektur eines CASE-Systems.............................................................250
256
Abbildungsverzeichnis
Abb. 6.26 Mögliches Ergebnis einer Risikoanalyse ........................................................251 Abb. 6.27 Risiken und mögliche Minimisierungsstrategien ............................................252
Index
A 10BaseT 63 Abarbeitungsreihenfolge 173 abstrakte Datenstruktur 162 abstrakter Datentyp 162 Access Control Lists 136 Siehe ACL ACL 103, 104, 134, 135, 136 active directory 115 Adressbus 9 Adreßregister 43 Adreßwerk 18 ADS 71, 104 Algorithmus 155 alignment 81 Allquantor 191 ANSI 3 Antikörper 132 Anweisungen 156 Anwendungsschicht 62 ASCII 2, 3, 4 Assemblercode 14 asynchron 45, 55 asynchrone RPC 80 atomare Formel 186 atomic transaction 95 Ausfalltoleranz 47, 48 Ausgaberegister 45 Aussagenlogik 186 Authentication Protocol 140 Authentication Server 138 Authentifikation 104, 134, 140 Autorisierung 133
B Befehlsregister 43 Belegung 187 Benutzerdokumentation 225
Benutzerkennung 133 Benutzeroberfläche 249 Benutzerschnittstelle 26 bereit-Liste 23 bereit-Zustand 23 Betriebsmittel 21 Betriebssystemkern 26 big endian 80, 81 block device 54 bootstrap 130 Bottom-up Entwurf 215 broadcast 78, 79 Bus 9 bus control 10 busy wait 10
C Cache 44, 53, 54, 96, 98, 99, 110, 113 call-by-name 172 call-by-value 172 Capability-oriented System 137 CASE Werkzeuge 246 CISC 17 Client 29 Client-Server 91, 94 close-to-open consistency 99 Codegenerierung 204 COM 122 Compilezeit 204 computing server 91, 93 context switch 22 Coprozessor 15 CORBA 121, 122 Crosscompiler 204
D DAP 70 data dictionary 167, 216, 217, 218, 247
258
Index
data marshaling 81 Datenbus 10 Datenfluss 169, 179, 180 Datenflussprinzip 180 Datenflussprogramme 180 Datenflussrechner 181 Datenflusssprachen 182 Datenpuffer 43 Datex-P 63 DCOM 122 delayed write 98, 99 Dienste 62, 79, 91, 93, 94, 126, 127, 134, 135 Dienstleistungen 30 directory server 93 DISP 71 Dispatcher 23, 24 DMA-Kanal 40, 44 DNS 70, 116 domain controller 134 Domäne 68 DSP 71 dual homed host 136
E Eingaberegister 45 Elternprozeß 22 Entity-Relationship 216 Entscheidungsbaum 220 Entscheidungstabellen 218 Entwicklungsdokumentation 226 ESCD 40 Ethernet 63 EUC 4 evolutionären Programmierentwicklung 234 Existenzquantor 191 explorative Entwicklung 233 extended markup language 81 External Data Representation 81
F Fähigkeiten 137 Fakten 194 FDDI 63 Fehlerkorrektur 48, 49, 51 file server 91, 93
File Transfer Protocol 70 fire wall 135, 136 Flaschenhals 18 floating licence 109 floppy disk 41 Flusssteuerung 45 Formel 186 frames 63 FtDisk 51
G G.711 86 G.723.1 86 garbage collection 111 ghostview 127 Glossar 167, 168 Gruppenkennung 133
H H.323 85 Hacker 124 HAL 29 Header 63 Hexadezimalcode 3 Hornformeln 188 Hornklauseln 190 HPFS 30 HTML 117, 125 HTTP 117
I I/O Request Package 38 Implementierungsdokumentation 225 information hiding 161, 248 inkrementelle Compiler 204 i-node 101 Instantiierung 163 Instanz 163 instruction decode 17 instruction fetch 17 Instruktionen 13 Interleaving 52 Internet 68 Interoperabilität 119 Interrupt 10 Interruptleitungen 10
Index Interrupt-Priorität 11 Interruptsystem 43, 45 Intranet 110 IP 63, 65, 67, 69, 74, 76, 77, 83 IP-PBX 87 IRP 38, 74, 103 ISDN 85, 87 ISO-OSI Schichtenmodell 62 IT-Konsolidierung 118
J Jackson-Diagramme 218 JAVA 111 JAVA Virtual Machine 111 Jini 121 Job 22 Jobmanagementsystem 108, 109
K kanonisch 174 Katastrophenhandbuches 225 Kerberos 104, 136, 138, 141, 142 Key Distribution Protocol 139 Kindsprozeß 22 Klassenhierarchie 165 Klauseln 189 Kommunikationspunkte 74 Kompatibilität 29 Kontrollfluss 179 Kontrollflussprinzip 179 Kontrollregister 43, 45 Korrektheit 189
L LAN 71 LAPD 63 Lastverteilung 108 Laufzeit 204 Laufzeitbibliothek 205 Laufzeitsystem 204 lazy evaluation 115 LDAP 71 legacy devices 40 lexikalische Analyse 204 Link Access Procedure for D-channels 63
259
Linux 114 Literal 186 little endian 80, 81 LLC 63 LOAD/STORE Architektur 20 local area network 71 Lockvogel 131 Logical Link Control 63 Logikprogramm 194 logische Festplatte 32 logische Maschinen 32 LPC 29
M mailbox 74, 79, 133 mailslot 78 Makrovirus 131 Maschinencode 32 Mehrbenutzerbetrieb 21 Mehrprogrammbetrieb 21 Meilensteine 207 memory mapped I/O 9, 39 memory mapping 54 Mengengerüst 214 message 22 Middleware 118, 119, 120, 122, 123 Mikroprogramm 17 Mikro-ROM 17 mirror 47 mobile computing 114 mount 73, 101 MS-DOS 28 MS-DOS/Windows 29 multicast 78 Multiprogramming 22, 27 multi-tasking 22 multi-user 27
N Nachrichtenpakete 61 name server 70 named pipe 77 named pipes 65, 77 Nassi-Shneiderman Diagramme 248 NBT 65 NC Siehe Netzcomputer
260
Index
NDR Siehe Network Data Representation Nebenwirkungen 174 NetBIOS 65 Network Access Layer 63 Network Data Representation 82 Network File System 73, 82 Network Information System 103 Netzcomputer 109, 111 Netzwerkcontroller 65, 69 Netzwerkschicht 63 NFS 72, 73, 74, 77, 82, 99, 101, 102, 103, 104 NIS Siehe Network Information System NLS 4 NNTP 117 node locking 109 non-preemptive scheduling 23 Northbridge 12
O Objekt 163 Objektklassen 164 Objektorientierte Entwicklung 232 Objektorientierte Modellierung 167 Offline-Aktualisierung 115 Opcode 15 Operanden 15 operating system 26 Operationssemantik 95, 98, 99 ORB 121 orthogonaler Befehlssatz 20
P paging 44 Paritätsbildung 48 Paßwort 124, 125, 126, 127, 132, 133, 134, 138 PBX Siehe Private Branch Exchange PCM 84, 85, 86, 89 Peripheriegeräte 9 Pflichtenheft 214 Phasen 207 physikalische Ebene 63 physikalischen Maschinen 32 Pixelebene 7 Pixeln 6
pixmaps 7 Plattenspeicher 41, 42 Plenum 243 Plug-and-Play 40 polling 10 polymorphe Viren 130 Polytypen 177 POP 117 Portabilität 249 Portierbarkeit 27, 29 Portierung 27 Ports 74, 75 POSIX 4, 28 power failure 53 Prädikate 191 Prädikatenkalkül 191 Prädikatenlogik 191 Präsentationsebene 62 preemptive scheduling 24 print server 91, 93 Private Branch Exchange 87 process server 93 program counter 16 Programminterne Dokumentation 226 Projektleitungsgruppe 243 Protokoll 62, 63, 65, 67, 69, 70, 75, 79, 83, 95, 101 Prozedurklauseln 194 Prozeß 22 Prozeßgruppe 46 Prozeßkontext 22 Prozeßzustände 23 Pufferung 53
Q QoS 86, 87 Qualitätsmodelle 227 Qualitätszertifikate 227 Quality of Service 86
R RAID 46, 47, 48, 49, 50, 51 RAID-linear 46 RAM-Disk 44 Random Access Maschine 16 Rapid Prototyping 234 Rastergrafik 6
Index raw device 54 raw interface 37 read ahead 96, 99 ready 22 Rechenwerk 16, 18 Rechnerfamilien 18 Redirector 66, 73, 74, 102, 103 Reduced Instruction Set Computer 20 Reduktion 190 referentielle Transparenz 174 Registerfiles 20 Reifemodell 227, 228, 229 Remote Procedure Call 79 Resolutionssatz 192 RISC 20 Roaming 112 Robustheit 29 Rolle 137 router 135, 136 RPC 79, 80, 81, 82, 99, 104 RTP 86 running 22
S SAM Siehe Security Account Manager Scheduler 23 Scheduling 23 Schichtenmodell 30 Schnittstelle 31, 33, 34, 39, 45, 53 screening 135 section object 54 Security Account Manager 104 Sektor 41, 53 semantic gap 17 semantischen Analyse 204 Server 29 Servospuren 42 shell 26, 132 shutdown 54 Sicherheitsprotokoll 126 Signal 23 Single Sign on Protocol 138 Sitzungsebene 62 Sitzungssemantik 95, 99 SMB 65, 66, 103 SMTP 117 snooper 113
261
Socket 76, 77, 78 Southbridge 12 special file 77 Speicherkapazität 47 Spiegelplatten 47, 51 Spiralenmodell 235 Spur 41, 42, 43, 53 stack pointer 16 Stapelverarbeitung 21 Statusregister 16 Steuerwerks 15 stream system 37 Streifen 46, 47, 48 stripes 51 Strukturierte Analyse 217 Stub Procedure 79 symbolic link 73 synchron 45, 55 synchrone RPC 80 syntaktischen Analyse 204
T task 22 Tautologie 187 TCP 62, 65, 67, 69, 74, 76, 77, 83 TCP/IP 67 Teilformeln 187 thread 55, 103 TLI 75 token 204 Top level Domäne 68 Top-down Strukturierung 215 Transaction Grant Server 140 Transaktionssemantik 95, 96 Translator 204 Transmission Control Protocol 67 Transport Driver Interface 103 Transport Layer Interface 75 Transportschicht 62, 65, 75, 79, 103 trap-function 138 Treiber 26, 34, 35, 37, 39, 44, 45, 52, 53 Trojanisches Pferd 126 trust relationship 134 twisted pair 63
262
Index
U UDI 35 UDP 62, 86 Übersetzer 204 UNC 73, 74, 77 Unicode 3, 4 Uniform Driver Interface 35 Uniform Resource Locator 70 Universal Naming Convention Siehe UNC UNIX-98 28 User Datagram Protocol 62
V Validierung 224 VDM 29 Vektorgrafik 6 verbindungslose Kommunikation 63 verbindungsorientiert 61 Vererbung 164 Verifikation 227 verifizieren 224 verteiltes Betriebssystem 92, 93 verteiltes Computersystem 92 Viren 130, 131, 132 virtual circuits 103 virtuelle Festplatte 32 virtuelle Maschine 32, 34, 62 virtueller Prozessor 22 Visualisierung 196 Visuelle Programmierung 198 Visuelle Spezifikation 197 visuelle Training 201 VoiceOverIP 84, 87 Vollständigkeit 189 volume 47 VoWLAN 85
Druck: Saladruck, Berlin Verarbeitung: Stein+Lehmann, Berlin
W Wächter 174 WAN 70 Warteschlange 22, 23, 24 Wartung 112 Wasserfallmodell 231 web browser 111 well known port number 74 wide area network 70 Win32 29 Windows 2000 30, 71, 115 Windows NT Executive 28 Windows XP 115 write on close 99 write through 98, 99 WWW 111, 117, 126
X X.25 63 X/Open 27 XDR Siehe External Data Representation XML 81 XON/XOFF 45
Z Zeigeinstrument 6 Zeitscheibe 24 Zugriffsrechte 96, 103, 104, 126, 127, 128, 129, 132, 133, 134, 135 Zugriffsschnelligkeit 47 Zugriffssemantik 95 Zustand 22 zustandsbehaftete Server 96 zustandslose Server 96 Zwischencode 204 zylinder skew 52 Zylindergruppe 41