/
Author: Mössenböck H. Rechenberg P. Liebig H. Flik T. Reinefeld A.
Tags: mikroprozessoren technische informatik rechnerarchitektur digitale systeme
ISBN: 978-3-662-44391-0
Year: 2012
Text
Das Ingenieurwissen: Technische Informatik
Hans Liebig • Thomas Flik • Peter Rechenberg
Alexander Reinefeld • Hanspeter Mössenböck
Das Ingenieurwissen:
Technische Informatik
Hans Liebig
TU Berlin
Berlin, Deutschland
Alexander Reinefeld
Zuse-Institut
Berlin, Deutschland
Thomas Flik
TU Berlin
Berlin, Deutschland
Hanspeter Mössenböck
Universität Linz
Linz, Österreich
Peter Rechenberg
Universität Linz
Linz, Österreich
ISBN 978-3-662-44390-3
DOI 10.1007/978-3-662-44391-0
ISBN 978-3-662-44391-0 (eBook)
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie;
detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
Springer Vieweg
Das vorliegende Buch ist Teil des ursprünglich erschienenen Werks „HÜTTE – Das Ingenieurwissen“, 34.
Auflage, Heidelberg, 2012.
© Springer-Verlag Berlin Heidelberg 2014
Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Jede Verwertung, die nicht ausdrücklich vom Urheberrechtsgesetz zugelassen ist, bedarf der vorherigen Zustimmung des Verlags. Das
gilt insbesondere für Vervielfältigungen, Bearbeitungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.
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 Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann
benutzt werden dürften.
Gedruckt auf säurefreiem und chlorfrei gebleichtem Papier
Springer Vieweg ist eine Marke von Springer DE. Springer DE ist Teil der Fachverlagsgruppe Springer
Science+Business Media.
www.springer-vieweg.de
Vorwort
Die HÜTTE Das Ingenieurwissen ist ein Kompendium und Nachschlagewerk für unterschiedliche Aufgabenstellungen und Verwendungen. Sie enthält in einem Band mit 17
Kapiteln alle Grundlagen des Ingenieurwissens:
– Mathematisch-naturwissenschaftliche Grundlagen
– Technologische Grundlagen
– Grundlagen für Produkte und Dienstleistungen
– Ökonomisch-rechtliche Grundlagen
Je nach ihrer Spezialisierung benötigen Ingenieure im Studium und für ihre beruflichen
Aufgaben nicht alle Fachgebiete zur gleichen Zeit und in gleicher Tiefe. Beispielsweise
werden Studierende der Eingangssemester, Wirtschaftsingenieure oder Mechatroniker in
einer jeweils eigenen Auswahl von Kapiteln nachschlagen. Die elektronische Version der
Hütte lässt das Herunterladen einzelner Kapitel bereits seit einiger Zeit zu und es wird
davon in beträchtlichem Umfang Gebrauch gemacht.
Als Herausgeber begrüßen wir die Initiative des Verlages, nunmehr Einzelkapitel in
Buchform anzubieten und so auf den Bedarf einzugehen. Das klassische Angebot der
Gesamt-Hütte wird davon nicht betroffen sein und weiterhin bestehen bleiben. Wir wünschen uns, dass die Einzelbände als individuell wählbare Bestandteile des Ingenieurwissens ein eigenständiges, nützliches Angebot werden.
Unser herzlicher Dank gilt allen Kolleginnen und Kollegen für ihre Beiträge und den
Mitarbeiterinnen und Mitarbeitern des Springer-Verlages für die sachkundige redaktionelle Betreuung sowie dem Verlag für die vorzügliche Ausstattung der Bände.
Berlin, August 2013
H. Czichos, M. Hennecke
VI
Vorwort
Das vorliegende Buch ist dem Standardwerk HÜTTE Das Ingenieurwissen 34. Auflage
entnommen. Es will einen erweiterten Leserkreis von Ingenieuren und Naturwissenschaftlern ansprechen, der nur einen Teil des gesamten Werkes für seine tägliche Arbeit braucht.
Das Gesamtwerk ist im sog. Wissenskreis dargestellt.
Inhaltsverzeichnis
Technische Informatik
H. Liebig, Th. Flik, P. Rechenberg, A. Reinefeld, H. Mössenböck
Mathematische Modelle
H. Liebig, P. Rechenberg
1
Boole’sche Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1 Logische Verknüpfungen und Rechenregeln . . . . . . . . . . . . . . . . . . . .
3
3
1.1.1 Grundverknüpfungen – 1.1.2 Ausdrücke – 1.1.3 Axiome – 1.1.4 Sätze
1.2
Boole’sche Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.2.1 Von der Mengen- zur Vektordarstellung – 1.2.2 Darstellungsmittel
1.3
Normal- und Minimalformen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.3.1 Kanonische Formen Boole’scher Funktionen – 1.3.2 Minimierung
von Funktionsgleichungen
1.4
Boole’sche Algebra und Logik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
1.4.1 Begriffe – 1.4.2 Logisches Schließen und mathematisches Beweisen
in der Aussagenlogik – 1.4.3 Beispiel für einen aussagenlogischen Beweis –
1.4.4 Entscheidbarkeit und Vollständigkeit
2
Automaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1
Endliche Automaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
12
2.1.1 Automaten mit Ausgabe – 2.1.2 Funktionsweise
2.2
Hardwareorientierte Automatenmodelle . . . . . . . . . . . . . . . . . . . . . . .
12
2.2.1 Von der Mengen- zur Vektordarstellung – 2.2.2 Darstellungsmittel –
2.2.3 Netzdarstellungen
2.3
Softwareorientierte Automatenmodelle . . . . . . . . . . . . . . . . . . . . . . . .
17
2.3.1 Erkennende Automaten und formale Sprachen – 2.3.2 Erkennende endliche
Automaten – 2.3.3 Turingmaschinen – 2.3.4 Grenzen der Modellierbarkeit
Digitale Systeme
H. Liebig
3
Schaltnetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1 Signaldurchschaltung und -verknüpfung . . . . . . . . . . . . . . . . . . . . . . .
21
22
3.1.1 Schalter und Schalterkombinationen – 3.1.2 Durchschaltglieder –
3.1.3 Verknüpfungsglieder
3.2
Schaltungen für Volladdierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
3.2.1 Volladdierer mit Durchschaltgliedern – 3.2.2 Volladdierer
mit Verknüpfungsgliedern
3.3
Schaltnetze zur Datenverarbeitung und zum Datentransport . . . . . . . . .
28
3.3.1 Arithmetisch-logische Einheiten – 3.3.2 Multiplexer – 3.3.3 Shifter – 3.3.4 Busse
3.4
Schaltnetze zur Datencodierung/ -decodierung und -speicherung . . . . .
32
3.4.1 Codierer, Decodierer – 3.4.2 Festwertspeicher – 3.4.3 Logikfelder –
3.4.4 Beispiel eines PLA-Steuerwerks
4
Schaltwerke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.1
Signalverzögerung und -speicherung . . . . . . . . . . . . . . . . . . . . . . . . . .
35
36
4.1.1 Flipflops, Darstellung mit Taktsignalen – 4.1.2 Flipflops, Abstraktion
von Taktsignalen
4.2
Registertransfer und Datenspeicherung . . . . . . . . . . . . . . . . . . . . . . . .
39
4.2.1 Flipflops auf der Registertransfer-Ebene – 4.2.2 Register, Speicherzellen –
4.2.3 Schreib-/Lesespeicher – 4.2.4 Speicher mit speziellem Zugriff
4.3
Schaltwerke zur Datenverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . .
42
4.3.1 Zähler – 4.3.2 Shiftregister – 4.3.3 Logik-/Arithmetikwerke
4.4
Schaltwerke zur Programmsteuerung und zur programmgesteuerten
Datenverarbeitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.1 PLA- und ROM-Steuerwerke – 4.4.2 Beispiele für programmgesteuerte
Datenverarbeitungswerke (Prozessoren)
45
VII
VIII
Inhaltsverzeichnis
5
Prozessorstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1
5.2
Überblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Maschinenbefehle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
48
48
50
5.2.1 Befehlsformate – 5.2.2 Befehlssatz – 5.2.3 Adressierungsarten
5.3
Akkumulator-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
5.3.1 Einadressrechner – 5.3.2 Beispiel für Mikroprogrammierung – 5.3.3 Beispiel
zur Maschinenprogrammierung
5.4
Register-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
56
5.4.1 Dreiadressrechner (RISC) – 5.4.2 Beschleunigung durch Fließbandtechnik –
5.4.3 Beispiel zur Maschinenprogrammierung
5.5
Parallel-Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
5.5.1 Superskalar vs. VLIW – 5.5.2 Ein Fünfbefehlrechner (VLIW) – 5.5.3 Beispiel
zur Maschinenprogrammierung
Rechnerorganisation
Th. Flik, bearbeitet durch A. Reinefeld
6
Informationsdarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1 Zeichen- und Zifferncodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
64
6.1.1 ASCII – 6.1.2 EBCDIC – 6.1.3 Binärcodes für Dezimalziffern (BCD-Codes) –
6.1.4 Oktalcode und Hexadezimalcode
6.2
6.3
Codesicherung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
66
68
6.3.1 Zustandsgröße – 6.3.2 Bitvektor – 6.3.3 Ganze Zahl – 6.3.4 Gleitpunktzahl –
6.3.5 Vektor
6.4
Maschinen- und Assemblerprogrammierung . . . . . . . . . . . . . . . . . . . .
71
6.4.1 Assemblerschreibweise – 6.4.2 Assembleranweisungen – 6.4.3 Makros –
6.4.4 Unterprogramme
7
Rechnersysteme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.1
Verbindungsstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
76
77
7.1.1 Ein- und Mehrbussysteme – 7.1.2 Systemaufbau – 7.1.3 Busfunktionen –
7.1.4 Busmerkmale – 7.1.5 Zentrale Busse und Punkt-zu-Punkt-Verbindungen –
7.1.6 Periphere Busse und Punkt-zu-Punkt-Verbindungen
7.2
Speicherorganisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
89
7.2.1 Hauptspeicher – 7.2.2 Speicherverwaltungseinheiten – 7.2.3 Caches –
7.2.4 Hintergrundspeicher
7.3
Ein-/Ausgabeorganisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98
7.3.1 Prozessorgesteuerte Ein-/Ausgabe – 7.3.2 DMA-Controllergesteuerte
Ein-/Ausgabe – 7.3.3 Ein-/Ausgabeprozessor – 7.3.4 Schnittstellen –
7.3.5 Ein-/Ausgabegeräte
7.4
Parallelrechner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
104
7.4.1 Vektorrechner – 7.4.2 Feldrechner – 7.4.3 Speichergekoppelte
Mehrprozessorsysteme – 7.4.4 Nachrichtengekoppelte Mehrprozessorsysteme
7.5
Rechnernetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
107
7.5.1 Serielle Datenübertragung – 7.5.2 Weitverkehrsnetze (WANs) –
7.5.3 Lokale Netze (LANs)
7.6
Leistungskenngrößen von Rechnersystemen und ihre Einheiten . . . . . .
8
Betriebssysteme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
8.1
Betriebssystemarten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
112
114
8.1.1 Stapelbetrieb – 8.1.2 Dialogbetrieb – 8.1.3 Einbenutzer- und Netzsysteme –
8.1.4 Mehrbenutzer- und Mehrprogrammsysteme – 8.1.5 Verteilte Systeme –
8.1.6 Echtzeitsysteme
8.2
Prozessorunterstützung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
116
8.2.1 Privilegierungsebenen – 8.2.2 Traps und Interrupts – 8.2.3 Ausnahmeverarbeitung
(exception processing)
8.3
Betriebssystemkomponenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.3.1 Prozessverwaltung – 8.3.2 Interprozesskommunikation –
8.3.3 Speicherverwaltung – 8.3.4 Dateiverwaltung – 8.3.5 Ein-/Ausgabeverwaltung
118
Inhaltsverzeichnis
Programmierung
P. Rechenberg, H. Mössenböck
9
Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
9.1 Begriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
9.2 Darstellungsarten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
9.2.1 Abstraktionsschichten
9.3
Einteilungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
126
9.3.1 Einteilung nach Strukturmerkmalen – 9.3.2 Einteilung nach Datenstrukturen –
9.3.3 Einteilung nach Aufgabengebiet
9.4
Komplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
Datentypen und Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . 130
10.1 Begriffe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
128
130
10.1.1 Datentyp – 10.1.2 Datenstruktur
10.2 Elementare Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.3 Zusammengesetzte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
130
131
10.3.1 Arrays – 10.3.2 Strukturen – 10.3.3 Zeiger und Referenzen
10.4
10.5
10.6
10.7
10.8
10.9
10.10
Verkettete Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Verzeichnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Mengen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Abstrakte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
Programmiersprachen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.1 Begriffe und Einteilungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
133
134
136
137
137
138
139
140
140
11.1.1 Universal- und Spezialsprachen – 11.1.2 Sequenzielle und parallele Sprachen –
11.1.3 Imperative und nichtimperative Sprachen (Denkmodelle)
11.2 Beschreibungsverfahren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
143
11.2.1 Syntax – 11.2.2 Semantik
11.3 Konstruktionen imperativer Sprachen . . . . . . . . . . . . . . . . . . . . . . . . .
144
11.3.1 Deklarationen – 11.3.2 Ausdrücke – 11.3.3 Anweisungen – 11.3.4 Prozeduren
(Methoden) – 11.3.5 Klassen – 11.3.6 Ausnahmebehandlung – 11.3.7 Parallelität
11.4 Programmiersprachen für technische Anwendungen . . . . . . . . . . . . . .
150
11.4.1 Sprachfamilien – 11.4.2 Die Fortran-Familie – 11.4.3 Die Pascal-Familie –
11.4.4 Die C-Familie
11.5 Programmbibliotheken für numerisches Rechnen . . . . . . . . . . . . . . . .
11.6 Programmiersysteme für numerisches und symbolisches Rechnen . . . .
11.7 Web-Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
Softwaretechnik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.1 Begriffe, Aufgaben und Probleme . . . . . . . . . . . . . . . . . . . . . . . . . . . .
154
155
155
156
156
12.1.1 Eigenschaften großer Programme – 12.1.2 Begriff der Softwaretechnik –
12.1.3 Software-Qualität – 12.1.4 Vorgehensmodelle
12.2 Problemanalyse und Anforderungsdefinition . . . . . . . . . . . . . . . . . . . .
12.3 Entwurf und Implementierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
159
160
12.3.1 Grobentwurf – 12.3.2 Feinentwurf – 12.3.3 Mensch-Maschine-Kommunikation
12.4 Testen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
163
12.4.1 Statische Testmethoden – 12.4.2 Dynamische Testmethoden –
12.4.3 Qualitätssicherung
12.5 Dokumentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12.6 Werkzeuge der Softwaretechnik . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
Ausblick: Informatik und Kommunikation . . . . . . . . . . . . . . . . .
166
167
168
IX
X
Inhaltsverzeichnis
Formelzeichen zur Programmierung . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
1
J
In der Informatik verbindet sich das axiomatische,
logisch-strukturtheoretische Denken der Mathematik
mit dem konstruktiven und ökonomischen, d. h.
praktisch-ingenieurmäßigen Handeln der Technik.
Die Informatik ist daher sowohl eine Strukturwissenschaft, die abstrakt (und immateriell) betrieben
wird, als auch eine Ingenieurwissenschaft, die
sich konkret (und materiell) mit der Entwicklung,
dem Bau und dem Betrieb technischer Produkte
befasst.
Von anderen Gebieten der Wissenschaften und der
Technik unterscheidet sich die Informatik durch die
Art dieser Produkte. Sie sind zum einen Hardware,
die aufgrund ihrer materiellen Beschaffenheit nur
schwer verändert werden kann, und zum andern
Software, die – mechanistisch betrachtet – leicht
zu verändern ist. Trotz ihrer gegensätzlichen Erscheinung sind Hardware und Software jedoch im
Prinzip bis auf ein unvermeidliches Minimum an
Rechenmaschine gegeneinander austauschbar.
In der Praxis existieren Hardware und Software nicht
getrennt für sich, sondern bilden eine Einheit, wobei die Grenzziehung zwischen beiden von konstruktiven und ökonomischen Bedingungen abhängt. Diese Hardware-Software-Systeme erstrecken sich von
kleinsten bis hin zu größten Anwendungen, die in ihrer Komplexität an die Grenzen der physikalischen
Realisierbarkeit wie der intellektuellen Beherrschbarkeit stoßen.
Wie in anderen Wissenschaften, so versucht man
auch in der Informatik, das millionenfache Zusammenwirken ihrer „Atome“ (bei der Hardware die
elektronischen Schalter, bei der Software die Befehle
der Programme) durch modulare und hierarchische
Gliederung, d. h. durch wiederholte Abstraktion zu
beherrschen.
Dieser mit Technische Informatik überschriebene
Teil J orientiert sich am Computer als dem technischen Repräsentanten der Informatik. Er beginnt mit
einer kurzen Einführung in wichtige theoretische
Tabelle 0-18. Historische Entwicklung von Hardware und Software
Generation
1
Entwicklung der Hardware
Bis Ende der fünfziger Jahre
Elektronenröhren als Schaltelemente;
zentrale Speicher von wenigen hundert
Maschinenwörtern.
Entwicklung der Software
Bis Ende der fünfziger Jahre
Assemblercode;
einfachste Betriebssysteme;
lochkartenorientierter Einzelbetrieb.
2
Seit Anfang der sechziger Jahre
Transistorschaltkreise;
Ferritkern-, Band-, Trommel-, Plattenspeicher.
Seit Anfang der sechziger Jahre
FORTRAN, ALGOL 60, COBOL;
umfangreichere Betriebssysteme;
lochkartenorientierter Stapelbetrieb.
3
Seit Mitte der sechziger Jahre
Teilweise integrierte Schaltkreise.
Seit Mitte der sechziger Jahre
PL/I, Pascal, APL, C;
Hochkomplexe Betriebssysteme;
dialogorientierter Mehrbenutzerbetrieb.
H. Liebig et al., Das Ingenieurwissen: Technische Informatik,
DOI 10.1007/978-3-662-44391-0_1, © Springer-Verlag Berlin Heidelberg 2014
Technische Informatik
H. Liebig
Th. Flik
P. Rechenberg
A. Reinefeld
H. Mössenböck
Technische
Informatik
2
Technische Informatik
Tabelle 0-18. (Fortsetzung)
Generation
4
Entwicklung der Hardware
Seit Anfang der siebziger Jahre
Überwiegend hochintegrierte Schaltkreise;
Halbleiterspeicher;
8-Bit-Prozessoren auf einem Chip,
1 000 bis 10 000 Transistorfunktionen,
Taktfrequenzen um 1 MHz.
Entwicklung der Software
Seit Anfang der achtziger Jahre
Programmiersprachen: Modula-2, Ada,
Lisp, Prolog;
Programmierumgebungen mit grafischer
Benutzeroberfläche;
hochentwickelte Textverarbeitungs- und
Grafikprogramme.
5
Seit Anfang der achtziger Jahre
Hochintegrierte Schaltkreise;
16- und 32-Bit-Prozessoren auf einem Chip,
bis zu 200 000 Transistorfunktionen,
Taktfrequenzen um 8 MHz.
Seit Anfang der neunziger Jahre
Vordringen der objektorientierten
Programmierung;
Programmiersprachen: C++ und Java;
Vereinigung der Verarbeitung von Text-,
Audio- und Videodaten (Multimedia);
Ausnutzung des Internets und seiner Kommunikationsmöglichkeiten (z. B. elektronische Post).
Seit den neunziger Jahren
32- und 64-Bit-Prozessoren
mit integrierten Caches
und interner Parallelverarbeitung,
bis zu 500 Millionen Transistorfunktionen,
Taktfrequenzen von mehreren GHz.
Im neuen Jahrhundert
Stromsparende Prozessoren;
viele Prozessorkerne auf einem Chip,
Kerne mit Multithreading,
Grafikbeschleuniger-Chips mit vielen hundert Kernen,
viele Milliarden Transistorfunktionen,
keine weitere Steigerung der Taktfrequenz.
Konzepte, den Mathematischen Modellen, fährt fort
mit der Beschreibung des Zusammenwirkens der
elektronischen Schaltungen, den Digitalen Systemen, behandelt dann Struktur- und Betriebsaspekte
in Rechnern, d. h. die Rechnerorganisation, und
schließt mit der Nutzbarmachung von Rechenanlagen für die verschiedensten Anwendungen, d. h.
mit ihrer Programmierung. Dabei bleiben ent-
Im neuen Jahrhundert
C#;
Integrierte Softwareentwicklungsumgebungen;
Programmkommunikation
über das Internet.
sprechend dem Grundlagencharakter des Werkes
besondere, nur für bestimmte Anwendungsgebiete relevante Computersysteme und -programme
ausgespart.
Zur Kennzeichnung der historischen Entwicklung
von Hardware und Software hat sich eine Einteilung in Generationen eingebürgert, die in der
voranstehenden Tabelle skizziert ist.
1 Boole’sche Algebra
Mathematische Modelle
H. Liebig, P. Rechenberg
1 Boole’sche Algebra
Die Boole’sche Algebra wurde 1854 von G. Boole
zur Formalisierung der Aussagenlogik formuliert
und 1938 von C. Shannon auf die Beschreibung
von Funktion und Struktur sog. kombinatorischer
Relais-Schaltungen angewendet. Seither wird sie zum
Entwurf digitaler Rechensysteme eingesetzt. – Die
Entsprechung zwischen falschen und wahren Aussagen in der Logik (Aussage x = falsch/wahr) und
offenen und geschlossenen Schaltern in der Technik
(Schalter x = offen/geschlossen) bildet die Grundlage
des Rechnerbaus. Mit der Boole’schen Algebra
können nämlich sowohl Aussagenverknüpfungen als
auch Schalterverknüpfungen beschrieben werden
(Boole’sche Variable x = 0/1).
oder ⊕), die Äquivalenz (ÄQUIVALENT; Operationszeichen: ↔ oder ≡) sowie die Implikation
(IMPLIZIERT, Operationszeichen: → oder ⊃).
Antivalenz y = x1 ↔
| x2 : y = 1, wenn entweder x1 = 1
oder x2 = 1 (d. h., x1 ist ungleich x2 ).
Äquivalenz y = x1 ↔ x2 : y = 1, wenn x1 äquivalent x2 (d. h., x1 ist gleich x2 ).
Implikation y = x1 → x2 : y = 1, wenn x1 impliziert x2 (d. h., x2 bezieht x1 ein bzw. x2 ist größer/gleich x1 ).
Tabelle 1-1. Logische Operationen; Wahrheitstabellen, For-
meln, Symbole
1.1 Logische Verknüpfungen
und Rechenregeln
1.1.1 Grundverknüpfungen
Die wichtigsten Grundoperationen sind in Tabelle 1-1
dargestellt. Zu ihnen zählen die Negation (NICHT,
NOT; Operationszeichen: Überstreichungen oder
vorangestelltes ¬), die Konjunktion (UND, AND;
Operationszeichen: · oder ∧) und die Disjunktion
(ODER, OR; Operationszeichen + oder ∨). Diese
sog. Boole’schen Grundverknüpfungen stehen einerseits für Verbindungen von „Ja-/Nein-Aussagen“
(z. B. umgangssprachlichen Sätzen, die nur wahr oder
falsch sein können), können aber andererseits auch
als Verknüpfungen binärer Systemzustände (z. B. von
elektrischen Signalen) angesehen werden (siehe 3.1).
Negation y = x̄ : y = 1, wenn nicht x = 1 (d. h., y = 1,
wenn x = 0).
Konjunktion y = x1 · x2 : y = 1, wenn x1 = 1 und
x2 = 1.
Disjunktion y = x1 + x2 : y = 1, wenn x1 = 1 oder
x2 = 1.
Weitere Grundverknüpfungen sind die Antivalenz
(ENTWEDER ODER, XOR; Operationszeichen: ↔
|
1.1.2 Ausdrücke
Logische Konstanten, Aussagenvariablen, Grundverknüpfungen und aus ihnen zusammengesetzte komplexere Verknüpfungen werden zusammenfassend als
Ausdrücke bezeichnet. In Analogie zu arithmetischen
Ausdrücken ist festgelegt, dass · Vorrang vor + hat.
3
4
Technische Informatik / Mathematische Modelle
Ferner ist es weithin üblich, den Bereich einer Negation durch Überstreichung anzugeben und, wenn es
nicht zu Verwechslungen kommen kann, Malpunkte
wegzulassen. Klammern dürfen jedoch nur dann weggelassen werden, wenn für die eingeklammerte Verknüpfung das Assoziativgesetz gilt (das ist für ·, +, ↔
und ↔
| der Fall, nicht jedoch für →). – Werden Ausdrücke mit den Symbolen der Tabelle 1-1 dargestellt,
so wird ihre „Klammerstruktur“ durch die Symbolstufung besonders anschaulich (vgl. z. B. die Gleichung
und das Blockbild für y1 in Bild 1-1).
Die Vielfalt der Darstellungsmöglichkeiten erlaubt es,
einzelne Operationen durch andere zu beschreiben.
Eine gewisse Standardisierung ergibt sich, wenn nur
die Operationen − , · und + benutzt werden; ↔
| ,↔
und → lassen sich damit folgendermaßen ausdrücken
(vgl. die Tabellen 1-1d bis f für die drei Grundverknüpfungen Antivalenz, Äquivalenz und Implikation)
x1 ↔
| x2 = x̄1 · x2 + x1 · x̄2 ,
x1 ↔ x2 = x¯1 · x̄2 + x1 · x2 ,
x1 → x2 = x̄1 + x2 .
Auswertung. Zur Auswertung von Ausdrücken legt
man Tabellen an: Links werden im Spaltenkopf die
im Ausdruck vorkommenden Variablen und zeilenweise sämtliche Kombinationen von 0 und 1 eingetragen. Rechts werden für alle diese Kombinationen
die Werte der Teilausdrücke so lange ausgewertet und
niedergeschrieben, bis der Wert des Ausdrucks feststeht. Tabelle 1-2 gibt ein Beispiel.
Tabelle 1-2. Auswertung der Ausdrücke a · b + c und (a +
c) · (b + c); beide haben für alle 0/1-Kombinationen von a, b
und c die gleichen Werte, d. h., es gilt a·b+c = (a+c)·(b+c)
1.1.3 Axiome
Die folgenden Axiome (1-1a) bis (1-5b) definieren
mit den Variablen a, b, c, den Konstanten 0, 1 und
den Operationen −, ·, + die Boole’sche Algebra, die
sich durch abweichende Rechenregeln und Operationen sowie durch das Fehlen von Umkehroperationen
von der gewöhnlichen Algebra unterscheidet.
a·b=b·a,
(1-1a)
a+b=b+a,
(1-1b)
(a · b) · c = a · (b · c) ,
(1-2a)
(a + b) + c = a + (b + c) ,
(1-2b)
(a + b) · c = a · c + b · c ,
(1-3a)
a · b + c = (a + c) · (a + c) ,
(1-3b)
a·1=a,
(1-4a)
a+0=a,
(1-4b)
a · ā = 0 ,
(1-5a)
a + ā = 1 .
(1-5b)
Axiome (1-1a) und (1-1b) erlauben das Vertauschen
von Operanden (Kommutativgesetze); Axiome
(1-2a) und (1-2b) das Weglassen von Klammern
(Assoziativgesetze), solange nicht + und ·, wie in
den Axiomen (1-3a) und (1-3b), gemischt auftreten
(Distributivgesetze). Der durch (1-3a) beschriebene Vorgang wird auch als „Ausmultiplizieren“,
in Analogie dazu der durch (1-3b) beschriebene
als „Ausaddieren“ bezeichnet. Axiome (1-4a) und
(1-4b) definieren das 1-Element und das 0-Element
(Existenz der neutralen Elemente); Axiom (1-5a)
mit (1-5b) definiert die „Überstreichung“ (Existenz
des Komplements). – Dass die Axiome für die in
Tabelle 1-1 definierten logischen Operationen gültig
sind, lässt sich durch Auswertung der Ausdrücke auf
beiden Seiten des Gleichheitszeichens zeigen (siehe
z. B. Tabelle 1-2 für (1-3b)).
Dualität. Den Axiomen ist eine Symmetrie zu ei-
gen, die durch ihre paarweise Nummerierung betont
ist. Sie ist gekennzeichnet durch Vertauschen von ·
und + sowie 0 und 1 und wird als Dualität bezeichnet. Wenn, wie in (1-1a) bis (1-5b), zwei Ausdrücke
äquivalent sind, so sind es auch die jeweiligen dualen
1 Boole’sche Algebra
Ausdrücke. Dieses Dualitätsprinzip gilt nicht nur für
die Axiome, sondern auch für alle Sätze.
1.1.4 Sätze
Aus den Axiomen der Boole’schen Algebra lässt sich
eine Reihe von Sätzen ableiten, die zusammen mit
den Axiomen als Rechenregeln zur Umformung von
Ausdrücken dienen. (Einfacher als aus den Axiomen
sind die Sätze durch Auswertung beider Gleichungsseiten zu beweisen.)
a·a=a,
0·a=0,
a+a=a,
1+a=1,
a + a · b = a , a · (a + b) = a ,
a + ā · b = a + b , a · (ā + b) = a · b ,
a · b = ā + b̄ ,
ā = a .
a + b = ā · b̄ ,
Tabelle 1-3. Tabellendarstellungen einer Funktion. a Darstellung von f : E → A mit Elementen von Mengen; b Darstellung von f, aufgefasst als eine Funktion y = f (x) mit
den Werten Boole’scher Vektoren bzw. als zwei Funktionen
y1 = f1 (x1 , x2 , x3 ) und y2 = f2 (x1 , x2 , x3 ) mit den Werten
Boole’scher Variablen
(1-6a,b)
(1-7a,b)
(1-8a,b)
(1-9a,b)
(1-10a,b)
(1-11)
Sätze (1-6) bis (1-9) erlauben es, Boole’sche Ausdrücke zu vereinfachen bzw. Schaltungen hinsichtlich ihres Aufwands zu minimieren; (1-10a) und (110b), die De-Morgan’schen Regeln, erlauben es zusammen mit (1-11), die Operationen NICHT, UND
und ODER durch NAND (negated AND) oder NOR
(negated OR) auszudrücken, d. h. Schaltungen nur aus
NAND-Schaltkreisen (siehe Bild 1-2c) oder nur aus
NOR-Schaltkreisen (siehe Bild 1-2d) aufzubauen.
1.2 Boole’sche Funktionen
1.2.1 Von der Mengen- zur Vektordarstellung
Eine Funktion f bildet eine Menge E von Eingangselementen (Eingabemenge, Urmenge) in eine Menge A von Ausgangselementen (Ausgabemenge, Bildmenge) ab, formal beschrieben durch
f: E → A ,
wobei es sich hier stets um Mengen mit diskreten Elementen handelt. Zur Beschreibung von Funktionen
gibt es eine Vielzahl an Darstellungsmitteln. Wenn
die Anzahl der Eingangselemente nicht zu groß ist,
bedient man sich gerne der Tabellendarstellung. Bei
der in Tabelle 1-3a definierten Funktion sind zwar alle Eingangs- und Ausgangselemente aufgeführt, aber
über ihre Art ist nichts ausgesagt; sie ergibt sich aus
der jeweiligen Anwendung. In der elektronischen Datenverarbeitung sind die Elemente wegen der heute verwendeten Schaltkreise binär codiert, d. h., jedes Element von E und von A ist umkehrbar eindeutig durch 0/1-Kombinationen verschlüsselt; auf diese Weise entstehen aus den Eingangselementen Boole’sche Eingangsvariablen. Tabelle 1-3b zeigt eine
Codierung für die in Tabelle 1-3a definierte Funktion.
Beschreibung mit Boole’schen Vektoren. Funktio-
nen mit binär codierten Elementen lassen sich durch
Boole’sche Ausdrücke beschreiben, sie heißen dann
Boole’sche Funktionen. Ihre Realisierung mit Schaltern (i. Allg. Transistoren) bezeichnet man als Schaltnetze (siehe 3).
Im einfachsten Fall ist eine Boole’sche Funktion
von n Veränderlichen eine Abbildung der 0/1Kombinationen der n unabhängigen Variablen x1 ,
x2 , . . . , xn (Eingangsvariablen, zuweilen auch kurz:
Eingänge) in die Werte 0 und 1 einer abhängigen
Variablen y (Ausgangsvariable, zuweilen kurz:
Ausgang). Fasst man die Eingangsvariablen zu einem
Boole’schen Vektor zusammen (Eingangsvektor x),
so lässt sich dies kompakt durch y = f (x) beschreiben. Liegen m Funktionen y1 = f1 (x), . . . , ym = fm (x)
mit m Ausgangsvariablen vor (Ausgangsvektor y), so
lassen sich diese ebenso zusammenfassen und durch
y = f (x) beschreiben. Es entsprechen sich also
f: E → A
und
y = f (x) .
5
6
Technische Informatik / Mathematische Modelle
Darin sind die binär codierten Elemente von E die
Werte von x und die binär codierten Elemente von A
die Werte von y.
Eine Funktion, bei der für sämtliche 0/1-Kombinationen ihrer Eingangsvariablen die Funktionswerte
ihrer Ausgangsvariable(n) definiert sind heißt
vollständig definiert (totale Funktion), andernfalls
unvollständig definiert (partielle Funktion).
1.2.2 Darstellungsmittel
Für Boole’sche Funktionen sind verschiedene Darstellungsformen möglich, die meist ohne Informationsverlust ineinander transformierbar sind. Deswegen
bedeutet der Entwurf eines Schaltnetzes i. Allg.
die Transformation von verbalen Angaben über die
Funktion in eine wirtschaftlich akzeptable Schaltung
für die Funktion; d. h. die Transformation der Beschreibung ihrer Funktionsweise in die Beschreibung
ihrer Schaltungsstruktur.
Tafeln, wie der Vektor Komponenten hat. – Bei
unvollständig definierten Funktionen entsprechen
den nicht definierten Wertezuordnungen leere Felder
(Leerstellen), sie werden auch als „don’t cares“
bezeichnet und spielen bei der Minimierung von
Funktionsgleichungen eine wichtige Rolle.
(Bild 1-1b). In der Gleichungsdarstellung stehen die Ausgangsvariablen links des Gleichheitszeichens, und die Eingangsvariablen erscheinen
innerhalb von Ausdrücken rechts des Gleichheitszeichens. Bei mehreren Ausgangsvariablen hat das Gleichungssystem so viele Gleichungen, wie der Ausgangsvektor Komponenten hat. – Bei unvollständig
definierten Funktionen kann der eingeschränkte Gültigkeitsbereich einer Gleichung als Bedingung ausgedrückt werden.
Gleichungen
Tabellen (Wertetabellen, Wahrheitstabellen; Tabel-
le 1-3b). In der Tabellendarstellung steht in jeder Zeile links eine 0/1-Kombination der Eingangsvariablen
(ein Wert des Eingangsvektors, Eingangswert), der
rechts die zugehörigen Werte der Ausgangsvariablen
(der Wert des Ausgangsvektors, Ausgangswert) zugeordnet sind. Tabellenzeilen mit demselben Ausgangswert werden manchmal zu einer Zeile zusammengefasst, wobei eine Eingangsvariable, die den Ausgangswert nicht beeinflusst, durch einen Strich in der
Tabelle gekennzeichnet wird. – Bei unvollständig definierten Funktionen sind die nicht definierten Wertezuordnungen nicht in der Tabelle enthalten.
Tafeln (Karnaugh-,Veitch-,
auch kurz KV-Diagramme; Bild 1-1a). Bei der Tafeldarstellung sind die
Eingangsvariablen entsprechend der matrixartigen
Struktur der Tafeln in zwei Gruppen aufgeteilt.
Die 0/1-Kombinationen der einen Gruppe werden
nebeneinander den Spalten, die der anderen Gruppe
zeilenweise untereinander den Zeilen der Tafel
zugeordnet. Jede Tafel hat so viele Felder, wie
es mögliche Kombinationen der Eingangswerte
gibt, d. h. bei n Variablen 2n Felder. In die Felder
werden die Ausgangswerte eingetragen: entweder
zusammengefasst als Vektor in eine einzige Tafel
oder als dessen einzelne Komponenten in so viele
Bild 1-1. Darstellungsmittel für Boole’sche Funktionen am
Beispiel der Funktion in Tabelle 1-3b. a Tafeln; b Glei-
chungen; c Blockbilder. Die abgebildeten Funktionen sind
in mehrfacher Weise interpretierbar: 1. y1 = 1, wenn 2
oder mehr der 3 Kandidaten xi zustimmen (2-aus-3-Voter),
2. y2 = 1, wenn die Quersumme der Dualzahl x3 x2 x1 ungerade ist (Paritätsprüfung), 3. y1 als Übertrag und y2 als Summe bei der Addition der drei Dualziffern x1 , x2 , x3 (Volladdierer, siehe 3.2.2)
1 Boole’sche Algebra
Blockbilder (Strukturbilder, Schaltbilder; Bild 1-1c).
Normalformen. Jede Boole’sche Funktion kann in
Blockbilder beschreiben sowohl die formelmäßige
Gliederung wie die schaltungsmäßige Struktur
einer Boole’schen Funktion und haben somit eine
Brückenfunktion beim Schaltnetzentwurf. Sie werden
z. B. mit den in Tabelle 1-1 dargestellten Symbolen
gezeichnet, die entsprechend der „Klammerstruktur“
miteinander zu verbinden sind. Die Eingangsvariablen sind die Eingänge des Schaltnetzes. Die
Negation einer Variablen wird entweder durch einen
Punkt am Symbol oder durch Überstreichung der
Variablen dargestellt. Die Ausgangsvariablen sind die
Ausgänge des Schaltnetzes. – Im Blockbild kann die
Eigenschaft einer Funktion, unvollständig definiert
zu sein, nicht zum Ausdruck gebracht werden.
zwei charakteristischen Formen geschrieben werden:
1. als disjunktive Normalform, das ist eine i. Allg.
mehrstellige Disjunktion (ODER-Verknüpfung) von
i. Allg. mehrstelligen Konjunktionstermen (UNDVerknüpfungen): Bild 1-2a; 2. als konjunktive
Normalform, das ist eine i. Allg. mehrstellige Konjunktion (UND-Verknüpfung) von i. Allg. mehrstelligen Disjunktionstermen (ODER-Verknüpfungen):
Bild 1-2b.
Die disjunktive Normalform kann mit NANDGliedern (Bild 1-2c) und die konjunktive Normalform mit NOR-Gliedern (Bild 1-2d) dargestellt
werden. Das ist deshalb wichtig, weil in der Technik vielfach nur NOR- oder NAND-Schaltkreise
zur Verfügung stehen. Die Eingangsvariablen sind
unmittelbar in normaler oder negierter Form an die
Verknüpfungsglieder angeschlossen (man beachte
den Wechsel der Überstreichung bei x4 ).
Verallgemeinerung der Darstellungsmittel. Nicht
alle der aufgeführten Darstellungsmittel sind unbeschränkt anwendbar. Tabellen sind durch ihre
Zeilenzahl begrenzt. Zeilen mit gleichen Ausgangswerten werden deshalb gerne zu einer Zeile
zusammengefasst. Tafeln sind auf etwa sechs bis acht
Eingangsvariablen begrenzt. Gleichungen bedürfen
einer gewissen Übersichtlichkeit; sie lassen vektorielle Beschreibungen nur sehr eingeschränkt zu.
– Bei umfangreichen Aufgabenstellungen abstrahiert
man deshalb von der Boole’schen Algebra und wählt
anwendungsspezifische Beschreibungsformen, z. B.
die in höheren Programmiersprachen oder auf der
sog. Registertransfer-Ebene üblichen Ausdrucksmittel (siehe 4), wie die Gleichung Z = X + Y
bzw. ein „+“-Kästchen oder das „+“-Zeichen für
die Addition von zwei Dualzahlen (siehe z. B. in
Bild 4-10). Sie sind Ausgangspunkt für den Entwurf
von operativen Schaltnetzen, wie die folgende Kette
von Darstellungstransformationen zeigt: „+“-Zeichen
in Bild 4-10 → Bild 3-1a → Bild 3-1b → Bild 1-1a
→ Bild 1-1b → Bild 1-1c in der 3. Interpretation.
1.3 Normal- und Minimalformen
1.3.1 Kanonische Formen Boole’scher Funktionen
Unter den zahlreichen Möglichkeiten, eine
Boole’sche Funktion durch einen Boole’schen
Ausdruck zu beschreiben, gibt es bestimmte, die sich
durch Übersichtlichkeit und Einfachheit besonders
auszeichnen.
Ausgezeichnete Normalformen.
Enthalten alle
Terme einer Normalform sämtliche Variablen der
Bild 1-2. Blockbilder von Normalformen sowie davon ab-
geleiteter Formen. a Disjunktive Normalform (hier y1 =
x1 x2 x3 + x2 x̄3 + x̄4 ); b konjunktive Normalform (hier y2 =
(x1 + x2 + x3 ) · (x2 + x̄3 ) · x̄4 ); c NAND/NAND-Form;
d NOR/NOR-Form. Die in a und b bzw. c und d abgebildeten Formen sind jeweils dual, nicht äquivalent. Die in a und
c bzw. b und d abgebildeten Formen sind hingegen äquivalent
7
8
Technische Informatik / Mathematische Modelle
Funktion genau einmal (normal oder negiert) und
sind gleiche Terme nicht vorhanden, so liegt eine
eindeutige Struktur vor, die als ausgezeichnete
disjunktive bzw. ausgezeichnete konjunktive Normalform bezeichnet wird. – Jede Boole’sche Funktion
lässt sich von einer in die andere Normalform umformen, mit den angegebenen Rechenregeln allerdings
z. T. nur unter erheblichem Rechenaufwand; besser
geht es unter Zuhilfenahme von Tafeln.
1.3.2 Minimierung von Funktionsgleichungen
Die Minimierung von Funktionsgleichungen dient
zur Vereinfachung von Schaltnetzen. Sie hat heute
wegen der geringeren Kosten der Transistoren innerhalb hochintegrierter Schaltungen nicht mehr die
Bedeutung wie früher. Trotzdem wird sie beim Schaltungsentwurf, insbesondere beim automatisierten,
computergestützten Entwurf, zur optimalen Nutzung
der Chipfläche eingesetzt; und auch in der Programmierung, z. B. zur übersichtlicheren Formulierung
bedingter Anweisungen, kann sie nützlich sein.
Die Minimierung besteht aus zwei Teilen: 1. dem
Aufsuchen sämtlicher Primimplikanten, das ergibt
UND-Verknüpfungen mit wenigen Eingängen, und
2. der Ermittlung der minimalen Überdeckung der
Funktion, das ergibt wenige UND-Verknüpfungen
und damit auch eine ODER-Verknüpfung mit
wenigen Eingängen.
Primimplikant. Für jeden Konjunktionsterm einer
Boole’schen Funktion in disjunktiver Normalform
gilt, dass, wenn er den Wert 1 hat, auch die Funktion
selbst den Wert 1 hat. Mit anderen Worten, jeder
Konjunktionsterm impliziert die Funktion, man sagt,
er ist Implikant der Funktion. Lässt sich aus einem
solchen Implikanten keine Variable herausstreichen,
ohne den Funktionswert zu ändern, so heißt er
Primimplikant oder Primterm. In der Tafel sind
Primterme anschaulich „rechteckige“ Felder (ggf.
unzusammenhängend) mit „maximal vielen“ Einsen
unter Einbeziehung von Leerstellen, die sich durch
einen einzigen Konjunktionsterm darstellen lassen
(z. B. sind āb̄c̄d̄, ab̄c und ad (auch ab̄c und bd),
nicht aber z. B. ab̄cd̄ Primterme der Funktion f
entsprechend den drei (fünf) umrandeten Feldern in
Bild 1-3).
Bild 1-3. Tafeln unvollständig definierter Funktionen f und
f¯ mit vier Variablen. Die gestrichelt eingerahmten Primtermfelder sind zur Gleichungsdarstellung der Funktion unnötig
Minimale Überdeckung. Alle Konjunktionsterme
einer Funktion, disjunktiv zusammengefasst, stellen
die Funktion in ihrer Gesamtheit dar, man sagt,
sie bilden eine Überdeckung der Funktion. Lässt
sich aus einer solchen Überdeckung kein Term
streichen, ohne die Funktion zu ändern, so heißt sie
minimale Überdeckung. In der Tafel gibt es dann
kein umrandetes Feld, das durch zwei oder mehrere
andere „erzeugt“ wird (z. B. ist f = āb̄c̄d̄ + ābc + ad,
nicht aber f = āb̄c̄d̄ + ābc + ad + bd, eine minimale
Überdeckung der Funktion f aus Bild 1-3).
Minimale Normalform. Die
Minimierung führt
gewöhnlich auf minimale disjunktive Normalformen. Programmierbare Verfahren zur exakten
Minimierung folgen strikt der oben beschriebenen
Zweiteilung (siehe z. B. [1]). Bei programmierten
heuristischen Verfahren (siehe z. B. [2]) sowie bei
manuellen grafischen Verfahren werden hingegen
meist beide Teile zusammengefasst, wobei in Kauf
genommen wird, gelegentlich nicht ganz das absolute Minimum an Verknüpfungen zu erhalten. Zur
grafischen Minimierung wird die Funktion als Tafel
dargestellt, und es werden alle jene Konjunktionsterme disjunktiv verknüpft herausgeschrieben, die
jeweils maximal viele Einsen/Leerstellen umfassen, und zwar so lange, bis alle Einsen der Tafel
berücksichtigt sind (z. B. entsteht gemäß Bild 1-3
f = ad + ābc + ād̄c̄d̄). – Der hier skizzierte Minimierungsprozess bezieht sich auf Funktionen mit einem
Ausgang. Funktionen mit mehreren Ausgängen
werden grafisch komponentenweise, algorithmisch
hingegen als Ganzes minimiert. Solche Funktionen spielen bei der Chipflächenreduzierung von
Steuerwerken (siehe 4.4.1) eine gewisse Rolle.
1 Boole’sche Algebra
„Rechnen“ mit Tafeln. Die Minimierung wird auch
beim Rechnen mit Boole’schen Funktionen angewendet, da es anderenfalls äußerst mühsam wäre, „don’t
cares“ in den Rechenprozess einzubeziehen.
Beispiel: Es soll die zu f = āb̄c̄d̄ + ābc + ad negierte Funktion f¯ gebildet werden, und zwar unter Berücksichtigung der „don’t cares“ aus Bild 1-3. Nach
Ablesen der „günstigsten“ Primterme ergibt sich f¯ =
āb̄c + ad̄ + bc̄ + c̄d.
1.4 Boole’sche Algebra und Logik
In der mathematischen Logik wird die Boole’sche Algebra zur Beschreibung der logischen Struktur von
Aussagen benutzt. Statt der Symbole 0 und 1 benutzt
man deshalb meist falsch und wahr, F und W bzw.
in den Programmiersprachen false und true oder F
und T.
In der mathematischen Logik sind die folgenden
Symbole üblich: Negation ¬, Konjunktion ∧, Disjunktion ∨, Implikation →, Äquivalenz ↔. Aus
Gründen der Einheitlichkeit wird im Folgenden hier
auch die Symbolik der Boole’schen Algebra, d. h.
auch die der Schaltalgebra benutzt.
Beispiel. Der Satz: „Wenn die Sonne scheint und es
warm ist oder wenn ich müde bin und nicht schlafen
kann, gehe ich spazieren.“ ist eine logische Verknüpfung der elementaren Aussagen „Die Sonne scheint“
(A), „Es ist warm“ (B), „Ich bin müde“ (C), „Ich kann
schlafen“ (D), „Ich gehe spazieren“ (E). Er wird als
Formel der Aussagenlogik so geschrieben:
(A ∧ B) ∨ (C ∧ ¬D) → E
bzw. (A · B +C · D) → E .
betrachtet nur die Verknüpfungen elementarer Aussagen, d. h. ganzer Sätze. In der Prädikatenlogik
kommt die Aufteilung der Sätze in Subjekte (Individuen) und Prädikate (Boole’sche Funktionen
von Subjekten) und die Einführung von Quantoren
hinzu. Da sich logische Formeln nach den Regeln der
Boole’schen Algebra kalkülmäßig transformieren,
z. B. vereinfachen oder in Normalformen überführen
lassen, spricht man auch von Aussagen- und Prädikatenkalkül. Die folgenden Ausführungen beziehen
sich hauptsächlich auf die einfachere, für viele
Anwendungen aber ausreichende Aussagenlogik.
Die Wahrheit einer zusammengesetzten Formel der
Aussagenlogik hängt nur von der Wahrheit ihrer Elementaraussagen ab. Dabei sind drei Fälle zu unterscheiden:
Allgemeingültigkeit (Tautologie): Die Formel ist,
unabhängig von der Wahrheit ihrer Elementaraussagen, immer wahr. Beispiel: „Wenn der Hahn
kräht auf dem Mist, ändert sich das Wetter, oder
es bleibt, wie es ist.“ (H → W + W).
Unerfüllbarkeit (Kontradikation): Die Formel ist,
unabhängig von der Wahrheit oder Falschheit
ihrer Elementaraussagen, immer falsch. Beispiel:
„Der Hahn kräht auf dem Mist und kräht nicht auf
dem Mist.“ (H · H).
Erfüllbarkeit: Es gibt mindestens eine Belegung
der Elementaraussagen mit wahr oder falsch,
die die Formel wahr macht. Beispiel: „Wenn der
Hahn kräht auf dem Mist, ändert sich das Wetter.“
(H → W).
Wenn X eine Formel der Aussagen- oder Prädikatenlogik ist, gelten folgende wichtige Beziehungen:
X ist allgemeingültig ↔ X ist unerfüllbar
1.4.1 Begri−e
X ist unerfüllbar ↔ X ist allgemeingültig
In der formalen Logik wird allein die Struktur von
Aussagen betrachtet, nicht die Frage, ob sie inhaltlich
wahr oder falsch sind. Die klassische formale Logik
lässt für jede Aussage nur die beiden Möglichkeiten
wahr und falsch zu („tertium non datur“); andere
Logiksysteme (intuitionistische Logik, mehrwertige
Logik, modale Logik) lockern diese Einschränkung.
In der klassischen Logik unterscheidet man Aussagenlogik und Prädikatenlogik. Die Aussagenlogik
X ist erfüllbar ↔ X ist nicht allgemeingültig
Die große Bedeutung der formalen Logik für Mathematik und Informatik beruht vor allem darauf, dass
sich mit ihr die Begriffe des logischen Schließens und
des mathematischen Beweisens formal fassen lassen.
Darauf bauen Verfahren zum automatischen Beweisen und die sog. logischen Programmiersprachen, wie
Prolog, auf.
9
10
Technische Informatik / Mathematische Modelle
1.4.2 Logisches Schließen und mathematisches
Beweisen in der Aussagenlogik
Die einfachste Form des logischen Schließens bildet
die Implikation A → B (Bild 1-4). Sie spielt deshalb
eine zentrale Rolle in der Logik.
Axiome sind ausgewählte wahre Aussagen. Ein (mathematischer) Satz ist eine Aussage, die durch logisches Schließen aus Axiomen folgt. Die Aussage
„B folgt aus den Axiomen A1 , A2 , . . . , An “ lautet damit
A1 · A2 · . . . · An → B .
Sie ist dann und nur dann ein Satz (Theorem), wenn
mit der Wahrheit von A1 , A2 , . . . , An , zugleich auch
B wahr ist, d. h., wenn sie allgemeingültig ist. Umgekehrt gilt: Wenn man zeigen kann, dass eine Formel X allgemeingültig ist, so ist X ein Satz, und man
hat X bewiesen. Der Beweis kann auch indirekt geführt werden, indem man nachweist, dass X unerfüllbar ist. Aus diesem Grund ist der Nachweis der Allgemeingültigkeit oder der Unerfüllbarkeit von zentraler Bedeutung. Alle Axiome der Boole’schen Algebra
(siehe 1.1.3) sind allgemeingültige Aussagen. Weitere
wichtige Beispiele aussagenlogischer Sätze:
((A → B) · A) → B
((A → B) · B) → A
(A · (B → A)) → B
(A → B) ↔ (B → A)
((A + B) · (A + C)) → (B + C)
Modus ponens
(Abtrennungsregel)
Modus tollens
Indirekter Beweis
(durch Widerspruch)
Kontraposition
Resolution
Für das Beweisen einer Formel X der Aussagenlogik
stehen folgende vier Verfahren zur Verfügung, die alle in jedem Fall zum Ziel führen. Da sich aus n beteiligten Elementaraussagen maximal 2n Kombinatio-
nen von wahr und falsch bilden lassen, wächst ihre
Ausführungszeit in ungünstigen Fällen exponentiell
mit n.
1. Durch Exhaustion. Man belegt die n Elementaraussagen mit den Werten wahr und falsch in
allen Kombinationen und prüft, ob X für alle
Belegungen wahr ist.
2. Mit der konjunktiven Normalform. Man transformiert X in die konjunktive Normalform. X ist dann
allgemeingültig, wenn in der konjunktiven Normalform alle Disjunktionen eine Elementaraussage und zugleich ihre Negation enthalten.
3. Mit der disjunktiven Normalform. Man transformiert X in die disjunktive Normalform. X ist
dann allgemeingültig, wenn in der disjunktiven
Normalform von X alle Konjunktionen eine
Elementaraussage und zugleich ihre Negation enthalten.
4. Durch Resolution. Man transformiert X in die konjunktive Normalform. In ihr sucht man ein Paar
von Disjunktionen, in denen eine Elementaraussage bejaht und negiert vorkommt, zum Beispiel
(A + B) · . . .· (A +C). Da (A + B) · (A+C) → (B +C)
gilt, kann man die „Resolvente“ (B + C) der Gesamtformel als neue Konjunktion hinzufügen, ohne ihren Wahrheitswert zu ändern. Entsprechend
haben die Formeln (A + B) · A und (A + B) · A jede für sich die Resolvente B. Man wiederholt die
Resolventenbildung („Resolution“) in allen möglichen Kombinationen unter Einbeziehung der hinzugefügten Resolventen so lange, bis entweder alle Möglichkeiten erschöpft sind oder sich schließlich zwei Resolventen der Form A und A ergeben.
Da A · A falsch ist, bekommt dadurch die ganze konjunktive Normalform den Wert falsch. Somit ist X unerfüllbar und X allgemeingültig. Wenn
dieser Fall nicht auftritt, ist X nicht allgemeingültig.
Die Resolution ist in der Aussagenlogik von untergeordneter Bedeutung. In der Prädikatenlogik ist sie
aber oft das einzige Verfahren, das zum Ziel führt.
1.4.3 Beispiel für einen aussagenlogischen Beweis
Bild 1-4. Implikation. a Definition; b Ersatz durch Negation
und Disjunktion; c sprachliche Formulierungen
(Aufgabe aus [3].) Gegeben sind die Aussagen:
1. Paul oder Michael haben heute Geburtstag.
2 Automaten
2. Wenn Paul heute Geburtstag hat, bekommt er heute
einen Fotoapparat. 3. Wenn Michael heute Geburtstag hat, bekommt er heute ein Briefmarkenalbum.
4. Michael bekommt heute kein Briefmarkenalbum.
Folgt daraus der Satz „Paul hat heute Geburtstag“?
Elementaraussagen: „Paul hat heute Geburtstag“ (P);
„Michael hat heute Geburtstag“ (M); „Paul bekommt
heute einen Fotoapparat“ (F); „Michael bekommt
heute ein Briefmarkenalbum“ (B). Es soll mit den in
1.4.2 angegebenen vier Methoden geprüft werden, ob
die Formel X
((P + M) · (P → F) · (M → B) · B) → P
(1-12)
allgemeingültig ist. Zum leichteren Rechnen beseitigt
man in 1-12 zuerst alle Implikationen A → B durch
die Disjunktionen A + B (vgl. Bild 1-4b) und erhält
(P + M) · (P + F) · (M + B) · B + P .
(1-13)
Exhaustion. Prüfung aller 24 Wertekombinationen in
verkürzter Weise:
B wahr ergibt sofort für X wahr.
B falsch ergibt:
P wahr ergibt sofort für X wahr.
P falsch ergibt für X:
M · wahr · M · wahr, und das ist wahr.
Konjunktive Normalform. Eine der konjunktiven Normalformen von (1-13) lautet: (P+P+ B+ M)·(M +P+
B + M). Die erste Disjunktion ist wahr wegen P + P,
die zweite wegen M + M. Somit ist X allgemeingültig.
Disjunktive Normalform. Man bildet die Negation
von (1-13):
(P + M) · (P + F) · (M + B) · B · P
(1-14)
multipliziert aus und vereinfacht. Eine der dabei entstehenden disjunktiven Normalformen lautet
M ·B·P· M+M·B·P·F · M
+M·B·P·B+M·B·P·F ·B.
Sie enthält in jeder Konjunktion eine Elementaraussage und deren Negation.
Resolution. Man bildet die Negation von X und erhält
(1-14). In (1-14) ergeben (P+ M) und P die Resolvente M und (M + B) und B die Resolvente M. M und M
ergeben die Resolvente falsch. Das heißt, X ist unerfüllbar und somit X allgemeingültig.
1.4.4 Entscheidbarkeit und Vollständigkeit
Da sich von jeder aussagenlogischen Formel feststellen lässt, ob sie allgemeingültig ist oder nicht, bilden die Formeln der Aussagenlogik hinsichtlich ihrer
Allgemeingültigkeit eine entscheidbare Menge. Man
sagt, die Aussagenlogik ist entscheidbar. Darüber hinaus kann man Axiomensysteme angeben, aus denen
sich sämtliche Sätze (d. h. alle allgemeingültigen Formeln) der Aussagenlogik ableiten lassen. Diese Eigenschaft bezeichnet man als Vollständigkeit. Es ergibt sich somit der Satz:
– Die Aussagenlogik ist entscheidbar und vollständig.
Für die Prädikatenlogik gelten die Sätze:
– Die Prädikatenlogik 1. Stufe ist vollständig.
(Gödels Vollständigkeitssatz, 1930).
– Die Prädikatenlogik 1. Stufe ist nicht entscheidbar (Church, 1936).
– Die Prädikatenlogik 2. Stufe ist nicht vollständig.
(Gödels Unvollständigkeitssatz, 1931).
Da man bereits für die formale Beschreibung der
Grundgesetze der Arithmetik die Prädikatenlogik
2. Stufe benötigt, kann man die letzte Aussage so interpretieren, dass es arithmetische Sätze (d. h. wahre
Aussagen über natürliche Zahlen) gibt, die sich nicht
mit den Mitteln der Prädikatenlogik beweisen lassen.
2 Automaten
Automaten wurden in den fünfziger Jahren insbesondere von E.F. Moore als Modelle „mathematischer
Maschinen“ diskutiert, aber auch von G.H. Mealy
1955 auf die formale Beschreibung sog. sequentieller
elektrischer Schaltungen angewendet. Aus diesen Ansätzen hat sich die Automatentheorie entwickelt. Ihre Ergebnisse sind grundlegend insbesondere für die
Konstruktion von Digitalschaltungen in Rechenanla-
11
12
Technische Informatik / Mathematische Modelle
gen und zur Definition der syntaktischen Struktur von
Programmiersprachen.
2.1 Endliche Automaten
2.1.1 Automaten mit Ausgabe
Endliche Automaten haben endliche Speicher und
somit eine endliche Menge von Zuständen. Nichtendliche Automaten haben demgegenüber einen
unbegrenzten Speicher, bei der in J 2.3.3 behandelten
Turingmaschine in der Form eines unendlich langen
Bandes.
Ein endlicher Automat mit Ausgabe ist durch zwei
Funktionen erklärt. Die Übergangsfunktion f bildet
die Zustände (Zustandsmenge Z), im allgemeinen Fall
kombiniert mit Eingangselementen (Eingangsgrößen,
Eingabesymbolen; Eingabemenge E), auf die Zustände ab:
f: E×Z → Z .
Die Ausgangsfunktion g bildet entweder (nach
Moore) die Zustände allein oder (nach Mealy) die
Zustands-Eingangs-Kombinationen in die Ausgangselemente (Ausgangsgrößen, Ausgabesymbole;
Ausgabemenge A) ab:
g: Z → A
(Moore-Automat) ,
g: E × Z → A
(Mealy-Automat) .
Dabei handelt es sich um Mengen mit diskreten Elementen. Beide Modelle sind zur Beschreibung von
Automaten geeignet. Moore-Automaten erfordern
i. Allg. mehr Zustände als die äquivalenten MealyAutomaten. Sie sind etwas leichter zu verstehen,
aber etwas aufwändiger zu realisieren. Die Wahl des
Modells hängt letztlich von der Anwendung ab.
2.1.2 Funktionsweise
In Bild 2-1 ist das Verhalten beider Automatenmodelle illustriert und dem Verhalten der in 1.3 beschriebenen Funktionen gegenübergestellt. Darin bezeichnet 1
die zeitliche Folge der Eingangselemente, 2 die Folge
der Zustände und 3 die Folge der Ausgangselemente.
Die Eingangselemente werden gemäß den Teilbildern
a, c und d folgendermaßen verarbeitet: Bei Funktionen (a) wird in jedem Schritt zu jedem Eingangselement entsprechend f ein Ausgangselement erzeugt.
Bei Automaten (c und d) wird in jedem Schritt – ausgehend von einem vorgegebenen Anfangszustand –
zu einem Eingangselement in Kombination mit einem
Zustand gemäß f der für den nächsten Schritt benötigte Zustand (Folgezustand) erzeugt. Weiterhin wird
im selben Schritt ein Ausgangselement erzeugt, beim
Moore-Automat (c) gemäß g nur vom jeweiligen Zustand abhängig und beim Mealy-Automat (d) gemäß
g von der Kombination von Eingangselement und Zustand abhängig.
Wie Bild 2-1b zeigt, gibt es auch Automaten ohne
Eingangselemente, sog. autonome Automaten, deren
Zustandsfortschaltung von selbst erfolgt. Auch gibt es
Automaten, bei denen die Zustände gleichzeitig die
Ausgangselemente sind; dann kann die Angabe der
Ausgangsfunktion entfallen.
2.2 Hardwareorientierte
Automatenmodelle
2.2.1 Von der Mengen- zur Vektordarstellung
Zur Beschreibung eines Automaten in Mengendarstellung gibt es eine Reihe an Darstellungsmitteln,
Bild 2-1. Gegenüberstellung des Verhaltens.
a Funktion f : E → A; b autonomer Automat
f : Z → Z und g: Z → A; c Moore-Automat f :
E × Z → Z und g: Z = A; d Mealy-Automat
f : E × Z → Z und g: E × Z → A. 1 Eingabe, 2 Zustände, 3 Ausgabe. Die bei d eingetragenen Symbole geben einen Ausschnitt der
Eingangs-Ausgangs-Transformation für den in
Tabelle 2-1a definierten Automaten mit z0 als
Anfangszustand wieder
2 Automaten
wie Tabellen, Tafeln, Graphen; in Tabelle 2-1a
ist nur die Tabellendarstellung gezeigt, und zwar
für einen Mealy-Automaten mit Z = {z0 , z1 , z2 },
E = {e0 , e1 , e2 , e3 } und A = {a0 , a1 , a2 }. Bei der Definition des Automaten in Mengendarstellung ist über
die Art und die Wirkung der Elemente von E und A
(und Z) nichts ausgesagt; sie wird durch die jeweilige
Anwendung bestimmt. Bei einem Fahrkartenautomaten z. B. sind die Eingangselemente Münzen und
die Ausgangselemente Fahrkarten. Das Erscheinen
der Eingangselemente bewirkt hier gleichzeitig die
Zustandsfortschaltung. Bei einem Ziffernrechenautomaten, einem Digitalrechner, sind Eingangs- wie
Ausgangselemente Ziffern bzw. Zahlen; hier werden
die vorhandenen Eingangselemente abgefragt, und
die Zustandsfortschaltung erfolgt automatenintern durch ein Taktsignal. – In der elektronischen
Datenverarbeitung sind die Elemente wegen der
heute verwendeten Schaltkreise binär codiert. Tabelle 2-1b zeigt den Automaten von Tabelle 2-1a
mit den Codierungen [x1 x2 ] = [00], [01], [10], [11]
für e0 , e1 , e2 , e3 , [y1 y2 y3 ] = [000], [101], [010] für
a0 , a1 , a2 und [u1 u2 ] = [00], [01], [10] für z0 , z1 , z2 .
Beschreibung mit Boole’schen Vektoren. Automaten mit binär codierten Elementen lassen sich durch
Boole’sche Funktionen beschreiben, und dementsprechend wird von Boole’schen Automaten gesprochen.
Ihre Realisierung mit Schaltern (i. Allg. Transistoren)
bezeichnet man als Schaltwerke (siehe 4).
Durch die Binärcodierung entstehen aus den Eingangselementen Boole’sche Eingangsvariablen
x1 , x2 , . . . , xn (im Folgenden kurz Eingänge), aus den
Tabelle 2-1. Tabellendarstellungen eines Automaten, a mit
Elementen von Mengen, b mit den Werten Boole’scher Vektoren
Ausgangselementen Boole’sche Ausgangsvariablen
y1 , y2 , . . . , ym (kurz Ausgänge) und aus den Zuständen Boole’sche „Übergangsvariablen“ u1 , u2 , . . . , uk .
Sie werden jeweils zu Boole’schen Vektoren
zusammengefasst: zum Eingangsvektor x, zum
Ausgangsvektor y und zum Übergangsvektor u. Es
entsprechen sich also (vgl. 1.3.1)
f:
E×Z →Z
g:
E×Z → A
und
u := f (u, x) ,
y = g(u, x) .
Darin sind die binär codierten Elemente von E die
Werte von x (Eingangswerte), die binär codierten Elemente von A die Werte von y (Ausgangswerte) und
die binär codierten Zustände von Z die Werte von u
(Übergangswerte).
2.2.2 Darstellungsmittel
Auch für Boole’sche Automaten gibt es eine Reihe
von Darstellungsformen, die ineinander transformierbar sind und jeweils für unterschiedliche Zwecke besonders geeignet sind, z. B. zur Darstellung der Abläufe in einem Automaten oder zur Darstellung seines
Aufbaus.
Der Entwurf eines Schaltwerkes – eine Hauptaufgabe
der Digitaltechnik – kann unter diesem Aspekt
aufgefasst werden als Transformation einer Funktionsbeschreibung des geforderten Verhaltens in eine
Struktur einer verdrahteten oder programmierten
Logikschaltung.
Graphen (Zustandsgraphen,
Zustandsdiagramme;
Bilder 2-2a und 2-2b). Die Graphendarstellung ist
besonders nützlich zur Veranschaulichung der Funktionsweise von Automaten; sie beruht – mathematisch
ausgedrückt – auf der Interpretation der Übergangsfunktion als bezeichneter gerichteter Graph in seiner
zeichnerischen Darstellung mit Knoten und Kanten.
Graphen machen die Verbindungen zwischen den
Zuständen sichtbar und verdeutlichen so Zustandsfolgen, d. h. Abläufe im Automaten. Kreise (Knoten)
stehen für jeden einzelnen Zustand. Sie werden
ohne Eintrag gelassen, wenn dieser nach außen hin
uninteressant ist, andernfalls werden sie symbolisch
bezeichnet oder binär codiert (codierte Zustände,
Übergangswerte). An den Übergangspfeilen (den
Kanten) werden links die Eingänge berücksichtigt,
13
14
Technische Informatik / Mathematische Modelle
ten (ausführliche Form, Tabelle 2-1b) bzw. nur mit
den relevanten Eingangswerten (komprimierte Form,
Bild 2-2c). Rechts stehen die Folgezustände sowie die
Ausgangswerte.
Tafeln (Bild 2-2d). Zur Tafeldarstellung werden
gemäß den beiden Funktionen des Automaten, der
Übergangsfunktion und der Ausgangsfunktion, zwei
Tafeln benötigt. An den Tafeln werden wegen ihrer
unterschiedlichen Bedeutung die codierten Zustände
und die Eingangswerte zweidimensional angeschrieben, i. Allg. vertikal die Zustände und horizontal die
Eingänge. In die eine Tafel werden die Folgezustände
und in die andere die Ausgangswerte eingetragen.
Gleichungen (Bild 2-2e). Zur Gleichungsdarstellung werden die Übergangsfunktion und die
Ausgangsfunktion komponentenweise mit den
Eingangs-, Ausgangs- und Übergangsvariablen
niedergeschrieben. Um zu verdeutlichen, dass es sich
bei der Übergangsfunktion auf der linken Seite der
Gleichungen um den Folgezustand handelt, wird das
Ergibtzeichen := anstelle des Gleichheitszeichens
benutzt.
(Strukturbilder, Schaltbilder; Bild 2-2f).
Blockbilder illustrieren gleichermaßen die formelmäßige Gliederung wie den schaltungsmäßigen
Aufbau eines Boole’schen Automaten und sind
somit Bindeglied beim Schaltwerksentwurf. Im
Gegensatz zum Graphen, bei dem jeder Wert des
Übergangsvektors einzeln als Kreis dargestellt wird,
treten in Blockbildern die einzelnen Komponenten
des Übergangsvektors, d. h. die Übergangsvariablen
selbst, in Erscheinung, und zwar als Kästchen für
eine jede Variable. Die Inhalte der Kästchen sind die
Übergangswerte, die bei jedem Schritt durch ihre
Nachfolger ersetzt werden.
Blockbilder
Bild 2-2. Darstellungsmittel Boole’scher Automaten am
Beispiel des Automaten in Tabelle 2-1b. a Graph mit Boole’schen Werten für die Ein-/Ausgänge; b Graph mit Boole’schen Ausdrücken für die Eingänge und Boole’schen Variablen für die Ausgänge; c Tabelle in verkürzter Form;
d Tafeln; e Gleichungen; f Blockbild. Der Automat dient
als Steuerwerk für die Carry-save-Addition (siehe 4.4.2)
oft nur mit ihren relevanten Werten (Teilbild a)
oder als Boole’sche Ausdrücke (Teilbild b); rechts
werden die Ausgänge berücksichtigt, entweder mit
ihren Werten (Teilbild a) oder oft nur deren aktive
Komponenten (Teilbild b).
Tabellen (Tabelle 2-1b und Bild 2-2c). In der Tabellendarstellung erscheinen links die Kombinationen der Zustände mit allen möglichen Eingangswer-
Verallgemeinerung der Graphendarstellung. Die
in Bild 2-2 angegebenen Darstellungsmittel eignen
sich für Automaten mit Steuerfunktion (Steuerwerke,
Programmwerke, siehe 4.4). Von diesen wiederum eignen sich besonders die Graphendarstellung
und die Blockbilder zur Verallgemeinerung (Abstraktion), insbesondere für Darstellungen auf der
Registertransfer-Ebene. Beim Graphen werden in
Anlehnung an höhere Programmiersprachen die
2 Automaten
Eingänge als parallel abfragbare Bedingungen und
die Ausgänge als parallel ausführbare Anweisungen
formuliert. Mit dieser Einbeziehung von operativen
Funktionen in die ablauforientierte Darstellung
erhält man eine implementierungsneutrale grafische Darstellungsform von Algorithmen, in der
der Fluss einer Zustandsmarke durch die Knoten
des Graphen den Ablauf des Algorithmus veranschaulicht (siehe Bild 4-21a). Diese Darstellung
dient insbesondere als Ausgangspunkt zum Entwurf
der (Steuer-)Schaltnetze in den Steuerwerken, wie
die folgende Kette von Darstellungstransformationen zeigt: Bild 4-21a → Bilder 2-2a oder b →
Bild 2-2c → Bild 2-2d → Bild 2-2e → Bild 2-2f.
– Solche „Programmflussgraphen“ gibt es in vielerlei Varianten, z. B. auch als Flussdiagramme
(siehe Bild 4-22a). Sie charakterisieren den typischen Datenverarbeitungsprozess in heutigen sog.
v.-Neumann-Rechnern und werden auch bei der
(imperativen) Programmierung verwendet.
Verallgemeinerung der Blockbilder. Mit Blockbil-
dern lassen sich zwar auch Automaten mit Steuerfunktion darstellen, sie sind aber besonders für Automaten mit operativer Funktion geeignet (Operationswerke, Datenwerke, siehe 4.3). Charakteristisch für
diese Werke ist die sehr große Anzahl von Zuständen, sodass diese nicht mehr wie bei Graphen einzeln
dargestellt werden können. Stattdessen wird der gesamte Übergangsvektor als Einheit betrachtet und für
ihn ein Kästchen gezeichnet. Das soll die Vorstellung
der Zustände als veränderlicher Inhalt eines Registers
im Automaten ausdrücken.
In den Registern werden also die Übergangswerte
gespeichert; über die mit Pfeilen versehenen Pfade
werden sie übertragen und dabei ggf. verarbeitet.
Dabei gelangt der neue Wert entweder in dasselbe Register, wobei der alte Wert überschrieben
wird (Ersetzen des Wertes), oder in ein anderes
Register, wobei der alte Werte gespeichert bleibt
(Kopieren des Wertes). Bei der Darstellung dieser Transfer- und Verarbeitungsfunktionen spricht
man von der Registertransfer-Ebene. Dabei wird
(ähnlich den verallgemeinerten Graphen bezüglich
der Ein- und Ausgänge) von der Boole’schen Algebra abstrahiert und zu anwendungsspezifischen
Beschreibungsweisen übergegangen, z. B. durch
Angabe der Steuertabelle oder durch Benutzung
von logischen und arithmetischen Operationszeichen
in den Blockbildern oder Hardware-Programmen
(siehe 4.4.2).
Im Allgemeinen arbeiten viele solcher operativer Automaten zusammen, sodass komplexe Verbindungs-/
Verknüpfungsstrukturen zwischen den Registern der
Automaten entstehen (siehe Bilder 5-4 und 5-7, auch
5-9).
2.2.3 Netzdarstellungen
In technischen Systemen arbeitet praktisch immer eine Reihe von verschiedenen Werken (abstrakt gesehen: Automaten) zusammen. An ihren Schnittstellen
entstehen Probleme hinsichtlich ihrer Synchronisation, gleichgültig ob die Werke in ein und derselben Technik oder unterschiedlich aufgebaut sind. Beispiele sind zwei Prozessoren, die einen gemeinsamen
Speicher benutzen, oder ein (elektronischer) Prozessor und ein (mechanisches) Gerät, die gemeinsam Daten übertragen.
Synchronisationsprobleme dieser Art sind 1962 von
C.A. Petri automatentheoretisch behandelt worden.
Auf dieser abstrakten Ebene werden sie mit den nach
ihm benannten Petri-Netzen gelöst. In der Software werden dazu spezielle Variablen benutzt, die man
als Semaphore bezeichnet. In der Hardware werden
spezielle Signale ausgetauscht, was als Handshaking
bezeichnet wird. Während Netzdarstellungen in der
Software eine eher untergeordnete Rolle spielen, sind
sie in der Hardware vielerorts in Gebrauch (auch in
der Steuerungstechnik).
Beispiele zur Synchronisation von Prozessen.
Synchronisation dient allgemein zur Abstimmung von
Handlungen parallel arbeitender Werke bzw. ihrer
„Prozesse“. Die Aufgabe besteht darin, den Ablauf
der Prozesse in eine bestimmte zeitliche Beziehung
zueinander zu bringen, gewissermaßen zur Gewährleistung einer zeitlichen Ordnung im Gesamtprozess.
Synchronisation kann erforderlich werden (1.) zur
Bewältigung von Konfliktsituationen zwischen den
Prozessen oder (2.) zur Herstellung einer bestimmten
Reihenfolge im Ablauf der Prozessaktivitäten. Die
folgenden zwei Beispiele illustrieren den Einsatz
von Petri-Netzen zur Behandlung von Problemen
15
16
Technische Informatik / Mathematische Modelle
und beim Verlassen des Abschnitts wieder in den gemeinsamen Zustand zurückgebracht. Auf diese Weise
ist gewährleistet, dass sich immer nur genau einer der
beiden Prozesse in dem kritischen Abschnitt befindet
(Vermeidung der Konfliktsituation).
Bild 2-3. Petri-Netze. a Illustration des Problems des
gegenseitigen Ausschlusses; b Illustration des ErzeugerVerbraucher-Problems
dieser Art (zu Petri-Netzen sowie zu deren Symbolik
siehe I 14.3).
Problem des gegenseitigen Ausschlusses (mutual exclusion). In Bild 2-3a sind zwei Prozesse miteinander vernetzt. Sie beschreiben zwei Werke, die einen
gemeinsamen Abschnitt des Gesamtprozesses nur exklusiv benutzen dürfen (zur Veranschaulichung: zwei
unabhängige Fahrzeuge, die auf eine Engstelle zufahren). – In einem Rechner bildet z. B. einen solchen
kritischen Abschnitt (critical region) das gleichzeitige
Zugreifen zweier Prozesse auf ein gemeinsames Betriebsmittel, wie den Systembus mit dem daran angeschlossenen Speicher (Problem der Busarbitration).
Die Prozesse in Bild 2-3a sind über einen gemeinsamen Zustand gekoppelt, der nur dann eine Marke
enthält, wenn der kritische Abschnitt frei ist. Bei einem Zugriff auf diesen wird die Marke entweder von
dem einen oder dem anderen Prozess mitgenommen
Erzeuger-Verbraucher-Problem (producer consumer
problem). In Bild 2-3b sind drei Prozesse miteinander vernetzt. Der linke und der rechte Prozess
beschreiben mit T 1 und T 2 getaktete Werke, von
denen das linke Daten erzeugt und das rechte Daten
verbraucht (zur Veranschaulichung: von denen das
eine eine Ware produziert und das andere die Ware
konsumiert). Der mittlere Prozess beschreibt ein
ungetaktetes, d. h. asynchron zu den beiden anderen
arbeitendes Werk, einen Puffer, der imstande ist,
eine Dateneinheit (die Ware) zwischenzulagern. – In
einem Rechner ist z. B. der Erzeuger ein Prozessor
und der Verbraucher ein Ausgabegerät; und der
Puffer ist das Datenregister eines Interface-Bausteins
(gepufferte, asynchrone Datenübertragung). Der
Puffer ist entweder leer (Marke im oberen Zustand)
oder voll (Marke im unteren Zustand). Der Erzeuger
produziert eine Dateneinheit und wartet, sofern der
Puffer nicht leer ist, anderenfalls füllt er ihn. Der
Verbraucher wartet, solange der Puffer nicht voll ist,
sodann leert er ihn und konsumiert die Dateneinheit.
Auf diese Weise ist gewährleistet, dass das im Puffer
befindliche Datum vom Erzeuger nie überschrieben
und vom Verbraucher nie doppelt gelesen werden
kann (Einhaltung der Reihenfolge).
Grenzen der Anwendbarkeit von Petri-Netzen.
In Bild 2-3 kommen die Vor- und Nachteile von
Petri-Netzen gut zum Ausdruck. Petri-Netze zeigen
das Zusammenwirken der Prozesse in einer abstrakten, das Verständnis des Gesamtprozessgeschehens
fördernden Form; Konflikte werden deutlich, da
eine Marke nur über einen von mehreren Wegen
laufen darf (vgl. in Bild 2-3a die Aktionen „kritischer
Abschnitt betreten“); es dominiert die Reihenfolge
der Handlungen, d. h., ob sie nebeneinander (parallel) ausgeführt werden können oder nacheinander
(sequentiell) ausgeführt werden müssen (vgl. in
Bild 2-3b die Aktionen „Puffer füllen“ bzw. „Puffer leeren“). Darüber hinaus erlauben die beiden
gezeigten Petri-Netze eine Verallgemeinerung der
jeweiligen Aufgabenstellung: Wenn das Netz in
2 Automaten
Bild 2-3a auf n Prozesse erweitert wird und der allen
gemeinsame Zustand mit m Marken initialisiert wird
(m < n), so beschreibt es die Freigabe des kritischen
Abschnitts für m von n Prozessen. Wenn im Netz
in Bild 2-3b der obere Pufferzustand mit n Marken
statt mit einer initialisiert wird, so beschreibt es die
Daten-/Materialübertragung für einen Puffer mit
einer Kapazität von n Speicher-/Lagerplätzen.
Die Teilbilder zeigen auch die Grenzen der Anwendbarkeit von Petri-Netzen: Während vernetzte
Automatengraphen räumlich verbundenen Funktionseinheiten entsprechen und aufgrund ihrer als
Eingänge und Ausgänge gekennzeichneten Synchronisationssignale auf diverse Blätter verteilt werden
können, ist das bei Petri-Netzen wegen der gemeinsamen Übergangsbalken gar nicht beabsichtigt. Daher
werden Petri-Netze schon bei wenig komplexen
Aufgabenstellungen sehr unübersichtlich. Eine
gewisse Abhilfe bringen sog. höhere Petri-Netze,
die aber schwieriger zu interpretieren sind. PetriNetze werden deshalb hauptsächlich zur Darstellung
grundsätzlicher Zusammenhänge benutzt. In der
Programmierung paralleler, verteilter Prozesse wird
hingegen sprachlichen Formulierungen der Vorzug
gegeben.
2.3 Softwareorientierte Automatenmodelle
2.3.1 Erkennende Automaten
und formale Sprachen
In der theoretischen Informatik und im Übersetzerbau
benutzt man Automaten als mathematische Modelle zur Erkennung der Sätze formaler Sprachen, d. h.
zur Feststellung der Struktur von Zeichenketten. Man
nennt diese Automaten deshalb auch erkennende Automaten und unterscheidet vier Arten, die in engster
Korrespondenz mit den verschiedenen Arten formaler Sprachen stehen:
– Endliche Automaten erkennen reguläre Sprachen.
– Kellerautomaten erkennen kontextfreie Sprachen.
– Linear beschränkte Automaten erkennen kontextsensitive Sprachen.
– Turingmaschinen erkennen unbeschränkte Sprachen.
Softwareorientierte Automaten weisen gegenüber
hardwareorientierten folgende charakteristische Unterschiede auf:
Sie liefern während des Lesens und Verarbeitens
einer Eingabe-Zeichenkette keine Ausgabe.
Sie haben ausgezeichnete Zustände: den Anfangszustand, in dem sie starten, und Endzustände, in
denen sie anhalten und die eingegebene Zeichenkette als erkannt signalisieren.
Sie existieren alle in einer deterministischen und
in einer nichtdeterministischen Variante.
Die Unterscheidungen von synchroner und asynchroner Arbeitsweise und von Moore- und MealyAutomat entfallen.
2.3.2 Erkennende endliche Automaten
Ein erkennender endlicher Automat (Bild 2-4) besteht
aus einer endlichen Anzahl von Zuständen z1 bis zn ,
einem Eingabeband mit den Zeichen s1 bis sm , einem
Lesekopf und einer Ja-Nein-Anzeige (Lampe). Ein
Zustand ist als Startzustand, ein oder mehrere Zustände sind als Endzustände ausgezeichnet. Das Band enthält eine Kette von Zeichen eines gegebenen Alphabets, und der Automat hat die Aufgabe, durch schrittweises Lesen der Zeichenkette festzustellen, ob sie
eine bestimmte, durch die Übergangsfunktion des Automaten festgelegte Struktur hat. Die Kette wird dazu
vom Lesekopf von links nach rechts abgetastet. Die
Erkennung geht folgendermaßen vor sich:
1. Am Anfang befindet sich der Automat im Anfangszustand, und das erste Zeichen s1 der Kette
steht über dem Lesekopf.
2. Der Automat führt Züge aus, indem er, gesteuert
durch den augenblicklichen Zustand zi und das aktuelle Zeichen s j , in den nächsten Zustand übergeht und den Lesekopf um eine Stelle nach rechts
bewegt.
3. Die Erkennung endet, wenn die Übergangsfunktion für den aktuellen Zustand und das aktuelle
Bandzeichen undefiniert ist. (Das ist u. a. dann der
Fall, wenn das Band vollständig gelesen ist. Die
auf das letzte Bandzeichen folgende Kette ist die
Bild 2-4. Erkennender endlicher Automat
17
18
Technische Informatik / Mathematische Modelle
leere Kette, durch ε bezeichnet.) Wenn dann der
aktuelle Zustand ein Endzustand ist, ist die Kette
erkannt (die Lampe leuchtet), wenn nicht, ist die
Kette nicht erkannt.
Die vorstehende Beschreibung lässt sich durch folgende Definitionen präzisieren:
Ein deterministischer endlicher Automat DFA (deterministic finite automaton) ist ein Quintupel
DFA = (Z, VT , δ, z1 , F) .
Darin ist Z eine endliche Menge von Zuständen,
VT eine endliche Menge von Eingabesymbolen, δ die Übergangsfunktion (eine Abbildung
Z × VT → Z), z1 ∈ Z der Anfangszustand, F ⊆ Z die
Menge der Endzustände.
Eine Konfiguration, geschrieben (z, α), ist das Paar
aus dem gegenwärtigen Zustand z und dem noch ungelesenen Teil α der Eingabekette. Dabei ist das am
weitesten links stehende Zeichen von α dasjenige, das
als nächstes gelesen wird.
Ein Zug, dargestellt durch das Zeichen , ist der Übergang von einer Konfiguration in die nächste. Wenn die
gegenwärtige Konfiguration (zi , bα) lautet, und es ist
δ(zi , b) = z j , dann führt der endliche Automat den Zug
(zi , bα) (z j , α) aus. Eine Zugfolge von n ≥ 0 Zügen
wird durch ∗ ausgedrückt.
Eine Zeichenkette σ ist erkannt, wenn es eine Zugfolge (z1 , σ) ∗ (p, ε) für irgendein p ∈ F gibt.
Als Sprache L(DFA) eines endlichen Automaten definiert man die Menge aller Zeichenketten, die der Automat erkennt; formal
L(DFA) = {α : α ∈ VT∗ ∧ ((z1 , α)
∗
(p, ε) ∧ p ∈ F)} .
Beispiel.
Ein Automat zur Erkennung aller Zeichenketten aus Nullen und Einsen mit einer durch 3 teilbaren Anzahl von Einsen lautet:
DFA = ({z1 , z2 , z3 }, {0, 1}, δ, z1 , {z1 })
Bild 2-5 zeigt die Übergangsfunktion δ als Tabelle
und als Graph.
Bei der Erkennung der Kette 110010 führt der Automat folgende Züge aus:
(z1 , 110010) (z2 , 10010) (z3 , 0010)
(z3 , 010) (z3 , 10)
(z1 , 0)
(z1 , ε)
Bild 2-5. Übergangsfunktion eines erkennenden endlichen
Automaten. a Tabelle, b Graph. z1 ist Startzustand und zugleich einziger Endzustand
Nichtdeterministischer Automat. Ein nichtdeter-
ministischer endlicher Automat kann von einem
Zustand aus bei einem bestimmten Eingabewert
in mehrere Folgezustände übergehen. Seine Übergangsfunktion wird dadurch i. Allg. mehrwertig; sie
ist eine Abbildung von Z × VT in Teilmengen der
Potenzmenge von Z. Jeder nichtdeterministische
endliche Automat lässt sich in einen äquivalenten deterministischen endlichen Automaten transformieren.
Grenzen der Anwendbarkeit. Endliche Automaten
sind zur Beschreibung von formalen Sprachen nur beschränkt tauglich. Sie können nur Sätze von Grammatiken ohne Klammerstruktur, wie Bezeichner, Zahlen
und einige andere erkennen.
2.3.3 Turingmaschinen
Die Turingmaschine (A.M. Turing, 1936) besteht
aus einer endlichen Anzahl von Zuständen z1
bis zn , einem einseitig unendlichen Band, einem
Schreib-/Lese-Kopf und einer Ja-Nein-Anzeige
(Lampe). Im Unterschied zum endlichen Automaten
wird das Band bei jedem Zug um eine Stelle nach
links oder rechts bewegt und ein Zeichen gelesen
und geschrieben. Alles Übrige ist wie beim endlichen Automaten. Die Übergangsfunktion hat zwei
Argumente: den aktuellen Zustand und das aktuelle
Bandsymbol; und sie hat drei Werte: den nächsten
Zustand, das Bandsymbol, mit dem das aktuelle
überschrieben werden soll, und die zweiwertige
2 Automaten
Angabe {L, R}, ob der Schreib-/Lese-Kopf nach
links oder rechts bewegt werden soll (Bild 2-6).
Formal ist eine Turingmaschine (TM) ein 6-Tupel:
TM = (Z, VT , V, δ, z1 , F)
Darin ist Z eine endliche Menge von Zuständen; VT
eine endliche Menge von Eingabesymbolen; V eine endliche Menge von Bandsymbolen, eines von ihnen, (blank), bedeutet das Leerzeichen (es ist VT ⊆
V − {}), δ die Übergangsfunktion, eine Abbildung
von Z × V nach Z × V × {L, R}, z1 ∈ Z der Anfangszustand und F ⊆ Z, die Menge der Endzustände.
Eine Konfiguration, geschrieben (z, a.β) ist das Tripel aus dem gegenwärtigen Zustand z, dem links vom
Schreib-/Lese-Kopf liegenden Teil α des Bandes und
dem Bandrest β. Das erste Zeichen von β befindet sich
über dem Schreib-/Lese-Kopf, alle übrigen Zeichen
von β rechts von ihm.
Ein Zug, dargestellt durch , ist der Übergang von einer Konfiguration in die nächste. Eine Zeichenkette σ
ist erkannt, wenn es eine Zugfolge (z1 , .σ) ∗ (p, α.β)
für irgendein p ∈ F und beliebigen Bandinhalt α.β
gibt.
Beispiel. Gesucht ist eine Turingmaschine TM,
die ihren anfänglichen Bandinhalt aus Nullen und
Einsen – eingerahmt durch die Begrenzungszeichen A (Anfang) und E (Ende) – hinter diesen
kopiert/transportiert. Strategie: 1. Zeichen (0 oder 1)
merken und durch Hilfszeichen $ überschreiben
(Position merken). 2. Bis zum ersten Leerzeichen ()
vorwärtsgehen und durch Zeichen überschreiben.
3. Zu $ zurückgehen und $ durch Zeichen überschreiben; zum nächsten Zeichen gehen. Schritte 1 bis 3
wiederholen, bis E erreicht ist.
TM hat 7 Zustände (Menge Z), die Eingabesymbole
lauten 0, 1, A, E (Menge VT ), die Bandsymbole lauten
0, 1, A, E, $, (Menge V). Die Übergangsfunktion (δ)
Bild 2-6. Turingmaschine
ist in Bild 2-7 als Zustandsgraph wiedergegeben; der
Startzustand ist z1 , der Endzustand ist z7 .
Im Graphen bedeutet die Anschreibung x → y, z
Überschreiben von Bandsymbol x durch Eingabesymbol y (aus x wird y) und Weiterrücken
des Schreib-/Lesekopfes nach links oder rechts
(z = L bzw. R). – Aus einer beispielsweisen Anfangsbandbeschriftung A 0 1 0 0 1 0 E entsteht die
Endbandbeschriftung A 0 1 0 0 1 0 E 0 1 0 0 1 0.
Das Beispiel verdeutlicht gleichermaßen die Stärken
und die Schwächen der Turingmaschine, wenn man
an einen technischen Aufbau zur elektronischen Datenverarbeitung denkt (vgl. die Diskussion in [4]).
Das ist allerdings für ihre Anwendung in der theoretischen Informatik weder beabsichtigt noch notwendig! – Ihre Stärke ist die Abfragbarkeit aller Bandsymbole auf einen Schlag (und somit die Ausführung
von Programmverzweigungen in einem Schritt). Ihre Schwäche ist das Fehlen jeglicher arithmetischer
und logischer Operationen. Wie am Beispiel zu sehen,
muss selbst eine so elementare Operation wie Kopieren/Transportieren äußerst umständlich programmiert
werden: Die Inspektion des Zustandsgraphen ergibt
20 Zeilen für die Tabellenform der Übergangsfunktion (20 Anschreibungen im Graphen); diese Tabelle
bildet gewissermaßen das Programm der Turingmaschine. Zum Kopieren/Transportieren eines einzigen
Zeichens sind für die oben gewählte Bandbeschriftung allein 15 Schritte notwendig (7 vor, 8 zurück).
Trotzdem: Man kann zeigen, dass Turingmaschinen
auch addieren und alle anderen arithmetischen Operationen ausführen können. Der unbeschränkt große
Bandspeicher und die Möglichkeit, Bandfelder wiederholt zu besuchen und ihren Inhalt zu ersetzen,
verleihen der Turingmaschine theoretisch die Fähigkeit, alle Algorithmen auszuführen. Erweiterungen
von Turingmaschinen auf mehrere Bänder, mehrere
Schreib-/Lese-Köpfe und nichtdeterministische Turingmaschinen bringen keinen Zuwachs an algorithmischen Fähigkeiten.
Turingmaschinen werden in der theoretischen Informatik vor allem zu zwei Zwecken herangezogen:
Zur Definition der Begriffe Algorithmus und
Berechenbarkeit: Zu jedem denkbaren Algorithmus (jeder berechenbaren Funktion) gibt
es eine Turingmaschine, die ihn ausführt. Man
19
20
Technische Informatik / Mathematische Modelle
Bild 2-7. Graph für die Beispiel-Turingmaschine. Die
entsprechende Tabelle kann durch Software (z. B. mit
Excel) oder durch Hardware (binär codiert mit Steuerwerk) verwirklicht werden (siehe 4.4). Dabei wird anstelle des Bands besser ein Speicher mit wahlfreiem
Zugriff mit vorgeschaltetem Vor-/Rückwärtszähler verwendet. Das Programm des Steuerwerks ist im Logikfeld „fest gespeichert“ oder mit Logikgattern „fest verdrahtet“. Zum Hardware-Aufbau siehe auch [1]
schreibt die Eingangsparameter (Argumente) auf
das Band, startet die Turingmaschine, und die
Turingmaschine hält nach endlich vielen Schritten
mit den Ausgabeparametern (dem Funktionswert)
auf dem Band an. Man macht die Ausführbarkeit
durch eine Turingmaschine sogar zur Definition
des Algorithmusbegriffs, indem man sagt:
Turingmaschinen sind formale Modelle von Algorithmen, und kein Berechnungsverfahren kann
algorithmisch genannt werden, das nicht von
einer Turingmaschine ausführbar ist (Churchsche
These).
Zur Erkennung der allgemeinsten Klasse formaler
Sprachen: Diese Klasse ist dadurch charakterisiert, dass es für jede zu ihr gehörende Sprache
eine Turingmaschine gibt, die eine Zeichenkette, die ein Satz der Sprache ist, erkennt. Zur
Erkennung wird die Kette auf das Band der
Turingmaschine geschrieben und die Turingmaschine gestartet. Wenn die Kette ein Satz der
Sprache ist, hält die Turingmaschine nach endlich
vielen Schritten in einem Endzustand an; wenn sie
kein Satz ist, hält die Turingmaschine entweder in
einem Nicht-Endzustand oder überhaupt nicht an.
Universelle Turingmaschinen. Verschiedenen Algorithmen entsprechen verschiedene Turingmaschinen.
Ihre Übergangsfunktionen kann man sich als ihre
fest verdrahteten Programme denken (siehe Bildunterschrift zu Bild 2-7). Man kann jedoch als
Gegenstück zum Computer auch universelle Turingmaschinen konstruieren, die eine beliebige auf ihr
Band geschriebene Übergangsfunktion interpretieren
und somit imstande sind, alle Turingmaschinen zu
simulieren.
Bedeutung für die Programmierungstechnik. Ob-
wohl Turingmaschinen nur mathematische Modelle
ohne jede praktische Anwendbarkeit sind, liefern sie
wertvolle Einsichten und haben deshalb Bedeutung
z. B. in folgenden Fragestellungen:
Wenn man Algorithmen, die in verschiedenen
Programmiersprachen geschrieben sind, hinsichtlich bestimmter Eigenschaften miteinander
vergleichen will, bietet sich die Turingmaschine
als einfachster gemeinsamer Standard an.
Um zu zeigen, dass eine neue Programmiersprache universell ist, d. h. alle Algorithmen zu formulieren gestattet, braucht man nur zu zeigen, dass
man mit ihr eine Turingmaschine simulieren kann.
Es gibt Probleme, die sich algorithmisch
prinzipiell nicht lösen lassen, was sich am
Modell der Turingmaschine besonders einfach
zeigen lässt. Darunter ist von größtem allgemeinen Interesse das Halteproblem, d. i. die
3 Schaltnetze
Frage „Gibt es einen Algorithmus, der entscheidet, ob ein beliebiges gegebenes Programm für
beliebig gegebene Eingabedaten nach endlich vielen Schritten anhält? “ Die Antwort lautet „Nein.“
Viele andere Probleme der Berechenbarkeit lassen
sich auf das Halteproblem zurückführen.
2.3.4 Grenzen der Modellierbarkeit
Endliche Automaten und Turingmaschinen sind autonome Modelle, d. h., sie modellieren nur Abläufe, die, einmal gestartet, unbeeinflusst durch die Au-
ßenwelt bis zu ihrem Ende ablaufen. Die Abläufe
in Computern sind dagegen nicht autonom, sondern
können durch Signale von außen unterbrochen werden (Interrupts). Zur Kommunikation einer Zentraleinheit mit der Außenwelt (E/A-Geräte, andere Computer in Computernetzen und verteilten Systemen) ist
die Möglichkeit wesentlich, einen Ablauf in der Zentraleinheit durch ein von außen kommendes Signal
zu unterbrechen. Mathematische Modelle von ähnlicher Einfachheit wie die Turingmaschine, die die Unterbrechbarkeit berücksichtigen, sind bisher nicht bekannt geworden.
Digitale Systeme
H. Liebig
Digitale Systeme sind wohlstrukturierte, sehr komplexe Zusammenschaltungen ganz elementarer, als
Schalter wirkender Bauelemente. Am Anfang der
Computertechnik Relais, heute fast ausschließlich
Transistoren, haben solche elektronischen Schalter
funktionell betrachtet nur zwei Zustände: Sie sind
entweder offen oder geschlossen. Dementsprechend
operieren digitale Systeme im mathematischen Sinn
nur mit binären Größen. Bit steht dabei als Kurzform
für „binary digit“.
Auch arithmetische Operationen und logische
Verknüpfungen lassen sich auf das „Rechnen“
mit binären Größen zurückführen: die Ziffern von
Dualzahlen sind entweder 0 oder 1; als Aussagen
aufgefasste Sätze sind entweder falsch oder wahr.
Diese Gemeinsamkeit ermöglicht es, arithmetische
und logische Prozesse mit digitalen Systemen, d. h.
durch das Zusammenwirken ihrer Schalter, nachzubilden.
Digitale Systeme können nach steigender Komplexität ihrer Struktur und ihrer Funktion in Schaltnetze,
Schaltwerke und Prozessoren eingeteilt werden.
Schaltnetze sind imstande, den Kombinationen
ihrer Eingangsgrößen einmal festgelegte Kombinationen ihrer Ausgangsgrößen zuzuordnen; sie
werden deshalb auch Zuordner oder im Englischen
„combinational circuits“ genannt.
Schaltwerke sind darüber hinaus in der Lage, vorgegebene Sequenzen ihrer Eingangsgrößen in vorgegebene Sequenzen ihrer Ausgangsgrößen abzubilden;
sie werden deshalb auch Automaten oder im Englischen „sequential machines“ genannt. Sie bilden die
Hardware-Implementierungen von Automaten/Algorithmen. Prozessoren entstehen aus der Zusammenschaltung von arithmetisch-logischen Schaltnetzen
mit operativen und steuernden Schaltwerken. Sie
eignen sich primär zur Durchführung numerischer
Berechnungen, insbesondere für die Verarbeitung
großer Datenmengen. Prozessoren bilden somit die
zentralen Verarbeitungseinheiten der elektronischen
Rechenanlagen (central processing units, CPUs).
3 Schaltnetze
Ihrer Struktur nach sind Schaltnetze rückwirkungsfreie Zusammenschaltungen von Schaltgliedern. Ihre
Funktion folgt den Gesetzen der Boole’schen Algebra
(Boole’sche Funktionen). Damit lässt sich der Begriff
Schaltnetz wie folgt definieren: Ein Schaltnetz ist die
schaltungstechnische Realisierung einer Boole’schen
Funktion, mathematisch ausgedrückt durch die Abbildung f mit dem Eingangsvektor x und dem Ausgangsvektor y:
(3-1)
21
22
Technische Informatik / Digitale Systeme
3.1 Signaldurchschaltung und -verknüpfung
Binär codierte Information wird durch Signale repräsentiert (z. B. elektrische Spannungen), die nur zwei
Pegel annehmen können (z. B. 0 V/+3 V, kurz /+ als
„Potenziale“). Die Bezeichnung der Signale erfolgt
durch beliebige Namen; die der Pegel durch die beiden Ziffern 0 und 1, üblicherweise 0 für und 1 für +
(„positive Logik“). Entsprechend den in der Mathematik gebräuchlichen Bezeichnungsweisen sind die
Signale Variablen und ihre Pegel deren Werte („das
Signal x hat den Pegel 3 V“ hat so die gleiche Bedeutung wie „die Variable x hat den Wert 1“).
Die Übertragung und Verarbeitung von Signalen erfolgt mit Schaltern, die zu komplexen Gebilden verbunden sind (Schalterkombinationen). Dabei können
mehrere Schalter mit ein und demselben Eingangssignal gesteuert werden. In Schaltbildern wird das
dadurch ausgedrückt, dass an jedem dieser Schalter
derselbe Signalnamen geschrieben wird; und der Bequemlichkeit halber gibt man den Schaltern selbst
auch gleich die Namen ihrer Steuersignale (das Signal x schaltet alle mit x bezeichneten Schalter bei
x = 1 auf „ein“).
In digitalen Systemen dienen Schalter und Schalterkombinationen (1.) zum Durchschalten von Signalen
(Durchschaltglieder, transmission gates), (2.) zum
Verknüpfen von Signalen (Verknüpfungsglieder,
combinational gates). Beide Arten von Schaltgliedern
(Gattern) erscheinen in Digitalschaltungen in großer
Mannigfaltigkeit und werden innerhalb eines Systems
auch gemischt eingesetzt.
Beispiel. Ein Schaltnetz zur Addition von zwei
Dualzahlen entsteht aus einer kettenförmigen Zusammenschaltung von „Kästchen“ (Bild 3-1a), von
denen jedes seinerseits Zusammenschaltungen von
Durchschalt- und Verknüpfungsgliedern enthält
(siehe Bild 3-9 und Bild 3-10). Jedes der Kästchen
führt die Addition eines Ziffernpaares aus. Sie
besteht aus der Berechnung der Ergebnisziffer zi und
des in Stelle i entstehenden Übertrags ui+1 aus den
Summandenziffern xi , yi und dem von der Stelle i − 1
kommenden Übertrag ui (Bild 3-1b).
Bild 3-1. Addition von zwei Dualzahlen Z = X+Y. a Schaltnetz mit Zahlenbeispiel; b Definition einer Teilschaltung
(Volladdierer)
Ende der Schaltung herzustellen, und zwar nach Maßgabe der an den Schaltern liegenden Eingangssignale.
Somit kann ein an dem einen Ende anliegendes variables oder konstantes Potenzial entweder an das andere Ende übertragen werden (Leitung durchgeschaltet)
oder nicht übertragen werden (Leitung nicht durchgeschaltet).
Operationen mit Schaltern. Es gibt Schalter mit
normaler und Schalter mit inverser Funktion; weiter
können Schalter mit normalen und invertierten
Signalen gesteuert werden; und schließlich können
Schalter seriell und parallel verbunden werden. Diese
Operationen erlauben die planmäßige Konstruktion
von Schalterkombinationen.
Normalfunktion/-ansteuerung eines Schalters (Identität): Die Leitungsverbindung ist durchgeschaltet,
wenn der Schalter geschlossen ist; das ist bei x = 1
der Fall (bei x = 0 ist er geöffnet), in Formeln durch
x ausgedrückt (Bild 3-2a);
3.1.1 Schalter und Schalterkombinationen
Inversfunktion/-ansteuerung eines Schalters (Negation, NICHT-Operation): Die Leitungsverbindung ist
durchgeschaltet, wenn der Schalter geschlossen ist;
das ist nicht bei x = 1 der Fall (sondern bei x = 0), in
Formeln x̄ (Bild 3-2b);
Schalter und Schalterkombinationen haben die Aufgabe, Leitungsverbindungen von einem zum anderen
Serienschaltung von Schaltern (Konjunktion, UNDVerknüpfung): Die Leitungsverbindung ist durchge-
3 Schaltnetze
Bild 3-2. Schalterkombinationen. a Identität (Schalter nor-
mal arbeitend und normal angesteuert); b Negation (Schalter invers arbeitend bzw. invers angesteuert); c Konjunktion
(Schalter in Serie geschaltet); d Disjunktion (Schalter parallelgeschaltet)
schaltet, wenn Schalter x1 geschlossen ist (x1 = 1)
und Schalter x2 geschlossen ist (x2 = 1), als Formel
x1 · x2 (Bild 3-2c);
Parallelschaltung von Schaltern (Disjunktion, ODERVerknüpfung): Die Leitungsverbindung ist durchgeschaltet, wenn Schalter x1 geschlossen ist (x1 = 1)
oder Schalter x2 geschlossen ist (x2 = 1), als Formel
x1 + x2 (Bild 3-2d).
Bei ausschließlicher Verwendung dieser Operationen
zum Aufbau einer Schalterkombination lässt sich
aus der Struktur einer Formel eine Schaltung „derselben“ Struktur entwickeln. Andere als mit diesen
Elementaroperationen aufgebaute Schaltungen lassen
sich zwar auch durch Formeln beschreiben, jedoch
sind ihre Strukturen nicht mehr von gleicher Gestalt.
Schaltung Bild 3-3a wird z. B. durch die darunter
angegebene Formel strukturgetreu wiedergegeben,
während für Schaltung Bild 3-3b lediglich die vier
Möglichkeiten von Leitungsverbindungen aus der
Formel abgelesen werden können (eine oben, eine
unten, zwei über Kreuz).
Bild 3-3. Zwei äquivalente Schalterkombinationen, a struk-
turgetreu, b nicht strukturgetreu durch Formeln beschreibbar. Die eingetragenen Schalterstellungen illustrieren den
Fall a = 1, b = 0, c unbestimmt
Bild 3-4. Durchschaltglieder (die Schalter stehen auch
stellvertretend für Schalterkombinationen). a UND-ODERGatter; b Ladungsspeicherung (bei a = 0); c Kurzschlussstrom (bei z. B. x1 = 1 und x2 = 0)
3.1.2 Durchschaltglieder
Durchschaltglieder schalten die variablen Potenziale
ihrer Eingangssignale xi (siehe Bild 3-4a) entsprechend den Werten ihrer Eingangssignale ai auf den
Ausgang y entweder rückwirkungsfrei durch (y = x1 ,
wenn a1 = 1 und a2 = 0; y = x2 , wenn a1 = 0 und
a2 = 1), gar nicht durch (y = ?, wenn a1 = 0 und
a2 = 0) oder rückwirkungsbehaftet durch (y = !, wenn
a1 = 1 und a2 = 1).
Im Falle y = x1 oder x2 gilt a1 a2 , und der Ausgang
hat denselben Wert wie der durchgeschaltete Eingang,
d. h., y = 1, wenn a1 = 1 und x1 = 1 oder a2 = 1 und
x2 = 1.
Im Falle y = ? „hängt“ der Ausgang „in der Luft“.
Dieser (neben 0 und 1) dritte Zustand ist entweder unzulässig, weil undefiniert, oder er hat wegen der Leitungskapazität des Ausgangs denjenigen Wert y = 0
oder 1, den er vor der „Abtrennung“ des Eingangssignals xi hatte (Ladungsspeicherung). In Schaltung
Bild 3-4b z. B. bleibt y = 1 bei a = 0 nur dann
konstant, wenn sich die aufgeladene Kapazität nicht
entladen kann, d. h., wenn kein Strom in die nachfolgende Schaltung fließt. Diese Annahme ist jedoch
etwas unrealistisch, denn selbst bei sehr hohem Eingangswiderstand der Folgeschaltung (Eingang „hochohmig“) fließen in Wirklichkeit – wenn auch sehr
geringe – Leckströme, sodass sich die Kapazität entladen kann. Deswegen muss, wenn der Wert der Variablen y über einen längeren Zeitraum „gespeichert“
werden soll, die Ladung periodisch durch ein Taktsignal erneuert werden oder durch Rückkopplung wieder aufgefrischt werden (siehe 4, insbesondere Bilder 4-3a bzw. 4-4a).
23
24
Technische Informatik / Digitale Systeme
Bild 3-5. Durchschaltglieder. a Funktion y = a1 · x1 + a2 ·
x2 + ā1 · ā2 · v (Ausgang auch bei a1 = 0, a2 = 0 definiert);
b Dioden-ODER-Gatter; c Dioden-UND-Gatter
Im Fall y = ! „verkoppelt“ der Ausgang die Eingänge.
Dieser Fall unterliegt der Bedingung, dass niemals zu
hohe Ströme von 1 = + nach 0 = fließen dürfen, da
sonst Schalter zerstört werden können (Kurzschlussströme). In Schaltung Bild 3-4c kann das bei a1 = 1,
a2 = 1, a3 = 0 eintreten, wenn mit x1 = 1 in der
vorhergehenden Schaltung (z. B. über einen Schalter
ohne Widerstand – Ausgang „niederohmig“) unmittelbar der Pluspol und mit x2 = 0 unmittelbar der
Massepol durchgeschaltet wird (z. B.,wenn Bild 3-8a
Ausgangsschaltung ist).
Grundschaltungen mit Dioden. Ladungsspeicherung
wird verhindert, wenn ein Zweig im Durchschaltglied
mit einem Widerstand versehen wird. In Bild 3-5a
z. B. ist bei a1 = 0 und a2 = 0 y = v = 0/1 = /+.
(Da kein Strom durch den Widerstand fließt, entsteht
an ihm kein Spannungsabfall, und das jeweilige Potenzial von v überträgt sich auf y.) Kurzschlussströme
werden verhindert, indem selbststeuernde Schalter
mit Ventilverhalten (Dioden) benutzt werden. Beim
ODER-Gatter, Bild 3-5b, wird v = gewählt, sodass
die Dioden (Schalter ai ) bei x1 = 1 durchgeschaltet
sind (d. h. Normalsteuerung der Schalter: ai = xi );
daraus folgt y = 1, wenn x1 = 1 oder x2 = 1. Beim
UND-Gatter, Bild 3-5c, wird v = + gewählt, sodass
die Dioden (Schalter ai ) bei xi = 1 gesperrt sind
(d. h. Inverssteuerung der Schalter: ai = x̄i ); daraus
folgt y = 1, wenn x1 = 1 und x2 = 1. In beiden
Schaltungen ist bei ungleichen Eingangspotenzialen
immer eine Diode gesperrt; und wie die Symbole
zeigen, können Kurzschlussströme nicht auftreten.
Ein Durchschaltglied in MOS-Technik.
Sowohl
Ladungsspeicherung als auch Kurzschlussströme
Bild 3-6. Universal-Durchschaltglied mit der Funktion
y = x0 · ā1 ā0 + x1 · ā1 a0 + x2 · a1 ā0 + x3 · a1 a0 . a MOSUND-ODER-Gatter; b Verwendung als Multiplexer; c Verwendung als Logikeinheit. Die Schaltung kann auch in umgekehrter Richtung zum Demultiplexen oder Decodieren
verwendet werden
werden verhindert, wenn alle Zweige im Durchschaltglied mit Serienschaltungen von Schaltern
ausgestattet sind (UND-Funktion) und die Schalter
so gesteuert werden, dass immer genau ein Zweig
durchgeschaltet ist. Bild 3-6 zeigt eine solche Schaltung mit MOS-Transistoren als Schalter (MOS, metal
oxide semiconductor). Darin werden die Transistoren
alle normal betrieben, jedoch mit normalen und
invertierten Signalen gesteuert. – Die Schaltung
kann als Multiplexer oder als Logikeinheit eingesetzt
werden. Als Multiplexer hat sie die Aufgabe, genau
eine der vier Eingangsleitungen x0 bis x3 mit den
Steuersignalen a1 und a0 auf den gemeinsamen
Ausgang y durchzuschalten, z. B. bei [a1 a0 ] = [11]
die unterste Leitung, d. h., y = x3 . Als Logikmodul
kann sie durch Wahl der Steuersignale x3 , x2 , x1 , x0
sämtliche 24 = 16 Verknüpfungsmöglichkeiten
mit den zwei Eingangsvariablen a0 und a1 bilden
und der Ausgangsvariablen y zuordnen, z. B. bei
[x3 x2 x1 x0 ] = [1000] die UND-Funktion, d. h.,
y = a0 · a1 , und bei [x3 x2 x1 x0 ] = [1110] die
ODER-Funktion, d. h., y = a0 + a1 .
3.1.3 Verknüpfungsglieder
Verknüpfungsglieder verknüpfen die Werte ihrer
Eingangssignale xi durch Schalterkombinationen
(in den Bildern 3-7a,b und 3-8a steht ein Schalter
jeweils stellvertretend für eine Schalterkombination)
und schalten entweder das Pluspotenzial auf den
Ausgang durch (y = 1 = +) oder das Massepotenzial
(y = 0 = ). Die Durchschaltung der konstanten
Potenziale + und der Stromversorgung bei Ver-
3 Schaltnetze
knüpfungsgliedern anstelle der variablen Potenziale 1
bzw. 0 bei Durchschaltgliedern ermöglicht eine Regenerierung der Spannungspegel des Ausgangssignals.
Deshalb können Verknüpfungsglieder (als „aktive“
Schaltungen) im Gegensatz zu Durchschaltgliedern
(als „passive“ Schaltungen) problemlos hintereinandergeschaltet werden. Begrenzungen ergeben
sich durch die Anzahl an Folgeschaltungen, die der
Ausgang eines Verknüpfungsgliedes zu „treiben“ hat
(fan-out) in Bezug auf den Strom, der zum Umladen
der kapazitiven Last nötig ist (Lastfaktor).
mit Transistoren. Bild 3-7a
dient als Grundschaltung für logische Operationen
in der Relaistechnik; in der Transistortechnik wird
sie hauptsächlich als Ausgangsschaltung verwendet
(z. B. als Stromtreiber bei ECL, emitter coupled
logic). In der Transistortechnik dient Bild 3-7b als
Grundschaltung für logische Operationen ( y = 1,
wenn nicht x = 1). Dabei erscheint die Wirkung
des Schalters (bzw. der Schalterkombination) am
Ausgang in invertierter Form (Inverterschaltung).
In Schaltungen mit bipolaren Transistoren werden
nur die Schalter, in Schaltungen mit unipolaren
Transistoren (MOS-Technik) darüber hinaus auch die
Widerstände mit Transistoren aufgebaut (die dann
nicht als Schalter arbeiten). Die in den Bildern 3-7c
bis f dargestellten MOS-Schaltungen erklären sich
aus der Invertierungseigenschaft der Grundschaltungen in Verbindung mit nur einem Schalter (Negation,
NICHT-Gatter), mit mehreren parallelen Schaltern
(negiertes ODER, negated OR, NOR-Gatter) bzw.
mit mehreren seriellen Schaltern (negiertes UND,
negated AND, NAND-Gatter).
Grundschaltungen
Verknüpfungsglieder in CMOS-Technik. Bild 3-8a
dient in der Transistortechnik gleichermaßen als
Ausgangsschaltung (z. B. als Stromtreiber bei
TTL, transistor transistor logic) wie als Grundschaltung für logische Operationen ( y = 1, wenn
nicht x = 1). Dabei muss die Wirkung der beiden
Schalter (bzw. der Schalterkombinationen) immer
gegensätzlich (komplementär) zueinander sein. In
CMOS-Technik (CMOS, complementary MOS)
werden dazu MOS-Transistoren mit komplementärer
Bild 3-7. Verknüpfungsglieder. a Transmitter-, b Inver-
ter-Grundschaltung; c, d NICHT-Gatter; e NOR-Gatter;
f NAND-Gatter; g Komplexgatter. Der Lastwiderstand in
e bis g ist entweder wie in c durch einen nMOS-Transistor
(nMOS-Technik) oder wie in d durch einen (komplementären) pMOS-Transistor (Pseudo-nMOS-Technik) realisiert
Funktion benutzt und ihre Verbindungsstrukturen
„unten“ und „oben“ dual zueinander aufgebaut:
in Bild 3-8b Transistor unten normal, oben invers
(NICHT-Gatter), in Bild 3-8c Transistoren unten
parallel/normal, oben seriell/invers (NOR-Gatter), in
Bild 3-8d unten seriell/normal oben parallel/invers
(NAND-Gatter).
Symbolik. Anstelle der vorgestellten, aufwändigen
Zeichnungen der logischen Gatter kann man in
logischen Blockbildern die in Tabelle 1-1 wiedergegebenen traditionellen Symbole nach der früheren
DIN 40 700-14 benutzen. Die in DIN 40 900-12
definierte neue Symbolik sowie in USA vielfach
benutzte Schaltzeichen sind in Tabelle I 14-1 dargestellt. – Das in (3-1) angewandte allgemeine
Halbkreissymbol dient sowohl zum Ausdrücken
des komplexen logischen Aufbaus elektronischer
Schaltkreise (Abstraktion von Elektronik-Details) als
auch der zusammenfassenden Darstellung von durch
Tabellen oder Gleichungen definierten Schaltnetzen
(Abstraktion von Logik-Details).
25
26
Technische Informatik / Digitale Systeme
behalten werden, jedoch sind aus technischen Gründen zusätzliche Schaltungsmaßnahmen nötig. Insbesondere wird zur Steigerung der Schaltgeschwindigkeit das Verkettungssignal ui+1 der Schaltung „aufgeladen“ sowie nach vier Serienschaltungen „verstärkt“,
siehe z. B. [1]. – Funktion. Mit xi = 0 und yi = 0 wird
ui+1 = 0 (Übertrag „killed“ – Ki = 1), mit xi = 0
und yi = 1 oder xi = 1 und yi = 0 wird ui+1 = ui
(Übertrag „propagated“ – Pi = 1), und mit xi = 1 und
yi = 1 wird ui+1 = 1 (Übertrag weder „killed“ noch
„propagated“, d. h. Übertrag „generated“ – Gi = 1).
Mit Pi = 1 und ui = 0 oder Pi = 0 und ui+1 = 1 wird
zi = 1, anderenfalls wird zi = 0 (Summenziffer gleich
1 bzw. 0), in Gleichungsform
ui+1 = xi yi + (xi ↔
| yi )ui ,
Bild 3-8. Komplementär-Verknüpfungsglieder (Inversbe-
trieb durch Punkt gekennzeichnet). a Inverter-Grundschaltung; b NICHT-Gatter; c NOR-Gatter; d NAND-Gatter;
e Mischgatter (Durchschalt-/Verknüpfungsglied)
3.2 Schaltungen für Volladdierer
Beginnend mit Addierern in sog. zweistufiger Logik
für Rechner der ersten und z. T. auch der zweiten
Generation (Carry-save-Addition, siehe 4.4.2) sind in
den späteren Generationen Addierer in mehrstufiger
Logik entwickelt worden (Carry-ripple-Addition, siehe 3.2.1). Um eine Vorstellung vom Aufbau und dem
Zusammenwirken der einzelnen Teilschaltungen gemäß Bild 3-1a, den sog. Volladdierern, zu vermitteln, sind in den Bildern 3-9 und 3-10 zwei von zahlreichen Schaltungsvarianten dargestellt. Anhand der
Tabelle Bild 3-1b lässt sich ihre Funktion überprüfen,
indem für jede Zeile die entsprechenden Schalterstellungen bzw. Gatterbelegungen eingetragen und daraus die Werte für ui+1 und zi ermittelt werden.
3.2.1 Volladdierer mit Durchschaltgliedern
Die in Bild 3-9a wiedergegebene Schaltung enthält
neben den vier NICHT-Gattern Bild 3-7c drei Durchschaltglieder gemäß Bild 3-6a und ein Durchschaltglied gemäß Bild 3-5a. Der Anschaulichkeit halber
ist der Volladdierer mit Schaltersymbolen gezeichnet.
Für eine funktionsfähige elektronische Schaltung in
MOS-Technik kann seine Struktur grundsätzlich bei-
zi = xi ↔
| yi ↔
| ui .
(3-2)
(3-3)
Bemerkung. Die MOS-Schaltung in Bild 3-9a ist
auf (normal arbeitende) nMOS-Transistoren zugeschnitten. Diese schalten 0 schneller durch als 1,
was beim Aufbau der Übertragungskette im Volladdierer berücksichtigt ist (Durchschalten von
und ggf. Weiterleitung). Die in CMOS verwendeten
(invers arbeitenden) pMOS-Transistoren schalten
hingegen 1 schneller als 0 durch, weshalb in CMOS
die Schalter in Durchschaltgliedern, sollen sie
Variablen durchschalten, als Paar komplementär
angesteuerter, parallel geschalteter nMOS/pMOSTransistoren realisiert werden. Solche „Komplementär“schalterkombinationen werden nicht nur in
Schaltnetzen, z. B. für Volladdierer, sondern auch
in CMOS-Speichergliedern, wie z. B. in Bild 4-4,
eingesetzt.
3.2.2 Volladdierer mit Verknüpfungsgliedern
Die in Bild 3-10a dargestellte Schaltung enthält
vier Verknüpfungsglieder der Struktur Bild 3-7b,
von denen zwei anstelle der einzelnen Schalter
Schalterkombinationen enthalten. Der Anschaulichkeit halber sind die Verknüpfungsglieder mit
Schalter- und Widerstandssymbolen gezeichnet. Für
eine funktionsfähige Schaltung in MOS-Technik
werden Schalter wie Widerstände durch Transistoren
realisiert; in CMOS-Technik werden für die Schalter
normal arbeitende Schalttransistoren und anstelle
3 Schaltnetze
Bild 3-9. Volladdierer mit Übertragsweiterleitung über Durchschaltglieder. a Schaltung; b Blockbild mit Gattersymbolen
Bild 3-10. Volladdierer mit Übertragsweiterleitung über Verknüpfungsglieder. a Schaltung; b Blockbild mit Bezug auf
Gleichungen
27
28
Technische Informatik / Digitale Systeme
der Widerstände die zu diesen Schalterkombinationen dualen Schalterkombinationen mit invers
arbeitenden Schalttransistoren eingesetzt. Es gibt
zahlreiche Schaltungsvarianten von Volladdierern,
siehe z. B. [2]. – Funktion: Mit xi = 1 und yi = 1
oder mit ui = 1 und xi = 1 oder yi = 1 wird ui+1 = 1,
anderenfalls wird ui+1 = 0 (Übertrag gleich 1 bzw.
0). Mit ui+1 = 0 und xi = 1 oder yi = 1 oder ui = 1
oder mit xi = 1 und yi = 1 und ui = 1 wird zi = 1,
anderenfalls wird zi = 0 (Summenziffer gleich 1
bzw. 0), in Gleichungsform
ui+1 = xi yi + ui (xi + yi ) ,
zi = ūi+1 (xi + yi + ui ) + xi yi ui .
Prozessoren sind ihre Operationen hingegen einer
strengen Auswahl unterzogen, nur diese werden
codiert (und ALU-intern decodiert; Bild 3-11a).
Aus der in Bild 3-9 dargestellten Volladdiererschaltung entsteht die Schaltung einer „1-Bit-Scheibe“
einer arithmetisch-logischen Einheit, wenn die
in der Volladdiererschaltung implizit enthaltenen
konstanten Steuervektoren durch explizit aus der
Schaltung herausgeführte variable Steuervektoren
ersetzt werden; n solche ALU-Scheiben werden zu
einer n-Bit-ALU zusammengesetzt, und es entsteht
Bild 3-11b.
(3-4)
(3-5)
Bemerkung. Der Volladdierer (zur Addition von
Dualziffern) bildet eine Art Keimzelle elektronischer
Datenverarbeitungswerke: n-mal gemäß Bild 3-1a
aufgebaut, entsteht ein Schaltnetz zur Addition
von Dualzahlen; aus diesem, gemäß Bild 3-11
verallgemeinert, entsteht ein Schaltnetz zur Durchführung arithmetischer und logischer Operationen;
aus diesem wiederum, gemäß Bild 4-19b mit Registern zusammengeschaltet, entstehen Schaltwerke
zur Datenverarbeitung mit in elektronischer Geschwindigkeit operierenden Schaltungen (universelle
Datenwerke), wie sie für Prozessoren in Rechenanlagen benötigt werden (vgl. die Strukturbilder in 5,
jeweils unterer Teil).
3.3 Schaltnetze zur Datenverarbeitung
und zum Datentransport
Datenverarbeitung und -transport bildet den Kern des
operativen Teils programmgesteuerter datenverarbeitender Geräte.
3.3.1 Arithmetisch-logische Einheiten
Arithmetisch-logische Einheiten werden oft nach
Gesichtspunkten guter technischer Integrierbarkeit
gebaut und enthalten deshalb neben sinnvollen
arithmetischen und logischen Operationen auch
eine große Anzahl sinnloser arithmetisch-logischer
Mischoperationen. Als eigenständige Chips erlauben
solche ALUs (arithmetic and logic units) die Ausführung auch dieser Mischoperationen; als Teile von
Arithmetik- und Logikoperationen. Mit den Steu-
ervektoren k = [k0 k1 k2 k3 ] zur Erzeugung der KillFunktion Ki (speziell k = [1000] in Bild 3-9),
p = [p0 p1 p2 p3 ] zur Erzeugung der Propagate-Funktion Pi , (speziell p = [0110] in Bild 3-9) und
r = [r0 r1 r2 r3 ] zur Erzeugung der Result-Funktion zi
(speziell r = [0110] in Bild 3-9) lauten die Funktionsgleichungen zur Beschreibung von Bild 3-9
als ALU-Glied (zu seiner symbolischen Darstellung
siehe auch Bild 3-9b mit (3-6) für Ki , (3-7) für Pi und
(3-9) für zi ):
Ki = k0 x̄i ȳi + k1 x̄i yi + k2 xi ȳi + k3 xi yi ,
(3-6)
Pi = p0 x̄i ȳi + p1 x̄i yi + p2 xi ȳi + p3 xi yi ,
(3-7)
ūi+1 = Ki + Pi ūi
oder ui+1 = K i · (Pi + ui ) ,
(3-8)
zi = r0 ūi Pi + r1 ūi Pi + r2 ui Pi + r3 ui Pi .
(3-9)
Für arithmetische Operationen ist r = [0110] oder
r = [1001] zu wählen; in diesem Fall ist zi nach (3-9)
von ui abhängig, d. h., zi = Pi ↔
| ui bzw. zi = Pi ↔ ui .
Für logische Operationen ist r = [0101] zu wählen; in diesem Fall ist zi nach (3-9) von ui unabhängig, d. h., zi = Pi . Mit der Zusammenschaltung von
n solchen ALU-Gliedern entsprechend Bild 3-11b
lassen sich arithmetische und logische Operationen
mit n-stelligen 2-Komplement-Zahlen und Bitvektoren der Länge n durchführen, eine Auswahl zeigt Tabelle 3-1.
Bedingungsoperationen. Zu ihnen zählen Operationen mit arithmetischen Operanden (Zahlen) und
logischem (Boole’schem) Ergebnis (wahr, falsch),
3 Schaltnetze
Bild 3-11. Arithmetisch-logische Einheit (ALU) für n Stellen mit Zahlenbeispiel Z = X − Y (X = +5, Y = −4). Die
Schrägstriche an den Leitungen geben deren Anzahl an (bei n = 4 entsteht – Z als 2-Komplement-Zahl interpretiert – das
„falsche“ Ergebnis Z = −7). a Symbol; b Schaltbild
z. B. X = Y, X Y, X < Y, X < 0 usw. Die
Boole’schen Werte dieser Relationen werden beim
Anlegen des Operationscodes für die Subtraktion
Z = X − Y aus den zum Condition-Code CC zusammengefassten Bedingungsbits z, n, c und v der ALU
gewonnen (Bild 3-11b):
das Zero-Bit z zeigt an, ob das Ergebnis null ist
(z = z̄n−1 z̄n−2 . . . z̄1 z̄0 ),
das Negative-Bit n zeigt an, ob das Ergebnis negativ
ist (n = zn−1 ),
das Carry-Bit c zeigt an, ob ein Übertrag in der höchsten Stelle entsteht (c = un ),
das Overflow-Bit v zeigt an, ob der Zahlenbereich
für 2-Komplement-Zahlen von n Stellen überschritten
wird (v = vn = un ↔
| un−1 ).
Carry-look-ahead-Technik.
Funktionsgleichungen
mit Propagate-Funktion, wie sie bei Arithmetik- und
Vergleichsoperationen auftreten, beschreiben die stufenförmige Ausbreitung des Übertragssignals (carry)
durch eine ganze Kette von Schaltgliedern (carry
ripple). Der Übertrag ist für jede Stufe erforderlich
und benötigt mit steigender Stufenanzahl immer
höhere Laufzeiten (vgl. z. B. die Beeinflussung der
ui durch u0 in Bild 3-11b). Um dies in Grenzen zu
halten, wird er für eine gewisse Stufenzahl (z. B. 4)
in jeder Stufe (d. h. der 2., der 3. und der 4.) vorausschauend berechnet (carry look-ahead, CLA).
Dadurch verringert sich die Laufzeit (hier auf ein
Viertel), und der Aufwand erhöht sich (nur auf
etwas mehr als das Doppelte). Diese Aussage ist
jedoch ggf. unter Berücksichtigung der Schaltungs-
Tabelle 3-1. Auswahl an ALU-Operationen (+ Addition, – Subtraktion, · Multiplikation)
k0
1
0
1
0
1
0
0
0
0
0
k1
0
0
1
0
1
0
0
0
0
0
k2
0
1
0
1
0
0
0
0
0
0
k3
0
0
0
1
0
0
0
0
0
0
p0
0
1
0
1
0
0
0
1
0
0
p1
1
0
0
1
0
0
1
1
0
0
p2
1
0
1
0
0
0
1
0
1
0
p3
0
1
1
0
0
1
1
0
1
0
r0
0
1
0
1
0
0
0
0
0
0
r1
1
0
1
0
0
1
1
1
1
0
r2
1
0
1
0
1
0
0
0
0
0
r3
0
1
0
1
1
1
1
1
1
0
Z=
X + Y + u0
X − Y − u0
X + u0
X − u0
X · 2 + u0
X UND Y
X ODER Y
NICHT Y
X
0
29
30
Technische Informatik / Digitale Systeme
technik zu relativieren. Zu weiteren Techniken zur
Beschleunigung des Übertrags siehe z. B. [3].
3.3.2 Multiplexer
Multiplexer dienen zum Durchschalten von mehreren
Leitungsbündeln (im Folgenden kurz Leitungen genannt) auf eine Sammelleitung; und zwar wird diejenige Leitung durchgeschaltet, deren Steuereingang
aktiv ist bzw. deren Adresse am Adresseingang anliegt (Bild 3-12a). Im Grenzfall hat der Multiplexer
nur einen einzigen solchen Dateneingang (Torschaltung), der uncodiert oder mit einer Adresse angewählt
wird (Bild 3-12b).
Funktionsgleichungen. Zu ihrer Darstellung in all-
gemeiner Form wäre die Einführung von Matrixoperationen nützlich [2]. Für den speziellen Fall eines
Multiplexers mit 3 Dateneingängen [c01 c00 ], [c11 c10 ],
[c21 c20 ], 3 Steuereingängen k0 , k1 , k2 ohne Decodierung und [y0 y1 ] als Datenausgang lauten sie
y0 = k0 c00 + k1 c10 + k2 c20 ,
y1 = k0 c01 + k1 c11 + k2 c21 .
(3-10)
(3-11)
Um die beschriebene Funktionsweise zu erfüllen, darf
nur ein Steuereingang ki = 1 sein; anderenfalls würde
je nach Realisierung des Multiplexers die Eingangsinformation ODER-verknüpft (beim Aufbau mit Verknüpfungsgliedern), oder es können Kurzschlussströme entstehen (z. B. über die Schalter in Bild 3-12c).
Sind andererseits alle ki = 0, so entsteht entweder am
Ausgang ein definierter Boole’scher Wert (z. B. 0 in
(3-10)), oder der Ausgang ist von den Eingängen abgetrennt (z. B. in Bild 3-12c). – Bei Multiplexerschaltungen mit integrierter Decodierung treten diese Fälle
nicht auf, da immer genau ein ki = 1 ist (siehe z. B.
Bild 3-6a).
Tristate-Technik. Schaltungen der in Bild 3-12c
wiedergegebenen Art lassen sich zur Dezentralisierung der Multiplexerfunktion benutzen. Die
Schalter (früher Dioden, heute Transistoren) dienen
als Ausgänge verstreut angeordneter (verteilter) Informationsquellen und brauchen nur noch verdrahtet
zu werden. Entsprechend der Multiplexerdefinition darf nur ein einziger Schalter „seine“ Quelle
durchschalten (Ausgang niederohmig), während alle
anderen Schalter ihre Quellen von dem gemeinsamen
Sammelpunkt abkoppeln müssen (Ausgänge hochohmig). Diese Quellen liefern weder 0 noch 1, befinden
sich also in einem dritten Zustand (tristate). Sind die
Ausgänge sämtlicher Quellen im dritten Zustand, so
ist es i. Allg. auch der Zustand auf der Sammelleitung
(in Signaldiagrammen als dritter Pegel zwischen dem
0- und dem 1-Pegel gezeichnet).
Während zur Erzielung geringer elektrischer Leistung
Tristate-Ausgänge mit „passiven“ Schaltern (Durchschaltglieder) ausreichen (Bild 3-12d oben), werden
für größere Leistungen, z. B. zur Überbrückung größerer Entfernungen zwischen Informationsquelle und
-senke, Tristate-Ausgänge mit „aktiven“ Schaltern
(Verknüpfungsglieder) benötigt (Bild 3-12d, unten).
Solche Tristate-Treiber findet man bei Bausteinausgängen oder als Sonderbausteine in Anwendungen,
bei denen die Multiplexerfunktion der Quellen über
die einzelnen Bausteine hinweg auf das gesamte
System verteilt ist, wie z. B. in Bus-Systemen.
3.3.3 Shifter
Shifter entstehen aus matrixförmigen Zusammenschaltungen von Multiplexern, mit denen durch
geeignete Wahl ihrer Steuereingänge mannigfaltige
Operationen ausführbar sind: z. B. mit dem LinksRechts-Shifter in Bild 3-13a die Multiplikation
mit 2, 4, 8 (durch Linksshift bei „Nachziehen“ der
Bild 3-12. Datenweg-/Datentransportschaltungen.
a Symbolische Darstellung eines Multiplexers;
b Symbole für ein Tor; c 1-Bit-Multiplexer mit
Schaltern (ohne Decodierer); d Tristate-Ausgang
bzw. -Treiber (beide logisch äquivalent), in
CMOS arbeitet der obere Treiber-Schalter invers
und wird durch ein NAND-Gatter angesteuert
3 Schaltnetze
Konstanten 0), die Division durch 2, 4, 8 (durch
Rechtsshift bei „Stehenlassen“ der Variablen x3 )
oder ein Rund-Shift nach links oder rechts (durch
Nachziehen von x3 bzw. x0 ). Das angegebene Schema
lässt sich auf n-stellige Operanden verallgemeinern
sowie durch die Einbeziehung weiterer Leitungen,
z. B. eines Eingangs für die Konstante 1, eines
Carry-Eingangs oder eines Overflow-Ausgangs,
erweitern. Um die Anzahl der Steuereingänge zu
reduzieren, werden wie bei ALUs oft nur die relevanten Shiftoperationen codiert (und shifterintern
decodiert). Spezielle Shifter mit nur einer einzigen
Shiftoperation enthalten keine Gatter oder Schalter
(z. B. werden bei k01 , k12 , k23 , k40 immer gleich 1 und
alle anderen ki j immer gleich 0 in Bild 3-13a zur
Bildung der Operation [y3 y2 y1 y0 ] = [x2 x1 x0 0] keine
Schalter benötigt).
Funktionsgleichungen. Für den speziellen Fall des
Shifters Bild 3-13a lauten sie ausschnittweise
y0 = x0 k00 + x1 k10 + x2 k20 + x3 k30 + 0 · k40 , (3-12)
y1 = x0 k01 + x1 k11 + x2 k21 + x3 k31 + 0 · k41 . (3-13)
Der jeweils letzte Term kann entfallen, wenn die Ansteuerung der Multiplexer mit ki j alle gleich 0 erlaubt ist. Das ist z. B. der Fall, wenn die Multiplexer
mit Verknüpfungsgliedern, jedoch nicht, wenn sie als
Durchschaltglieder aufgebaut sind.
Barrel-Shifter. Das
sind spezielle Shifter, die
zwei Operanden (ggf. auch dieselben) miteinander
zu einem Bitvektor doppelter Länge verbinden
(konkatenieren) und daraus einen Bitvektor einfacher
Wortlänge ausblenden (extrahieren). Wegen ihres
matrixförmigen Aufbaus (Zeilendrähte, Spaltendrähte, Transistoren in den Kreuzungspunkten)
eignen sie sich besonders gut für hochintegrierte
Schaltungen (Bild 3-13b). Je nach Ansteuerung
der Transistoren lassen sich die konkatenierten
Operandenteile an die Ausgänge durchschalten, z. B.
[z3 z2 z1 z0 ] = [x1 x0 y3 y2 ] bei x und y als Operanden
oder [z3 z2 z1 z0 ] = [x1 x0 x3 x2 ] bei y = x als Operanden
(2. und 6. Diagonale von links unten in Bild 3-13b).
Shifter dieser Art benötigt man zur schnellen Durchführung von Shiftoperationen über zwei Register,
wie sie in Rechnern bei den Shiftbefehlen, aber auch
innerhalb arithmetischer Befehle auftreten, z. B. beim
Multiplikations- oder beim Divisionsbefehl.
3.3.4 Busse
Busse entstehen durch Zusammenschaltung verteilter
Informationsquellen (Sender) und -senken (Empfänger) über dezentralisierte Multiplexer und Torschaltungen. Bild 3-14a zeigt z. B. einen Bus, der die Sender (Index S) und die Empfänger (Index E) von sechs
Systemkomponenten A bis F miteinander verbindet.
– Ein Bus ist also eine Vorrichtung zum Transport
von Information: Funktionell ein Knoten mit sternförmig angeordneten Schaltern und Abgriffen, technisch
eine Leitung mit verteilt angeschlossenen Schaltungen für die (oft paarweise auftretenden) Sender und
Empfänger der Systemkomponenten (z. B. ALUs, Register usw. innerhalb eines Mikroprozessors; Prozessoren, Speicher usw. innerhalb eines Mikroprozessorsystems).
Funktionsweise.
Aufgrund der Multiplexerfunktion
eines Busses (Bild 3-14b) darf immer nur eine Quelle senden, d. h. ihre Information auf den Bus schalten. Die Senken sind je nach Funktion ohne Tore ausgestattet, d. h. empfangen diese Information immer
Bild 3-13. Shifter. a Prinzipschaltung für 4-BitOperand x und Konstante 0; b Barrel-Shifter in
MOS-Technik für zwei 4-Bit-Operanden x und
y (y0 nicht ausgewertet)
31
32
Technische Informatik / Digitale Systeme
(broadcasting), oder sie sind mit Toren ausgestattet
und empfangen die Information nur, wenn sie angewählt werden (selective addressing).
Unidirektionale Busse haben nur eine Quelle oder
nur eine Senke – die Information läuft von der
Quelle aus bzw. zur Senke hin in nur einer Richtung.
Anderenfalls handelt es sich um bidirektionale Busse
– die Information läuft mal in der einen, mal in
der anderen Richtung. Die Systemkomponenten
können unidirektional und bidirektional an einen
bidirektionalen Bus angeschlossen werden. Die
Auflösung der Richtungen erfolgt i. Allg. innerhalb
der Systemkomponenten.
Technischer Aufbau.
Busse werden im Rechnerbau
benutzt (1.) zum Informationsaustausch zwischen
den Schaltungen innerhalb eines Chips (chipinterne
Busse), (2.) zum Verbinden der Chips zu Systemen
(chipübergreifende oder Systembusse).
Bei chipinternen Bussen (vgl. Bild 4-13 als Beispiel
für die Zusammenschaltung von Registerspeicher und
ALU-Gliedern innerhalb eines Prozessorchips) dienen für Sender wie Empfänger einfache Schalter als
Tore, die über die Phasen eines internen Takts gesteuert werden; zur Beschleunigung der Signalübertragung werden die Busleitungen ggf. vorgeladen.
Bei chipübergreifenden Bussen (vgl. etwa Bild 5-7
als Beispiel einer Zusammenschaltung von Prozessor und Speicher zu einem Mikroprozessorsystem)
dienen in den Sendern Tristate-Treiber und in den
Empfängern Schalter oder Gatter als Tore, die auf
chipintern getaktete Speicherschaltungen wirken. Die
zeitliche Abfolge der (nicht getakteten) Bussignale
zwischen den einzelnen (intern getakteten) Systemkomponenten ist durch bestimmte Regeln festgelegt
(Busprotokolle).
Bild 3-14. Bus (ausgezogen) mit Anschlüssen von Systemkomponenten (gestrichelt). a Technische Struktur, b logisches Äquivalent. Bidirektionaler Informationsfluss erfordert verdrahtete Logik
Bild 3-15. Datencodier-/-decodierschaltungen mit Andeutung ihrer Programmierbarkeit. a Codierer; b Decodierer;
c ROM (read-only memory); d PAL (programmable array
logic); e PLA (programmable logic array)
3.4 Schaltnetze zur Datencodierung/
-decodierung und -speicherung
Datencodierung/-decodierung bildet – obwohl auch
im operativen Teil zu finden – den Kern des steuernden Teils programmgesteuerter datenverarbeitender Geräte.
3.4.1 Codierer, Decodierer
Codierer dienen zum Verschlüsseln (Codieren) von
Information im 1-aus-n-Code (genau 1 Bit von n Bits
gesetzt) durch die Codewörter eines Binärcodes fester
Wortlänge (i. Allg. viel weniger als n Bits). Der Codierer, Bild 3-15a, gibt dasjenige Codewort aus, dessen zugeordneter Eingang (genau einer von n) aktiv
ist. – Decodierer dienen zum Entschlüsseln von Codewörtern eines Binärcodes fester Wortlänge in den
1-aus-n-Code. Der Decodierer, Bild 3-15b, aktiviert
denjenigen Ausgang (genau einen von n), dessen Codewort an seinen Eingängen anliegt.
In den Symbolen kann der Code unmittelbar binär
oder in symbolischer Entsprechung eingetragen werden (siehe z. B. Bild 4-22).
Funktionsgleichungen. Sie lauten für die Spezialfälle der Codierung/Decodierung der ersten vier Wörter des Dualcodes mit k0 bis k3 als Eingangs- und
y0 , y1 als Ausgangsgrößen (Codierer) bzw. x0 , x1 als
Eingangs- und k0 bis k3 als Ausgangsgrößen (Decodierer) in ausführlicher Form (links), sodass man
die Codewörter [00], [01], [10] und [11] erkennen
kann, und in vereinfachter Form (rechts), sodass ih-
3 Schaltnetze
re logische Struktur zum Ausdruck kommt (ODERVerknüpfungen beim Codierer, UND-Verknüpfungen
beim Decodierer)
y 1 = k 0 · 0 + k1 · 0 + k2 · 1 + k3 · 1 ; y1 = k 2 + k 3 ,
(3-14)
y0 = k 0 · 0 + k1 · 1 + k2 · 0 + k3 · 1 ; y0 = k 1 + k 3 ,
(3-15)
k0 = (x1 ↔ 0) · (x0 ↔ 0) ; k0 = x̄1 x̄0 ,
(3-16)
Funktionsgleichungen.
Sie entstehen für den speziellen Fall eines Festwertspeichers mit der Wortlänge 2
und der Kapazität 4 aus (3-14) und (3-15), wenn darin die ki durch (3-16) bis (3-19) ersetzt werden, wobei anstelle der unveränderlichen Konstanten 0 und
1 veränderliche, d. h. programmierbare „Konstanten“
ci j treten:
y1 = x̄1 x̄0 c01 + x̄1 x0 c11 + x1 x̄0 c21 + x1 x0 c31 , (3-20)
k1 = (x1 ↔ 0) · (x0 ↔ 1) ; k1 = x̄1 x0 ,
(3-17)
y0 = x̄1 x̄0 c00 + x̄1 x0 c10 + x1 x̄0 c20 + x1 x0 c30 . (3-21)
k2 = (x1 ↔ 1) · (x0 ↔ 0) ; k2 = x1 x̄0 ,
(3-18)
Jede dieser Gleichungen beschreibt nach Einsetzung
bestimmter Werte für ci j , d. h. nach der Programmierung, die gespeicherte Funktion in ausgezeichneter
disjunktiver Normalform.
k3 = (x1 ↔ 1) · (x0 ↔ 1) ; k3 = x1 x0 .
(3-19)
Für die angegebene Funktionsweise des Codierers ist
es erforderlich, dass genau ein Eingang ki = 1 ist; anderenfalls werden die durch die Eingangssignale angewählten Codewörter ODER-verknüpft.
3.4.2 Festwertspeicher
Festwertspeicher entstehen durch Zusammenschaltung eines Decodierers (zur Entschlüsselung der
Adressen) und eines Codierers (zur Speicherung
der unter den Adressen stehenden Wörter). Im
Gegensatz zu Schreib-/Lesespeichern kann die in
einem Festwertspeicher stehende Information nur
gelesen werden, weshalb er als ROM (read-only
memory) bezeichnet wird. Das heißt aber nicht, dass
sein Inhalt überhaupt nicht verändert werden kann.
Beim ROM steht zwar der Decodierinhalt fest, aber
der Codierer ist „programmierbar“ (Bild 3-15c).
Sein Inhalt wird von der Herstellerfirma festgelegt
(factory ROM, FROM) oder vom Kunden mithilfe
von Entwurfs-Software programmiert (programmable ROM, PROM), gegebenenfalls gelöscht und
wieder programmiert (erasable PROM, EPROM).
Dieser Vorgang geschieht vor der Inbetriebnahme des
Gerätes, in dem der Festwertspeicher eingebaut ist.
Der Decodierteil enthält bei einer Adresslänge von n
Bit die Adressen 0, 1 . . . 2n − 1. Im Codierteil können demnach 2n Wörter untergebracht werden; bei
einer Wortlänge von m Bit beträgt die Kapazität des
ROM dann 2n m-Bit-Wörter, d. h. 2n · m Bit. – Die
Zeit, die vom Anlegen der Adresse bis zum Erscheinen des Worts vergeht, heißt Zugriffszeit. (Zur Kennzeichnung von Speichern durch technische Daten siehe auch 4.2.3.)
3.4.3 Logikfelder
Logikfelder entstehen – wie ROMs – durch Zusammenschaltung eines Decodierers und eines Codierers.
Ihre industriellen Bezeichnungen sind PAL (programmable array logic) und PLA (programmable logic array).
Beim PAL ist der Decodierer programmierbar, und
der Codierinhalt steht fest (Bild 3-15d). Schaltungstechnisch wird das erreicht, indem die UND-Gatter
im Decodierteil mit einem PAL-Eingang unmittelbar
(Programmierung von 1), negiert (Programmierung
von 0) oder überhaupt nicht verbunden werden (Programmierung von –, d. h., die entsprechende Variable wird nicht zur Decodierung herangezogen). Die
ODER-Gatter im Codierteil sind nicht programmierbar, sondern mit einer festen Anzahl UND-Gatter verdrahtet.
Ein PLA kombiniert die Programmierbarkeit des PAL
hinsichtlich seines Decodierteils mit der des ROM
hinsichtlich seines Codierteils und ist damit der flexibelste Logikbaustein (Bild 3-15e). PLAs haben seit
der Einführung der Mikroprogrammierung – zuerst
als Diodenmatrizen, später mit Transistoren aufgebaut – im Rechnerbau eine große Bedeutung erlangt.
Ihr Vorteil liegt im matrixförmigen Aufbau, was insbesondere bei ihrer Verwendung innerhalb hochintegrierter Schaltungen zum Tragen kommt. Ihr Einsatz
als Bausteine in der Form „draußen“ programmierbarer Felder (field PLA, FPLA) wird mehr und mehr
durch universellere, d. h. intern mit Speichergliedern
33
34
Technische Informatik / Digitale Systeme
versehene Bausteine, z. B. FPGAs (field programmable gate arrays), abgelöst.
Funktionsweise. Das Logikfeld gibt diejenigen
„Codewörter“ ODER-verknüpft aus, deren (gegebenenfalls unvollständige und mehrdeutige) „Adressen“
hinsichtlich ihrer 0- und 1-Bits mit der eingangs
anliegenden 0/1-Kombination übereinstimmen. Die
hier benutzte Speicherterminologie ist streng genommen nur anwendbar, wenn die ODER-Verknüpfung
der Codewörter nicht ausgenutzt wird. Das trifft bei
vielen Anwendungen zu, z. B. bei der Speicherung
von Funktionstabellen in Steuerwerken.
Mit einem PAL oder einem PLA lassen sich so viele
Funktionen unmittelbar in disjunktiver Normalform
verwirklichen, wie ODER-Gatter im Baustein enthalten sind, wobei die Anzahl der UND-Gatter beim
PAL pro ODER-Gatter (Minimierung durchführen)
und beim PLA in seiner Gesamtheit (Kapazität beachten) vorgegeben ist.
3.4.4 Beispiel eines PLA-Steuerwerks
Bild 3-16 zeigt ein PLA, dessen Struktur für eine
bestimmte Anwendung programmiert ist. Dabei
handelt es sich um die Speicherung der Tabelle einer
Boole’schen Steuerungsfunktion (in Bild 4-21 im
PLA-Symbol eingetragen). Die Anordnung der
Transistoren ist in ihrer Gestalt gleich der Anordnung
der 0/1-Werte in der Tabelle: Links ist überall dort
ein Transistor mit einem normalen PLA-Eingang
verbunden, wo in der Tabelle eine 0 steht, und überall
dort mit einem negierten Eingang, wo in der Tabelle
eine 1 steht. Rechts ist an all denjenigen Stellen
ein Transistor vorhanden, an denen in der Tabelle
eine 1 steht. Wenn das PLA Teil einer integrierten
Schaltung ist, werden dazu vom Hersteller an den
entsprechenden Kreuzungspunkten von Zeilen- und
Spaltendrähten Transistoren eingebaut. Bei einem
PLA-Baustein sind dagegen an allen Kreuzungspunkten Transistoren vorhanden: sie werden jedoch
an den Stellen, an denen keine Transistoren sein
sollen, mithilfe eines Programmiergerätes von ihren
Eingängen abgetrennt und so wirkungslos gemacht
(fusible links) oder dort, wo sie wirksam werden
sollen, mit ihren Eingängen angeschlossen (antifuse
technique).
Funktionsdarstellung/-speicherung.
Obwohl die
linke wie die rechte Transistormatrix als NORFunktionen verwirklicht sind (vgl. Bild 3-7d), lassen
sich wegen der Negationsglieder an den Ein- und
Ausgängen die linke Matrix als UND-Matrix (AND
plane) und die rechte Matrix als ODER-Matrix
(OR plane) interpretieren. Zum Beispiel sind in
der 2. Zeile die NOR-Verknüpfung u0 + u1 + x̄0 der
UND-Verknüpfung ū0 ū1 x0 und in der 5. Zeile die
NOR-Verknüpfung ū0 + u1 der UND-Verknüpfung
u0 ū1 äquivalent. In der 2. Spalte rechts werden diese
Terme durch die NOR-Verknüpfung ū0 ū1 x0 + u0 ū1
zusammengefasst und anschließend negiert, was der
ODER-Verknüpfung ū0 ū1 x0 + u0 ū1 äquivalent ist.
Jede einzelne Funktion, hier an u∗1 gezeigt, ist also
durch UND-ODER-Verknüpfungen, d. h. in disjunktiver Normalform beschreibbar. Auf diese Weise ist
es möglich, mit einem PLA beliebige Boole’sche
Funktionen in minimaler, in ausgezeichneter oder in
irgendeiner disjunktiven Normalform „dazwischen“
zu verwirklichen.
Aus der in Bild 3-16 wiedergegebenen Schaltung
eines Steuerschaltnetzes lässt sich auf einfache Weise
ein Steuerschaltwerk verwirklichen. Dazu werden
die Ausgänge u∗0 und u∗1 mit den Eingängen u0 bzw.
u1 verbunden, sodass die gestrichelt gezeichneten
Schalttransistoren mit ihren nachgeschalteten Invertern zusammengenommen zwei sog. D-Flipflops
gemäß Bild 4-3a bilden. Das PLA enthält das
Steuerprogramm, und in den Flipflops wird der
Zustand gespeichert, der den momentanen Stand des
Programmablaufs widerspiegelt; vgl. Bild 4-21.
Bemerkung. Solche PLA-Steuerwerke bilden den
Ausgangspunkt der Entwicklung elektronischer
Programmsteuerwerke: Ein PLA, wie beschrieben
rückgekoppelt, ergibt gemäß Bild 4-20a ein speziell
auf eine bestimmte Aufgabenstellung zugeschnittenes
Steuerwerk. Wird das Register in der Rückkopplungsschleife als Zähler ausgebildet und anstelle
des PLA ein ROM verwendet, so ergibt sich gemäß
Bild 4-20b ein universell für viele Anwendungen
einsetzbares Steuerwerk. Wird darüber hinaus das
ROM ersetzt durch ein RAM (Schreib-/Lesespeicher,
siehe 4.2.3) oder ein CAM (Assoziativspeicher)
mit der Funktion eines Cache (siehe 5.4.1), so
entstehen Schaltwerke zur Programmsteuerung mit
in elektronischer Geschwindigkeit austauschbaren
4 Schaltwerke
Bild 3-16. Transistorstruktur eines PLA. Zur Einsparung von Transistoren, Zeilendrähten oder ausgangsseitigen Spaltendrähten lassen sich die Matrizen komprimieren, was in etwa einer Minimierung der Funktionsgleichungen entspricht. Die Widerstande sind in Wirklichkeit wieder wie in Bildern 3-7c oder d mit Transistoren realisiert. Im
Grunde handelt es sich bei PLAs innerhalb eines
VLSI-Chips um eine Aufbautechnik, d. h. um eine von vielen Möglichkeiten der Transistorplatzierung und -verdrahtung. Man vergegenwärtige sich
beispielsweise, dass die einzelnen Funktionen auch
dezentralisiert aufgebaut werden können und dann
als normale Gatterlogik erscheinen
Programmen (universelle Programmwerke), wie sie
für Prozessoren in Rechenanlagen benötigt werden
(vgl. die Strukturbilder in 5, jeweils oberer Teil).
4 Schaltwerke
Ihrer Struktur nach sind Schaltwerke rückgekoppelte Schaltnetze. Ihre Funktion wird beschrieben durch
rückwirkungsbehaftete (rekursive) Boole’sche Funktionen (Boole’sche Automaten/Algorithmen). Damit
lässt sich der Begriff Schaltwerk auch wie folgt definieren: Ein Schaltwerk ist die schaltungstechnische
Realisierung eines Boole’schen Automaten bzw. Algorithmus, mathematisch ausgedrückt durch das Paar
f, g einer rekursiven Funktion f für den Übergangsvektor u (Übergangsfunktion) und einer gewöhnlichen Funktion g für den Ausgangsvektor y (Ausgangsfunktion), beide abhängig vom Übergangsvektor u und vom Eingangsvektor x. Im Gegensatz zu einer Funktion, deren Ergebnis nur vom Argument abhängt, hängt das Ergebnis eines Algorithmus außerdem vom Zustand, d. h. dem Wert von u ab, der sich
i. Allg. von Schritt zu Schritt ändert. Diese „Zustandsrekursion“ wird – wie in höheren Programmiersprachen bei der Schreibweise von Anweisungen üblich –
durch das Zeichen „:=“ ausgedrückt (lies (4-1): Der
Wert von u wird ersetzt durch den Wert von f (u, x)).
Modularer Aufbau.
Schaltwerke verwirklichen verschiedene Arten von Algorithmen: (1.) operative, die
aufgrund von Anweisungen bestimmte Operationen
ausführen (Operationswerke), und (2.) steuernde, die
solche Anweisungen in zeitlicher Reihenfolge ausgeben (Steuerwerke). Operative und steuernde Algorithmen sind in technischen Geräten als einzelne
Schaltwerke realisiert (z. B. in Taschenrechnern zur
Ausführung bestimmter Operationen oder in Werkzeugmaschinen zur Steuerung bestimmter Abläufe).
Sie kommen aber auch – insbesondere in der elektronischen Datenverarbeitung – als zusammenwirkende
Schaltwerke vor. Dann wird das Operationswerk oft
Datenwerk genannt (es führt die Datenverarbeitung
aus) und das Steuerwerk oft Programmwerk genannt
(es führt die Programmabarbeitung durch). In dieser
Konstellation bilden sie ein in gewisser Hinsicht autonomes Schaltwerk: ein programmgesteuertes Datenverarbeitungswerk (Prozessor, Rechner).
Zur Ermöglichung der durch das Zeichen := in (4-1)
ausgedrückten Rekursion, d. h. des fortwährenden
35
36
Technische Informatik / Digitale Systeme
Bild 4-1. Informationsfluss in einem programmgesteuer-
ten Datenverarbeitungswerk; die beiden Teilwerke sind untereinander zum Signalaustausch über ihre Eingangs- und
Ausgangsleitungen verbunden. 1 Befehle zur Programmverzweigung, 2 Befehle zur Datenverarbeitung (operative
Signale), 3 Bedingungen für Programmverzweigung (steuernde Signale), 4 Ergebnisse der Datenverarbeitung und
-veränderung
schrittweisen Ersetzens des Zustands, dienen die
natürlichen Signallaufzeiten im Übergangsschaltnetz,
oder es bedarf für jede Komponente u von u des
künstlichen Einbaus von Schaltungen, sog. Speicherglieder (kurz Flipflops), die ihre Eingangswerte
(den Zustand) mithilfe eines Taktsignals um eine
Taktperiode verzögern oder über mehrere Taktperioden speichern (taktsynchrone Schaltwerke, kurz
Synchronschaltwerke).
Da diese Art von Schaltwerken von weitaus größerer
Bedeutung ist, wird der Zusatz taktsynchron bzw.
Synchron- i. Allg. weggelassen. – In Datenwerken
dienen die Speicherglieder zum Speichern des
Zustands der Daten, d. h. der Werte der zu verarbeitenden Größen, und in Programmwerken zum
Speichern des Zustands von Programmen, d. h.
ihres gegenwärtigen Ausführungsstands. Programmgesteuerte Datenverarbeitung lässt sich demnach
erklären als Komposition aus zwei sich gegenseitig beeinflussenden Zustandstransformationen
(Bild 4-1).
4.1 Signalverzögerung und -speicherung
Bei den Speichergliedern in den Synchronschaltwerken handelt es sich gewöhnlich um getaktete Flipflops
in vielerlei Varianten, die je nach Schaltung ihre Eingangsinformation über eine Taktperiode verzögern
(D-Flipflops, D delay), über mehrere Takte speichern
(SR-Flipflops, S set, R reset) und darüber hinaus ggf.
invertieren (z. B. JK-Flipflops; J jump, K kill).
Bild 4-2. D-Flipflop in der Rückkopplung von f . a Ausschnitt aus (4-1) für eine Komponente von u; b Verhalten
mit Einbeziehung des Taktsignals
4.1.1 Flip¦ops, Darstellung mit Taktsignalen
Das D-Flipflop dient zur Verzögerung des Wertes genau einer Komponente u von u in (4-1) um genau ein
Taktintervall (Bild 4-2). Mit dem Takt T tastet es den
Pegel von u ab und hält ihn ab der negativen Taktflanke T = 1 → 0 für die Dauer eines Taktintervalls
stabil (Signal w). In getakteten digitalen Systemen erfolgen sämtliche Signaländerungen gleichzeitig, und
zwar zu den z. B. durch die negative Taktflanke definierten Zeitpunkten, sodass neben w auch alle anderen Eingangswerte von f sich nur zu diesen Zeitpunkten ändern können, d. h., dass u nach einer gewissen Zeit (elektronisch gesprochen der Einschwingzeit
von f ) stabil ist und danach für die nächste Abtastung
zur Verfügung steht.
Grundschaltungen in Master-Slave-Technik. Das
Signaldiagramm Bild 4-2b illustriert die Funktion
des D-Flipflops anhand des zeitlichen Verlaufs
von Eingangs- und Ausgangssignalen, sodass die
Wirkung der Signalflanken unmittelbar zur Geltung
kommt. Um seine Funktion durch Boole’sche Gleichungen beschreiben zu können, muss jedoch auf die
Signalpegel zurückgegriffen werden. Die Funktion
lautet in Worten (vgl. Bild 4-2b): Der Wert von u
wird mit T = 1 einer (in Bild 4-2 nicht gezeichneten)
Zwischengröße v zugewiesen (Vorspeicher, Master)
und mit T = 0 gehalten, gleichzeitig erscheint er als
Wert von v mit T = 0 als Ausgangsgröße w (Haupt-
4 Schaltwerke
Ungetaktete Flip¦ops. Die
Bild 4-3. Schaltungen des D-Flipflops, a mit Schaltern,
Invertern und deren natürlicherweise vorhandenen Eingangskapazitäten, b mit Latches, c mit Flipflops
speicher, Slave) und wird mit T = 1 gehalten. Die
Funktion lautet in Gleichungsform (die Verwendung
von „ud = . . . “ anstelle von „u := . . . “ entsprechend
(4-1) soll auf die gatterinternen Signalverzögerungen
in Schaltwerken hinweisen; d delay):
vd = T u + T v ,
(4-3)
w = Tv + Tw .
(4-4)
d
Gleichungen (4-3) und (4-4) lassen sich in zahlreichen Varianten als Schaltungen verwirklichen:
Bild 4-3a eignet sich für Anwendungen, in denen der
Eingang nicht mit einer Steuergröße geschaltet wird,
d. h. T unmittelbar auf u wirkt, z. B. in den Registern
von Steuerwerken. Bild 4-3b eignet sich für Anwendungen, in denen der Eingang mit einer Steuergröße
geschaltet wird, d. h. T über ein UND-Gatter auf u
wirkt, wie z. B. bei Registern in Operationswerken.
Bild 4-3c ist die Grundschaltung von mit Verknüpfungsgliedern aufgebauten Master-Slave-Flipflops;
durch Variation ihrer Eingangsbeschaltung entstehen
verschiedene Typen getakteter Speicherglieder (siehe
Bild 4-7).
beiden elementaren
rückgekoppelten Bausteine in den Bildern 4-3b
und 4-3c werden (ohne Bezug auf ein Taktsignal)
Fangschaltung oder (ungetaktetes) Latch bzw.
Kippschaltung oder (ungetaktetes) Flipflop genannt
(Bild 4-4). Sie dienen als Grundbausteine für
Register- und Speicherelemente. Kennzeichnend
für sie ist die Schleife mit den beiden hintereinandergeschalteten Invertern (in Bild 4-4a unmittelbar sichtbar, in Bild 4-4b in den NOR-Gattern
versteckt). Diese elektrotechnisch „positive“ Rückkopplung ermöglicht es, den Wert einer Boole’schen
Variablen u, der vorher „gefangen“ (ud = d bei l = 1)
bzw. „gesetzt“ (ud = 1 bei s = 1) oder „rückgesetzt“
wurde (ud = 0 bei r = 1), in den Schaltungen zu
speichern (ud = u bei l = 0 bzw. s = 0 und r = 0).
Mit diesen Funktionsbeschreibungen der beiden
ungetakteten Speicherglieder lässt sich die Funktion
der Schaltungen Bilder 4-3b und 4-3c sowie die
daraus entstehenden getakteten Speicherglieder in
Bild 4-7 folgendermaßen erklären: Während T = 1
übernimmt der Master die eingangsseitig anstehende
Information; und mit T = 0 übergibt er sie dem
Slave, der sie am Ausgang zur Verfügung stellt.
Zweiphasentakt. Beim Entwurf von Schaltwerken
mit Reaktion auf Signalflanken – man spricht auch
von Asynchronschaltwerken (siehe z. B. [1]) – muss
die Wirkung der ungleichmäßigen Signallaufzeiten
durch die Gatter des Übergangsschaltnetzes berücksichtigt werden. Sie können zu strukturellen
oder funktionellen Hazards führen (das sind zufällig
auftretende, unerwünschte Signalflanken bei eigentlich konstantem Signalverlauf), oder sie können zu
konkurrenten Hazards führen (z. B. zwischen zwei
sich „gleichzeitig“ ändernden Rückkopplungswerten,
sodass zufällige, unerwünschte Wertekombinationen
auftreten).
Die Schaltungen in Bild 4-3 sind frei von Hazards,
wenn anstelle von T und T die Phasen ϕ1 und ϕ2 eines Zweiphasentakts benutzt werden (Bild 4-5). Bei
einem solchen Takt dürfen sich die 1-Pegel der beiden
Taktphasen zu keinem Zeitpunkt überlappen, auch
nicht in unmittelbarer Nähe der Taktflanken. Dann
ist sichergestellt, dass bei ihrer Verwendung in Synchronschaltwerken zu keinem Zeitpunkt der „Kreislauf“ der über sie rückgekoppelten Zustandsgrößen
„geschlossen“ ist.
37
38
Technische Informatik / Digitale Systeme
Bild 4-4. Schaltungen und Funktion ungetakteter Speicher-
D-, SR-, JK-Flip¦op. Bild 4-3 zeigt drei Schaltungsbeispiele für das D-Flipflop in Master-Slave-Technik.
Master-Slave-Schaltungen für das SR-Flipflop und
das JK-Flipflop entstehen durch geringfügige Änderungen der Schaltung Bild 4-3c: Das SR-Flipflop
entsteht durch Weglassen des Negationsgatters am
Eingang der Schaltung; das JK-Flipflop entsteht aus
dem SR-Flipflop, indem seine Ausgänge über Kreuz
rückgekoppelt und zusätzlich auf die Eingangs-UNDGatter geschaltet werden. Alle diese Flipflops können
durch Symbole dargestellt werden, und ihr Verhalten
lässt sich durch Tabellen beschreiben (Bild 4-7).
glieder. a Latch (d data, l latch); b Flipflop (s set, r reset)
Einsatz in Synchronschaltwerken. Betrachtet man
4.1.2 Flip¦ops, Abstraktion von Taktsignalen
Wie erläutert, wird vom Takt abstrahiert, indem er in
seiner „logischen“ Erscheinungsform als Boole’sche
Variable ersetzt wird durch sein „technisches“ Äquivalent in der Form hochgestellter Zeitindizes. Flipflops dienen als Bausteine beim Aufbau von Synchronschaltwerken.
Bild 4-6a z. B. im Intervall t + 1, so steht am Eingang des D-Flipflops ut+1 an, und an seinem Ausgang erscheint wt+1 , d. h. wegen (4-5) ut . Da sich
in Synchronschaltwerken die Ausgangssignale aller
D-Flipflops (Vektor ut ) mit allen Eingangssignalen
des Schaltwerks nur gleichzeitig ändern können (Vektor xt ), schreibt man die Übergangsfunktion (4-1) in
der Form
ut+1 = f (ut , xt ) .
(4-6)
D-Flip¦op als Baustein. Der Signalverlauf Bild 4-6b
zeigt das Verhalten des D-Flipflops, aber in einer
gegenüber Bild 4-2b von Einschwingvorgängen abstrahierten Form. Daraus lässt sich seine Funktion
unmittelbar ablesen. Sie lautet in Worten: Der Wert
von u wird über genau ein Taktintervall verzögert
und der Ausgangsgröße w zugewiesen. Sie lautet als
Gleichung (die Verwendung von „ut+1 /ut “ anstelle
von „u :=“ entsprechend (4-1) weist auf die gatterexterne Signalverzögerung in Synchronschaltwerken
hin; t, t + 1 zwei aufeinanderfolgende Taktzeitintervalle):
wt+1 = ut .
(4-5)
Bild 4-6. D-Flipflop. a Symbol für eine Komponente von u;
b von den Taktsignalen abstrahiertes Verhalten
Die Beschreibung des Verzögerungsgliedes erfolgt also in 4.1.1 wie in 4.1.2 durch Boole’sche Gleichungen, jedoch dort mit und hier ohne T (vgl. (4-3), (4-4)
gegenüber (4-5)).
Bild 4-5. Zweiphasentakt mit den Taktphasen ϕ1 und ϕ2
Bild 4-7. Symbole und Funktion getakteter Speicherglieder.
a D-Flipflop; b SR-Flipflop (S = 1 und R = 1 verboten);
c JK-Flipflop (universelles Logik-/Speicherglied)
4 Schaltwerke
Bezüglich der Ausgangsfunktion lassen sich zwei
Fälle unterscheiden (vgl. 2.1.1): 1. Die Ausgangssignale (Vektor yt ) hängen nur von den Zuständen
ab (Moore-Automat). 2. Sie hängen auch von
den Eingangssignalen ab (Mealy-Automat); in
Gleichungsform:
yt = g(ut ) ,
(4-7)
yt = g(ut , xt ) .
(4-8)
Beide Schaltwerksstrukturen lassen sich ineinander
überführen. Während die Schaltwerksstruktur (4-6)
allein (Flipflopausgänge gleich Schaltwerksausgänge) typisch für Register und Speicher ist (siehe 4.2),
findet die Struktur (4-6), (4-7) insbesondere in Datenwerken (siehe 4.3) und die Struktur (4-6), (4-8) insbesondere in Programmwerken Anwendung (siehe 4.4).
Bemerkung. Der Vorteil von Synchronschaltwerken
ist, dass sämtliche „Einschwingvorgänge“ am Ausgang des Übergangsschaltnetzes f beim Schaltwerksentwurf ignoriert werden können. Deshalb werden in
der industriellen Praxis komplexe digitale Systeme
entweder vollständig in Synchrontechnik aufgebaut
(z. B. Prozessoren, ganze Rechner), oder sie werden
in Asynchrontechnik mit sog. Handshake-Signalen
versehen (z. B. zur Abstimmung der einzelnen Komponenten, wie Prozessoren, Speicher, Controller, Interfaces innerhalb eines Rechners).
4.2 Registertransfer und Datenspeicherung
Flipflops sind die kleinsten Einheiten zum Speichern
von Information: die Speicherelemente. Sie dienen
zur Speicherung einzelner Bits, z. B. des Zustands
Boole’scher Variablen. Register dienen zur Speicherung von aus Bits zusammengesetzten Wörtern, z. B.
der Werte Boole’scher Vektoren oder arithmetischer
Größen. Speicher schließlich dienen zur Speicherung
von aus solchen Wörtern aufgebauten Sätzen, z. B.
zur kurz- oder längerfristigen Aufbewahrung von Daten jeglicher Art.
4.2.1 Flip¦ops auf der Registertransfer-Ebene
Zur Speicherung eines Bits wird bei D-Flipflops der
Wert der Variablen rückgekoppelt, bei SR- oder JKFlipflops ist das nicht nötig. Außerdem muss der Wert
Bild 4-8. Speicherelement. a Schaltung mit D- bzw. SR-
Flipflop; b Darstellung auf der Registertransfer-Ebene. Zur
Hervorhebung der Speicherelemente ist das Schaltnetz op
ohne sein (halbkreisförmiges) Funktionssymbol gezeichnet
der Variablen verändert werden können, z. B. indem
ihr der Wert einer anderen Variablen oder einer Funktion zugewiesen wird. Bild 4-8a zeigt entsprechende Schaltungen und Bild 4-8b das Symbol für die
„Ansteuerung“ eines Flipflops als Speicherelement z
(gleichzeitig der Name der Variablen des Speicherelements sowie die Bezeichnung seines Ausgangs) mit
einer Variablen x und, im Vorgriff auf 4.3, einer Funktion op(z, y) über einen Multiplexer (mit den Steuergrößen a und b).
Das Gleichheitszeichen in diesem wie in den
folgenden Bildern weist auf die Äquivalenz der
Schaltungen hin, während der gebogene Pfeil den
Übergang auf eine höhere Abstraktionsebene, hier
die Registertransfer-Ebene, anzeigt. In den Formeln
stellt sich diese Abstraktion durch eine detailärmere, dafür aber übersichtlichere Ausdrucksweise
mit den Mitteln algorithmischer Sprachen dar
(Registertransfer-Sprachen, Hardware-Sprachen).
Die in Bild 4-8 dargestellte Schaltung lässt sich als
Gleichung mit der Boole’schen Algebra beschreiben,
und zwar mit implizit dargestelltem Takt bzw. vom
Takt abstrahiert (a = 1 und b = 1 verboten):
zt+1 = at · xt + bt · op(zt , yt ) = at · bt · zt ,
z := a · x + b · op(z, y) + ā · b̄ · z .
(4-9)
(4-10)
Die Schaltung lässt sich auch als Zuweisung in einer
algorithmischen Sprache beschreiben, zuerst unüblich
ausführlich und dann wie üblich abgekürzt in drei verschiedenen Formen:
z := if a then x else if b then op(z, y) else z (4-11)
z := if a then x else if b then op(z, y)
if a then z := x else if b then z := op(z, y)
(4-12)
if a then z := x, if b then z := op(z, y)
(4-14)
(4-13)
39
40
Technische Informatik / Digitale Systeme
Die letzte Form wird zur Beschreibung von Schaltungen bevorzugt. Sie gibt die in der Schaltung enthaltene Gleichzeitigkeit der Auswertung der Bedingungsgrößen durch die Verwendung des Kommas als
Zeichen für (gleichberechtigte) Aufzählungen am
besten wieder. In dieser Eigenschaft wird das
Komma sowohl für die taktsynchron gleichzeitige
Ausführung von bedingten wie von bedingungslosen Anweisungen benutzt (in der Programmierung
guarded commands bzw. compount statements
genannt).
und ggf. unter welchen Bedingungen sie ausgeführt
werden, geht nur daraus hervor, an welchen Stellen
sie im Hardware-Programm erscheinen.
Operationen. Zur Kennzeichnung einzelner Registerelemente werden in spitze Klammern gesetzte
Indizes verwendet; z. B. ist Zi das Element Nr. i
des Registers Z. Die mit diesem Register Z ausführbaren Operationen lassen sich explizit beschreiben
durch Zi := . . . oder Z := . . . (Schreiben) sowie
4.2.2 Register, Speicherzellen
Zur Speicherung eines Wortes (von Bits) wird eine
Anzahl von Speicherelementen „horizontal“ aneinandergesetzt und als Einheit betrachtet. Die Speicherelemente werden parallel betrieben: bei n-maligem Aufbau einer Schaltung aus Bild 4-8 entsteht die Schaltung in Bild 4-9. Dann lautet (4-14) zuerst in einer
auf Boole’sche Vektoren verallgemeinerten und dann
in einer höheren Programmiersprachen angepassten
Form (mit a und b als Steuergrößen, X, Y und Z als
Rechengrößen und ADD z. B. als der arithmetischen
Operation „Addition“):
Bild 4-10. Komplexe Register-Schaltnetz-Kombination auf
der Registertransfer-Ebene. a mit Steuersignalen; b ohne
Steuersignale
if a then z := x, if b then z := op(z, y)
(4-15)
if a then Z := X, if b then Z := ADD(Z, Y) (4-16)
In Formeln wie Bildern wird die mit dem Übergang
zur Registertransfer-Ebene verbundene Abstraktion
vom Detail unterstützt, indem auf die explizite
Angabe der Steuergrößen verzichtet wird und die
Operationen durch ihre üblichen Operationszeichen
wiedergegeben werden, z. B. ADD durch „+“. Mit
Z := X, Z := Z + Y entsprechend (4-16) bzw.
Bild 4-9b wird dann lediglich das Vermögen der
Schaltung beschrieben, die Transport- oder die Additionsoperation auszuführen. Zu welchen Zeitpunkten
Bild 4-9. Einfache Register-Schaltnetz-Kombination auf
der Registertransfer-Ebene. a mit Steuersignalen; b ohne
Steuersignale
Bild 4-11. Beschaltung der Registerelements Zi aus
Bild 4-10 mit D-Flipflop Bild 4-3b und z. B. Addierglied
Bild 3-9a
4 Schaltwerke
implizit durch Verwendung von Z auf der rechten
Seite solcher Anweisungen (Lesen). Allein aufgrund
des Vorkommens solcher Anweisungen in ein und
demselben Hardware-Programm entstehen die für
die Registertransfer-Ebene typischen komplexen
Zusammenschaltungen von Registern und Schaltnetzen (z. B. X := Z, Y := Z, Z := X, Z := Z + A in
Bild 4-10).
Bild 4-11 zeigt eine „Scheibe“
von 1 Bit der in Bild 4-10 dargestellten RegisterSchaltnetz-Kombination. Die eigentlich notwendige
Rückkopplung des Flipflopinhalts auf sich selbst
entfällt hier, da bei a = 0 und c = 0 sowie ϕ2 = 0 die
Eingangskapazität des ersten Inverters kurzzeitig die
Speicherung der Master-Information übernimmt.
Schaltungsbeispiel.
4.2.3 Schreib-/Lesespeicher
Zur Speicherung eines Satzes (von Wörtern) wird
eine Anzahl von Registern/Speicherzellen „vertikal“
untereinandergesetzt und als Einheit betrachtet.
Die Speicherzellen werden über Torschaltungen/Multiplexer auf Schreib-/Lesebusse geschaltet,
z. B. wie in Bild 4-12a dargestellt. Von den Speicherzellen ist immer nur eine auf einmal adressierbar,
wobei alle hinsichtlich ihres Zugriffs gleichberechtigt
sind. Zur Unterscheidung von Speichern mit anderen
Zugriffsarten werden Schreib-/Lesespeicher deshalb
als Speicher mit wahlfreiem Zugriff (random access
memory, RAM) bezeichnet.
Operationen und Kenngrößen. Zur Kennzeichnung einzelner Speicherzellen werden in eckige
Klammern gesetzte Indizes verwendet; z. B. ist
Z[i] die Speicherzelle Nr. i des Speichers Z. Die
mit diesem Speicher Z ausführbaren Operationen
lassen sich explizit darstellen durch Z[Adresse] :=
Datum (Schreiben) sowie implizit durch Verwendung
von Z[Adresse] innerhalb von Anweisungen in
Hardware-Programmen (Lesen).
Der Inhalt einer Speicherzelle wird als Wort bezeichnet, ihre Kenn-Nummer als Adresse. Die Anzahl der
Bits pro Wort heißt Wortlänge oder -breite, die der
Bits pro Adresse Adresslänge oder -breite. Die Anzahl der Zellen pro Speicher heißt Kapazität; sie beträgt das 2n -fache der Anzahl n der Adressbits. Die
Zugriffszeit ist die Zeit vom Anlegen der Adresse
bis zum Erscheinen des gelesenen Worts. – Mit diesen technischen Daten werden auch Festwertspeicher
(ROMs) gekennzeichnet.
Einsatz im Rechnerbau. RAMs dienen (1.) haupt-
sächlich zur Speicherung von ausführbereiten Daten
und Programmen innerhalb von Rechenanlagen (prozessorexterne Speicher) und (2.) zur Zwischenspeicherung von Operanden und Ergebnissen innerhalb
von Prozessoren (prozessorinterne Speicher).
Als prozessorexterne Speicher (Hauptspeicher, Primärspeicher) haben RAMs große Kapazitäten. Ihre
Speicherelemente sind nicht getaktet. Sie werden
mit Signalen für die Bausteinanwahl (Select) und
die Übertragungsrichtung (Read/Write) angesteuert
(Symbol siehe Bild 4-12b). Eine im Baustein integrierte Steuerlogik sorgt für den ordnungsgemäßen
zeitlichen Ablauf der Lese- und Schreiboperationen und schaltet die bidirektionalen Datenbustreiber
durch. Aufbau und Schaltungstechnik prozessorexterner Speicher, insbesondere von Sekundärspeichern,
folgen den unterschiedlichsten Prinzipien und
Technologien.
Als prozessorinterne Speicher (Registerspeicher,
Registersätze) haben RAMs kleine Kapazitäten,
sind getaktet und i. Allg. als sog. Multiport-Speicher
ausgelegt (Symbole für 2- und 3-Port-Speicher siehe
Bilder 4-12c und d). Sie erlauben gleichzeitiges Lesen
und Schreiben über mehrere Tore. Dazu dienen mehrere unabhängige interne Multiplexer/Demultiplexer
einschließlich ihrer Adressdecodierer. Aufbau und
Schaltungstechnik sind an die Struktur des Prozessors angepasst. Die einzelnen Speicherelemente
bestehen i. Allg. nicht aus vollständigen MasterSlave-Flipflops, sondern z. B. nur aus den Mastern,
während die Slaves für alle Speicherzellen und jeden
Ausgang nur einmal aufgebaut werden (entweder im
Symbol mit enthalten oder als Register an anderer
Stelle im Prozessor vorhanden).
Bild 4-13 zeigt links einen
2-Port-Speicher der „Wortlänge“ 1 Bit mit als Schalter gezeichneten Transistoren. Die Speicherelemente
einer solchen „Speicherebene“ sind eindimensional
untereinander angeordnet und haben unterschiedliche
Adressen. Ein Element wird durch seine Zeilen-
Schaltungsbeispiel.
41
42
Technische Informatik / Digitale Systeme
Bild 4-12. Schreib-/Lesespeicher auf der Registertransfer-Ebene. a Blockbilder mit Steuersignalen; b Symbol ohne Steuersignale; c Symbol eines 2-Port-Speichers mit 2 Schreib-/Lesebussen; d Symbol eines 3-Port-Speichers mit 1 Schreibbus
und 2 Lesebussen
adresse und Angabe der Richtung angewählt und
mit der Leitung Bus1 oder Bus2 verbunden. Zur
Erzielung einer Wortlänge von mehr als einem Bit
werden mehrere solcher Speicherebenen nebeneinander aufgebaut und mit denselben Adressleitungen
angesteuert.
4.2.4 Speicher mit speziellem Zugri−
Zwei wichtige Speicher dieser Art sind LIFOSpeicher (kurz LIFOs) und FIFO-Speicher (kurz
FIFOs).
Mit LIFO (last in first out) bezeichnet man einen Zugriffsalgorithmus, bei dem das jeweils als letztes in
den Speicher eingegebene Wort am Speicherausgang
als erstes wieder verfügbar ist. Man kann sich einen
solchen Speicher als Stapel (stack) vorstellen, ähnlich
einem Tellerstapel, oder als Keller (cellar), der oben
(entspricht dem Speichereingang) gefüllt und auch
oben (entspricht dem Speicherausgang) geleert wird.
Mit Speichern dieser Zugriffsart ist es möglich, Information, die wegen wichtigerer Aufgaben zunächst
nicht weiterverarbeitet werden soll, in einer Reihenfolge aufzubewahren, die bei der Rückkehr umgekehrt durchlaufen wird.
Mit FIFO (first in first out) bezeichnet man einen Zugriffsalgorithmus, bei dem das jeweils als erstes in
den Speicher eingegebene Wort am Speicherausgang
auch als erstes wieder verfügbar ist. Man kann sich
einen solchen Speicher als Schlange (queue) vorstel-
len, ähnlich einer Menschenschlange, oder als Röhre
(pipe), die hinten (entspricht dem Speichereingang)
gefüllt und vorn (entspricht dem Speicherausgang)
geleert wird. Mit Speichern dieser Zugriffsart ist es
möglich, Information, die pulsierend ankommt, zum
Durchsatzausgleich in derselben Reihenfolge zu puffern, in der sie später weiterverarbeitet werden soll.
4.3 Schaltwerke zur Datenverarbeitung
Wenn Schaltnetze zur Datenverarbeitung und zum
Datentransport (3.3), z. B. zur Durchführung arithmetischer Operationen, mit Registern rückgekoppelt
zusammengeschaltet werden, entstehen mannigfache
Schaltwerke zur Datenverarbeitung (Datenwerke).
Die Schaltnetze enthalten die Verarbeitungsfunktionen, und die Register speichern die momentanen
Werte der Operanden (den Datenzustand).
4.3.1 Zähler
Zähler sind elementare Datenwerke, deren Registerelemente untereinander durch „Zähllogik“
verknüpft sind. Im Register steht ein variabler
Operand (die zu verändernde Zahl), zu/von dem
durch das Schaltnetz ein konstanter Operand (die
Zähleinheit) addiert/substrahiert wird. – Bild 4-14
zeigt eine Schaltung für den einfachsten Fall eines
Vorwärtszählers für Dualzahlen (Symbol dieses
Zählers siehe Bild 4-15a).
4 Schaltwerke
Bild 4-13. Zusammenschaltung eines 2-PortSpeichers mit einer ALU über 2 bidirektionale
Busse für eine 1-Bit-Scheibe gemäß Bild 4-19 b
(die Punkte hinter den ϕ1 und ϕ2 bedeuten UNDVerknüpfungen mit Steuersignalen; zur Schaltung
des ALU-Gliedes siehe z. B. Bild 3-9).
Ein Speicher- und ein ALU-Latch zusammengenommen gleichen in ihrer Wirkung einem MasterSlave-Flipflop. Mit ϕ1 wird die am ALU-Ausgang
anstehende und auf einen der Busse durchgeschaltete Information in ein Speicher-Latch (Master)
übernommen; und mit ϕ2 wird der Inhalt eines dieser Latches über einen der Busse in ein RegisterLatch (Slave) übertragen und erscheint damit am
ALU-Eingang (in MOS-Technik wegen des Voraufladens von Bus und ALU etwas komplizierter
in 4 Taktphasen [2])
Bild 4-14. Schaltung des Zählers Bild 4-15a (Steuergröße
zhl = 1: Zählen; zhl = 0: nicht Zählen, d. h. Speichern)
A := A − 1). Zähler – wie überhaupt alle Schaltwerke
– werden beim Einschalten in einen bestimmten Zustand versetzt. Dies erfolgt durch asynchron wirkende Set-/Reset-Signale, die wie Stromversorgung und
Takt zu den „technischen“ Signalen zählen und deren
Leitungen in logischen Blockbildern nicht gezeichnet
werden. Auch während des Betriebs kann ein Zähler
auf bestimmte Werte gestellt werden (A := X). Wird
der Zählmodus nicht geändert, so kehrt der Zähler
nach n Schritten wieder in seinen Ausgangszustand
zurück; er arbeitet modulo n.
Auf der Registertransfer-Ebene wird die in Bild 4-15
dargestellte Symbolik benutzt, wobei von den Steuersignalen abstrahiert wird.
4.3.2 Shiftregister
Bild 4-15.
Zähler auf der Registertransfer-Ebene.
a Vorwärtszähler; b Rückwartszähler mit Stelleingang;
c Vor-/Rückwärtszähler mit Stelleingang
Operationen. Beim Vorwärtszählen wird die Zähl-
einheit Inkrement und beim Rückwärtszählen Dekrement genannt; Vorwärtszählen und Rückwärtszählen
können in einem Zähler vereinigt sein (A := A + 1,
Shiftregister, auch als Schieberegister bezeichnet,
sind elementare Datenwerke, deren Registerelemente
untereinander durch „Shiftlogik“ verbunden sind.
Der im Register stehende Bitvektor kann durch das
Schaltnetz um eine Position nach links oder nach
rechts geschoben werden. – Bild 4-16 zeigt eine
Schaltung für den einfachsten Fall eines Linksshiftregisters (Symbol dieses Shiftregisters siehe
Bild 4-17a).
43
44
Technische Informatik / Digitale Systeme
Bild 4-16. Schaltung des Shiftregisters Bild 4-17a (Steuergröße sh f = 1: Shiften; sh f = 0: nicht Shiften, d. h.
Speichern)
Mehrere Shiftregister können untereinander über
Serienein- und -ausgänge verbunden werden. Besitzen sie darüber hinaus auch Parallelein- und
-ausgänge, so kann parallel in seriell dargestellte
Information umgeformt werden und umgekehrt.
Auf der Registertransfer-Ebene wird die in Bild 4-17
dargestellte Symbolik benutzt, wobei von den Steuersignalen abstrahiert wird.
4.3.3 Logik-/Arithmetikwerke
Bild 4-17. Shiftregister auf der Registertransfer-Ebene.
a Linksshiftregister; b Rechtsshiftregister mit Paralleleingang; c Links-/Rechtsshiftregister mit Paralleleingang
Operationen. Beim arithmetischen Linksshift um
eine Stelle wird in die Registerstelle rechts außen eine
Null nachgezogen; dies entspricht der Multiplikation
mit 2 bei „Abschneiden“ des Überlaufs, wenn die
im Register stehende 0/1-Kombination als Dualzahl in 2-Komplement-Darstellung aufgefasst wird
(A := A · 2). Beim arithmetischen Rechtsshift behält
die Registerstelle links außen ihren Wert bei; dies
entspricht der Division durch 2 mit „Abschneiden“
des Rests (A := A/2).
Beim Rundshift sind die beiden äußeren Registerstellen gekoppelt, sodass beim Linksrundshift um eine
Stelle in die Registerstelle rechts außen der Inhalt der
Registerstelle links außen und beim Rechtsrundshift
in die Registerstelle links außen der Inhalt der Registerstelle rechts außen hineingeschoben wird (A :=
linksrund A bzw. A := rechtsrund A).
Werden Rundshiftregister der Länge n Bit mit nur einer 1 initialisiert, so können sie zum Zählen modulo n
benutzt werden (Ringzähler). Auch andere als die beschriebenen Möglichkeiten können für die Ansteuerung der äußersten rechten oder linken Stelle vorgesehen werden; so kann z. B. die Rückführung beim
Rundshift negiert erfolgen, dann entsteht ein Zähler modulo 2n (Möbiusringzähler, auch Johnsonzähler genannt).
Logik-/Arithmetikwerke sind elementare bis komplexe Datenwerke, deren Register-/Speicherelemente
über eine arithmetisch-logische Einheit (ALU)
verbunden sind. Zu einer im Register stehenden
Zahl können mit der ALU z. B. laufend weitere
Zahlen addiert werden; man sagt, die Zahlen werden
akkumuliert. – Bild 4-18 zeigt eine Schaltung für
den einfachen Fall einer Kette aus Volladdierern
(VA) als „ALU“ (Symbol dieses Akkumulators siehe
Bild 4-19a).
Operationen. Bei den arithmetischen Operationen
wird die im Register stehende 0/1-Kombination
als Dualzahl interpretiert und je nach Operation
allein oder mit einer weiteren Dualzahl „verknüpft“. An Operationen sind i. Allg. vorgesehen:
Inkrementierung (A := A + 1), Dekrementierung
(A := A − 1), Komplementbildung (A := −A), Linksshift (A := A · 2), Rechtsshift (A := A/2), Addition
(A := A + X), Subtraktion (A := A − X). Dabei wird
das Zwischenergebnis Y der jeweiligen Operation
getestet: i. Allg. auf „zero“ (z = (Y = 0)), auf
Bild 4-18. Schaltung des Akkumulators Bild 4-19a (Steu-
ergröße add = 1: Addieren; add = 0: nicht Addieren,
d. h. Speichern)
4 Schaltwerke
4.4.1 PLA- und ROM-Steuerwerke
Bild 4-19. Logik-/Arithmetikwerke auf der Registertrans-
fer-Ebene. a Addierwerk, Akkumulator; b Rechenwerk, oft
auch als Integer-Unit bezeichnet
„negative“ (n = Yn − 1), auf „carry“ (c Übertrag),
auf „overflow“ (v Überschreitung des Zahlenbereichs
bei 2-Komplement-Zahlen).
Bei den Boole’schen Operationen wird die im Register stehende 0/1-Kombination als Bitvektor aufgefasst. In der ALU sind entweder alle Operationen, die
mit zwei Boole’schen Variablen möglich sind, oder
eine bestimmte Auswahl aus diesen vorgesehen: Löschen (A := 0), Negation (A := NOT A), Transport
(A := X), Konjunktion (A := A AND X), Disjunktion
(A := A OR X), Antivalenz (A := A XOR X).
Auf der Registertransfer-Ebene wird die in Bild 4-19
dargestellte Symbolik benutzt.
4.4 Schaltwerke zur Programmsteuerung
und zur programmgesteuerten
Datenverarbeitung
Wenn Schaltnetze zur Datencodierung/-decodierung
und -speicherung (3.4), z. B. zur Speicherung
feststehender Steuerfunktionen, mit Registern rückgekoppelt zusammengeschaltet werden, entstehen
Schaltwerke zur Programmsteuerung (Programmwerke). Die Schaltnetze enthalten die Steuerprogramme,
und die Register speichern deren momentanen
Ausführungsstand (Programmzustand). Sie reichen
von elementaren, nur mit 0/1-Mustern programmierbaren bis zu komplexen, mit einem umfangreichen
Befehlsvorrat programmierbaren Werken.
Zusammengeschaltet mit den in 4.3 behandelten Datenwerken entstehen Werke zur programmgesteuerten
Datenverarbeitung (siehe Bild 4-1).
PLA-Steuerwerke sind elementare Programmwerke,
bei denen der Programmspeicher auf der Basis von
Logikfeldern (vgl. 3.4.3) ausgeführt und über ein Register rückgekoppelt ist (Bild 4-20a). Typisch ist das
gleichzeitig mögliche (parallele) Abfragen der Eingangssignale, sodass Mehrfachverzweigungen unmittelbar programmierbar sind. Technisch gesehen eignen sich PLA-Steuerwerke dann als Programmwerke,
wenn die Anzahl der Ein-/Ausgänge und die Kapazität des PLA beim Entwurf frei wählbar sind, wie z. B.
innerhalb eines hochintegrierten digitalen Systems,
etwa eines Mikroprozessors. Bei der Programmierung des Steuerspeichers geht man unmittelbar von
einer die Gleichzeitigkeit von Abfragen und Anweisungen betonenden Darstellung des Steuerungsalgorithmus aus, z. B. einem Zustandsgraphen (Bild 4-21a
als Beispiel).
Anstelle der „regular logic“ des PLA können auch andere Schaltnetzformen zur Verwirklichung der Steuertabelle verwendet werden, z. B. „random logic“ in
der Form von Gatternetzen. Dann ist das Programm in
der Schaltnetzstruktur versteckt, und man spricht von
fest verdrahteten Programmen bzw. Steuerwerken.
ROM-Steuerwerke sind komplexe Programmwerke,
bei denen der Programmspeicher auf der Basis von
Festwertspeichern (siehe 3.4.2) ausgebildet ist und
vielfach mit einem Zähler (sequencer) adressiert
wird (Bild 4-20b). Typisch ist die nur nacheinander
mögliche (serielle) Auswertung der Eingangssignale, sodass nur Einfachverzweigungen unmittelbar
programmierbar sind. ROM-Steuerwerke werden
vorteilhaft dann als Programmwerke eingesetzt,
wenn die Modifizierbarkeit der Steuerprogramme
im Vordergrund steht, z. B. um Fehler im Programm
korrigieren oder es an geänderte Anforderungen
anpassen zu können. Die Programmierung des Steuerspeichers erfolgt in ähnlicher Weise wie bei einem
Digitalrechner, d. h., man geht von einer die zeitliche
Abfolge von Abfragen und Anweisungen betonenden
Darstellung des Steuerungsalgorithmus aus, z. B.
einem Flussdiagramm (Bild 4-22a als Beispiel).
Anstelle von ROMs können auch andere Speicher
für die Steuertabelle eingesetzt werden, z. B. RAMs.
Dann lassen sich Programme während des Betriebs
45
46
Technische Informatik / Digitale Systeme
Bild 4-20. Programmwerke. a PLA-Steuerwerk; b ROMSteuerwerk (1 Operationen, 2 Code und 3 Adresse für Programmverzweigungen; MXC Multiplexer Control, steuert
die Datenwege im Steuerwerk)
nachladen oder auswechseln, und man spricht von dynamisch programmierbaren Steuerwerken.
4.4.2 Beispiele für programmgesteuerte
Datenverarbeitungswerke (Prozessoren)
Die Bilder 4-21 und 4-22 zeigen zwei Zusammenschaltungen von Programm- und Datenwerken (vgl.
Bild 4-1) als speziell auf die Aufgabenstellung zugeschnittener (konstruierter) Prozessor bzw. als universell für verschiedene Aufgaben verwendbarer (programmierter) Prozessor.
Bild 4-22. Carry-save-Addition von zwei Dualzahlen.
a Flussdiagramm, b Blockbild, Codes: ld – load, no – no
operation, xr – exclusive or, an – and, sh – shift, bn – branch
never, ba – branch always, bx – branch if external, bz –
branch if zero, Initialisierung von I mit „no – – ba 0“. Zur
Optimierung und zu weiteren Varianten siehe [1]
Es handelt sich dabei um „Maschinen“ für die Berechnung der Summe von in zwei Registern A und
C gespeicherten Dualzahlen nach einem Algorithmus,
der von Burks, Goldstine und von Neumann für die
Addition in ihrem Universalrechner der ersten Generation vorgesehen war und der in verallgemeinerter
Form heute bei der Verwirklichung von Multiplikationsalgorithmen angewendet wird, siehe z. B. [1]. Dieser – weil die Überträge in allen Stellen gleichzeitig
gebildet und für den jeweils nächsten Schritt gerettet
werden – Carry-save-Addition genannte Algorithmus
tritt hier in zwei charakteristischen Darstellungen auf.
Horizontale Darstellung. In Bild 4-21a ist der Al-
Bild 4-21. Carry-save-Addition von zwei Dualzahlen. a Zustandsgraph, b Blockbild auf der Registertransfer-Ebene.
Initialisierung von I mit [00 000]. Zur Optimierung siehe [1]
gorithmus als „horizontales Programm“ in der Form
eines Zustandsgraphen wiedergegeben. Das Attribut
„horizontal“ soll darauf hinweisen, dass – wenn immer möglich – die Operationen „nebeneinander“, d. h.
parallel, ausgeführt werden. Zum Beispiel wird im
Zustand 1 (siehe Graph) der Operand C auf Null abgefragt, und bei C = 0 werden gleichzeitig die beiden
4 Schaltwerke
Operationen A := A XOR C (XOR für alle Stellen)
und C := C AND A (AND für alle Stellen) ausgeführt
und nach Zustand 2 verzweigt.
Im Programmwerk in Bild 4-21b findet sich dieser
Programmausschnitt in der 3. Zeile des PLA in binär
codierter Form wieder. Wenn dieser „Befehl“ im
Register I (instruction register) steht, so wird damit
der Zustand [10] = 2 als Folgezustand festgelegt und
gleichzeitig mit [101] im Datenwerk die parallele
Ausführung der beiden oben genannten Operationen
veranlasst. Die Bedingung C = 0 wird im Datenwerk
in jedem Schritt ermittelt und steht so dem Programmwerk dauernd zur Auswertung zur Verfügung.
Die Bedingung Addieren hingegen kommt von
„außen“, sie kann man sich vorstellen als von der
Add-Taste eines Taschenrechners abgeleitet oder dem
Add-Befehl eines Universalrechners decodiert (dann
heißt das Addiere-Programm Mikroprogramm).
Schaltungsdetails folgen den früher behandelten Prinzipien (siehe z. B. Bild 3-16 oder Bild 2-2f für das
PLA-Steuerwerk).
Vertikale Darstellung. In Bild 4-22a ist der Algorithmus als „vertikales Programm“ in der Form eines Flussdiagramms dargestellt. Das Attribut „vertikal“ soll darauf hinweisen, dass die Operationen – so
gut wie immer – nacheinander, d. h. seriell, ausgeführt
werden. Zum Beispiel wird in Schritt 2 (siehe Diagramm) der Operand C auf Null abgefragt, und bei C
0 wird Schritt 3 ausgeführt. Dort und in den Schritten 4 und 5 werden nacheinander die drei Operationen
X := A, A := A XOR C und C := C AND X ausgeführt.
Im Programmwerk in Bild 4-22b findet man diesen
Programmausschnitt unter den Adressen 1 bis 4 des
ROM wieder. Wenn z. B. der Befehl Nr. 1 in I steht, so
wird mit „no“ im Datenwerk keine Operation veranlasst, mit „bz 0“ wird jedoch das Z-Bit abgefragt (zuvor mit „1d C C“ oder „sh C“ beeinflusst). Bei Z = 0
wird bei Adresse 2 fortgefahren, und die drei oben genannten Operationen werden seriell ausgeführt. Auch
hier wird die Abfrage auf 0 im Datenwerk dauernd
ermittelt; die Bedingung Addieren kommt wieder von
„außen“.
Wegen seines Befehlsformates, bei dem jeder Befehl
neben der logischen Operation eine bedingte Sprungoperation enthält, ist ein solcher Prozessor gewissermaßen die Minimalform eines Superskalarprozessors.
Hardware-/Software-Programme. Sowohl das ho-
rizontale als auch das vertikale Programm in den
Bildern enthalten einen operativen Anteil, die Datenanweisungen, und einen steuernden Anteil, die
Programmverzweigungen, die in den Programmen
nicht modular getrennt in Erscheinung treten, sondern ineinander verwoben sind. Mit der Abstraktion
von diesen beiden Teilwerken eines programmgesteuerten Datenverarbeitungswerkes wird die
Registertransfer-Ebene verlassen und die Ebene
der Beschreibungsformen algorithmischer Programmiersprachen erreicht. Die Art der Realisierung
des Algorithmus, ob als Schaltung (Hardware) oder
als Programm (Software) tritt in den Hintergrund.
Hardware- und Software-Beschreibung des Algorithmus unterscheiden sich nur durch mehr oder
weniger Parallelität, z. B. in (1) und (2) durch die
unterschiedliche Verwendung von Kommas und
Semikolons:
Auf die in den Beispielen beschriebene Weise kann
man für technisch-mathematische Probleme spezielle
Rechner konstruieren oder diese auf universellen
Rechnern programmieren. „Eingekapselt“ unterscheiden sie sich nur durch ihre Geschwindigkeit
bei der Bearbeitung des Problems. Komplexere als
in diesem Beispiel behandelte Probleme enthalten
anstelle solcher elementarer (logischer) komplexere
(arithmetische) Operationen, sodass ihre Beschreibungen mehr den üblichen Rechenprogrammen
technisch-wissenschaftlicher Anwendungen ähneln.
Die Konstruktion solcher Spezialrechner erfolgt
heute von ihrer algorithmischen Beschreibung
ausgehend, z. B. in VHDL [3], einer HardwareBeschreibungssprache, in Zukunft evtl. über sog.
Silicon-Compiler, ähnlich wie heute ihre Program-
47
48
Technische Informatik / Digitale Systeme
mierung auf einem Universalrechner z. B. von
einem C/C++-Programm ausgehend über einen
C/C++-Compiler läuft.
Praktisch relevante Aufgabenstellungen, wie z. B. der
Bau eines Spezialrechners für die Schnelle FourierTransformation oder der Bau eines Universalrechners
zur Programmierung aller möglichen Anwendungsfälle, unterscheiden sich von diesem Beispiel hauptsächlich durch eine drastische Steigerung der Komplexität.
5 Prozessorstrukturen
Während Kapitel 4 Spezialprozessoren behandelt,
also Prozessoren, die aus Anwendersicht nicht
programmierbar sind, sondern nur auf ihren Bestimmungszweck hin aufgebaut sind, behandelt
Kapitel 5 Universalprozessoren, also Prozessoren,
die voll programmierbar und somit keinen Einschränkungen hinsichtlich ihrer Bestimmung unterworfen
sind. – Die Formulierung „aus Anwendersicht“ ist
interpretierbar. Es kann sich um irgendeine Anwendung handeln: die Simulation einer „Welt“, die
programmiert werden soll. Es kann sich auch um
eine besondere Anwendung handeln: die Simulation eines „Prozessors“ oder eines „Rechners“, die
programmiert werden soll. Dann handelt es sich um
Prozessor- bzw. Rechnerbau.
5.1 Überblick
Dieser erste Abschnitt vermittelt eine grundlegende
und übergreifende Sicht des Universalprozessorbaus;
für den nicht an grundsätzlichen Fragestellungen Interessierten ist er entbehrlich.
Ausgehend von den beiden CSA-Spezialprozessoren
Bilder 4-21b und 4-22b gelangt man auf zwei ganz
unterschiedlichen Wegen zu Universalprozessoren,
wobei die entstehenden Architekturformen zwar im
Prinzip den CSA-Prozessoren folgen, aber von einer
drastischen Komplexitätssteigerung begleitet sind:
Weg 1. Zusätzlich zum Addiere-Programm („oben“ in
den Bildern) wird eine Reihe weiterer operativer Programme eingebracht, die zusammengenommen den
Befehlssatz (instruction set) des auf diese Weise entstehenden Rechners bilden.
Weiterhin wird ein übergeordnetes Simulationsprogramm eingebracht, das die Befehle eines Anwenderprogramms samt der durch sie zu verarbeitenden Daten aus einem zusätzlichen, i. Allg. prozessorexternen
Speicher holt und den Aufruf der diesen Befehlen zugeordneten operativen Programme organisiert.
Das so entstehende Gesamtprogramm wird Mikroprogramm genannt, wobei dieser Begriff gleichfalls auch
für ein einzelnes operatives Programm benutzt wird.
Die Entwicklung des Mikroprogramms ist Aufgabe
des Hardware-Ingenieurs. Ein mit den Befehlen des
Befehlssatzes geschriebenes Programm wird Maschinenprogramm genannt.
Mit diesem Ansatz entstehen universell programmierbare Rechner: durch Verallgemeinerung von
Bild 4-21b die klassische Akkumulator-Rechnerarchitektur, traditionell als v.-Neumann-Rechner
bezeichnet, durch Verallgemeinerung von Bild 4-22b
die klassische Register-Rechnerarchitektur, seit
dem Aufkommen der RISCs als CISC bezeichnet
(complex instruction set computer).
Die angesprochene Komplexitätssteigerung betrifft
oben in den beiden Bildern das Interpretationsprogramm des Rechners, das Mikroprogramm: beim
v.-Neumann-Rechner unveränderlich gespeichert
im PLA (horizontal mikroprogrammiert, d. h. Mikrooperationen mehr neben- als untereinander),
beim CISC veränderbar gespeichert im ROM (vertikal mikroprogrammiert, d. h. Mikrooperationen
mehr unter- als nebeneinander),
unten in den Bildern das Maschinenprogramm
und seine Daten in Kopie als extrem kleiner
Ausschnitt: beim v.-Neumann-Rechner in den
Registern, beim CISC im Registerblock.
Die Originale von Maschinenprogramm und
Daten befinden sich im zusätzlichen Speicher.
Bild 5-1 illustriert die Zusammenhänge.
Obwohl verallgemeinert zu Universalprozessoren,
interpretiert die geschilderte Sicht der Dinge die
entstehenden Prozessoren im Grunde doch als
Spezialprozessoren, nämlich Instruktionsinterpretationsprozessoren. Sie werden für einen bestimmten
Zweck, nämlich zur Interpretation (allgemeiner:
Simulation) der Architektur des zu „bauenden“ Universalprozessors konstruiert. Davon unbenommen
5 Prozessorstrukturen
bleibt, dass wirkungsvollere Elementaroperationen
vorgesehen werden und dass man durch Programmierbarkeit des jetzt Mikroprogrammspeicher oder
Steuerspeicher (control store) genannten Programmspeichers das Mikroprogramm in gewissen Grenzen
ändern kann – man spricht von Mikroprogrammierbarkeit – und so ohne Hardwareänderungen
Prozessorvarianten herstellen kann.
Weg 2. Anstelle des Addiere-Programms („oben“ in
Bild 4-22b) wird automatisch, d. h. mittels Hardware,
irgendein Anwenderprogramm geladen, und zwar
ausschnittweise aus einem zusätzlichen, i. Allg. prozessorexternen Speicher in den Programmspeicher
des entstehenden Rechners.
Anstelle der Addiere-Daten („unten“ im Bild) wird
programmiert, d. h. durch Software, ein Ausschnitt
der zugehörigen Anwenderdaten geladen, und zwar
aus dem zusätzlichen Speicher in den Datenspeicher
des Rechners.
Hier gibt es keine Programm-Interpretationshierarchie, und man spricht traditionell nicht von
Mikroprogrammierung (ggf. in extenso von extrem
horizontaler Mikroprogrammierung: Mikrooperationen sämtlich nebeneinander), sondern nur von
Maschinenprogrammierung. Während vorher das
Problem bestand, das Mikroprogramm möglichst
effizient zu erstellen, ist jetzt das Problem, den Programmspeicher vorausschauend mit den als nächstes
auszuführenden Maschinenprogrammteilen zu füllen.
Mit diesem Ansatz entstehen ebenfalls universell
programmierbare Rechner: durch Verallgemeinerung von Bild 4-22b die moderne RegisterRechnerarchitektur, gemeinhin als RISC bezeich-
Bild 5-1. Weg zum Universalprozessor: Bild 4-21 →
v.-Neumann-Rechner Bild 5-4, Bild 4-22 → CISC (ohne
Bild)
net (reduced instruction set computer), sowie
die modernen Parallel-Rechnerarchitekturen: die
Superskalar-Rechner und die VLIW-Rechner (very
long instruction word).
Die angesprochene Komplexitätssteigerung bedingt
oben im Bild irgendein Anwenderprogramm (Maschinenprogramm), gespeichert als kleiner Ausschnitt in der Form einer laufend sich ändernden Kopie im prozessorinternen Programmspeicher (Cache),
unten irgendwelche Daten des Maschinenprogramms, gespeichert in Kopie als kleiner
Ausschnitt im Multiport-Registerblock und
darüber hinaus ggf. in einem weiteren schnellen
prozessorinternen Speicher.
Das Originalprogramm mit seinen Daten befindet
sich im zusätzlichen Speicher. Bild 5-2 illustriert
die Zusammenhänge.
Diese Sicht der Dinge interpretiert die entstehenden Prozessoren nun von vornherein als Universalprozessoren, denn sie werden für keinen
bestimmten Zweck konstruiert, abgesehen von wenigen speziellen Funktionseinheiten, die zusätzlich
eingebaut werden. Davon unbenommen bleibt, dass
wirkungsvollere Elementaroperationen vorgesehen
werden, z. B. solche, die sich gut in Einzelschritte
zerlegen lassen und somit mehr als einen Takt
benötigen (sodass – genau genommen – doch wieder
Mikroprogrammierung ins Spiel kommt).
Eine weitere, ganz andere Art von Prozessor„bau“
ist die, auf einem existierenden Rechner A, dem
„Wirts“rechner, einen neuen Rechner B, den
„Gast“rechner, mittels eines Instruktionsinterpre-
Bild 5-2. Weg 2 zum Universalprozessor: Bild 4-22 →
RISC Bild 5-7, → Superskalar-Rechner (ohne Bild), →
VLIW-Rechner Bild 5-9
49
50
Technische Informatik / Digitale Systeme
tationsprogramms zu programmieren. Hier gibt es
zwar eine Programm-Interpretationshierarchie, man
spricht aber auch hier nicht von Mikroprogrammierung (bzw. in extenso von extrem vertikaler
Mikroprogrammierung: Mikrooperationen sämtlich
untereinander). Dies ist reine Programmierung
und somit weder dem Hardware-Design noch dem
Hardware-Software-Codesign, sondern ganz dem
Software-Design zuzuordnen, nämlich der allumfassenden Aufgabenstellung der Simulation. – Man
könnte einen solchen Rechner der Systematik folgend als VISC bezeichnen (virtual instruction set
computer).
Zur Maschinenprogrammierung.
Die Funktion eines Prozessors erfolgt durch Festlegung seines
Maschinenprogramms, d. h. bestimmter Abfolgen
seiner Befehle. Die Programmierung stützt sich dabei
natürlich auf die Maschinenbefehle des Prozessors,
repräsentiert durch Befehlsformate, Befehlssatz,
Adressierungsarten; siehe 5.2 – Typisch für einen
v.-Neumann-Rechner ist seine Programmierbarkeit
über den Akkumulator mit dem einen Operanden
und dem Speicher für den anderen Operanden;
siehe 5.3: Akkumulator-Architektur. – Typisch für
die Programmierung eines CISC ist, dass Operanden bzw. deren Adressen in die Register gebracht
werden und dass mit den Operanden dort direkt
oder gleich im Speicher, nun aber registerindirekt
gerechnet wird (deshalb auch Speicher-/SpeicherArchitektur genannt). Auf diese Weise werden
wegen der kurzen Registeradressen auch Mehradressbefehle so kurz, dass sie in ein Speicherwort
passen. Das gilt erst recht für einen RISC, wo
ausschließlich in den Registern gerechnet wird
mit der Konsequenz, sie programmiert mit Lade/Speichere-Befehlen füllen bzw. leeren zu müssen
(deshalb auch Lade-/Speichere-Architektur genannt).
Kennzeichnend für diese beiden Architekturen ist
also, dass die Programmierung über die Register
erfolgt; siehe 5.4: Register-Architektur. – Typisch
für die Programmierung eines Superskalar-Rechners
ist, dass auf die hier mögliche parallele Arbeitsweise
auf Befehlsebene keine Rücksicht genommen zu
werden braucht, da sie im Prozessor – für den Programmierer unsichtbar – automatisiert abläuft. Die
Maschinenprogrammierung eines VLIW-Rechners
hingegen muss auf dessen parallele Arbeitsweise
auf Befehlsebene zugeschnitten sein; hier wird
die Parallelisierung zwar auch automatisiert, aber
durch den Compiler bewerkstelligt. Kennzeichnend
für diese beiden Architekturen ist also, dass die
Programmierung mit parallel angeordneten Befehlen
erfolgt; siehe 5.5: Parallel-Architektur.
Weitere Rechnerarchitekturen, insbesondere mit
Parallelverarbeitung auf höheren Ebenen, wie Vektorrechner, Feldrechner und Mehrprozessorsysteme,
werden in späteren Kapiteln behandelt.
5.2 Maschinenbefehle
Prozessoren haben viele, oft sich über mehrere
Speicherwörter erstreckende und ggf. viele Takte
dauernde Maschinenbefehle (complex instruction set
computer, CISC) oder vergleichsweise wenige, in ein
Speicherwort passende und nur wenige Takte dauernde Befehle (reduced instruction set computer, RISC),
bei Skalar-Prozessoren mit nur einem Operationscode, aber mehreren, in ihrer Anzahl wechselnden
Adressen und mehr oder weniger Möglichkeiten zu
deren Modifikation (Adressierungsarten).
5.2.1 Befehlsformate
Dreiadressbefehle. Arithmetik-/Logikbefehle enthal-
ten zwei Adressen für die beiden Operanden und
eine für das Ergebnis. Beispiel: Add-Befehl mit der
Wirkung S := X + Y
ADD S, X, Y
(5-1)
Bedingte Sprungbefehle enthalten zwei Adressen für
die beiden Vergleichsgrößen und eine für das Sprungziel. Beispiel: Branch-if-equal-Befehl mit der Wirkung if X = Y then goto L
BEQ X, Y, L
(5-2)
Zweiadressbefehle. Bei Arithmetik-/Logikbefehlen
muss zur Adressierung des Ergebnisses eine der Operandenadressen mitbenutzt werden, d. h., der Inhalt
der Speicherzelle für den einen Operanden wird
durch das Ergebnis der Operation ersetzt. Sollen
die Werte beider Operanden erhalten bleiben, so
muss der Operation ein Move- oder Load-Befehl
5 Prozessorstrukturen
vorangestellt werden; dann ist (5-1) der folgenden
Befehlsfolge äquivalent:
d. h. S := X ;
S := S + Y ;
LOAD S, X
ADD
S, Y
Bei bedingten Sprungbefehlen ist kein Platz für die
notwendigen drei Größen, sodass Programmverzweigungen mit zwei Befehlen programmiert werden
müssen: Ein Compare-Befehl enthält die Adressen
der Vergleichsgrößen und speichert die Vergleichsergebnisse als Condition-Code CC = [z n c v] im
Statusregister SR des Prozessors, Branch-if-. . . Befehle enthalten die Sprungadresse und werten den
Condition-Code aus; somit ist (5-2) der folgenden
Befehlsfolge äquivalent:
CMP X, Y
BEQ L
d. h.
z := Y − X = 0 ;
if z then goto L ;
weshalb entsprechende Rechner auch als Kelleroder Stack-Architekturen bezeichnet werden. Bei
der Durchführung einer Operation wird der oberste
Stack-Eintrag (top of stack, TOS) ersetzt durch die
Verknüpfung des zweitobersten (TOS−1 ) mit dem
obersten Stack-Eintrag (TOS). Die Reihenfolge der
Befehle muss dieser Verarbeitungsweise angepasst
werden. Sie entspricht der sog. Postfixnotation, auch
umgekehrte polnische Notation genannt, wie sie bei
Anwendung bestimmter Übersetzungsverfahren aus
der syntaktischen Analyse arithmetischer Ausdrücke
entsteht. Zum Füllen und Leeren des Stack (und
somit seiner obersten Stack-Zelle TOS) sind neben
den Nulladressbefehlen auch Lade- und SpeichereBefehle nötig, die mit Push bzw. Pop bezeichnet
werden. – Beispiel: Der arithmetische Ausdruck
in y := a · b + u · (v + w) lautet in Postfixnotation
(Operationszeichen nach den Operanden) vollständig
geklammert bzw. in klammerfreier Form:
((a, b) ·, (u, (v, w) +)·) +
Einadressbefehle. Bei Arithmetik-/Logikbefehlen
sowie beim Compare-Befehl entfällt eine der
Operandenadressen, sodass seine Adresse einem
ausgezeichneten Register zugeordnet werden muss,
dem Akkumulator (AC). Der Akkumulator hat seinen
Namen von der Möglichkeit fortwährenden Aufaddierens (Sammelns, Akkumulierens) von Zahlen. Die
„Adresse“ dieses Registers ist implizit im Operationscode enthalten. Mit Load- und Store-Befehlen
wird der Akkumulator geladen bzw. sein Inhalt
gespeichert; somit sind (5-1) und (5-2) den folgenden
Befehlsfolgen äquivalent:
LDA X
ADD Y
STA S
d. h.
LDA X
CMP Y
BEQ L
d. h.
Nulladressbefehle.
AC := X ;
AC := AC + Y ;
S := AC ;
AC := X ;
z := (AC − Y = 0) ;
if z then goto L ;
Fehlt in den Befehlen die Angabe von Adressen völlig, so müssen die Operanden zur
Verarbeitung immer in zwei ganz bestimmten Speicherzellen vorliegen. Das sind z. B. die „obersten“
beiden Zellen eines LIFO-Speichers (Kellers, Stack),
ab· uvw+ ·+
Die Reihenfolge der Befehle einer Stack-Architektur
folgt dieser Notation (für das Beispiel siehe Tabelle 5-1):
5.2.2 Befehlssatz
Der Vorrat an Maschinenbefehlen, der Befehlssatz,
variiert von Rechner zu Rechner stark. Allen gemeinsam ist ein Repertoire grundlegender Befehle, ohne
Tabelle 5-1. Programm mit Stackbelegung bei einem Null-
adressrechner
Programm
PUSH A
TOS
A
TOS−1
PUSH B
B
A
MUL
A·B
PUSH U
U
A·B
PUSH V
V
U
A·B
PUSH W
W
V
U
ADD
V +W
U
A·B
MUL
U · (V + W)
A·B
ADD
A·B+U ·(V +W)
POP Y
TOS−2
TOS−3
A·B
51
52
Technische Informatik / Digitale Systeme
die selbst kleine Aufgaben nur sehr umständlich und
ineffizient zu programmieren wären (obwohl im Prinzip eine Handvoll elementarer Befehle genügt, jede
Aufgabe zu programmieren). Das im Folgenden skizzierte Grundrepertoire enthält eine Auswahl typischer
Maschinenbefehle, wobei die Adressen X, Y, Z in den
Befehlen Datenspeicher- oder Registeradressen sein
können. Bei L handelt es sich um eine Programmspeicheradresse (Label).
Arithmetik-/Logikbefehle. Zu
den Arithmetikbefehlen zählen alle Befehle, die ihre Operanden als
Dualzahlen interpretieren (vorzeichenlos, unsigned;
vorzeichenbehaftet, signed, integer):
Clear
CLR X
X := 0
Increment
INC X
X := X + 1
Decrement DEC X
X := X − 1
Negate
NEG X
X := −X
Shift Left
ASL X,k
X := X · 2k
X := X/2k ohne Rest
Shift Right ASR X,k
Add
ADD Z,X,Y Z := X + Y
Subtract
SUB Z,X,Y Z := X − Y
Multiply
MUL Z,X,Y (Z, Z + 1) := X · Y
Divide
DIV Z,X,Y Z := (X, X + 1)/Y,
X + 1 := Rest
Zu den Logikbefehlen zählen alle Befehle, die ihre
Operanden als Bitvektoren interpretieren:
Not
NOT
X
X := not X
And
AND
Z,X,Y Z := X and Y
Or
OR
Z,X,Y Z := X or Y
Exclusive Or
XOR
Z,X,Y Z := X xor Y
Neben den Arithmetik- und den Logikbefehlen
gibt es Befehle, die keiner dieser beiden Gruppen
eindeutig zugeordnet werden können:
Move
MOV
Z,X
Z := X
Rotate Left
RSL
X,k
X := rotate left
k bits
Rotate Right
RSR
X,k
X := rotate right
k bits
Sprungbefehle. Dazu zählen alle Befehle, die
den Programmzähler beeinflussen und damit Programmverzweigungen hervorrufen können. Jumpoder Unconditional-Branch-Befehle entsprechen den
Gotos höherer Programmiersprachen:
Branch Always BRA
goto L
L
Conditional-Branch-Befehle werten die Bedingungsbits des Statusregisters aus, die i. Allg. durch den unmittelbar davor stehenden Befehl beeinflusst werden.
Das sind in der Regel ein Compare-Befehl oder –
als Nebeneffekt wirkend – auch viele der anderen
Befehle, insbesondere Arithmetikbefehle. Steht genügend Platz im Befehlswort zur Verfügung, sodass drei
Adressen untergebracht werden können, entfällt diese
Zweischrittigkeit (und somit der Compare-Befehl). –
Im Folgenden steht cc für einen Bedingungscode, der
die auszuwertende Relation :: bestimmt, konkret EQ
für =, NE für , LO für <, LS für ≤, GR für >, GS für ≥:
Compare
Branch if cc
CMP
Bcc
X,Y
L
if X::Y
then goto L
Weiterhin gibt es Sprungbefehle, die vor der Ausführung des Sprungs den Programmzähler retten, sodass
später an diese Stelle im Programm zurückgesprungen werden kann. Diese Befehle sind paarweise definiert und dienen zum Anschluss von Unterprogrammen (Subroutinen), d. h. zum Hinsprung ins Unterprogramm und zum Rücksprung ins Hauptprogramm:
Jump to Subroutine
Return from Subroutine
JSR SUB
RTS
call SUB
return
Systembefehle. Dazu zählen Befehle, die auf den
Betrieb des Rechners wirken, wie StatusregisterBefehle, Trap- und Interrupt-Befehle, auch die
Befehle
No Operation
Halt
NOP
HLT
5.2.3 Adressierungsarten
Um effizient programmieren zu können, insbesondere immer wiederkehrende Befehlsfolgen in der Form
sog. Schleifen, bedient man sich verschiedener Möglichkeiten, die Adressen in den Befehlen zu modifizieren. Die elementarsten Adressierungsarten sind
5 Prozessorstrukturen
Bild 5-3. Veranschauli-
chung der Wirkung der
Adressierungsarten am
Beispiel des LDA-Befehls:
a immediat, b direkt, c indirekt, d indiziert
@
x
y
z
Bild 5-4. Einadressrechner mit Beispielprogramm Z := X + Y im Speicher. Die Register IR, Operand, Zähler sowie μIR
sind nur vom Mikroprogramm, nicht vom Maschinenprogramm beeinflussbar
53
54
Technische Informatik / Digitale Systeme
neben der direkten oder absoluten Adressierung die
folgenden Adressmodifizierungen: (1.) Anstelle der
Adresse des Operanden im Befehl wird der Operand
selbst als Konstante angegeben, um ihn direkt zu verarbeiten (immediat). (2.) Anstelle der Adresse des
Operanden wird die Adresse der Adresse des Operanden angegeben, um mit verschiedenen, erst später
bekannten Parametern zu arbeiten (indirekt). (3.) Die
Adresse des Operanden wird hochgezählt, um die
Größen eines Feldes, d. h. die mit einem Index ansprechbaren Elemente des Feldes, nacheinander erreichen zu können (indiziert). – Weitere Adressierungsarten sowie eine systematische Darstellung finden sich in [1].
Bild 5-3 zeigt die Wirkung der immediaten, der indirekten und der indizierten Adressierung. Zum Vergleich ist die „direkte“ Adressierung mit einbezogen,
also der ohne weitere Kennzeichnung benutzte Normalfall der Adressierung.
Immediate Adressierung. Im Befehl zeigt ein Bit
an, ob die Adresse des Operanden oder der Operand selbst gemeint ist; zur symbolischen Kennzeichnung der immediaten Adressierung, des Direktoperanden, wird gewöhnlich das Zeichen # benutzt, siehe
Bild 5-3a: nach Ausführung des LDA-Befehls steht
die Zahl n im AC.
Indirekte Adressierung.
Im Befehl zeigt ein Bit an,
ob die Adresse des Operanden oder die Adresse einer
Adresse des Operanden gemeint ist; zur symbolischen
Kennzeichnung eines indirekt angesprochenen Operanden wird gewöhnlich das Zeichen @ benutzt, siehe
Bild 5-3c: nach Ausführung des LDA-Befehls steht
die Zahl n im AC; man sieht im Bild gut den zweiten Speicherzugriff, die dabei entstehende, wirksam
werdende Adresse heißt effektive Adresse. Bei registerindirekter Adressierung – die Adresse des Operanden steht im Register – wird statt @ das Register in
Klammern gesetzt.
Indizierte Adressierung. Im Befehl gibt bei einem
einzigen Indexregister das Indexbit, bei mehreren die
Registernummer an, ob ein bzw. welcher Index zur
Adresse hinzuaddiert wird; zur symbolischen Kennzeichnung eines indiziert angesprochenen Operanden
gibt man das in [ ] eingeschlossene Indexregister an,
z. B. [I] für das Indexregister IX, siehe Bild 5-3d:
nach Ausführung des LDA-Befehls steht die Zahl n
im AC; man sieht im Bild die Addition vor dem
Zugriff auf den Operanden, die dabei entstehende,
wirksam werdende Adresse ist die effektive Adresse.
5.3 Akkumulator-Architektur
Die klassische Akkumulator-Rechnerarchitektur
führt auf den Einadressrechner, in Bild 5-4 exemplarisch dargestellt: „oben“ das PLA mit dem
Mikroprogramm und das μIR (Mikroinstruktionsregister), „unten“ das Register IR (Instruktionsregister)
für die Aufnahme eines Befehls mit seinen Teilen
Code, Mode, und Adresse, der Programmzähler
PC (siehe 5.3.1), des Weiteren die Register AC
(Akkumulator), MQ (Multiplikator/Quotient), IX
(Index) usw. Das Programm mit seinen Daten befindet sich im Speicher P/D (Programm/Daten). – Die
Struktur des Rechners ist auf seine Spezifikation und
somit auf das Mikroprogramm zugeschnitten: mit
speicherbezogenen Einadressbefehlen von im Prinzip
hoher Komplexität. Jeder Befehl benötigt wenige
bis viele Takte, und die Maschinenprogrammierung
ist einfach, etwa in der Art der Programmierung
eines Taschenrechners, jedoch mit Möglichkeiten zur
Programmsteuerung.
5.3.1 Einadressrechner
Zur Illustration seiner Arbeitsweise zeigt Bild 5-4
rechts oben eine sehr kleine Programmieraufgabe:
Zwei im Speicher befindliche Zahlen mit den Variablennamen bzw. den symbolischen Adressen X und Y
sind zu addieren und das Ergebnis an eine dritte Größe mit der symbolischen Adresse Z zuzuweisen (in
Wirklichkeit anstatt der symbolischen Adressen drei
Nummern, zwei als Quelladressen und eine als Zieladresse). Die Befehlslänge ist gleich der Wortlänge
des Speichers. Das Programm benötigt 6 Speicherzugriffe (von i. Allg. je mehreren Takten). – In einer höheren Programmiersprache würde man die Aufgabe
durch Z := X + Y beschreiben.
Programmzähler (besser Befehlszähler, Instrukti-
onszähler). Der Programmzähler (program counter
PC) hat die elementare Aufgabe, die hintereinander
5 Prozessorstrukturen
angeordneten Befehle bzw. Instruktionen eines
Maschinenprogramms einzeln aus dem Speicher
zu holen und in das Instruktionsregister IR des
Prozessors zu bringen. Dort werden sie decodiert
und ihrer speziellen Bestimmung zugeführt: Mit
dem Befehls-Code wird die dem Befehl zugeordnete
Stelle im Mikroprogramm angewählt, mit dem
Adress-Mode wird die Adressierungsart ausgewählt
und der Befehlsteil Adresse entsprechend interpretiert. Ein Einadressbefehl besteht also aus den drei
Teilen Code, Mode und Adresse.
Statusregister. Der Condition-Code CC wird mit
weiterer Information insbesondere für die Interruptverarbeitung und Managementaufgaben des
Betriebssytems, wie z. B. Supervisor-/User-Betrieb,
im Statusregister gespeichert. Zusammen mit dem
Programmzähler enthält es in jedem Moment
die relevante Information über den Zustand des
Prozessors.
5.3.2 Beispiel für Mikroprogrammierung
Bild 5-5 zeigt einen Teil des Mikroprogamms
des Einadressrechners, und zwar für die drei
Befehle LDA, ADD und STA des Beispielprogramms aus Bild 5-4: das oben beschriebene
Befehl-Holen (Abruf), das Adresse-Interpretieren
und ggf. -Modifizieren (Modif) sowie das BefehlDecodieren/-Ausführen (Decode/Execute). Um sich
die Funktionsweise eines solch elementaren Rechners
in allen Einzelheiten klarzumachen, spiele man das
Beispielprogramm in Bild 5-4 mithilfe des Mikroprogramms Bild 5-5 Schritt für Schritt bzw. Takt für
Takt durch und simuliere auf diese Weise die Ausführung des Programms und somit die Arbeitsweise
des Rechners. Dazu wähle man eine Startadresse
für den ersten Befehl LDA X, schreibe sie in das
PC-Kästchen und trage die Veränderungen infolge
der Ausführung von := fortlaufend in die Register ein;
die Ausführung von .= wirkt auf Leitungen, nicht auf
Register. – Der Nutzen dieser Übung besteht darin,
dass man auf diese Weise einen guten Einblick in die
Funktionsweise eines Digitalrechners gewinnt. Weitergehende Ausführungen zur Mikroprogrammierung
findet man insbesondere in [1].
Bild 5-5. Ausschnitt aus dem Mikroprogramm des Einadressrechners mit den Befehlen des Beispielprogramms
aus Bild 5-4 (-> bedeutet goto). Alle mit , getrennten Anweisungen werden im selben Takt ausgeführt. Somit benötigt das Mikroprogramm zur Decodierung/Ausführung der
Befehle LDA, ADD und STA jeweils 1 Takt (unter der hier
gewählten Annahme von Speicherzugriffen zu je 1 Takt)
5.3.3 Beispiel zur Maschinenprogrammierung
Zur Programmierung des Polynoms
p = a0 xn + a1 xn−1 + . . . + an−1 x + an
ist die indizierte Adressierung geeignet. Damit
braucht das Inkrementieren der Adresse des Koeffizientenfeldes nicht explizit programmiert zu werden.
Das folgende Maschinenprogramm orientiert sich an
der mathematischen Formulierung der Aufgabe in
der Form des sog. Horner-Schemas, bekannt vom
55
56
Technische Informatik / Digitale Systeme
Bild 5-6. Maschinenprogramm zur Polynomberechnung;
die Operanden (Variablenwerte) und ihre symbolischen
Adressen (Variablennamen) als Speicherabzug
numerischen Rechnen lange vor der Erfindung des
Computers.
p = (. . . ((a0 x + a1 )x + . . . + an−1 )x + an
Das Programm in Bild 5-6 ist als Schleife organisiert, sodass es für Polynome mit wählbarem Grad n
gilt. Der Grad, die Koeffizienten sowie das Argument
müssen gegeben sein und im Speicher stehen, entweder zuvor ermittelt oder eingelesen. Das Ergebnis
wird im Akkumulator fortschreitend aufgebaut und ist
beim Verlassen der Schleife, nach Addition des letzten Koeffizienten, dort verfügbar. – Code, Mode und
Adresse in den Befehlen sind symbolisch angegeben.
Ein solches symbolisches Programm wird von einem
Systemprogramm des Betriebssystems, dem Assembler, übersetzt, und zwar in die binären Entsprechungen der Befehlsteile; dabei werden die Adressquerbezüge und somit die Nummern für die Adressen automatisch ermittelt. – Auch hier ist es eine hilfreiche Übung, das Programm durchzuspielen, nun aber
Befehl für Befehl, wodurch man einen guten Einblick in die Maschinenprogrammierung/-ausführung
gewinnt.
5.4 Register-Architektur
Die moderne Register-Rechnerarchitektur führt auf
den Dreiadressrechner, in Bild 5-7 exemplarisch
x
y
z
Bild 5-7. Dreiadressrechner mit Beispielprogramm Z := X + Y im Cache und im Speicher. Fließbandregister gestrichelt ge-
zeichnet. Die vier Stufen: 1. Befehl lesen, d. h. PC → Programm-Cache → IR; 2. Operanden lesen, d. h. IR → Registerblock
→ IU-Eingänge, bzw. Sprungadresse bereitstellen, d. h. IR → PC; 3. Ergebnis ermitteln und bereitstellen, d. h. IU-Eingänge
→ IU → IU-Ausgang und ggf. → CC; 4. Ergebnis schreiben, d. h. IU-Ausgang → Registerblock. Im Programm sind Register, die Operanden enthalten, durch Fettdruck hervorgehoben
5 Prozessorstrukturen
dargestellt (wobei – vgl. 5.1 – gegenüber Bild 4-22b
die bedingten Sprungbefehle der Maschinenprogramme im Speicher hier nicht neben, sondern unter den
Arithmetik-/Logikbefehlen angeordnet sind, sodass
alle Befehle streng sequentiell ausgeführt werden
müssen, dafür aber letztere als Dreiadressbefehle).
Zu sehen sind: „oben“ der Programm-Cache P’
für den Ausschnitt aus einem Maschinenprogramm
(siehe 5.4.1), weiterhin der PC und das IR, „unten“
der 3-Port-Registerblock D’ für einen Datenausschnitt sowie die arithmetisch-logische Einheit plus
Multiplizierer, zur Festkomma- oder GanzzahlEinheit zusammengefasst (Integer-Unit, IU). Das
gesamte Programm mit seinen Daten befindet sich im
Speicher P/D.
Der Rechner weist in erster Linie Dreiadressbefehle
geringer Komplexität auf (bis +, − und ·, insbesondere
keine Befehle, die auf Iterationen basieren). Sie arbeiten ausschließlich mit den Registern des Registerblocks. Nur die wichtigsten Adressierungsarten sind
eingebaut. Für das Register-Laden und -Speichern
werden weitere Befehlsformate nötig, da Ladeund Speichere-Befehle anders aufgebaut sind als
Arithmetik-/Logikbefehle und bedingte Sprungbefehle. In jedem Takt wird genau ein Befehl fertiggestellt.
Lade-/Speichere-Befehle benötigen jedoch oft einen
zusätzlichen Takt. Die Maschinenprogrammierung ist
schwierig; sie geht in Richtung Mikroprogrammierung, entsprechend aufwändig ist der Compilerbau.
5.4.1 Dreiadressrechner (RISC)
Bild 5-7 zeigt rechts oben die sehr kleine Programmieraufgabe, zwei im Speicher befindliche Zahlen X
und Y zu addieren und das Ergebnis an eine dritte
Größe Z zuzuweisen. Das Programm im Cache (als
Kopie) benötigt 3 (Haupt-)Speicherzugriffe (von nur
wenigen Takten).
Wie man am Programm in Bild 5-7 sieht, gibt es bei
dieser Art Rechner zum Laden und Speichern der Register ein Problem. Da Befehlslänge und Adresslänge
beide gleich der Wortlänge des Speichers sind, kann
ein ld- bzw. ein st-Befehl nicht die benötigte Information aufnehmen, sodass mehrere Befehle notwendig werden. Spezielle set-Befehle laden die Speicheradresse eines Operanden in ein Register: setl lädt die
entsprechende Nummer als den unteren Teil (l lower
part), setu als den oberen Teil (u upper part) in das Register ri . Erst damit sind ld und st in der Lage, einen
Operanden zwischen Register und Speicher zu transportieren: ld lädt Register r j (z. B. r4 im Programm
in Bild 5-7) mit dem Inhalt der Speicherzelle, dessen Adresse in ri steht (r1 im Programm), st speichert den Inhalt eines Registers ri (r6 im Programm)
in eine Speicherzelle, deren Adresse in r j steht (r3 im
Programm). Aus dieser Umständlichkeit in der Maschinenprogrammierung darf aber nicht geschlossen
werden, ein RISC sei ineffizienter als ein CISC. Was
beim RISC als Maschinenprogramm abläuft, muss ein
CISC im Mikroprogramm bewerkstelligen (wobei die
Taktfrequenz des CISC i. Allg. niedriger als die des
RISC ist).
Die Aufgabe ist aber nicht typisch für die übliche
Verwendung von Rechnern dieser Art. Es werden
nicht im Speicher einzelne Rechenoperationen
durchgeführt; vielmehr werden zuerst die zu verarbeitenden Größen mittels set-/ld-Befehle in die
Register gebracht, dort wird gerechnet, und anschließend werden die Ergebnisse mittels set-/st-Befehlen
zurück in den Speicher gebracht (siehe 5.4.3).
Programm-Cache (vielfach Instruction-Cache oder
Befehls-Cache). Der Programm-Cache hat die Aufgabe, aktuelle Ausschnitte des Maschinenprogramms
bereitzustellen (cache: etwa Depot). Er ist als schneller, prozessorinterner Speicher mit der Wirkung eines
Assoziativspeichers (content addressable memory,
CAM) realisiert, der nicht nur die zur Programmausführung benötigten Befehlswörter, sondern auch
deren (Haupt-)Speicheradressen enthält. Dadurch
können für Befehle, die im Cache stehen, Speicherzugriffe und damit Systembusbelegungen entfallen.
Befehle hingegen, die nicht im Cache stehen, werden
wie üblich aus dem Speicher geholt und verarbeitet,
darüber hinaus aber auch in den Cache geladen in der
Annahme, dass sie in allernächster Zukunft wieder
benötigt werden (Ausnutzung der Programmlokalität
bei Programmschleifen). Dabei werden bei kleineren
Caches diejenigen Befehle überschrieben, deren
Benutzung am weitesten zurückliegt (least recently
used, LRU; eine besonders anschauliche Erklärung
liefert [1]). Bei größeren Caches, insbesondere
bei solchen, die neben Befehlen auch Operanden
57
58
Technische Informatik / Digitale Systeme
enthalten, geht man in der Aufbautechnik andere
Wege (direct mapped, set associative caches).
Allgemein lassen sich in Assoziativspeichern abgelegte Tabellen unter Vorgabe einer oder mehrer Tabellenspalten durchsuchen, und zwar in einem einzigen Schritt, wobei Tabellenzeilen durch Treffer markiert werden. Die damit verbundenen (assoziierten)
Tabelleninhalte (hier die Befehle) werden anschließend ausgelesen (beim Cache im selben Schritt). –
Trotz ihrer Vielseitigkeit sind Assoziativspeicher bisher im Rechnerbau nicht in größerem Umfang eingesetzt worden.
5.4.2 Beschleunigung durch Fließbandtechnik
Der in Bild 5-7 gezeigte Rechner hat die Eigenschaft,
dass ein Dreiadressbefehl in genau einem Takt vollständig ausgeführt wird; man erkennt im Bild die kursiv nummerierten vier Phasen der Befehlsausführung,
die räumlich/zeitlich innerhalb eines Takts durchlaufen werden: 1 → 2 Befehl lesen, 2 → 3 Operanden
lesen, 3 → 4 Ergebnis ermitteln, 4 → 5 Ergebnis
schreiben. Die einzelnen Phasen, nun aber mehrerer,
aufeinander folgender Befehle, lassen sich auch überlappend ausführen, man sagt: in Fließbandtechnik, in
Pipelining. Aber nur, wenn an den kursiv nummerierten Stellen Register eingebaut werden (bis auf 5).
Im Fließbandbetrieb werden „oben“ in die Pipeline
laufend neue Befehle hineingeschoben, die in dieser
Reihenfolge die Pipeline durchlaufen. In ihr befinden
sich immer vier Befehle in jeweils aufeinander folgenden Phasen. Auf diese Weise wird in jedem Takt
ein Befehl fertig, obwohl ein jeder 4 Takte dauert. –
Die Taktfrequenz wird von der Signallaufzeit der sog.
längsten Logikkette bestimmt (kritischer Pfad). Sie
lässt sich mit Pipelining erhöhen, sodass die Leistung
des Rechners signifikant steigt. Man spricht anstelle
von Phasen von Stufen, dennoch gelegentlich zur Abgrenzung gegenüber anderen Pipelines von PhasenPipelining.
Auffallend ist, dass die Mikroprogrammsteuerung
des Rechners verkümmert. Ganz entfällt sie aber nur,
wenn keine Leertakte nötig sind, was aber im Rechnerbau nur theoretisch möglich ist. Sofern nämlich
z. B. die immer notwendigen Lade-/SpeichereBefehle mehr als einen Takt kosten, müssen
Leertakte erzeugt werden, sodass Lücken auf dem
Fließband entstehen, anschaulich „bubbles“ in der
Pipeline. – Sehr grundsätzliche Ausführungen zur
Fließbandtechnik findet man in [1].
Fazit. Die Leistungssteigerung ist durch räumliche
und zeitliche Parallelität zu erklären, wobei die zeitliche Parallelität nicht auf Gleichzeitigkeit derselben
Operationen, sondern auf Überlappung aufeinander
folgender Operationen beruht.
Fließbandkon¦ikte und deren Lösungen. Es gibt
im Wesentlichen zwei Arten von Fließbandkonflikten: (1.) Datenkonflikte; sie entstehen, wenn ein
Registeroperand benötigt wird, der sich noch in
der Pipeline befindet, also noch nicht ins Register
geschrieben wurde (dort steht noch der vorige Wert).
(2.) Sprungkonflikte; sie entstehen dadurch, dass bei
einer Programmverzweigung Befehle geholt werden
(vorsorglich), bevor das Ergebnis der Verzweigung
ermittelt worden ist (und die dann gültig oder
ungültig sind).
Beide Konfliktarten können verhindert werden
durch Leertakte (interlocking), was aber zu einer
Leistungsminderung führt. Datenkonflikte werden
effizient dadurch vermieden, dass nicht erst gewartet
wird, bis das IU-Ergebnis im Register steht, sondern
das Ergebnis gewissermaßen am Registerblock
vorbei gleich wieder an die IU-Eingänge gelegt
wird (bypassing, forewarding). Fehlfunktionen bei
Sprungkonflikten – Befehle des falschen Zweigs sind
in der Pipeline – werden durch diverse, teilweise
raffiniert ausgeklügelte Vorhersagemechanismen
gemindert. Einfachere Lösungen hängen operativen
Befehlen eine Bedingung an, sodass Sprungbefehle
z. T. vermeidbar werden (predication). Oder nach dem
Sprung angeordnete Befehle werden einfach immer
mit ausgeführt (delayed branches); dann muss der
Compiler diesen Schlitz (delay slot) geschickt füllen.
Komplexere Lösungen, die auf den angesprochenen
Vorhersagetechniken beruhen, sind die statische,
die dynamische und die adaptive Sprungvorhersage;
auch die Vorhersage des Sprungziels ist in Gebrauch.
Schließlich werden zur Lösung beider Konfliktarten Wertvorhersagetechniken benutzt sowie eine
ganz andere Art von Konfliktvermeidung, nämlich
Programmpfade unterschiedlicher, eigenständiger
Prozesse, sog. Threads, reihum auszuführen, sodass
Abhängigkeiten selbst bei „tiefen“ Pipelines gar nicht
erst auftreten können (multithreading). – Zu allen
diesen Techniken siehe [2].
5 Prozessorstrukturen
5.4.3 Beispiel zur Maschinenprogrammierung
Das Programm zur Polynomberechnung aus 5.3.3,
nun für den Dreiadressrechner formuliert, zeigt
Bild 5-8. Dabei ist die Aufgabenstellung insofern
modifiziert, als der Grad des Polynoms als fest
angenommen wird (n = 3). Dann kann die Schleife
„abgerollt“ werden, sodass Sprünge überflüssig
sind. Das ergibt eine geringere Ausführungszeit und
kommt überdies der Fließbandtechnik zugute. – Das
Programm zeigt die typische Programmierung eines
Rechners in Lade-/Speichere-Architektur, wobei für
die skalare Größe X die Vorteile dieser Architektur
mehr zum Tragen kommen als für die vektorielle
Größe A (A benötigt viel Platz!). In Wirklichkeit ist
das Programm viel länger, da die gezeigten load- und
store-Befehle Pseudobefehle sind; das sind eigentlich
sog. Makrobefehle, erst vom Assembler werden sie
in wirkliche Maschinenbefehlsfolgen transformiert
(vgl. das Programm in Bild 5-7).
Das Programm Bild 5-8 hat gegenüber dem Programm Bild 5-6 den Vorteil einer viel höheren
Geschwindigkeit, aber den Nachteil eines nur maximal zulässigen n (hier n = 3). Dieser Nachteil
lässt sich eliminieren, wenn die Erzeugung des
Maschinenprogramms dem Assembler oder dem
Compiler übertragen wird. Diesem muss somit n
vor der Programm-Übersetzung bekannt sein, d. h.
genau genommen beim Programm-Schreiben. Steht
diese Information aber erst zur Programm-Laufzeit
fest, etwa durch Berechnen oder Einlesen, so lässt
sich das Problem einer solchen Flexibilisierung
von n nur lösen, indem Programm-Ausführen und
Programm-Übersetzen verzahnt ablaufen. Das geschieht folgendermaßen: (1) Fehlt dem Prozessor
beim Ausführen Information, hier z. B., wie viele
mul/add-Paare ausgeführt werden sollen, so wird auf
Übersetzen umgeschaltet. (2) Mit der berechneten
bzw. eingelesenen Information wird beim Übersetzen
das nächste Programmstück generiert und wieder
auf Ausführen umgeschaltet. – Auf diese Weise
lassen sich auch Programmläufe untersuchen und
Laufzeitcharakteristika ermitteln, sodass entschieden
werden kann, ob das betrachtete Programmstück so,
wie es vorliegt, ausgeführt wird oder neu übersetzt
wird, bevor es weiter ausgeführt wird (profiling,
Profilbildung). Man schreibt diese Eigenschaft
Bild 5-8. Maschinenprogramm zur Polynomberechnung
mit Operanden im Speicher und im Registerblock
JIT-Compilern zu (just in time compiling, Laufzeitübersetzung). Ausführliches zu dieser Thematik
siehe [2].
5.5 Parallel-Architektur
Als Kern einer Parallel-Rechnerarchitektur dienen
Superskalar-Prozessoren. Sie arbeiten mit mehr
als einem Befehl pro Instruktion, wirken jedoch
wie Skalar-Prozessoren (mit einem einzigen Befehl
pro Instruktion, siehe 5.3 und 5.4). Als Kern einer
solchen Rechnerarchitektur dienen des Weiteren
VLIW-Prozessoren. Diese arbeiten mit mehreren
Befehlen pro Instruktion und wirken auch so (siehe
5.5.2, 5.5.3).
5.5.1 Superskalar vs. VLIW
Bezüglich der Hardware zur Parallelisierung sind
Superskalar-Prozessoren viel aufwändiger als VLIWProzessoren aufgebaut. (Bezüglich prozessorinterner
Speicher ist es umgekehrt.) In beiden Fällen müssen
die Probleme, die aufgrund der parallelen Ausführung von eigentlich sequentiell gemeinten Befehlen
entstehen, gelöst werden: entweder durch Hardware (beim Rechnerbau) oder durch Software (im
Compilerbau).
Im einfachsten Fall „injizieren“ Hardwarelösungen Leertakte (interlocks) und Softwarelösungen
59
60
Technische Informatik / Digitale Systeme
Leerbefehle (no operations) in den Befehlsstrom.
Anspruchsvollere Lösungen ordnen – wann immer
möglich – die Befehlsreihenfolge um, selbstverständlich ohne die beabsichtigte Wirkung des Programms
zu verändern. Wenn man der Vorstellung folgt – von
einem Programm in höherer Programmiersprache
ausgehend –, dass die Befehle immer streng sequentiell ausgeführt werden, so muss der Prozessor
bzw. der Compiler die im Programm enthaltenen
Parallelisierungsmöglichkeiten erkennen und umsetzen (instruction scheduling). Dabei sind die
Anforderungen bezüglich der Superskalar- und der
VLIW-Befehlsausführung unterschiedlich:
Im Superskalar-Fall werden zur Laufzeit, d. h. durch
den Prozessor, die Abhängigkeiten erkannt und Konflikte aufgelöst. Im VLIW-Fall geschieht dasselbe zur
Übersetzungszeit, also durch den Compiler. Das Ziel
ist, Interlocks bzw. No-Operations zu minimieren. Im
Superskalar-Fall verteilt die Hardware die Befehle
„dynamisch“ auf die parallelen Funktionseinheiten.
Im VLIW-Fall verteilt die Software die Befehle
„statisch“ auf die entsprechenden Positionen der Instruktionen. – Weitere Möglichkeiten, diese statische
Operationsparallelität zu erhöhen, sind das SchleifenAbrollen (siehe 5.4.3) und das Software-Pipelining
(siehe 5.5.3). Dazu sowie zur Erhöhung der sog.
dynamischen Operationsparallelität siehe [2].
5.5.2 Ein Fünfbefehlrechner (VLIW)
Als Modellrechner mit VLIW-Architektur dient
der Fünfbefehlrechner Bild 5-9 (wobei gegenüber Bild 4-22b in den Maschinenprogrammen
im Speicher nun nicht nur zwei, sondern mehrere
Befehle nebeneinander gespeichert vorliegen und
ein Multiport-Registerblock mit so vielen Ports
vorhanden ist, dass die fünf Befehle voll parallel
ausgeführt werden können). Zu sehen sind: „oben“
der Programm-Cache P’ für den Ausschnitt eines
Maschinenprogramms, der PC, das IR und „unten“
der 8-Port-Registerblock D” für einen kleineren
Ausschnitt der Daten und ein 2-Port-Daten-Cache
D’ für einen größeren Ausschnitt der Daten sowie
die beiden Integer-Units IU, i. Allg. kombiniert mit
weiteren speziellen Einheiten, wie einer FloatingPoint-Unit. Das gesamte Programm mit seinen Daten
befindet sich im Speicher P/D.
Der Rechner besitzt registerbezogene Dreiadressbefehle. Für das Füllen und Leeren der Register sind
Lade- und Speichere-Befehle nötig. In jedem Takt
werden mehrere Befehle fertiggestellt (hier bis zu
fünf ). Lade-/Speichere-Befehle benötigen jedoch
oft einen zusätzlichen Takt, wodurch Bubbles in der
Lade-/Speichere-Pipeline entstehen. Die Maschinenprogrammierung ist sehr schwierig, u. a. wegen
Berücksichtigung der Befehlsparallelität in Verbindung mit der Fließbandverarbeitung. Sie entspricht
im Schwierigkeitsgrad anspruchsvoller Mikroprogrammierung, und dementsprechend hochkomplex
ist der Compilerbau.
Hervorstechend in Bild 5-9 ist der gegenüber dem
Speicher P/D auf mehrfache Wortlänge erweiterte Programm-Cache P’ mit dem langen IR. Die
fünf Befehle sind nebeneinander angeordnet: zwei
Dreiadressbefehle für die Fließbänder mit den IUs,
zwei Lade-/Speichere-Befehle sowie ein bedingter Sprungbefehl. Die beiden Integer-Operationen
werden taktsynchron parallel ausgeführt, d. h., die
sie auslösenden Befehle werden als Doppelbefehle behandelt. Alle Befehle befinden sich, vom
Compiler parallelisiert, im Speicher P/D, und zwar
so, dass sie in der beabsichtigten Ordnung in den
Programm-Cache P geladen werden können. Die
Lade-/Speichere-Befehle ermöglichen es, Operanden
zwischen dem Registerblock und dem Speicher bzw.
dem 2-Port-Daten-Cache zu transportieren: ld lädt ein
Register mit dem Inhalt einer Speicher-/Cachezelle,
st speichert einen Registerinhalt in eine Speicher/Cachezelle.
Das Programmbeispiel. Bild 5-10 zeigt das sehr kleine Programm zur Addition von zwei Zahlen, wie es
im Programm-Cache steht, des Weiteren die Daten
im Daten-Cache. Es benötigt unter der Annahme von
1 Takt pro Cache-Zugriff 6 Takte (gefüllte Fließbänder auch für die anfänglichen Befehlsausführungen
vorausgesetzt).
5.5.3 Beispiel zur Maschinenprogrammierung
Wie bei Rechnern in Register-Architektur so auch
bei Rechnern in Parallel-Architektur: Zuerst werden
die zu verarbeitenden Größen mittels ld-Befehle in
die Register gebracht, dann in oft vielstufigen Fließ-
5 Prozessorstrukturen
Bild 5-9. Fünfbefehlrechner (Fließbandregister unvollständig gezeichnet); neben den in der Mitte hervorstechenden zwei
Integer-Einheiten IU befinden sich – nicht durch eigenständige Symbolik gekennzeichnet – die Lade-/Speichere-Einheit
und die Programm-Verzweige-Einheit. – Der Prozessor arbeitet fünffach parallel, d. h. maximal fünf Einheiten können fünf
Befehle pro Instruktion gleichzeitig ausführen
Bild 5-10. Beispielpro-
gramm Z := X + Y für
den Fünfbefehlrechner
(Sprungbefehle treten
nicht auf)
bändern direkt verarbeitet und schließlich ihre Ergebnisse mittels st-Befehle zurück in den Daten-Cache
gebracht. Die Leistungsfähigkeit gegenüber einem skalaren Rechner in Register-Architektur ist
nur dann höher, wenn Parallelverarbeitung von
der Aufgabenstellung her möglich ist. Das ist
bei der kleinen Aufgabe aus 5.5.2 nicht der Fall.
Es trifft hingegen zu, wenn die Addition zweier
Skalare aus Bild 5-10 verallgemeinert wird zur
Addition eines Skalars mit einem Vektor. Dann
lässt sich ein entsprechendes Additionsprogramm
für einen VLIW-Rechner so organisieren, dass
die drei Operationen ld, add, st für die Additionen der skalaren Größe, etwa einer Konstanten,
mit den Komponenten des Vektors in Fließbandorganisation erfolgen, wobei die Register des
Registerblocks – programmiert – dieselbe Rolle wie
die Pipeline-Register – strukturiert – einer entsprechenden Hardware spielen. Man spricht deshalb von
Software-Pipelining.
61
62
Technische Informatik / Digitale Systeme
Bild 5-11.
Maschinenprogramm für
Software-Pipelining mit Adressen und
Operanden im Speicher und im Registerblock. Register, die Operanden
enthalten, sind im Programm durch Fettdruck hervorgehoben. Die eingetragenen
Größen zeigen einen Schnappschuss
während der Ausführung der Instruktion
„loop“. Der loop-Counter lc ist anfänglich auf 100 gestellt
Bild 5-11 zeigt das entsprechende Programm, wobei
realistischerweise gegenüber dem Fünfbefehlrechner
Bild 5-9 nun die Adressen wieder in den Registern
stehen (wie bei der Registerarchitektur). In ein und
derselben Instruktion codierte und in ein und demselben Takt parallel ablaufende Befehle sind durch das
Fehlen von ; am Ende einer Programmzeile kenntlich gemacht. (Instruktionen als Ganzes sind nach wie
vor mit ; abgeschlossen.) Bild 5-11 zeigt neben dem
Programm eine schematische Darstellung, in der die
Cache-Zellen sowie die Register nicht als Blocks gezeichnet sind, sondern „verstreut“, um die Wirkung
des Pipelining besser erkennen zu können. Die hervorgehobenen Zellen und Register bilden die dreistufige Pipeline, die Einträge geben eine ausgewählte,
bestimmte Situation innerhalb der Schleife wieder, also bei gefüllter Pipeline. – Zum Laden der Register
mit den Anfangsdaten sowie zum anfänglichen Füllen der Pipeline sind vor der eigentlichen PipelineSchleife – es handelt sich um eine einzige Instruktion
aus vier Befehlen! – ein Vorspann und zum abschließenden Leeren der Pipeline ein Nachspann erforderlich, was im Programm durch Linien markiert ist.
Der augenscheinliche Nachteil der Speicherung vieler No-Operation-Befehle in VLIW-Programmen – in
Bild 5-10 zu sehen, in Bild 5-11 versteckt – lässt
sich durch höhere Organisationsformen bezüglich des
Instruktion-Holens vermeiden, etwa durch Anheften
der Anzahl der Befehle pro Instruktion, durch Angabe einer Endekennung für jede Instruktion oder –
wiederum – durch den Aufbau von Multiport-Caches,
nun aber zur Programmspeicherung; zu diesen Möglichkeiten siehe z. B. [2].
Die vorgestellte Aufgabe ist hinsichtlich des
Software-Pipelining auf den Fünfbefehlrechner
Bild 5-9 abgestimmt (nämlich die Addition eines
Skalars mit einem Vektor). Wird die Aufgabe weiter
verallgemeinert, nämlich auf die Addition zweier
Vektoren, so ist mit diesem Rechner kein optimales
Software-Pipelining mehr möglich. Denn dann
kann das dafür notwendige gleichzeitige Lesen nun
der zwei Vektorkomponenten und Schreiben der
dritten Komponente wegen des 2-Port-Daten-Cache
nicht mehr in ein und demselben Takt erfolgen.
Es stehen also – allgemein formuliert – nicht ausreichend Betriebsmittel zur Verfügung, was nun
zu Bubbles in der Pipeline führt; man spricht von
Ressourcen-Konflikten.
Zur konfliktfreien Ausführung der so verallgemeinerten Aufgabe genügt also der vorgestellte Fünf befehl-
5 Prozessorstrukturen
rechner nicht. Dazu wäre ein Sechsbefehlrechner
mit einem 3-Port-Daten-Cache mit 3-fach-CacheZugriff nötig, sodass Zweimal-Operanden-Lesen und
Einmal-Ergebnis-Schreiben ohne Störung parallel ablaufen könnten. Eine nun mögliche ld-ld-add-stInstruktion würde zwar drei Takte dauern, aber jeden Takt ein Ergebnis liefern. (Man erweitere die Bilder 5-9 und 5-11 in dieser Weise, sodass – ungeachtet einer Realisierung – eine zwar aufwändigere, aber
auch leistungsfähigere Parallel-Architektur entsteht.)
Konklusion. Mit den Parallel-Rechnerarchitekturen
schließt sich – abstrahiert – der Kreis der hier
in Kapitel 5 behandelten Rechner. Stellt man sich
nämlich vor, einen ersten Rechner (Ende Kapitel 5!), einen VLIW-Rechner „Wirt“, mit einem Instruktionsinterpretations- bzw. -simulationsprogramm für einen zweiten Rechner (Anfang Kapitel 5!), einen v.-Neumann-Rechner „Gast“, zu betreiben, und zwar nur zu diesem einen Zweck, so
würde sich der Programm-Cache während des ersten Programmlaufs mit dem Simulationsprogramm
füllen. Ist der Cache groß genug, sodass er das gesamte Simulationsprogramm aufnimmt, bleibt dieses
unverändert, und der Cache wird vorteilhaft durch
ein PLA ersetzt. Weiterhin werden bis auf Akkumulator und ALU die universellen durch spezielle
Funktionseinheiten ersetzt und anstelle der universellen Mehradressbefehle nur noch spezielle Codes für
ein paar Möglichkeiten an Befehlsausführungen verwendet. Auf diese Weise entsteht aus dem VLIWRechner Bild 5-9 der v.-Neumann-Rechner Bild 5-4:
in gängiger Terminologie horizontal mikroprogrammiert mit dem Mikroprogramm im PLA und darauf
zugeschnittener Register-/Logik-Struktur. Das heißt
umgekehrt, nämlich Mikroprogrammierung eine Ebene höher auf die Maschinenprogrammierung übertragen: Ein VLIW-Rechner ist horizontal programmierbar (hohe Parallelität), ein Superskalar-Rechner
ist vertikal programmierbar (geringe Parallelität) und
RISC, CISC und der v.-Neumann-Rechner sind extrem vertikal programmierbar (keine Parallelität an
Maschinenbefehlen).
Bemerkung. Leistungsangaben und -vergleiche blieben in diesem Kapitel bewusst ausgespart. Sie erscheinen wegen der völlig freien Programmierbarkeit von Universalrechnern nur sinnvoll, wenn sie
streng aufgabenbezogen sind, d. h., wenn die zu
lösende Aufgabe auf den zu vergleichenden Rechnern programmiert wird; dementsprechend gelten die
Messergebnisse dann nur für diese Aufgabenstellung.
(Einen gewissen Anhaltspunkt liefern BenchmarkAngaben.) – Wie die vorangehenden Beispiele zeigen, gibt es bei der Auslegung eines Prozessors einen
weiten Spielraum, der begrenzt ist einerseits durch
das schaltungstechnisch Machbare und andererseits
durch das programmierungstechnisch Wünschenswerte. Wie dieser Spielraum zu nutzen ist, wird durch
die Einbettung des Prozessors in das gesamte Rechensystem bestimmt, d. h. die Einbeziehung des
Prozessors in die Rechnerorganisation und die Programmierung (siehe die folgenden Kapitel).
Rechnerorganisation
Th. Flik, bearbeitet durch A. Reinefeld
Rechnerorganisation umfasst die Struktur und die
Funktion der Komponenten eines Rechnersystems
sowie die für deren Zusammenwirken erforderlichen
Verbindungsstrukturen und Kommunikationstechniken; hinzu kommt die für den Betrieb benötigte
Systemsoftware. Zentraler Teil eines Rechnersystems
ist der Prozessor, wie er in seinen prinzipiellen
Strukturen in Kapitel 5 beschrieben ist. Er bestimmt
die Informationsdarstellung, d. h. die Codierung der
Befehle und Daten für deren Speicherung, Transport
und Verarbeitung. Zusammen mit den Speicher- und
Ein-/Ausgabeeinheiten sowie den sie verbindenden
Übertragungswegen entstehen leistungsfähige Rechnersysteme. Systeme höchster Leistungsfähigkeit
sind dabei als Mehrprozessorsysteme oder als Verbund von Rechnern in Rechnernetzen ausgelegt. Die
für den Betrieb von Rechnersystemen erforderliche
Systemsoftware bezeichnet man als Betriebssystem.
Abhängig von verschiedenen Rechneranwendungen
gibt es unterschiedliche Betriebssystemarten.
63
64
Technische Informatik / Rechnerorganisation
6 Informationsdarstellung
Die Informationsverarbeitung in Rechnersystemen
geschieht durch das Ausführen von Befehlen mit
Operanden (Rechengrößen). Da Befehle selbst wieder Operanden sein können, z. B. bei der Übersetzung
(Assemblierung, Compilierung eines Programms),
bezeichnet man Befehle und Operanden zusammenfassend als Daten. Ihre Darstellung erfolgt
heute ausschließlich in binärer Form. Die kleinste
Informationseinheit ist das Bit (binary digit, Binärziffer), das zwei Werte (Zustände) annehmen
kann, die mit 0 und 1 bezeichnet werden (vgl. 3.1).
Technisch werden die beiden Werte in unterschiedlichster Weise dargestellt, z. B. durch Spannungspegel, Spannungssprünge, Kondensatorladungen,
Magnetisierungsrichtungen oder Reflexionseigenschaften von Oberflächen. Zur Codierung der Daten
werden Bits zu Codewörtern zusammengefasst, die
in ihrer Bitanzahl den für Speicherung, Transport
und Verarbeitung erforderlichen Datenformaten
entsprechen. Standardformate sind das Byte (8 Bits)
und geradzahlige Vielfache davon, wie das Halbwort
(half word, 16 Bits), das Wort (word, 32 Bits) und das
Doppelwort (double word, 64 Bits). Der Terminus
Wort bezeichnet einen Bitvektor von der Länge
der jeweiligen Verarbeitungs- und Speicherbreite
eines Rechners, hier eines 32-Bit-Rechners, er
wird aber auch unabhängig davon im Ausdruck
Codewort verwendet. Weitere Datenformate sind
das Bit, das Halbbyte (4 Bits, Nibble, Tetrade) und
das Bitfeld (mit variabler Bitanzahl – im Gegensatz
zum Bitvektor). – Zur Angabe der Anzahl von Bits
oder Codewörtern verwendet man in der Informatik in Anlehnung an die Einheitenvorsätze der
Physik die Vorsätze: Kilo K = 210 = 1024, Mega
M = 220 = 1 048 576, Giga G = 230 = 1 073 741 824,
Tera T = 240 , Peta P = 250 .
In grafischen Darstellungen von Datenformaten werden die Bits mit null beginnend von rechts nach links
nummeriert. Gleichzeitig wird ihnen die im Hinblick
auf die Darstellung von Dualzahlen zukommende
Wertigkeit zugewiesen (Bild 6-1). Das Bit ganz
rechts gilt als niedrigstwertiges Bit (least significant
bit, LSB), das Bit ganz links als höchstwertiges Bit
(most significant bit, MSB).
Bild 6-1. Datenformate. a Byte; b 32-Bit-Wort
6.1 Zeichen- und Zi−erncodes
Die rechnerexterne Informationsdarstellung erfolgt symbolisch mit den Buchstaben, Ziffern und
Sonderzeichen unseres Alphabets. Rechnerintern
werden diese Zeichen (characters) binär codiert.
Die wichtigsten hierfür eingesetzten Zeichencodes
sind der ASCII und der EBCDIC mit 7- bzw. 8-BitZeichendarstellung. Um die internationale Vielfalt an
Zeichensätzen erfassen zu können, werden größere
Codetabellen mit 16 Bits (Unicode UTF-16 [11])
und 32 Bits (Unicode UTF-32 und UCS, Universal
Character Set, ISO/IEC 10 646) verwendet. Neben
den Zeichencodes gibt es reine Zifferncodes: die Binärcodes für Dezimalziffern, den Oktalcode und den
Hexadezimalcode. – Zu Zeichen- und Zifferncodes
siehe z. B. [1].
6.1.1 ASCII
Der ASCII (American Standard Code for Information Interchange) ist ein 7-Bit-Code mit weltweiter
Verbreitung in der Rechner- und Kommunikationstechnik (Tabelle 6-1). Er erlaubt die Codierung von
128 Zeichen, und zwar von 96 Schriftzeichen und
32 Zeichen zur Steuerung von Geräten und von Datenübertragungen (Tabelle 6-2). Der ASCII ist in der
internationalen Norm ISO/IEC 646 festgelegt. Diese
behält zwölf der 96 Schriftzeichen einer sprachenspezifischen Nutzung vor, wobei nach DIN 66 003
acht zur Codierung der Umlaute, des Zeichens ß
und des Paragraphzeichens § genutzt werden (siehe
Tabelle 6-1). – Rechnerintern wird den ASCIICodewörtern wegen des Datenformats Byte ein
achtes Bit (MSB) hinzugefügt, teils mit festem
Wert, teils als Paritätsbit oder aber zur Codeerweiterung (ISO/IEC 8859). Mit unterschiedlichen
Erweiterungen werden verschiedene Sprachgruppen
berücksichtigt, so z. B. die Gruppe Europa, Amerika,
Australien (ISO/IEC 8859-1 und -15, als Latein 1
bzw. Latein 9 bezeichnet).
6 Informationsdarstellung
Tabelle 6-1. ASCII. US-amerikanische Version/deutsche Version (die mit Schrägstrich unterteilten Tabellenplätze des
ASCII sind nationalen Varianten vorbehalten)
6.1.2 EBCDIC
Der EBCDIC (Extended Binary Coded Decimal
Interchange Code) ist ein 8-Bit-Code (Tabellen 6-3
und 6-2), der vorwiegend bei Großrechnern Verwendung findet. Von den 256 Tabellenplätzen sind 64 mit
Steuerzeichen belegt. Die in Tabelle 6-3 angegebenen
Schriftzeichen entsprechen denen des ASCII, die
nicht besetzten Plätze sind bestimmten Sprachbereichen oder bestimmten Sprachen vorbehalten:
westliche Sprachen (Latein 1), östliche Sprachen
(Latein 2), Kyrillisch, Griechisch oder Arabisch.
6.1.3 Binärcodes für Dezimalzi−ern
(BCD-Codes)
BCD-Codes benutzen vier oder mehr Bits pro
Codewort zur binären Codierung von Dezimalziffern. Der gebräuchlichste ist der Dualcode mit
4-Bit-Codewörtern (Tetraden), bei dem den Bits
die Gewichte 8, 4, 2 und 1 zugeordnet sind (Dualzahlcodierung der Dezimalziffern). Man spricht
von gepackter Darstellung. Bei Dezimalziffern in
einem der Zeichencodes ASCII oder EBCDIC, bei
denen der 4-Bit-Dualcode um die höherwertigen
Bits 011 bzw. 1111 erweitert ist, spricht man von
ungepackter Darstellung. Andere BCD-Codes sind
z. B. der Exzess-3-, der Aiken-, der Gray-, der
Biquinär- und der 2-aus-5-Code (Tabelle 6-4). Da bei
diesen Codes der Vorrat der möglichen Codewörter
nicht voll ausgeschöpft wird, ist der Aufwand an
Bits zur Darstellung von Dezimalziffern (allgemein:
Zahlen) höher als bei der reinen Dualzahlcodierung. Diese Redundanz wird bei einigen Codes zur
Codesicherung genutzt.
6.1.4 Oktalcode und Hexadezimalcode
Zifferncodes gibt es auch für die Zahlensysteme zur
Basis 8 und 16, den Oktal- bzw. Hexadezimalcode
(Sedezimalcode). Sie haben für die Arithmetik nur
geringe Bedeutung und werden fast ausschließlich zur
kompakten rechnerexternen Darstellung von binär codierter Information benutzt. Bei der oktalen Darstellung werden, beginnend beim LSB, jeweils drei Bits
zusammengefasst, denen entsprechend ihrem Wert als
Dualzahl, die „Oktalziffern“ 0 bis 7 zugeordnet werden. Bei der hexadezimalen Darstellung werden jeweils vier Bits zusammengefasst und ihnen die Ziffern 0 bis 9 und A bis F (oder 0 bis f ) des Hexadezimalsystems zugeordnet. Dazu je ein Beispiel für dasselbe Muster von 16 Bits:
1 010 011 100 101 1102 = 1234568
1010 0111 0010 11102 = A72E16
65
66
Technische Informatik / Rechnerorganisation
Tabelle 6-2. Alphabetisch geordnete Zusammenfassung der
Steuerzeichen des ASCII und des EBCDIC mit ihren Bedeutungen
Zeichen
ACK
BEL
BS
BYP
CAN
CR
CSP
CUi
DCi
DEL
DLE
DS
EM
ENP
ENQ
EO
EOT
ESC
ETB
ETX
FF
FS
GE
GS
HT
IFS
IGS
INP
IR
IRS
IT
IUS
ITB
LF
MFA
NAK
NBS
NL
NUL
POC
PP
RES
RFF
RNL
RPT
RS
SA
Bedeutung
Acknowledge
Bell
Backspace
Bypass
Cancel
Carriage Return
Control Sequence Prefix
Customer Use i
Device Control i
Delete
Data Link Escape
Digit Select
End of Medium
Enable Presentation
Enquiry
Eight Ones
End of Transmission
Escape
End of Transmission Block
End of Text
Form Feed
File/Field Separator
Graphic Escape
Group Separator
Horiziontal Tabulation
Interchange File Separator
Interchange Group Separator
Inhibit Presentation
Index Return
Interchange Record Separator
Indent Tab
Interchange Unit Separator
Intermediate Transmission Block
Line Feed
Modified Field Attribute
Negative Acknowledge
Numeric Backspace
New Line
Null
Program-Operator Communication
Presentation Position
Restore
Required Form Feed
Required New Line
Repeat
Record Separator
Set Attribute
Tabelle 6-2. (Fortsetzung)
Zeichen
SBS
SEL
SFE
SI
SM
SO
SOH
SOS
SPS
SP
STX
SUB
SW
SYN
TRN
UBS
US
VT
WUS
Bedeutung
Subscript
Select
Start Field Extended
Shift In
Set Mode
Shift Out
Start of Heading
Start of Significance
Superscript
Space
Start of Text
Substitute
Switch
Synchronous Idle
Transparent
Unit Backspace
Unit Separator
Vertical Tabulation
Word Underscore
6.2 Codesicherung
Transport und Speicherung von Daten können Störungen unterliegen, die auf Übertragungswege bzw.
Speicherstellen wirken. Dadurch hervorgerufene
Änderungen von Binärwerten führen zu Fehlern
in der Informationsdarstellung. Um solche Fehler
erkennen und ggf. korrigieren zu können, muss
die Nutzinformation durch Prüfinformation ergänzt
werden (redundante Informationsdarstellung). Eine
solche Codesicherung erfolgt entweder für einzelne
Codewörter oder für Datenblöcke (Blocksicherung). Die Einzelsicherung ist kennzeichnend für
die Übertragung einzelner Zeichen, z. B. zwischen
Prozessor und einem Terminal. Die Blocksicherung
wird bei blockweiser Datenübertragung, z. B. bei
der Datenfernübertragung und in Rechnernetzen,
sowie bei blockweiser Speicherung eingesetzt. Alle
Sicherungsverfahren beruhen darauf, dass die vom
Sender erzeugte und mit übertragene Prüfinformation
mit der vom Empfänger unabhängig errechneten
Prüfinformation übereinstimmen muss. Nutzbits
und Prüfbits sind bezüglich einer Störung und der
Fehlererkennung oder -korrektur gleichrangig. – Zur
Codesicherung siehe auch [5] und z. B. [10].
6 Informationsdarstellung
Tabelle 6-3. EBCDIC
Tabelle 6-4. Binärcodes für Dezimalziffern. (Beim 2-aus-
5-Code folgt die Darstellung der Ziffer 0 nicht der Gewichtung.)
Gewichte
0
1
2
3
4
5
6
7
8
9
Dual
8421
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
Exzess-3 Aiken
2421
0011
0000
0100
0001
0101
0010
0110
0011
0111
0100
1000
1011
1001
1100
1010
1101
1011
1110
1100
1111
Gray Biquinär
543210
0000 000001
0001 000010
0011 000100
0010 001000
0110 010000
0111 100001
0101 100010
0100 100100
1100 101000
1101 110000
2-aus-5
74210
11000
00011
00101
00110
01001
01010
01100
10001
10010
10100
Einzelsicherung. Ein Maß für die Anzahl der
bei einem bestimmten Code erkennbaren bzw.
korrigierbaren Fehler in einem Codewort ist die
Hamming-Distanz h des redundanten Codes. Diese
gibt an, wie viele Stellen eines Codewortes mindestens geändert werden müssen, damit ein anderes
gültiges Codewort entsteht. Bei der einfachsten Codesicherung durch ein Paritätsbit ist h = 2, womit ein
1-Bit-Fehler erkannt, jedoch nicht korrigiert werden
kann. Der Wert des Paritätsbits wird so bestimmt,
dass die Quersumme des redundanten Codewortes
entweder gerade (even parity) oder ungerade wird
(odd parity). Die Hamming-Distanz h = 3 erreicht
man z. B. bei acht Bit Nutzinformation durch vier
zusätzliche Prüfbits in geeigneter Codierung. Dies
erlaubt es, entweder einen 2-Bit-Fehler zu erkennen
oder einen 1-Bit-Fehler zu korrigieren. Entscheidet
man sich für eine 1-Bit-Fehlerkorrektur, so ist diese
bei einem 2-Bit-Fehler schädlich.
Blocksicherung. Bei der Blocksicherung wird einem
Datenblock eine aus allen Codewörtern abgeleitete,
gemeinsame Blocksicherungsinformation hinzugefügt. Einfache Verfahren sind z. B. das Bilden von
Paritätsbits über alle Bits für jede Bitposition oder
das Bilden der Summe über alle Codewörter.
Durch Kombination der Einzelsicherung jedes Codeworts und der Blocksicherung entsteht die sog. Rechtecksicherung. Sie erlaubt das Erkennen und Korrigieren von 1-Bit-Fehlern im Schnittpunkt einer fehlerhaften Zeile und einer fehlerhaften Spalte (error
correcting code, ECC). Für einen Datenblock von
z. B. 8 Bytes benötigt man 15 Prüfbits.
Mit weniger Prüfbits kommen die Hamming-Codes
aus. Hier genügen für einen Block von 8 Bytes (z. B.
ein 64-Bit-Speicherwort) 7 Prüfbits, um 1-Bit-Fehler
zu erkennen und zu korrigieren. Mit einem achten
67
68
Technische Informatik / Rechnerorganisation
Prüfbit können zusätzlich 2-Bit-Fehler erkannt werden (single error correction, double error detection,
SECDED). Angewandt wird diese Sicherung z. B. bei
Hauptspeichern in Servern.
Ein weiteres Verfahren der Blocksicherung, das bei
der Datenübertragung und bei blockweiser Speicherung eingesetzt wird, ist die Blocksicherung mit zyklischen Codes (cyclic redundancy check, CRC, [10]).
Dabei werden die Bits der aufeinanderfolgenden Codewörter als Koeffizienten eines Polynoms betrachtet
und durch ein fest vorgegebenes, sog. Generatorpolynom dividiert. Die binären Koeffizienten des sich
ergebenden Restpolynoms bilden die Prüfinformation, meist zwei Bytes, die an den Datenblock angefügt
wird. Bei Fehlerfreiheit lässt sich der gesicherte Code ohne Rest durch das Generatorpolynom dividieren.
Tritt ein Restpolynom (Fehlerpolynom) auf, so kann
aus diesem ggf. auf die Fehlerart geschlossen werden.
Durch geeignete Wahl des Generatorpolynoms kann
das Prüfverfahren auf die Erkennung bestimmter Fehlerarten zugeschnitten werden. Die Polynomdivision
lässt sich mit geringem Hardwareaufwand durch ein
mit Exklusiv-ODER-Gattern rückgekoppeltes Schieberegister realisieren [2].
6.3 Datentypen
Dieser Abschnitt behandelt die Datentypen aus
hardwarebezogener Sicht. Eine programmierungsorientierte Darstellung gibt Kapitel 10. Der Begriff
Datentyp umfasst die Eigenschaften von Datenobjekten hinsichtlich ihres Datenformats und ihrer
inhaltlichen Bedeutung (Interpretation). Elementare
Datenformate, d. h. Formate, die nur eine Rechengröße umfassen, sind die Standardformate Byte,
Halbwort, Wort, Doppelwort, die als Speicher- und
Transporteinheiten benutzt werden. Hinzu kommen
die Zusatzformate Bit und Bitfeld. Bitoperanden werden im Prozessor als Elemente der Standardformate
adressiert. Bitfelder werden zur Verarbeitung in den
Registerspeicher des Prozessors geladen, dort rechtsbündig gespeichert und um die zum Standardformat
fehlenden höherwertigen Bits ergänzt. Abhängig von
der Transportoperation sind dies 0-Bits (zero extension) oder Kopien des höchstwertigen Operandenbits
(sign extension).
Tabelle 6-5. Elementare Datentypen
Datentyp
Zustandsgröße
Bitvektor
ganze Zahl
Gleitpunktzahl
Datenformate
Bit
Standardformate, Bitfeld
Standardformate, Bitfeld
Wort, Doppelwort
Die Interpretation der in diesen Datenformaten enthaltenen Bits erfolgt durch die logischen und arithmetischen Operationen des Prozessors (siehe 5.2.2). Die
zu einem bestimmten Datentyp gehörenden Operationen interpretieren die Bits in gleicher Weise, z. B. die
arithmetischen Befehle für Operanden einer bestimmten Zahlendarstellung. Aufgrund der elementaren Datenformate spricht man von elementaren Datentypen;
Tabelle 6-5 zeigt deren wichtigste Vertreter, nach denen dieser Abschnitt gegliedert ist.
Fasst man Rechengrößen zu komplexeren Datenobjekten zusammen, so erhält man Datenstrukturen.
Sie sind vor allem in den höheren Programmiersprachen von Bedeutung (siehe 10). Häufig vorkommende
Strukturen, wie Stack und Feld, werden hinsichtlich
des Zugriffs durch den Prozessor unterstützt, z. B.
durch ein Stackpointerregister und dazu passende
Maschinenbefehle oder Adressierungsarten. Sind für
die Interpretation einer solchen Datenstruktur spezielle Maschinenbefehle vorhanden, so spricht man
– aus der Sicht der Rechnerhardware – von einem
höheren Datentyp. Dies ist z. B. bei den sog. Vektorrechnern der Fall, deren Datentypen in diesem
Abschnitt mitbetrachtet werden.
6.3.1 Zustandsgröße
Der Datentyp Zustandsgröße basiert auf dem Datenformat Bit mit den zwei Werten 0 und 1. Typische
bitverarbeitende Operationen sind: Testen, Testen und
Setzen, Testen und Rücksetzen, Testen und Invertieren. Die drei letztgenannten Operationen werden atomar, d. h. unteilbar ausgeführt. Sie dienen in Betriebssystemen zur Realisierung von Semaphoren und kritischen Programmabschnitten (siehe 8.3.2).
Das Testergebnis wird für Verzweigungen ausgewertet. Beispiel: Test von Bit 5 des Registers r2. Wenn
Bit 5 = 1, dann Sprung zur Adresse, die durch Additi-
6 Informationsdarstellung
on des Displacements d16 (2-Komplement-Zahl) zum
Befehlszähler entsteht (Branch on Bit Set).
bb1 5,r2,d16
6.3.2 Bitvektor
Ein Bitvektor besteht aus einer Aneinanderreihung
einzelner Bits in z. B. einem der Standarddatenformate. Die auf diesen Datentyp anwendbaren Operationen umfassen die Boole’schen Operationen AND, OR
und XOR sowie NOT. Beispiel: Ausblenden („maskieren“) der Bits 0 bis 3 eines im Register r1 stehenden ASCII-Zeichens (Ziffer 5) mittels der Maske
0x0f und Speichern des Ergebnisses in r2. Das Präfix
0x steht als programmiersprachliche Kennzeichnung
hexadezimaler Angaben (6.4.1).
and.b r2,r1,0x0f
Quelle r1:
Maske 0x0f:
00110101
00001111
Ziel r2:
00000101
6.3.3 Ganze Zahl
Beim Datentyp ganze Zahl unterscheidet man die
vorzeichenlose Zahl (unsigned binary number,
Dualzahl) und die vorzeichenbehaftete Zahl in
2-Komplement-Darstellung (signed binary number,
integer, 2-Komplement-Zahl), dargestellt in den
Standardformaten. Die wichtigsten Operationen
sind die vier Grundrechenoperationen Addition,
Subtraktion, Multiplikation und Division sowie die
Vorzeichenumkehr.
Der Zahlenwert einer n-stelligen vorzeichenlosen
Zahl ZU mit den binären Ziffern ai ist
ZU =
n−1
ai 2i .
i=0
Bei einer n-stelligen 2-Komplement-Zahl ZS ergibt er
sich zu
ZS = −an−1 2n−1 +
n−2
ai 2i ,
i=0
wobei das höchstwertige Bit an−1 als Vorzeichenbit
interpretiert wird. Bei positivem Vorzeichen (an−1 = 0)
ist der Zahlenwert gleich dem der vorzeichenlosen
Zahl gleicher Codierung, bei negativem Vorzeichen
(an−1 = 1) ist er gleich der vorzeichenlosen Zahl gleicher Codierung, jedoch um die Größe des halben Wertebereichs (2n−1 ) in den negativen Zahlenraum verschoben (Tabellen 6-6 und 6-7).
Die Befehle für die Addition und die Subtraktion sind
von den beiden Zahlendarstellungen unabhängig. Die
ALU erzeugt jedoch Bedingungsbits (siehe 3.3.1)
zur Anzeige von Bereichsüberschreitungen, die
eine nachträgliche Interpretation zulassen: für die
vorzeichenlosen Zahlen zeigt das Carry-Bit C
(Übertragsbit) eine Bereichsüberschreitung an, für
vorzeichenbehaftete Zahlen das Overflow-Bit V
(Überlaufbit). Ausgewertet werden diese Bits z. B.
bei Programmverzweigungen (siehe 5.2.2). Spezielle
Additions- und Subtraktionsbefehle beziehen das
Übertragsbit C in die Operationen mit ein, sodass
Zahlen, deren Stellenanzahl die Standardformate
überschreitet, in mehreren Schritten addiert bzw.
subtrahiert werden können.
Die Multiplikation führt bei einfacher Operandenbreite von Multiplikand und Multiplikator
(meist gleich der Verarbeitungsbreite des ProzesTabelle 6-6. Darstellung ganzer Zahlen ZU (vorzeichenlos)
und ZS (vorzeichenbehaftet) im Datenformat Byte
Binärcode
00000000
00000001
00000010
..
.
01111111
10000000
10000001
..
.
11111111
ZU
0
1
2
..
.
127
128
129
..
.
255
ZS
0
1
2
..
.
127
−128
−127
..
.
−1
Tabelle 6-7. Wertebereich für ganze Zahlen ZU (vorzeichenlos) und ZS (vorzeichenbehaftet) bei der Darstellung
mit n Bits
n
8
16
n
ZU
0 bis 255
0 bis 65 535
0 bis 2n − 1
ZS
−128 bis +127
−32 768 bis +32 767
−2n−1 bis +2n−1 − 1
69
70
Technische Informatik / Rechnerorganisation
sors) auf ein Produkt doppelter Breite, wahlweise
auch einfacher Breite. Bei der Division hat der
Dividend doppelte (wahlweise einfache) Breite; Divisor, Quotient und Rest haben einfache
Breite. Ein Divisor mit dem Wert null führt
zum Befehlsabbruch (zero-divide trap, 8.2.2).
– Zur Arithmetik mit ganzen Zahlen siehe
z. B. [6, 8, 9].
6.3.4 Gleitpunktzahl
Zahlendarstellung. Für das Rechnen mit reellen
(eigentlich: rationalen) Zahlen hat sich in der Rechnertechnik die halblogarithmische Zahlendarstellung
mit Vorzeichen, Mantisse und Exponent, d. h. die der
Gleitpunktzahlen ( floating-point numbers) durchgesetzt. Gegenüber den ganzen Zahlen erreicht man mit
ihnen einen wesentlich größeren Wertebereich bei
allerdings geringerer Genauigkeit. Die Gleitpunktdarstellung ist in IEEE 754–1985 bzw. DIN/IEC
60 559 festgelegt; nach ihr ergibt sich der Wert ZFP
einer Zahl zu
ZFP = (−1)s(1. f )2e−bias .
Gleitpunktzahlen werden in zwei Grundformaten codiert (Bild 6-2): einfach lang mit 32 Bits (single precision), doppelt lang mit 64 Bits (double precision).
Rechenwerksintern kann überdies in je einem erweiterten Format gearbeitet werden, um höhere Rechengenauigkeiten zu erzielen (meist wird einheitlich ein
80-Bit-Format benutzt). Der Übergang auf die Grundformate erfolgt dann durch Runden.
s ist das Vorzeichen (sign) der Gleitpunktzahl (0 positiv, 1 negativ). Die Mantisse 1. f (significand) wird
Bild 6-2. Grundformate für Gleitpunktzahlen. a Einfach
lang (32 Bits); b doppelt lang (64 Bits)
als gemischte Zahl in normalisierter Form angegeben.
Dazu wird sie, bei entsprechendem Vermindern des
Exponenten, so weit nach links verschoben, bis sie
eine führende Eins aufweist. Der „Dualpunkt“ steht
immer rechts von dieser Eins. In den Grundformaten
gespeichert wird lediglich der Bruch f ( fraction); die
führende Eins wird von der Gleitpunktrecheneinheit
( floating-point unit, FPU) automatisch hinzugefügt.
Die Mantisse hat den Wertebereich 1.0 ≤ 1. f < 2.0.
Die Information des vorzeichenbehafteten Exponenten E wird im Datenformat durch den transformierten Exponenten e (biased exponent) dargestellt. Dazu wird zu E in seiner 2-Komplement-Darstellung eine Konstante (bias = 127 bzw. 1023) addiert, sodass
sich eine positive (vorzeichenlose) Zahl ergibt: e =
E + bias. Dadurch kann das Vergleichen von Gleitpunktzahlen, genauer, von deren Beträgen, als Ganzzahloperation realisiert werden. – Tabelle 6-8 zeigt
für beide Grundformate die Zahlenbereiche und die
Genauigkeiten bei normalisierter Darstellung (siehe
auch 10.2).
Der kleinste und der größte Exponentwert (e) sind
zur Darstellung von null und unnormalisierten Zahlen bzw. von unendlich und Nichtzahlen (not a numbers, NaNs) reserviert (Bild 6-3). Unnormalisierte
Zahlen haben mit der Mantisse der Form 0. f eine
Tabelle 6-8. Zahlenbereiche und Genauigkeiten für Gleitpunktzahlen einfacher und doppelter Länge
Datenformat
Mantisse
größter relativer Fehler
Genauigkeit
transformierter Exponent e
Bias
Bereich für E
kleinste positive Zahl
größte positive Zahl
einfache Länge
32 Bits
24 Bits
2−24
≈ 7 Dezimalstellen
8 Bits
127
−126 bis 127
2−126 ≈ 1, 2 · 10−38
(2 − 2−23 ) 2127 ≈ 3, 4 · 1038
doppelte Länge
64 Bits
53 Bits
2−53
≈ 16 Dezimalstellen
11 Bits
1023
−1022 bis 1023
2−1022 ≈ 2, 2 · 10−308
(2 − 2−52 ) 21023 ≈ 1, 8 · 10308
6 Informationsdarstellung
geringere Genauigkeit als normalisierte Zahlen (Bereichsunterschreitung); sie werden mit e = 1 interpretiert (Tabelle 6-9). Nichtzahlen dienen u. a. zur
Kennzeichnung nichtinitialisierter Variablen und zur
Übermittlung von Diagnoseinformation, wie sie z. B.
während einer Berechnungsfolge aufgrund ungültiger
oder nicht verfügbarer Operanden erzeugt wird.
ein Runden der Werte beim Anpassen an die
Grundformate. Dabei sollen exakte Ergebnisse
arithmetischer Operationen erhalten bleiben (z. B.
bei der Multiplikation mit 1). Die bestmögliche
Behandlung von Rundungsfehlern geschieht durch
das sog. korrekte Runden. Dabei wird der Wert gleich
dem nächstgelegenen Wert im Zielformat gesetzt,
im Zweifelsfall gleich dem Wert mit geradzahliger
Endziffer. Hingegen wird beim Aufrunden der Wert
in Richtung plus unendlich, beim Abrunden in Richtung minus unendlich gerundet. Durch den Einsatz
beider Verfahren lassen sich Resultate mittels zweier
Schranken darstellen, innerhalb deren der korrekte
Wert liegt (Intervallarithmetik). Beim Runden gegen
null werden die das Grundformat überschreitenden
Bitpositionen abgeschnitten.
Operationen. Die Norm sieht als arithmetische
6.3.5 Vektor
Bild 6-3. Darstellung von Gleitpunktzahlen. a Null; b unnormalisiert; c normalisiert; d unendlich; e Nichtzahl
Operationen die Addition, die Subtraktion, die
Multiplikation, die Division, die Restbildung, den
Vergleich und das Radizieren vor. Hinzu kommen
Konvertierungsoperationen zwischen den Gleitpunktformaten und solche zwischen Gleitpunktzahlen und
2-Komplement-Zahlen sowie wenigstens einer Darstellung für Dezimalzahlen (BCD-Strings). Heutige
Gleitpunktrecheneinheiten unterstützen ferner u. a.
trigonometrische und logarithmische Operationen.
– Zur Arithmetik mit Gleitpunktzahlen siehe z. B. [3,
4, 8, 9].
Runden. Die rechenwerksinterne Verarbeitung in den
erweiterten Formaten erfordert, sofern die überzähligen Stellen des Bruches f relevante Werte aufweisen,
Tabelle 6-9. Beispiele zur Codierung von Gleitpunktzahlen
einfacher Länge. a Null; b unnormalisiert; c normalisiert;
d unendlich; e Nichtzahlen
a
b
c
d
e
s
0/1
0/1
0/1
0/1
0/1
0/1
0/1
0/1
e
00000000
00000000
00000000
00000001
11111110
11111111
11111111
11111111
f
00. . . 00
00. . . 01
11. . . 11
00. . . 00
11. . . 11
00. . . 00
00. . . 01
11. . . 11
=
=
=
=
=
=
=
=
Wert
±0
±0.00 . . . 01·2−126
±0.11 . . . 11·2−126
±1.00 . . . 00·2−126
±1.11 . . . 11·2+127
±∞
Nichtzahl
Nichtzahl
Der Begriff Vektor steht in der Informatik für einen
höheren Datentyp, der auf der Datenstruktur Feld
mit Datenobjekten eines einheitlichen elementaren
Datentyps basiert. Vektoren werden durch sog. Vektorbefehle elementweise verarbeitet, d. h., ein Befehl
löst mehrere Elementaroperationen aus. Implementiert werden diese Datentypen auf Vektorrechnern
(siehe 7.4.1). Die wichtigsten Vektordatentypen sind:
Vektor aus Bitvektoren mit den Elementaroperationen
AND, OR und XOR, Vektor aus ganzen Zahlen mit
den Elementaroperationen Addition und Subtraktion
und Vektor aus Gleitpunktzahlen mit den Elementaroperationen Addition, Subtraktion, Multiplikation,
Reziprokwertbildung, Normalisieren sowie weiteren,
speziellen Operationen.
6.4 Maschinenund Assemblerprogrammierung
Die Informationsverarbeitung in einem Rechner
erfolgt durch ein Programm, das als Folge von
Maschinenbefehlen im Hauptspeicher steht und vom
Prozessor Befehl für Befehl gelesen und ausgeführt
wird. Die Befehle sind, wie Texte und Zahlen, binär
codiert. Bei der Programmierung des Rechners
wird zur leichteren Handhabung eine symbolische
Schreibweise angewandt. In der hardwarenächsten
Ebene, der Assemblerebene, entspricht dabei ein
71
72
Technische Informatik / Rechnerorganisation
symbolischer Befehl einem Maschinenbefehl. Die
symbolische Schreibweise wird Assemblersprache
genannt. Sie ist durch den Befehlssatz des Prozessors
geprägt, jedoch in ihrer Symbolik und Befehlsdarstellung (Notation, Syntax) von der Hardware unabhängig. Die Umsetzung eines in einer Assemblersprache
geschriebenen Programms (Assemblerprogramm,
Assemblercode) in ein vom Prozessor ausführbares
Programm (Maschinenprogramm, Maschinencode) übernimmt ein Übersetzungsprogramm, der
Assembler (siehe z. B. [7]). Anweisungen an den
Assembler, wie z. B. das explizite Zuordnen von
symbolischen zu numerischen Adressen, erfolgen
durch Assembleranweisungen (Direktiven), die wie
die Maschinenbefehle in das Programm eingefügt
werden. Sie haben mit wenigen Ausnahmen keine
codeerzeugende Wirkung.
6.4.1 Assemblerschreibweise
Assemblerprogramm. Bild 6-4 zeigt links den
grundsätzlichen Aufbau eines Assemblerprogramms
am Beispiel der Polynomauswertung, wie sie für die
Akkumulator-Architektur in 5.3 beschrieben ist:
y = a 3 x3 + a 2 x2 + a 1 x1 + a 0
= ((a3 x + a2 )x + a1 )x + a0
Jede der Programmzeilen enthält einen Befehl mit
den Bestandteilen Marke (optional), Operation
und Adresse, die durch Leerzeichen (spaces) oder
Tabulatoren voneinander getrennt sind. Operationsund Adressteil beschreiben dabei den eigentlichen
Maschinenbefehl. Mit der Marke (label) kann man
einen Befehl als Einsprungstelle für Programmverzweigungen kennzeichnen. Der Adressteil ist hier
für einen Einadressrechner ausgelegt, insofern ist
hier nur ein einziger Operand angegeben. Bei einem
Dreiadressrechner wären es drei Operanden, z. B.
drei durch Kommata getrennte Registeradressen.
Daran anschließend kann, mit z. B. einem Semikolon
beginnend, die Zeile kommentiert werden. Dieser
Kommentar unterstützt die Programmdokumentation
und hat keine Wirkung auf den Assembliervorgang.
Die Assembleranweisungen des Programms mit den
hier gewählten Abkürzungen ORG, DC, DS, EQU
und END unterliegen der gleichen Formatierung,
wobei jedoch ihre Marken- und Adressteile in individueller Weise genutzt werden. – Bild 6-4 zeigt rechts
das gleiche Programm, jedoch in der höheren, aber
hardwarenahen Programmiersprache C.
Symbole, Zahlen und Ausdrücke. Als Operationssymbole werden üblicherweise leicht merkbare Abkürzungen (mnemonics) verwendet. Sie können um
Bild 6-4. Polynomauswer-
tung, links als Assemblerprogramm für einen Einadressrechner (angelehnt an Bild 5-6),
rechts als C-Programm. Der
Befehl TRAP bewirkt eine Programmunterbrechung und führt
in das Betriebssystem zurück
(8.2.2)
6 Informationsdarstellung
ein Suffix zur Bezeichnung des Datenformats erweitert sein, z. B. der Maschinenbefehl „Addiere wortweise“ ADD.W oder die Assembleranweisung „Definiere Konstante als Byte“ DC.B. Mnemonisch vorgegeben sind auch die Bezeichner der allgemeinen
Register des Prozessors, z. B. R0, R1, ... sowie SP
für das Stackpointerregister und SR für das Statusregister. – Adresssymbole im Markenfeld sind weitgehend frei wählbar, unterliegen jedoch Vorschriften,
wie: das erste Zeichen muss ein Buchstabe (Alphazeichen) sein, gewisse Sonderzeichen, z. B. das Leerzeichen, dürfen nicht verwendet werden. Gültige Symbole sind z. B. LOOP und VAR_1. Marken vor Maschinenbefehlen werden meist mit „:“ abgeschlossen,
vor Assembleranweisungen nicht. – Je nach Assembler wird Groß- oder Kleinschreibung verwendet, oder
es ist beides zugelassen.
Zahlen sind dezimal (in Bild 6-4 keine Kennung),
hexadezimal (in Bild 6-4 mit dem Präfix 0x, ggf. $)
und oktal (Präfix @) darstellbar, z. B. 5, –2, 0xFFC0,
@701. Zeichen und Zeichenketten (Textoperanden)
werden üblicherweise durch einschließende Hochkommas gekennzeichnet, z. B. ‘TEXT NR. 1‘. Sie
werden vom Assembler im ASCII oder EBCDIC
dargestellt. Bitvektoren werden in binärer Schreibweise (z. B. Präfix %) oder in Hexadezimal- oder
Oktalschreibweise dargestellt.
Adresssymbole, Zahlen, Bitvektoren und Textoperanden im Adressteil können durch arithmetische und logische Operatoren zu Ausdrücken verknüpft werden,
die vom Assembler ausgewertet werden. Sie dienen
zur Darstellung von Konstanten und Speicheradressen. Beispiel: Der Wert von IO+2 in Bild 6-4 ergibt
sich aus dem vom Assembler ermittelten Wert von IO,
erhöht um 2.
6.4.2 Assembleranweisungen
Die Assembleranweisungen in Bild 6-4 haben folgende Wirkungen: ORG (origin) gibt mit 0x1000 die
Anfangsadresse der nachfolgenden Speicherbelegung
an. Diese erfolgt hier für einen Speicher mit byteweiser Addressierung und einer Wortbreite von 16 Bit.
Die EQU-Anweisung (equate) weist dem Symbol IO
die Konstante 0xFFC0 zu. Dies hat keinen Einfluss
auf die Speicherbelegung, sondern nur textersetzende Wirkung. Im vorliegenden Programm bezeichnet
die Konstante die Adresse eines Geräteregisters, über
das der Wert X im Wortformat eingelesen wird. Der
um 2 erhöhte Wert IO+2 bezeichnet ein zweites Geräteregister, an das zum Programmende der Ergebniswert P ausgegeben wird. Die Berechnung des Ausdrucks IO+2 führt der Assembler während der Übersetzung des Programms durch. EQU dient grundsätzlich der Übersichtlichkeit und erleichtert eine nachträgliche Änderung eines solchen Wertes (wie #define
in C).
Die erste DC-Anweisung (define constant) erzeugt im
Speicher eine Konstante im Wortformat (16 Bit) mit
dem Wert 3 und der symbolischen Adresse N und
weist N die durch ORG vorgegebene erste numerische Adresse 0x1000 zu. Die zweite DC-Anweisung
erzeugt ein Speicherfeld mit vier Konstanten im Wortformat und weist dem Symbol ARRAY die nächste
freie Adresse 0x1002 als Feldanfangsadresse zu. Mit
DS (define storage) wird entsprechend der Angabe
im Adressteil und abhängig vom Suffix ein Wortspeicherplatz für die Variable X reserviert, ohne ihn zu
initialisieren. Das Symbol X erhält als Wertzuweisung den um die acht Byteadressen (vier Wörter)
des Feldes erhöhten Wert 0x100A. Die zweite DSAnweisung reserviert in gleicher Weise das Speicherwort mit der Adresse 0x100C für die Variable P.
Die Adressvergabe für die Maschinenbefehle beginnt
im Anschluss an die Variable P mit der Adresse
0x100E, die so den Adresswert für die Marke
POLY bildet. Durch Weiterzählen dieser Adresse, entsprechend den von den Befehlen belegten
Speicherwörtern, wird auch der Marke LOOP ein
Adresswert zugeordnet. Auf diese Weise ermittelt
der Assembler in einem ersten Durchgang durch das
Assemblerprogramm die Adresswerte aller als Marken auftretenden symbolischen Adressen. In einem
zweiten Durchgang codiert er dann die Befehlszeilen
und erzeugt so das Maschinenprogramm. Das Ende
des Assemblercodes (physischer Abschluss) wird
dem Assembler mit der END-Anweisung angezeigt.
Sie gibt außerdem in ihrem Adressteil mit POLY
die Programmstartadresse (hier 0x100E) für das
Betriebssystem vor. Der logische Abschluss des Programms, d. h. der Rücksprung in das Betriebssystem
erfolgt durch einen Trap-Befehl (siehe auch 8.2.2).
– Der Maschinencode wird entweder direkt im
Speicher an der von der ORG-Anweisung angegebe-
73
74
Technische Informatik / Rechnerorganisation
nen Adresse erzeugt, oder er wird zunächst in eine
Datei geschrieben und dann später in einem eigenen
Ladevorgang dort abgelegt.
6.4.3 Makros
Assemblersprachen erlauben neben der 1-zu-1-Umformung symbolischer Maschinenbefehle auch die 1zu-n-Übersetzung sog. Makrobefehle in i. Allg. mehrere Maschinenbefehle. Diese Ausdehnung einer Zeile Assemblercode in n Zeilen Maschinencode während des Übersetzungsvorgangs wird als Makroexpansion bezeichnet. Ein solcher Makrobefehl hat dabei den gleichen Aufbau wie ein symbolischer Maschinenbefehl. Beispiel: Makrobefehl load für das Laden eines Speicheroperanden, bestehend aus der Befehlsfolge setl, setu und ld, angelehnt an das Programm in Bild 5-7 in 5.4.1. Beschrieben wird der Makrobefehl durch eine Makrodefinition, die vom Assembler (jetzt auch als Makroassembler bezeichnet)
während des Assembliervorgangs ausgewertet wird:
load
macro
setl
setu
ld
endm
/1,/2,/3
/2,/3
/2,/3
/1,/2
An den Stellen der Makroaufrufe „load“ im Assemblerprogramm, z. B.
:
load
:
r4,r1,x
erzeugt der Makroassembler die in der Makrodefinition durch die Anweisungen macro und endm (end macro) eingeschlossene Befehlsfolge und ersetzt dabei
die formalen Parameter /1, /2, /3 durch die jeweils im
Aufruf angegebenen aktuellen Parameter, hier durch
r4, r1 und x:
:
setl
setu
ld
:
r1,x
r1,x
r4,r1
Zur Steigerung der Effizienz der Codeerzeugung bedient man sich zusätzlich der bedingten Assemblierung. Sie ermöglicht es, bei der Makroexpansion in
Abhängigkeit der aktuellen Parameter unterschiedlichen Maschinencode zu erzeugen. So kann z. B. eine Programmschleife, wenn die Anzahl ihrer Durchläufe während der Assemblierung bekannt ist, davon
abhängig „abgerollt“ codiert oder als Schleife codiert
werden. – Standardmakros können in Makrobibliotheken zur Verfügung gestellt werden.
6.4.4 Unterprogramme
Ein Unterprogramm (Prozedur, subroutine) ist eine
in sich abgeschlossene Befehlsfolge mit meist eigenem lokalen Datenbereich, die an beliebigen Stellen
eines übergeordneten Programms, z. B. des Hauptprogramms oder eines Unterprogramms, wiederholt aufgerufen und ausgeführt werden kann. Nach Abarbeitung der Befehlsfolge wird das übergeordnete Programm hinter der Aufrufstelle fortgesetzt (Bild 6-5).
Beim Aufruf wird das Unterprogramm i. Allg. mit
Rechengrößen versorgt, und es gibt Ergebnisse an
das übergeordnete Programm zurück. Man bezeichnet
diese Größen (wie bei der Makrotechnik) als Parameter und den Vorgang als Parameterübergabe. Vorteile dieser Technik sind: sich wiederholende Befehlsfolgen werden (anders als bei der Makrotechnik) nur
einmal gespeichert; die Programme werden gegliedert
und sind dadurch übersichtlicher und leichter zu testen; Unterprogramme können unabhängig vom übergeordneten Programm übersetzt werden; Standardunterprogramme können in Programmbibliotheken zur
Verfügung gestellt werden.
Bild 6-5. Zweimaliges Aufrufen eines Unterprogramms;
zeitlicher Ablauf entsprechend der Nummerierung
6 Informationsdarstellung
Aufruf und Rückkehr. Der Aufruf eines Unterpro-
gramms erfolgt durch einen speziellen Sprungbefehl
(z. B. jump to subroutine, jsr) mit der Angabe der
Startadresse des Unterprogramms als Sprungziel. Vor
Neuladen des Befehlszählers wird dessen Inhalt als
Rücksprungadresse auf den durch ein Stackpointerregister sr verwalteten Stack oder in ein Pufferregister
des Prozessors (Link-Register) geschrieben. Die
Rückkehr erfolgt durch einen weiteren speziellen
Sprungbefehl (z. B. return from subroutine, rts), der
als letzter Befehl des Unterprogramms den obersten
Stackeintrag bzw. den Inhalt des Link-Registers als
Rücksprungadresse in den Befehlszähler lädt.
Parameterübergabe. Als Parameter werden beim
Aufruf entweder die Werte von Operanden (call
by value) oder deren Adressen (call by reference)
übergeben. Die Wertübergabe erfordert lokalen
Speicherplatz im Unterprogramm und ist für ein-
zelne Werte geeignet. Die Adressübergabe ist bei
zusammengesetzten Datenobjekten, z. B. Feldern,
notwendig, da für die (vielen) Werte meist nicht
genügend Speicherplatz zur Verfügung steht (z. B.
in den allgemeinen Registern). Sie erlaubt außerdem
auf einfache Weise die Parameterrückgabe (auch für
einzelne Werte).
Grundsätzlich erfolgt die Parameterübergabe durch
Zwischenspeicherung an einem Ort, der sowohl für
das Hauptprogramm als auch für das Unterprogramm
zugänglich ist. Übliche Orte sind
– der Registerspeicher des Prozessors, sofern die Anzahl der freien Register ausreicht,
– der Stack als Hauptspeicherbereich, der über das
Stackpointerregister allgemein zugänglich ist.
Bild 6-6 zeigt die Wert- und Adressübergabe im Stack
an einem Beispiel, erklärt durch die Zeilenkommentare und die in Bild 6-7 gezeigte dynamische Stackbe-
Hauptprogramm:
Bild 6-6. Parameterübergabe und Statusretten auf dem Stack, adressiert über das Stackpointerregister sp, bei einem Spei-
cherwortformat von 32 Bit. Beim Aufruf des Unterprogramms „subr“ werden an dieses der Wert „x“ (call by value) und
die Adresse „string“ (call by reference) als Eingangsparameter übergeben; „x“ erhält nach der Rückkehr in das aufrufende
Programm den Ergebniswert und wirkt somit auch als Ausgangsparameter. Die Inhalte der vom Unterprogramm benutzten
Prozessorregister r4 und r5 werden von ihm zunächst auf den Stack gerettet und später wiederhergestellt. Das aufrufende
Programm gibt zum Abschluss den Parameterbereich auf dem Stack wieder frei
75
76
Technische Informatik / Rechnerorganisation
Bild 6-7. Zeitliche Abfolge für die Belegung und Freigabe
des Stacks durch das Programm aus Bild 6-6. Die Num-
mern spiegeln die Positionen des Stackpointers nach
Ausführung der jeweiligen Programmzeile wider
legung. Zur Vereinfachung der Programmierung von
Stackzugriffen und allgemein von Feldern (arrays)
werden Erweiterungen der registerindirekten Adressierung verwendet:
– Registerindirekte Adressierung mit Prädekrement
oder mit Postinkrement: Bei -(sp) und -(ri) wird der
Registerinhalt sp bzw. ri um die Byteanzahl des im
Befehl angegeben Datenformats vor dem Speicherzugriff vermindert, bei (sp)+ und (ri)+ um diese
Byteanzahl nach dem Speicherzugriff erhöht.
– Registerindirekte Adressierung mit Displacement:
Bei d(sp) und d(ri) wird die effektive Adresse
durch Addition des Registerinhalts sp bzw. ri
und eines im Befehl stehenden Displacements d
(2-Komplement) gebildet.
Schachtelung von Aufrufen. Ein Unterprogramm
kann seinerseits Unterprogramme aufrufen und erhält
damit die Funktion eines übergeordneten Programms.
Der Aufrufmechanismus kann sich so über mehrere
Stufen erstrecken. Man bezeichnet das als Schachtelung von Unterprogrammaufrufen und unterscheidet
drei Arten:
Einfache Unterprogramme folgen dem behandelten
Schema von Aufruf und Rückkehr, wobei der für das
Retten der Rücksprungadresse benutzte Stack mit seinem LIFO-Prinzip genau der Schachtelungsstruktur
entspricht. Lokale Daten und Parameter können in gesonderten Speicherbereichen oder auf dem Stack verwaltet werden.
Rekursive Unterprogramme können aufgerufen werden, bevor ihre jeweilige Verarbeitung abgeschlossen
ist. Dies geschieht entweder direkt, wenn sich das Unterprogramm selbst aufruft, oder indirekt, wenn der
Wiederaufruf auf dem Umweg über ein oder mehrere Unterprogramme erfolgt. Bei rekursiven Unterprogrammen muss dafür gesorgt werden, dass mit jedem
Aufruf ein neuer Datenbereich bereitgestellt wird, damit der zuletzt aktuelle Bereich nicht beim erneuten
Aufruf überschrieben wird. Hierfür bietet sich wiederum der Stack an.
Reentrante (wiedereintrittsfeste) Unterprogramme
können wie rekursive Unterprogramme aufgerufen
werden, bevor sie ihre jeweilige Verarbeitung abgeschlossen haben. Bei ihnen ist jedoch der Zeitpunkt
des Wiederaufrufs nicht bekannt, so z. B. beim Aufruf
durch Interruptroutinen. Das heißt, das an beliebigen
Stellen von einem Interrupt unterbrechbare Unterprogramm muss bei jedem Wiederaufruf selbst für das
Retten seines Datenbereichs und seines Status sorgen.
Auch hier bietet sich wieder der Stack an. Diese
Technik findet z. B. bei Mehrprogrammsystemen
Einsatz (8.1.4).
7 Rechnersysteme
Rechnersysteme bestehen aus Funktionseinheiten,
die durch Übertragungswege – Busse oder Punktzu-Punkt-Verbindungen – miteinander verbunden
sind. Aktive Einheiten (master) sind in der Lage, Datentransfers auszulösen und zu steuern. Sie arbeiten
entweder programmgesteuert (Prozessoren) oder ihre
Funktion ist fest vorgegeben bzw. in eingeschränktem
Maße programmierbar (controller). Passive Einheiten
(slaves) werden von einem Master gesteuert (z. B.
Speicher und passive Schnittstelleneinheiten zur
Peripherie).
Einfache Rechnersysteme bestehen aus einem Zentralprozessor als einzigem Master, aus Speichern
und passiven Ein-/Ausgabeeinheiten als Slaves
sowie aus Übertragungswegen. Eine Erhöhung der
Leistungsfähigkeit erhält man durch weitere Master
zur Steuerung von Ein-/Ausgabevorgängen, z. B.
Direct-memory-access-Controller (DMA-Controller)
oder Ein-/Ausgabeprozessoren oder durch weitere
Prozessoren zur Unterteilung der zentralen Datenver-
7 Rechnersysteme
arbeitung. Dieser Weg führt zu parallelarbeitenden
Rechnern hoher Rechenleistung.
Darüber hinaus werden Rechner und periphere Geräte über Verbindungsnetzwerke zu Rechnernetzen verbunden. Diese erlauben es dem Benutzer, mit anderen Netzteilnehmern Daten auszutauschen und die
Rechen-, Speicher- und Ein-/Ausgabeleistung des gesamten Netzes mitzubenutzen.
7.1 Verbindungsstrukturen
Die herkömmlichen Verbindungsstrukturen zwischen
den Funktionseinheiten eines Rechners sind parallele oder serielle Busse, an die mehrere Funktionseinheiten als „Buskomponenten“ (Master und Slaves)
über mechanisch, elektrisch und funktionell spezifizierte Schnittstellen angekoppelt sind. Sie erlauben
es in einfacher Weise, Rechner hinsichtlich Art und
Anzahl der Funktionseinheiten flexibel zu konfigurieren. Sie haben allerdings den Nachteil, dass immer
nur zwei Busteilnehmer miteinander kommunizieren
können und dass die erreichbaren Bustaktfrequenzen
weit unter denen von Prozessoren liegen. Bei hohem Leitungsaufwand erzielt man ggf. unzureichende
Übertragungsraten.
Busse werden deshalb verstärkt durch serielle und
parallele Punkt-zu-Punkt-Verbindungen ersetzt,
die jeweils nur zwei Funktionseinheiten miteinander verbinden. Diese Technik, verbunden mit
differenzieller Signaldarstellung, wie sie für die
serielle Übertragung gebräuchlich ist, ermöglicht
sehr viel höhere Taktfrequenzen und damit höhere
Übertragungsraten. Ausgehend von seriellen Punktzu-Punkt-Verbindungen kann die Übertragungsrate
zwischen zwei Übertragungspartnern individuell
angepasst werden, indem ggf. mehrere serielle
Verbindungen parallel betrieben werden. Als weiterer
Vorteil gegenüber Bussen können mehrere Punktzu-Punkt-Verbindungen gleichzeitig aktiv sein, also
mehr als nur zwei Übertragungspartner gleichzeitig miteinander kommunizieren. – Vertiefend zu
Abschnitt 7.1 siehe z. B. [2].
7.1.1 Ein- und Mehrbussysteme
Einbussysteme. Bei Einbussystemen sind alle
Funktionseinheiten eines Rechners über einen zentralen Bus, den Systembus, der im Wesentlichen
die Signalleitungen des Prozessors umfasst (Prozessorbus) miteinander verbunden (Bild 7-1a). Die
Datenübertragung geschieht parallel über mehrere
Datenleitungen (paralleler Bus), z. B. im Byteformat
(8-Bit-Bus bei 8-Bit-Prozessoren) oder in geradzahligen Vielfachen von Bytes (z. B. 32-Bit-Bus bei
32-Bit-Prozessoren). Typisch ist eine solche Struktur
für die Steuerungstechnik (embedded control ), wobei
die Gesamtstruktur oft in einem einzigen Halbleiterbaustein, einem sog. Mikrocontroller untergebracht
ist.
Einbussysteme haben den Nachteil, dass immer nur
ein einziger Datentransport stattfinden kann und somit bei mehreren Mastern im Rechner (im Bild 7-1a
Prozessor und DMA-Controller, DMAC) Engpässe
auftreten können. Ferner ist der Bus durch die erforderliche Buslänge und ggf. die Vielzahl der angeschlossenen Buskomponenten mit einer großen kapazitiven Buslast in seiner Übertragungsgeschwindigkeit beschränkt, was sich insbesondere beim Hauptspeicherzugriff nachteilig auswirkt.
Mehrbussysteme mit Bridges. Bei Mehrbussys-
temen sind die Funktionseinheiten eines Rechners
entsprechend ihren unterschiedlichen Übertragungsgeschwindigkeiten und ihren Wirkungsbereichen
auf Busse unterschiedlicher Leistungsfähigkeit
verteilt, die wiederum durch Steuereinheiten miteinander verbunden sind. Bild 7-1b zeigt eine solche
Mehrbusstruktur mit Prozessorbus, Systembus und
Peripheriebus in einer bzgl. ihrer Übertragungsgeschwindigkeiten hierarchischen Anordnung. Der
Prozessorbus als sehr schneller Bus (z. B. 64 Bit
breit) verbindet die Komponenten hoher Übertragungsgeschwindigkeit, d. h. Prozessor/Cache
und Hauptspeicher. Der Systembus (z. B. PCI-Bus,
32 Bit breit, siehe 7.1.5) dient den langsameren
Komponenten, hauptsächlich den Schnittstellen und
Steuereinheiten für die Ein-/Ausgabe, weshalb er
häufig auch als Ein-/Ausgabebus bezeichnet wird.
Verbunden sind die beiden Busse über eine Überbrückungssteuereinheit, eine sog. Bridge, die einerseits die Busaktivitäten entkoppelt und andererseits
die Datenübertragung zwischen den Komponenten
der verschiedenen Busse koordiniert. Sie enthält ggf.
zusätzliche Steuereinheiten, wie DMA-Controller,
Interrupt-Controller und Bus-Arbiter.
77
78
Technische Informatik / Rechnerorganisation
Bild 7-1. Rechnersysteme. a Einbussystem mit Prozessorbus/Systembus (z. B. 16 Bit parallel); b bridge-basiertes Mehrbussystem mit Prozessorbus (z. B. 64 Bit parallel), Systembus/Ein-/Ausgabebus (z. B. 32 Bit parallel), Peripheriebus (seriell
oder 8 Bit parallel) und einem Anschluss an ein lokales Rechnernetz (seriell); c hub-basiertes Rechnersystem (PC) mit
Punkt-zu-Punkt-Verbindungen und Bussen
Der Peripheriebus ist üblicherweise als Kabelverbindung ausgeführt mit serieller oder paralleler
(8 oder 16 Bit breiter) Datenübertragung für den
Anschluss eigenständiger Geräte (rechnerintern
oder -extern, z. B. SCSI) oder Prozesskomponenten
(rechnerextern, z. B. Feldbusse). Seine Verbindung
mit dem Systembus erfolgt über eine Steuereinheit
(oft als Host-Adapter bezeichnet). In einfacherer
Ausführung reduziert sich der Peripheriebus auf eine
Punkt-zu-Punkt-Verbindung mit passiver Anbindung
an den Systembus (z. B. IDE/ATA). Moderne Peripherie“busse“ verbinden eine Vielzahl von Geräten
Punkt-zu-Punkt, entweder aneinandergereiht als
Kabel“bus“ (z. B. FireWire) oder in netzähnlicher
Struktur (z. B. USB). – Bild 7-1b zeigt darüber hinaus
die (serielle) Ankopplung des Rechners an ein lokales
Netz über eine Netzsteuereinheit (LAN-Controller,
local area network controller).
Gegebenenfalls ist in einer solchen Mehrbusstruktur
der Systembus/Ein-/Ausgabebus in zwei Busse aufgeteilt, die dann über eine weitere Bridge miteinander verbunden sind. Typisch ist dies bei älteren PCStrukturen mit PCI-Bus (Systembus) und langsame-
rem ISA-Bus (Ein-/Ausgabebus). Letzterer ist heute
nur noch in Industrieanwendungen zu finden.
Mehrbussysteme mit Hubs. Eine für PCs typische
Mehrbusstruktur zeigt Bild 7-1c. Sie weist anstelle
der Bridges sog. Hubs (zentrale Verteiler) und darüber hinaus weitere Strukturverbesserungen auf: (1.)
Der Cache kommuniziert mit dem Prozessor nicht
mehr über dessen Busschnittstelle (Frontside-Cache,
z. B. 64 Bit), sondern über eine zusätzliche, höher
getaktete Punkt-zu-Punkt-Verbindung mit ggf. breiterem Datenweg (Backside-Cache, z. B. 128 Bit). (2.)
Der Speicher-Hub vermittelt sämtliche Datenübertragungen zwischen den ihn umgebenden schnellen
Komponenten, wobei Übertragungen zwischen
unterschiedlichen Übertragungspartnern gleichzeitig
stattfinden können. Mit Ausnahme des Prozessorbusses (64 Bit), sind sämtliche Datenwege als schnelle
parallele Punkt-zu-Punkt-Verbindungen ausgelegt:
zum Hauptspeicher (z. B. 2 × 64 Bit), zur Grafikeinheit (z. B. 16 Bit), zum Systembus-Hub (z. B.
16 Bit) und zum E/A-Hub (z. B. 8 Bit). Der SpeicherHub ist für die Übertragungen mit Pufferspeichern
7 Rechnersysteme
ausgestattet, außerdem enthält er die Steuerung
für den Hauptspeicher (DRAM-Controller). Der
Systembus-Hub versorgt den schnellen Systembus 1
(z. B. PCI-X-Bus, 64 Bit breit, siehe 7.1.5). (3.)
Der E/A-Hub steuert sämtliche peripheren Busse
(z. B. USB, FireWire) und Anschlüsse (z. B. für
Festplatten, LAN) sowie den langsameren Systembus
2 (z. B.: PCI-Bus, 32 Bit breit, siehe 7.1.5). Durch die
schnelle Punkt-zu-Punkt-Verbindung zum SpeicherHub und durch Pufferspeicher im E/A-Hub können
diese Einheiten ggf. gleichzeitig mit Daten versorgt
werden. Der E/A-Hub enthält zusätzliche Funktionseinheiten, wie DMA-Controller, Interrupt-Controller,
Timer. (4.) Die peripheren Busse und Anschlüsse des
E/A-Hubs übertragen mit hohen Übertragungsraten
seriell, der Systembus 2 parallel. – Man bezeichnet
die aufeinander abgestimmten Hubs (bzw. Bridges)
eines Rechners als dessen Chipsatz.
7.1.2 Systemaufbau
Rechner für allgemeine Anwendungen werden üblicherweise als modulare Mehrkartensysteme aufgebaut, sodass man in ihrer Strukturierung flexibel ist.
Dabei werden zwei alternative Prinzipien verfolgt.
Entweder wird ein festes Grundsystem durch zusätzliche Karten über Steckverbindungen erweitert, wobei
Anzahl und Funktion der Karten in gewissem Rahmen wählbar sind, oder das System insgesamt wird
durch steckbare Karten konfiguriert, sodass auch das
Grundsystem als Karte wählbar bzw. austauschbar ist.
Erwünscht ist dabei eine möglichst große Auswahl an
Karten, was durch die Standardisierung von Systembussen und Punkt-zu-Punkt-Verbindungen gefördert
wird.
Das Prinzip des Erweiterns eines Grundsystems
findet man bevorzugt bei Arbeitsplatzrechnern (PCs,
Workstations) und Servern. Hier sind elementare Funktionseinheiten, wie Chipsatz, Taktgeber
(Clock) und das BIOS-Flash-RAM (Basic I/O
System, Treibersoftware des Betriebssystems),
fest auf einer Grundkarte (main board, mother
board) untergebracht. Weitere Komponenten, wie
Prozessor, prozessorexterner Cache, Hauptspeicher
und Grafik-Controller, sind mittels spezieller, vom
Chipsatz versorgter Stecksockel (slots) zusteckbar.
Darüber hinaus bietet der Systembus universelle
Steckplätze für quasi beliebige Zusatzkomponenten,
womit ein solches System vielfältig erweiterbar ist.
Man bezeichnet den Systembus deshalb auch als
Erweiterungsbus. Weitere elementare Komponenten,
wie Festplatte, DVD-Laufwerk, LAN, Tastatur und
Monitor, werden mittels Kabelverbindungen mit
dem Chipsatz bzw. mit dem Grafik-Controller verbunden. Externe Speicher und Ein-/Ausgabegeräte
werden mittels serieller Verbindungen oder paralleler
Verbindungen vom Chipsatz aus betrieben. Solche
Verbindungen sind entweder als Kabelbusse ausgelegt, d. h., die Geräte sind in Reihe miteinander
verbunden, oder sie werden über interne und ggf.
externe Hubs netzartig betrieben, d. h., die Geräte
sind in einer Stern- oder Baumstruktur miteinander
verbunden.
Erkennt ein Rechnersystem beim Hochfahren seines
Betriebssystems seine Komponenten (Steckkarten,
Geräte am Kabelbus und an externen Hubs) selbst
und führt es daraufhin z. B. die Vergabe von Interruptprioritäten, das Zuordnen von DMA-Kanälen und
Adressbereichen an die Komponenten selbst durch,
so spricht man von dem Prädikat „Plug and Play“;
ist das Hinzufügen von Komponenten während des
laufenden Betriebs möglich, von „Hot Plug and
Play“.
Das Prinzip des Konfigurierens eines ganzen Systems
wird bei Rechnern für den industriellen Einsatz angewandt, um in der Strukturierung eine noch höhere Flexibilität als bei Arbeitsplatzrechnern zu erreichen. Dazu werden die Funktionseinheiten (Baugruppen) grundsätzlich als Steckkarten realisiert und diese dann als Einschübe nebeneinander in z. B. einem
19 Zoll breiten Baugruppenträger (19-Zoll-Rack) mit
bis zu 20 Steckplätzen untergebracht. Zur Zusammenschaltung der Karten werden ihre Signalleitungen über Steckverbindungen auf eine Rückwandverdrahtung geführt. Diese Verdrahtung wird durch eine
Leiterplatine (backplane) realisiert. Man bezeichnet
einen solchen Bus auch als Backplane-Bus.
7.1.3 Busfunktionen
Ein Bus vermittelt die unterschiedlichen Funktionsabläufe zwischen den Busteilnehmern. Diese
Abläufe unterliegen Regeln, die als sog. Busprotokolle Bestandteil der Busspezifikation sind. Zur
79
80
Technische Informatik / Rechnerorganisation
Einhaltung dieser Regeln, in denen sich die Funktion
der Bussignale, ihr Zeitverhalten und ihr Zusammenwirken widerspiegeln, sind bei den Busteilnehmern
entsprechende Steuereinheiten erforderlich. Die
wesentlichen Funktionen sind
– die Datenübertragung zwischen Master und Slave,
– die Busarbitration (Buszuteilung) bei mehreren
Mastern,
– die Interruptpriorisierung und
– Dienstleistungen (utilities), wie Stromversorgung,
Taktversorgung (Systemtakt, Bustakt), Systeminitialisierung (Reset-Signal), Anzeige einer zu geringen Versorgungsspannung und Anzeige von Systemfehlern.
Hinzu kommen ggf. Funktionen, wie
– die Selbstidentifizierung von an den Bus angeschlossenen Komponenten (indem z. B. Steckkarten während einer Systeminitialisierung
Statusinformation liefern, Plug and Play) und
– das Testen elektronischer Bauteile von Buskomponenten zur Fehlererkennung während des Betriebs
(IEEE 1149.1: JTAG Boundary Scan).
Zur Realisierung dieser Funktionen unterscheidet
man bei einem parallelen Bus fünf Leitungsbündel:
Datenbus, Adressbus, Steuerbus, Versorgungsbus und
Testbus.
Buszyklus bei synchronem und asynchronem Bus.
Die Datenübertragung für eine Schreib- oder Leseoperation wird vom Master ausgelöst und umfasst
die Adressierung des Slave und die Synchronisation
der Übertragung. Die Regeln, nach denen ein solcher
Buszyklus abläuft, sind als Busprotokoll festgelegt.
Bei einem synchronen Bus (als der vorherrschenden
Busart) sind Master und Slave durch einen gemeinsamen Bustakt miteinander synchronisiert, d. h., die
Übertragung unterliegt einem festen Zeitraster mit
fest vorgegebenen Zeitpunkten für die Bereitstellung
und Übernahme von Adresse und Datum (Bild 7-2).
Gesteuert wird der Ablauf durch ein Adressgültigkeitssignal (Address-Strobe-Signal AS), das die
Bereitstellung der Adresse und somit den Beginn
des Zyklus anzeigt, und durch ein Read/Write-Signal
(R/W), das dem Slave die Transportrichtung vorgibt.
Bild 7-2. Buszyklus (Schreiboperation) bei einem synchronen Bus; Buszykluszeit von drei Takten einschließlich eines Wartezyklus; gegenpolige Doppellinien stehen für unterschiedliche Signalzustände bei mehreren Leitungen, die
Mittellinie zeigt den Signalzustand Tristate an (vgl. 3.3.2)
Um den Bustakt nicht an den langsamsten Busteilnehmer anpassen zu müssen, wird das Protokoll
durch ein Bereit-Signal (READY) erweitert. Dieses
ist zunächst inaktiv und wird vom Slave aktiviert, sobald er zur Datenübernahme (Schreibzyklus) bzw. zur
Datenabgabe (Lesezyklus) bereit ist. Der Busmaster
fragt dieses Signal erstmals nach der für ihn kürzestmöglichen Buszykluszeit von 2 Takten ( je ein Takt
für die Adress- und für die Datenübertragung) ab. Ist
es inaktiv, so verlängert er den Buszyklus taktweise
um Wartezyklen (wait states), bis er den Bereitzustand
vorfindet (siehe Bild 7-2). Da sich das Aktivieren und
Inaktivieren der Signale AS und READY gegenseitig
bedingen (AS → READY → AS → READY), spricht
man auch von Handshake-Synchronisation und von
Handshake-Signalen.
Bei einem asynchronen Bus gibt es keinen gemeinsamen Bustakt. Die Synchronisation erfolgt aber
auch hier durch Handshake-Signale; diese sind
jedoch getrennt getaktet oder ungetaktet. Der Master
zeigt die Gültigkeit von Adresse und Datum durch
ein Adress- und ein Datengültigkeitssignal an, und
der Slave signalisiert asynchron dazu seine Datenübernahme bzw. -bereitstellung durch Aktivieren
eines Bereit-Signals. Abhängig von diesem führt der
Master einen Buszyklus ohne oder mit Wartezyklen
durch.
Bei beiden Busarten kann das Ausbleiben des BereitSignals bei defektem oder fehlendem Slave durch
7 Rechnersysteme
einen Zeitbegrenzer (watch-dog timer) überwacht
werden, der dem Master das Überschreiten einer
Höchstzeit mittels eines Trap-Signals meldet (bus
error trap, 8.2.2), worauf dieser den Zyklus abbricht
und eine Programmunterbrechung einleitet. Diese
Möglichkeit wird insbesondere bei Steuerungssystemen genutzt.
Adressierung und Busankopplung.
Die Anwahl eines Slave innerhalb eines Buszyklus geschieht durch
Decodierung der vom Master ausgegebenen Adresse (Bild 7-3). Dabei wird genau ein Slave aktiviert
(Signal SELECT), der sich daraufhin mit seinen Datenleitungen an den Bus ankoppelt. Ausgangspunkt
für die Decodierung ist die Festlegung der Größe der
Adressräume der Slaves in Zweierpotenzen. Bei einem Slave-Adressraum der Größe 2m und einem Gesamtadressraum der Größe 2n (n ≥ m), bilden die
m niederwertigen Adressbits die Distanz (offset) im
Adressraum des Slave. Die verbleibenden n − m höherwertigen Bits dienen, wie Bild 7-3 zeigt, zu dessen
Anwahl. – Im entkoppelten Zustand sind die Datenanschlüsse des Slave hochohmig, d. h. offen (TristateTechnik, siehe 3.3.2).
Busarbitration. Befinden
sich an einem Bus
mehrere Master, so muss der Buszugriff verwaltet
werden, um Konflikte zu vermeiden. Dazu muss
jeder Master, wenn er den Bus benötigt, diesen
anfordern (bus request). Ein sog. Busarbiter (arbiter:
Schiedsrichter) entscheidet anhand der Auswertung
der Prioritäten der Master („Priorisierung“) über die
Busgewährung (bus grant). Grundsätzlich muss dabei
ein Master niedrigerer Priorität den Bus frühestens
nach Abschluss des momentanen Buszyklus abgeben.
Bild 7-3.
Adressierung und Busankoppelung
Speichereinheit bei einem synchronen Bus
einer
– Busse, die die Busarbitration ermöglichen, bezeichnet man als multimasterfähig.
Bei zentraler Priorisierung ist jedem Master eine
Priorität in Form einer Request- und einer GrantLeitung als eigenes Leitungspaar zum Busarbiter
zugewiesen (Bild 7-4a). Bei dezentraler Priorisierung werden sämtliche Anforderungssignale auf einer
gemeinsamen Leitung zusammengefasst und dem
Busarbiter zugeführt (Bild 7-4b). Dieser schaltet bei
Freiwerden des Busses das Grant-Signal auf eine sog.
Daisy-chain-Leitung, die die Anforderer miteinander
verkettet. Die Priorisierung besteht darin, dass ein
Anforderer seinen Daisy-chain-Ausgang blockiert
und den Bus übernimmt, sobald das Grant-Signal
an seinem Daisy-chain-Eingang anliegt. Master
hingegen, die keine Anforderung haben, schalten das
Daisy-chain-Signal durch. So erhält der Anforderer,
der dem Busarbiter in der Kette am nächsten ist, die
höchste Priorität; die nachfolgenden Master haben
entsprechend abnehmende Prioritäten.
Die dezentrale Lösung hat den Vorteil einer geringeren Leitungsanzahl, aber den Nachteil, dass die
Buspriorität eines Masters durch seine Position in der
Daisy-Chain festgelegt ist (bei einem Erweiterungsbus durch den verwendeten Stecksockel). Bei zentraler Priorisierung können dagegen auf einfache Weise unterschiedliche Priorisierungsstrategien realisiert
werden, z. B. nach jeder Buszuteilung automatisch
rotierende Prioritäten (Gleichverteilung, faire Zuteilung) oder programmierte Prioritäten.
Interruptpriorisierung. Interruptsignale werden von
Buskomponenten mit Slave-Funktion, z. B. passiven
Bild 7-4. Busarbitration. a Zentrale Priorisierung im Arbiter; b dezentrale Priorisierung im Arbiter und in der DaisyChain
81
82
Technische Informatik / Rechnerorganisation
Ein-/Ausgabeeinheiten, aber auch von solchen mit
Master-Funktion, z. B. DMA-Controllern, erzeugt.
Wie bei der Busarbitration müssen die Anforderungen priorisiert werden. Dies geschieht zum
einen prozessorintern, sofern der Prozessor codierte
Interruptanforderungen zulässt (z. B. sieben Prioritätsebenen bei drei Interrupteingängen und einem
vorgeschalteten Prioritätencodierer), zum andern
prozessorextern, wenn es nur einen Interrupteingang
gibt oder wenn bei codierten Anforderungen einzelne Interruptebenen des weiteren nach Prioritäten
unterteilt werden müssen (siehe 8.2.2).
Bei zentraler Priorisierung werden die Interruptsignale über eigene Interrupt-Request-Leitungen einem
Interrupt-Controller zugeführt (in Bild 7-5a für einen
Prozessor mit nur einem Interrupteingang gezeigt).
Dieser priorisiert sie und unterbricht den Prozessor
(u. U. in der Bearbeitung einer Interruptroutine
niedrigerer Priorität), indem er ihm die Anforderung
durch ein Interrupt-Request-Signal (IREQ) signalisiert. Bei einem maskierbaren Interrupt entscheidet
der Prozessor anhand seiner Interruptmaske, ob er der
Anforderung stattgibt. Wenn ja, quittiert er die Unterbrechung durch sein Interrupt-Acknowledge-Signal
(IACK) und übernimmt in einem damit aktivierten
Lesezyklus (Interrupt-Acknowledge-Zyklus) die vom
Interrupt-Controller der Anforderung zugeordnete
Vektornummer (zur Identifikation,siehe 8.2.3). In
einem Mehrmastersystem muss der Prozessor dazu
den Bus anfordern (Busarbitration).
Bei dezentraler Priorisierung werden die Interruptsignale auf einer einzigen Interrupt-Request-Leitung
(IREQ) zusammengefasst dem Interrupteingang des
Prozessors zugeführt, der die Unterbrechungsgewährung über seine Interrupt-Acknowledge-Leitung
(IACK) allen Interruptquellen mitteilt (Bild 7-5b).
Die Priorisierung und somit die Anwahl des Anforderers höchster Priorität geschieht durch eine
Daisy-chain-Leitung, deren Anfang mit dem Signalzustand „aktiv“ belegt ist. Die erste Quelle in der
„Kette“ hat die höchste Priorität; die daran anschließenden Quellen haben abnehmende Prioritäten. Diese
Konfiguration ermöglicht ein Interruptprotokoll, das
es einem Anforderer erlaubt, eine Interruptroutine
niedrigerer Priorität zu unterbrechen (sofern die
Interruptmaske des Prozessors zuvor explizit zurückgesetzt wurde; siehe auch [2]). Der Anforderer
gibt sich danach in dem durch das IACK-Signal
ausgelösten Lesezyklus durch seine Vektornummer
zu erkennen.
Bei den (seriellen) Punkt-zu-Punkt-Verbindungen mit
der für sie typischen paketorientierten Übertragung
gibt es üblicherweise keine Steuersignale mehr, so
auch keine Interruptsignale. Hier erfolgt die Signalisierung auf den Datenleitungen durch spezielle Anforderungspakete.
7.1.4 Busmerkmale
Datenbus- und Adressbusbreite.
Bei Prozessorbussen sind Daten- und Adressbus in ihrer Leitungsanzahl an die Verarbeitungsbreite bzw. die Adresslänge
des Prozessors angepasst. Bei einfacheren Prozessoren in eingebetteten Systemen umfasst der Datenbus
8 oder 16 Bit mit typischen Werten für den Adressbus von 16 oder 24 Bit (Adressräume von 64 Kbyte
bzw. 16 Mbyte). Leistungsfähigere Prozessoren haben Datenbusbreiten von 32 oder 64 Bit mit Adressbusbreiten von z. B. 32 Bit (Adressraum von mehreren Tbyte). Systembusse gibt es mit denselben Busbreiten. Diese müssen in einem Mehrbussystem nicht
mit denen des Prozessorbusses übereinstimmen (siehe
Funktion von Bridges und Hubs, 7.1.1, Bild 7-1).
Übertragungsrate und Bustaktfrequenz.
Bild 7-5. Interruptpriorisierung, a zentral im InterruptController, b dezentral in der Daisy-Chain
Die maximal mögliche Übertragungsrate, die sog. Busbandbreite, wird bei parallelen Bussen (Verbindungen) in
der Einheit Gbyte/s angegeben. Sie ergibt sich als
Produkt aus Bustaktfrequenz und Datenbusbreite in
7 Rechnersysteme
Bytes, geteilt durch die Anzahl der für die Übertragung eines Datums benötigten Bustakte. Die Bustaktfrequenz ist dabei die maximale Frequenz, mit
der die Signalleitungen eines Busses arbeiten können.
Bei synchronen Bussen ist diese Frequenz durch das
für Master und Slave gemeinsame Bustaktsignal bestimmt; bei asynchronen Bussen obliegt deren Einhaltung den Buskomponenten, die die HandshakeSignale erzeugen. Da die Anzahl an Bustakten pro
Datenübertragung auch unter eins liegen kann, z. B.
bei Übertragungen mit Double- und Quad-Data-Rate,
gibt man die Leistungsfähigkeit von Bussen häufig
auch als Anzahl an Datentransfers pro Sekunde an,
z. B. als GT/s, woraus sich mittels der Datenbusbreite
wiederum die Busbandbreite ermitteln lässt. Bei seriellen Bussen (Verbindungen) wird die Übertragungsrate aufgrund der bitseriellen Übertragung in Gbit/s
angegeben. – Die Busbandbreite ist eine idealisierte
Größe; die real erreichbaren Übertragungsraten sind
meist geringer.
Bei seriellen Punkt-zu-Punkt-Verbindung wird kein
eigenes Taktsignal geführt. Hier wird der für den Datenempfang erforderliche Takt aus den Pegelübergängen des Datensignals abgeleitet. Dazu wird das Datensignal so codiert, dass die für die Taktgewinnung
erforderlichen Pegelübergänge quasi unabhängig von
der Datenbitfolge gewährleistet werden (z. B. 8b/10bCodierung, [13]).
Geteilter Bus und Multiplexbus. Die bisher zugrundegelegte Aufteilung der Busse in Datenbus
und Adressbus (split bus) erfordert einen hohen Leitungsaufwand. Dieser wird z. B. bei Bussen, die für
Mehrprozessorsysteme konzipiert sind, dazu genutzt,
den Adress- und den Datenbus den Prozessoren
getrennt, d. h. einzeln zuzuweisen. Auf diese Weise
können Adress- und Datenübertragungen mehrerer
Prozessoren überlappend ablaufen (split transactions), wodurch die Busauslastung verbessert werden
kann. Beim sog. Multiplexbus (mux bus) werden
die Daten und Adressen hingegen auf denselben
Leitungen im Zeitmultiplexbetrieb übertragen. Auf
diese Weise lässt sich (bei gleicher Leitungsanzahl,
bezogen auf den geteilten Bus) die Busbandbreite
verdoppeln. Ein solcher Adress-/Datenbus arbeitet
z. B. mit 64-Bit-Datenübertragung und 32- oder
64-Bit-Adressierung. Multiplexbusse zahlen sich
insbesondere bei Blockbuszyklen aus. Auch sie erlauben Split-Transaktionen von Prozessoren, nämlich
zeitlich verzahnt.
Buszyklusarten. Bei parallelen Bussen gibt es ne-
ben dem Buszyklus des Prozessors für das Lesen oder
Schreiben eines einzelnen Datums (single cycle, minimal 2 Takte, Bild 7-6a, vgl. 7.1.3) weitere Buszyklusarten für die zusammenhängende Übertragung
von Daten.
Beim Blockbuszyklus (burst cycle), der zum Laden
und Rückschreiben von Cache-Blöcken dient, werden vier oder acht aufeinanderfolgend gespeicherte Datenwörter übertragen, wobei der Datenübertragung der Transport der Adresse des ersten Datums
vorangestellt wird (minimal 5 Takte, 2-1-1-1-Burst,
Bild 7-6b). Aufgrund der Übertragung der Daten im
Taktabstand spricht man auch von einfacher Übertragungsrate (single data rate, SDR). Die Steuerung des
Bild 7-6. Zeitverhalten verschiedener Buszyklenarten bei
Angabe der Zeitpunkte des Anlegens einer Adresse, A,
und des Abschlusses des Datentransports, D, gemessen in
Bustaktschritten. a Einzelner Buszyklus eines Prozessors;
b Blockbuszyklus eines Cache-Controllers; c langer Blockbuszyklus eines Systembusses, gesteuert durch eine Bridge
oder einen Master am Bus; d wie c, jedoch mit doppelter
Datenrate; e DMA-Zyklus eines DMA-Controllers
83
84
Technische Informatik / Rechnerorganisation
Zyklus obliegt dem Cache-Controller, die Adressfortzählung den Speicherbausteinen.
Beim langen Blockbuszyklus (long burst cycle), den
es auf Systembussen gibt (z. B. PCI- und PCI-X-Bus)
und der durch die Bridge oder durch Master am Bus
gesteuert wird, folgt auf die Adresse des ersten Datums ein größerer Datenblock fester oder ggf. beliebiger Länge (Bild 7-6c). In einer Variante dieses Zyklus
werden beide Flanken des Bustaktes für die Übertragungssteuerung genutzt, d. h., die Übertragung erfolgt mit doppelter Übertragungsrate (double data rate, DDR, z. B. PCI-X-Bus 2.0, Bild 7-6d). Blockbuszyklen ermöglichen eine gute Leitungsnutzung.
Beim DMA-Zyklus (DMA cycle) wird ein Datenblock
beliebiger Länge übertragen, wobei bei Übertragungen auf Bussen zu jedem Datentransport auch ein
Adresstransport gehört (Bild 7-6e). Den DMAZyklus gibt es außerdem mit Double-Data-Rate,
nämlich bei Festplattenübertragungen (IDE/ATA,
SCSI); hier aber ohne explizite Adressierung. –
Bemerkung: Die Nutzung beider Taktflanken zur
Verdoppelung der Übertragungsrate findet man
nicht nur bei Bussen, sondern auch bei Punkt-zuPunkt-Verbindungen und Speicherbausteinen (z. B.
DDR-SRAM, DDR-SDRAM).
7.1.5 Zentrale Busse
und Punkt-zu-Punkt-Verbindungen
Tabelle 7-1 zeigt einige gebräuchliche zentrale Busse (Systembusse, Ein-/Ausgabebusse) und
zentrale Punkt-zu-Punkt-Verbindungen mit ihren
Übertragungsraten, ergänzt um die Anzahl an Datenleitungen, die Übertragungstaktfrequenzen und
weitere Angaben. Die Busse übertragen parallel,
mit Taktfrequenzen, die aus physikalischen Gründen
(Reflexionen, kapazitive Lasten, Übersprechen)
wesentlich unter denen von Prozessoren liegen. Die
Punkt-zu-Punkt-Verbindungen hingegen erlauben
sehr hohe Taktfrequenzen, da ihre Leitungen aufgrund der nur zwei Übertragungsteilnehmer mit
geringen Reflexionen und geringen kapazitiven
Lasten betrieben werden können. Außerdem erlauben
sie aufgrund der geringen Anzahl an Datenleitungen
die differenzielle Signaldarstellung, wodurch sich
Störungen auf den Leitungen weitgehend aufheben.
Sie erzielen so bei geringerer Leitungsanzahl sehr
viel höhere Übertragungsraten als die Busse. Darüber
hinaus sind sie hinsichtlich der Übertragungsrate
skalierbar, indem serielle Verbindungen zu mehreren
parallel betrieben werden können. – Die in der Tabel-
Tabelle 7-1. Merkmale einiger zentraler Busse (parallel) und zentraler Punkt-zu-Punkt-Verbindungen (seriell)
Bus,
DatenTaktArt
Idealisierte (Netto-)
FußPunkt-zu-Punktleitungen
freq.
Übertragungsrate
noten
Verbindung
Anzahl
MHz
Mbyte/s
parallel:
ISA
16
8,33
8,33
1
EISA
32
8,33
33
1
PCI 2.2
32
33
132
2
PCI-X 1.0
64
133
1064
2
PCI-X 2.0
64
133
DDR
2128
2
iPSB
32
10
40
2
VME
32
10
40
1
VME320
64
20
DDR
320
2
seriell:
PCI-Express 2.0
2 × 16
2500
DDR
2 × 8000
3, 4, 5
Hypertransport 3.1
2 × 16
3200
DDR
2 × 12800
3
Quick Path Interconnect
2 × 16
3200
DDR
2 × 12800
3, 5
1)
Geteilter Bus; 2) Multiplexbus; 3) Punkt-zu-Punkt-Verbindung; 4) 8b/10b-Codierung; 5) Duplexbetrieb 2 × . . . , (diff.)
steht für Leitungspaar
7 Rechnersysteme
le aufgeführten Busse und Verbindungen sind in ihren
Einsatzbereichen typisch für ältere PCs (ISA, EISA,
PCI), für heutige PCs/Workstations/Server (PCI,
PCI-X, PCI-Express, HyperTransport, Quick Path
Interconnect) und für industrielle Rechnersysteme
(iPSB, VME).
ISA, EISA. Als System- bzw. Ein-/Ausgabebus in
PCs war lange Zeit der ISA-Bus (Industry Standard
Architecture) mit einer Datenbusbreite von 16 Bit und
einer Bustaktfrequenz von 8,33 MHz vorherrschend.
Er zeichnet sich als Erweiterungsbus durch bis zu 10
Steckplätze und eine große Vielfalt kostengünstiger
Steckkarten aus. Heute ist er nur noch in industriellen
Systemen verbreitet.
Höhere Prozessortakte und damit höhere Anforderungen an die Busbandbreite führten (unter Wahrung der
Kompatibilität zum ISA-Bus) zum EISA-Bus (Extended ISA-Bus) mit einer Datenbusbreite von 32 Bit
und annähernder Wahrung der Bustaktfrequenz von
8,33 MHz. Er fand jedoch aufgrund höherer Kosten
keine weite Verbreitung.
PCI/PCI-X-Bus.
Weiter steigende Anforderungen
führten 1992 zum PCI-Bus (Peripheral Component
Interconnect Bus, [6, 8]), einem Multiplexbus mit
in der Standardausführung 32 Bit Breite (in einer
Erweiterung 64 Bit) und mit einer Bustaktfrequenz
von meist 33 aber auch 66 MHz. Als Erweiterungsbus
erlaubt er (bei 33 MHz) maximal vier Steckplätze;
für mehr Steckplätze (und feste Komponenten) ist er
kaskadierbar.
Eine zum PCI-Bus weitgehend kompatible Weiterentwicklung ist der PCI-X-Bus, der in der Revision
1.0 von 1999 [7] als 64-Bit-Multiplexbus mit 100
und 133 MHz getaktet ist, aber auch herkömmliche PCI-Komponenten mit geringerer Frequenz
akzeptiert. Die gegenüber dem PCI-Bus erhöhte
Taktfrequenz von 133 MHz wird durch Fließbandtechnik erreicht, indem die Signallaufzeiten auf dem
Bus und durch die Decodierlogik des Empfängers
auf zwei Taktschritte halber Schrittweite aufgeteilt
werden. Die höheren Frequenzen verringern die
Anzahl an Steckplätzen auf zwei bzw. einen. Eine
effiziente Busnutzung ist durch Split-Transactions
gegeben, bei denen der Slave eine Übertragung
(Lesezugriff) nach der Adressierung unterbrechen
und sie später fortsetzen kann. Zwischenzeitlich ist
der Bus anderweitig nutzbar.
PCI-Express (PCI-E, PCIe). PCI und PCI-X genügen
den hohen Anforderungen an Übertragungsraten
(z. B. 10-Gigabit-Ethernet oder InfiniBand) und
zeitabhängigen Video- und Audio-Daten (streaming)
nicht mehr. Hinzu kommt der Anspruch, Übertragungen vollduplex und darüber hinausgehend generell
mehrere Übertragungen gleichzeitig durchführen
zu können. So wurde als Nachfolger der beiden
Busse und softwarekompatibel zu diesen die serielle Punkt-zu-Punkt-Verbindung PCI-Express mit
paketorientierter Übertragung eingeführt. Sie wird
als Verbindung zwischen Bausteinen und zwischen
Karten (Grundkarte, Steckkarten) eingesetzt (chipto-chip und board-to-board interconnect) und ersetzt
in einer PC-Struktur, wie der in Bild 7-1c gezeigten,
die parallelen Punkt-zu-Punkt-Verbindungen und die
Systembusse. Übertragen werden 2 × 1 Bit über zwei
differenzielle Leitungspaare, die vollduplex arbeiten,
eine sog. Lane, mit einer Taktfrequenz von 2, 5 GHz.
Unter Berücksichtigung der zur Taktrückgewinnung
eingesetzten 8b/10b-Datencodierung [13] ergibt sich
daraus eine Nettoübertragungsrate von 2 × 2 Gbit/s.
Eine PCI-Express-Verbindung kann aus 1, 2, 4, 8, 12,
16 oder 32 solcher Lanes bestehen, d. h., durch Parallelbetrieb mehrerer Lanes kann die Übertragungsrate
vervielfacht werden. Überbrückbar sind Entfernung
bei z. B. 16 parallelen Lanes (×16) von bis zu 20 Zoll
(50, 8 cm). – Zur Vertiefung siehe [1].
HyperTransport (HT). HyperTransport ist wie PCIExpress eine serielle Punkt-zu-Punkt-Verbindung,
überträgt paketorientiert mit differenzieller Signalübertragung und wird ebenfalls als Verbindung zwischen Bausteinen und zwischen Karten eingesetzt [5].
Sie nutzt jedoch die Double-Data-Rate-Übertragung
und verzichtet auf die 8b/10b-Codierung, was sie
insbesondere befähigt, den Prozessorbus zu ersetzen.
Die einfachste Verbindung, ein Link, ist sowohl für
Halbduplex- als auch für Vollduplexbetrieb konzipiert
und überträgt 2 Bit pro Richtung parallel mit einer
maximalen Übertragungsrate von 2 × 5600 Mbit/s bei
einer Taktfrequenz von 1,4 GHz. Die derzeit aktuelle
Hypertransport-Version 3.1 erlaubt eine nominelle
Datenübertragung von 25, 6 Gbyte/s bei 3, 2 GHz
85
86
Technische Informatik / Rechnerorganisation
Taktfrequenz. Eine HyperTransport-Verbindung kann
aus 1, 2, 4, 8 oder 16 solcher Links bestehen, womit
eine Parallelität pro Richtung von bis zu 32 Bit
erreicht wird. Die beiden Übertragungsrichtungen
müssen dabei nicht symmetrisch ausgelegt sein. Die
maximale Leitungslänge beträgt 24 Zoll (61 cm).
VMXbus (VME extended bus), und einen seriellen
Nachrichtenbus, den VMSbus (VME serial bus),
ergänzt.
Quick Path Interconnect (QPI).
Periphere Verbindungen werden (abgesehen von
Funk- und Infrarot-Übertragungen) mit Kabeln
realisiert. Es gibt sie zum einen als Gerätebusse
für die Anbindung von Ein-/Ausgabegeräten und
Hintergrundspeichern an den Rechner, zum andern
als sog. Feldbusse zur Anbindung von Sensoren,
Aktoren (auch Aktuatoren genannt) und übergeordneten Einheiten, insbesondere in der Prozess-,
Automatisierungs- und Steuerungstechnik. Gerätebusse sind entweder parallele oder serielle Busse mit
einem Trend zur seriellen Übertragung, Feldbusse
sind dagegen fast ausschließlich serielle Busse. – Der
Begriff Bus spiegelt die logische Betrachtung wider.
Physikalisch werden die Busse fast ausschließlich
durch Punkt-zu-Punkt-Verbindungen realisiert. Gebräuchlich ist hier das Hintereinanderschalten der
Komponenten, d. h. die Form des Strangs, sowie das
Verbinden der Komponenten unter Verwendung von
Verteilern (Hubs), d. h. die Form des Baums.
Serielle Busse sind gegenüber parallelen Bussen
kostengünstiger (Kabel, Stecker, Anpasselektronik),
einfacher zu verlegen und ermöglichen aufgrund
geringerer Übersprechprobleme größere Leitungslängen und höhere Taktfrequenzen, d. h. Übertragungsraten. Maximale Leitungslänge und Taktfrequenz
hängen vom Leitungsmaterial und der Leitungsart
ab, und zwar mit steigenden Werten für (1.) Kupfer
in Form von unverdrillten Zweidrahtleitungen, verdrillten Zweidrahtleitungen (twisted pair, TP) und
Koaxialleitungen, (2.) Glas in Form von Glasfaserkabeln (Lichtwellenleitern, LWL) mit unterschiedlichen
Übertragungsmodi. Serielle Busse unterscheiden
sich in ihren technischen und strukturellen Aspekten
oft nur wenig von lokalen Netzen (siehe 7.5.3).
– Tabelle 7-2 zeigt die wichtigsten Daten der
folgenden Busse/Punkt-zu-Punkt-Verbindungen.
Als Konkurrenzprodukt zu HyperTransport hat Intel im Jahr 2008 die
Punkt-zu-Punkt Prozessorverbindung QPI eingeführt,
um den bis dahin gebräuchlichen Front-Side-Bus zu
ersetzen. Bei einer Taktfrequenz von 3, 2 GHz überträgt QPI 25, 6 Gbyte/s über einen Link mit 20 Leitungspaaren. Jeder Link schafft 20 Bits pro Übertragung, davon 16 Nutzbits und 4 Bits für HeaderInformationen.
iPSB (Multibus II).
Zwei wichtige industrielle Busse
sind die Firmenstandards iPSB und VMEbus.
Der iPSB (intel Parallel System Bus), ein 32-BitMultiplexbus, ist einer von drei miteinander kombinierbaren und sich ergänzenden Backplane-Bussen
(Multibus-II-Standard). Als zentraler Systembus ist
er an allen Stecksockeln der Backplane zugänglich. Ergänzt wird er durch den iLBX-II-Bus (intel
Local Bus Extension), einen schnellen Split-Bus
für Prozessor-Speicher-Übertragungen mit kurzer
Leitungsführung zwischen benachbarten Stecksockeln, und den seriellen iSSB-Bus (intel Serial
System Bus), der als sog. Nachrichtenbus innerhalb
einer Backplane wie auch zwischen den Backplanes
verschiedener Baugruppenträger eingesetzt werden
kann.
VMEbus. Der VMEbus (Versa Module Europe
Bus), ebenfalls ein Backplane-Bus, wurde als asynchroner Split-Bus mit einer Datenbusbreite von
32 Bits und einer Busbandbreite von 40 Mbyte/s
entwickelt. Er wurde dann zum 64-Bit-Multiplexbus
erweitert, zunächst mit Single-Data-Rate (VME64),
dann mit Double-Data-Rate (VME64x), jeweils
mit Verdoppelung der Busbandbreite. Inzwischen
überträgt er synchron mit Double-Data-Rate und
einer Busbandbreite von 320 Mbyte/s (VME320,
[11]). Den Bustakt liefert der jeweilige Sender, auf
eine Empfangsquittierung wird verzichtet (source
synchronous protocol). – Wie der iPSB wird der
VMEbus durch einen schnellen Speicherbus, den
7.1.6 Periphere Busse
und Punkt-zu-Punkt-Verbindungen
SCSI.
SCSI (Small Computer System Interface Bus,
ANSI-Standard) ist ein paralleler Gerätebus für den
Anschluss prozessorinterner und -externer Geräte,
wie Festplatte, CD/DVD-Laufwerk, Drucker, Scan-
7 Rechnersysteme
Tabelle 7-2. Parallele und serielle Gerätebusse und Punkt-zu-Punkt-Verbindungen mit ihren maximalen Übertragungsraten (abhängig von der Leitungsart). Bei Serial Attached SCSI, Serial ATA, Fibre Channel und InfiniBand ist mit Mbyte/s
die Nettoübetragungsrate angegeben (8b/10b-Codierung!). Der maximale Geräteabstand soll die Einsatzmöglichkeiten verdeutlichen: rechnernah, rechnerfern
Bus,
Punkt-zu-PunktVerbindung
parallel:
SCSI (wide)
IDE/ATA
seriell:
Serial Attached SCSI
Serial ATA
Serial ATA II
Serial ATA III
Fire Wire (IEEE 1394a)
Fire Wire (IEEE 1394b)
USB 1.1: low speed
USB 1.1: full speed
USB 2.0: high speed
USB 3.0: super speed
Fibre Channel
InfiniBand (×4)
InfiniBand (×4, DDR)
InfiniBand (×4, QDR)
max. Übertragungsrate
Mbyte/s
Mbit/s
Leitungsart
max.
Reichweite
320
133
Kupfer
Kupfer
12 m
0, 45 m
Kupfer
Kupfer
Kupfer
Kupfer
Kupfer
Kupfer/Glasfaser
Kupfer
Kupfer
Kupfer
Kupfer
Kupfer
Glasfaser
Kupfer/Glasfaser
Kupfer/Glasfaser
Kupfer/Glasfaser
6m
1m
1m
1m
4, 5 m
100 m
3m
5m
5m
5m
50 m
10 km
15 m/150 m
15 m/150 m
15 m/150 m
300
150
300
600
50
100
0,18
1,5
60
500
400
3000
1500
3000
6000
400
800
1,5
12
480
5000
4000
1000
2000
4000
10 000
20 000
40 000
ner. Es gibt ihn in verschiedenen Entwicklungsstufen
mit 8 Bit (narrow) oder 16 Bit (wide) Datenbusbreite
für bis zu 7 bzw. 15 durch ein Kabel miteinander
in Reihe verbundene Teilnehmer (Strangstruktur),
mit Taktfrequenzen von 5, 10, 20 und 40 MHz und
der maximalen Übertragungsrate von 320 Mbyte/s.
Mittels einer Disconnect-/Reconnect-Steuerung
können auf ihm Wartezeiten, z. B. das Positionieren des Schreib-/Lesearms einer Festplatte, durch
zwischenzeitliches Zuteilen des Busses an einen
anderen Busteilnehmer überbrückt werden. Die
Gerätesteuerung insgesamt erfolgt auf einer hohen
Kommandoebene. – SCSI wird vorwiegend bei Rechnern mit hohen Leistungsanforderungen eingesetzt,
so bei Servern.
SAS. SAS (Serial Attached SCSI) ist der serielle
Nachfolger von SCSI mit einer Übertragungsrate von
zunächst 3 Gbit/s (8b/10b-Codierung: 2, 4 Gbit/s
netto, d. h. 300 Mbyte/s). Der Anschluss an den
SAS-Controller erfolgt punkt-zu-punkt; mittels
eines sog. Fan-out-Expander sind jedoch mehrere
Geräte anschließbar. Überbrückt werden bis zu 6 m,
übertragen wird vollduplex über zwei differenzielle
Leitungspaare. Die SCSI-Kommandostruktur ist beibehalten. Aufgrund gleicher Kabel und Stecker wie
bei SATA (siehe unten) und da der SAS-Controller
das SATA-Protokoll beherrscht, können an ihm
auch SATA-Geräte betrieben werden. – Seit Jahren
gibt es bereits andere serielle Realisierungen des
SCSI-Kommandosatzes: FireWire und Fibre Channel
(siehe unten) sowie Serial Storage Architecture.
IDE/ATA. IDE (Integrated Drive Electronics), auch
als ATA (AT Attachment) bezeichnet, ist eine 16 Bit
parallele Schnittstelle (Punkt-zu-Punkt-Verbindung),
die ursprünglich für den Anschluss von bis zu zwei
rechnerinternen Festplatten an den ISA-Bus (AT-Bus)
87
88
Technische Informatik / Rechnerorganisation
mit einer Kabellänge von max. 45 cm ausgelegt
wurde. Diese Funktionalität wurde inzwischen
hinsichtlich anderer Systembusse (PCI) und einer
größeren Vielfalt an Gerätetypen (wie bei SCSI)
erweitert. Des Weiteren wurde die ursprüngliche,
programmierte Datenübertragung (programmed I/O,
PIO) mit Übertragungsraten von 3 bis 20 Mbyte/s um
die DMA-gesteuerte Übertragung mit Double-DataRate und Übertragungsraten von 33 bis 133 Mbyte/s
(Ultra ATA/133) ergänzt. Die Gerätesteuerung erfolgt
auf hoher Kommandoebene. Die für die Geräteerweiterung erforderlichen Kommandosätze sind in
dem ursprünglichen ATA-Kommandoformat „verpackt“, weshalb man auch von ATAPI-Schnittstelle
spricht (ATA Package Interface). – IDE wurde
vorwiegend bei PCs eingesetzt. IDE-Festplatten sind
preiswerter als SCSI-Festplatten, mit dem Nachteil
geringerer Zugriffsraten, Speicherkapazitäten und
Zuverlässigkeit.
Serial ATA (SATA). Serial ATA ist der serielle Nachfolger von IDE/ATA und mit diesem softwarekompatibel. Bei PCs hat SATA einen Marktanteil von über
90 Prozent. Die Übertragungsrate betrug in der Version SATA I 1, 5 Gbit/s, was bei 8b/10b-Codierung
1, 2 Gbit/s netto, d. h. 150 Mbyte/s bedeutet. In der
Version SATA II wird bei doppelter Frequenz mit
3 Gbit/s übertragen und in der Version SATA III seit
2009 mit 6 Gbit/s. Die Verbindung arbeitet punkt-zupunkt (jetzt mit nur noch einem einzigen Gerät), mit
je einem differenziellen Leitungspaar pro Richtung
und über eine Entfernung von bis zu 1 m. Die Übertragung erfolgt halbduplex für Daten und vollduplex
für die Synchronisation.
FireWire. FireWire ist ein serieller Bus mit
Strang- oder Sternstruktur, an dem bis zu 64 Geräte
mit einem maximalen Geräteabstand von 4, 5 m
(Twisted-Pair-Kabel) betrieben werden können.
Übertragen wird mit 400 Mbit/s (IEEE 1394a),
800 Mbit/s (IEEE 1394b), sowie 1600 (IEEE
1394c) und 3200 Mbit/s. Konzipiert wurde er für
Multimedia-Anwendungen im Konsumbereich und
sieht dafür u. a. die isochrone Datenübertragung
vor, d. h., er garantiert die Übertragung innerhalb
eines bestimmten Zeitrahmens, wie dies für Audiound Video-Übertragungen erforderlich ist. Geräte
am Bus können dabei direkt miteinander kommu-
nizieren, ohne den Rechner über den Host-Adapter
(Bild 7-1b) zu beanspruchen (peer-to-peer). Darüber
hinaus wird FireWire für den Anschluss prozessorexterner Geräte, z. B. Festplatten, eingesetzt.
– FireWire steht in Konkurrenz zu USB.
Universal Serial Bus.
Der Universal Serial Bus
(USB) ist ein serieller Bus und wird wie FireWire
für Multimedia-Anwendungen, insbesondere aber
als Gerätebus bei PCs zur Versorgung langsamer
Geräte, wie Tastatur und Maus, und schneller Geräte,
wie externe Festplatten, Scanner usw., eingesetzt.
Es gibt ihn derzeit mit drei verschiedenen Übertragungsraten mit Abwärtskompatibilität: USB
1.1 mit 1, 5 Mbit/s (low speed) und 12 Mbit/s (full
speed), USB 2.0 mit 480 Mbit/s (high speed) und
USB 3.0 mit 5 Gbit/s. Er hat eine Stern-StrangStruktur mit Hubs als Sternverteiler, von denen
jeweils mehrere Punkt-zu-Punkt-Verbindungen ausgehen. Die Übertragung erfolgt grundsätzlich asynchron, Multimedia-Anwendungen werden durch die
isochrone Übertragung unterstützt. Die Übertragungen laufen immer über den Host-Adapter (Bild 7-1b),
d. h. über den Rechner.
Fibre Channel. Der Fibre Channel (FC, ANSI-
Standard) ist eine serielle Verbindung mit Übertragungsraten von 4 Gbit/s (4 GFC) bis 14 Gbit/s
(16 GFC), bei Entfernungen von bis zu 50 m (Kupferleitungen) und 10 km (Lichtwellenleiter). Aufgrund
der 8b/10b-Codierung der Daten resultiert daraus bei
4 GFC eine Nettoübertragungsrate von 400 Mbyte/s
pro Richtung. Eingesetzt wird Fibre Channel in drei
unterschiedlichen Verbindungsformen: (1.) „Pointto-Point-Topology“. Hier wird nur ein einziges
Gerät mit einem Rechner verbunden. (2.) „Arbitrated
Loop Topology“. Hier werden in der ursprünglichen
Ausführung bis zu 126 Geräte und Rechner punkt-zupunkt in einer Ringstruktur miteinander verbunden
(oft als Doppelring ausgelegt, Dual Loop). Heute
wird anstelle des Rings bevorzugt eine Sternstruktur
verwendet, in der ein Hub als zentraler Vermittler
fungiert. Der Arbitrated Loop ist eine der seriellen
SCSI-Varianten. (3.) „Fabric Topology“. Hier können
sehr viele Rechner und Geräte (bis zu 16 Millionen
adressierbar) in großen, netzförmigen Systemen miteinander verbunden werden. Dies wird insbesondere
für die Realisierung von Speichernetzen (storage
7 Rechnersysteme
area networks, SANs) genutzt, bei denen die Server
lokaler Netze mit ihren Speichereinheiten, sog. RAIDs (redundand arrays of independent/inexpensive
disks), zu einem eigenen Netz zusammengefasst
werden, um unabhängig vom lokalen Netz auf
Dateien schnell und wahlfrei zugreifen zu können.
Die Netzstruktur ist hier üblicherweise eine sog.
Switched Fabric (Schaltgewebe), bei der mittels
mehrerer Switches Mehrwegeverbindungen zwischen
allen Netzteilnehmern herstellbar sind (any-to-any
network).
InšniBand.
InfiniBand, zunächst als PCI-BusNachfolger vorgesehen, hat seinen Einsatzbereich
bei der Vernetzung von Servern und Hintergrundspeichern (Storage Area Networks) sowie
als Kommunikationsnetzwerk in leistungsfähigen Cluster-Systemen. Diese findet in Form der
Switched Fabric statt (siehe dazu Fibre Channel).
Übertragen wird vollduplex über zwei differenzielle
Leitungspaare (1×-Link) mit 2,5 GHz-Takt und mit
8b/10b-Codierung, d. h. mit 2 × 2, 5 Gbit/s brutto
bzw. 2 × 250 Mbyte/s netto. Erhöht wird die Übertragungsrate durch den Parallelbetrieb von 4 oder 12
solcher Verbindungen (4×-, 12×-Link), ggf. ergänzt
um Double-Data-Rate (DDR), Quad-Data-Rate
(QDR), Fourteen-Data-Rate (FDR) oder EnhancedData-Rate (EDR), wobei FDR und EDR mit einer
64b/66b-Codierung bis zu 300 Gbit/s erzielen. Aufgrund seiner hohen Datenübertragungsrate und der
geringen Latenzzeit von weniger als 1 Mikrosekunde
eignet sich Infiniband zum Nachrichtenaustausch
in paralellen Hochleistungsrechnern. Hierfür stehen
große nichtblockierende Schalter (switches) mit 288
Ports (DDR) bzw. 648 Ports (QDR) zur Verfügung,
mit denen verschiedene Netzwerktopologien (z. B.
mehrdimensionaler Torus, hypercube, butterfly, fat
tree) gebaut werden können.
Feldbusse. Kommunikationssysteme in der Prozess-,
Automatisierungs- und Steuerungstechnik bestehen
meist aus hierarchisch strukturierten Netzen, basierend auf ggf. unterschiedlichen Feldbussen, die
über Gateways und Bridges (siehe 7.5.3) miteinander verbunden sind. Von den in lokalen Netzen
(LANs) gebräuchlichen Techniken unterscheiden
sich Feldbusse jedoch durch geringere Installations-
kosten, geringere Komplexität der Protokolle und
ihre „Echtzeitfähigkeit“. Sie haben außerdem eine
hohe Zuverlässigkeit, hohe Fehlertoleranz, geringe
Störanfälligkeit und sind ggf. multimasterfähig. Die
meisten dieser seriellen Busse benutzen auf der
untersten, der physikalischen Ebene die Schnittstellennorm RS-485 (Electronic Industries Association,
EIA) und erreichen, abhängig von der Streckenlänge,
Übertragungsraten von 100 kbit/s (bis zu 1000 m)
bis zu 1 Mbit/s (unter 50 m). Zu den bekanntesten
der gut 50 Feldbusse zählen: Profi-Bus, FIP-Bus,
CAN-Bus, Bitbus und Interbus S.
Der sog. IEC-Bus (General Purpose Interface Bus,
GPIB, IEC 625, IEEE 488) ist ein 8 Bit paralleler
Bus für den Datenaustausch zwischen einem Rechnersystem und Mess- und Anzeigegeräten, z. B. in Laborumgebungen (Messbus). Seine Länge ist auf 20 m
beschränkt, die maximale Übertragungsrate beträgt
1 Mbyte/s. Die bis zu 15 möglichen Busteilnehmer
haben Senderfunktion (talker, z. B. Messgeräte) oder
Empfängerfunktion (listener, z. B. Signalgeneratoren,
Drucker) oder beides (z. B. Messgeräte mit einstellbaren Messbereichen). Verwaltet wird der Bus von einer
Steuereinheit (Rechner), die gleichzeitig auch Sender
und Empfänger sein kann.
7.2 Speicherorganisation
Speicher sind wesentliche Bestandteile von Rechnern
und werden für die heutzutage großen Programmpakete und Datenmengen mit entsprechend großen
Kapazitäten benötigt. Da Speicherzugriffe den Durchsatz und damit die Leistungsfähigkeit eines Rechners
stark beeinflussen, müssen Speicher kurze Zugriffszeiten haben. Beide Forderungen, große Kapazitäten
und kurze Zugriffszeiten, führen zu hohen Kosten
bzw. sind technisch nicht herstellbar. Einen Kompromiss zwischen niedrigen Kosten und hoher Leistungsfähigkeit stellt eine hierarchische Speicherstruktur dar
mit Speichern, die im einen Extrem große Kapazitäten mit langen Zugriffszeiten und im andern Extrem kurze Zugriffszeiten bei geringen Kapazitäten
aufweisen (Bild 7-7).
Zentraler Speicher einer solchen Hierarchie ist der
Hauptspeicher (Arbeitsspeicher, Primärspeicher;
ein Halbleiterspeicher) als der vom Prozessor direkt
adressierbare Speicher, i. Allg. ergänzt um eine
Speicherverwaltungseinheit. Dieser wird versorgt
89
90
Technische Informatik / Rechnerorganisation
Bild 7-7. Speicherhierarchie
von Hintergrundspeichern (Sekundärspeichern)
mit großen Kapazitäten (z. B. magnetischen und
optischen Plattenspeichern, langsameren FlashHalbleiterspeichern, Magnetbandspeichern). Zur
Beschleunigung des Prozessorzugriffs auf den
Hauptspeicher werden zwischen Prozessor und
Hauptspeicher ein oder mehrere kleinere Pufferspeicher, sog. Caches, geschaltet. Als schnelle
Halbleiterspeicher erlauben sie die Anpassung an die
vom Prozessor vorgegebene Zugriffszeit. Schließlich gibt es in dieser Hierarchie den Registerspeicher
(Registerblock) des Prozessors und ggf. einen Befehlspuffer (instruction queue/pipe) mit Zugriffszeiten
gleich dem Verarbeitungstakt des Prozessors (siehe
auch 5.4.1).
Der Hauptspeicher, die Caches und die Prozessorregister sind als Halbleiterspeicher sog. flüchtige Speicher, d. h., ihr Inhalt geht beim Abschalten der Versorgungsspannung verloren. Hintergrundspeicher mit
magnetischer oder optischer Speicherung sind hingegen nichtflüchtig. – Zur Speicherorganisation siehe
z. B. auch [2].
7.2.1 Hauptspeicher
Der Hauptspeicher realisiert einen Speicherraum mit
fortlaufend nummerierten Speicherzellen und direktem Zugriff auf diese (man spricht von wahlfreiem
Zugriff). Die Kapazitäten der Hauptspeicher von PCs
liegen typischerweise bei mehreren Gbyte. Eingesetzt werden derzeit Speicherbausteine (dynamische
RAMs: DRAMs) mit Kapazitäten von bis zu 8 Gbyte.
Mehrere solcher Bausteine (z. B. acht oder 16)
werden zu steckbaren Modulen, sog. DIMMs (dual
in-line memory modules) mit 64-Bit-Zugriffsbreite
zusammengefasst. Im Hinblick auf den Datentransfer
mit Caches werden DRAM-Bausteine verwendet,
die den 4-Wort- und 8-Wort-Blockbuszyklus von
Cache-Controllern unterstützen, indem sie Folgezugriffe mit gegenüber dem Erstzugriff (lead-off cycle)
verkürzten Zugriffszeiten erlauben (Bild 7-6).
Gebräuchlich sind hier synchrone, d. h. getaktete
DRAMs (SDRAMs), anfänglich mit Single-DataRate-Datenzugriff. Seit der zweiten Generation
arbeiten sie mit Double-Data-Rate, d. h. mit zwei
Folgezugriffen pro Takt (DDR-SDRAM), bei Taktfrequenzen von z. B. 133 und 200 MHz (DDR266,
DDR400). In der dritten Generation (DDR2SDRAM) weisen sie eine erneute Verdoppelung der
Übertragungsrate auf, und zwar durch Verdoppelung
der Übertragungstaktfrequenz gegenüber der chipinternen Taktfrequenz (z. B. DDR2-800 mit 400 MHz
Übertragungsfrequenz und Double-Data-Rate). Zur
Aufrechterhaltung des Datenflusses wird bei den
DDR-SDRAMs mit jedem Interntakt auf zwei Daten,
bei DDR2-SDRAMs auf vier Daten gleichzeitig zugegriffen. Die Folgegeneration der DDR3-SDRAMs
arbeitet mit 8-fachem Internzugriff, wodurch die
Übertragungstaktfrequenz erneut verdoppelt werden kann. – Schnittstelle zu den DIMMs ist der
DRAM-Controller, der die Speicherzugriffe durch
Kommandos steuert. Verbunden ist er mit ihnen
entweder (herkömmlich) über einen oder zwei busartige 64-Bit-Speicherkanäle oder punkt-zu-punkt mit
serieller Übertragungstechnik (10 Bit breit schreiben,
14 Bit breit lesen) und ggf. höherer Kanalanzahl.
Die Speicherung eines Bits erfolgt bei DRAMs mittels der Ladung eines Kondensators, mit dem Vorteil
eines geringen Chipflächenbedarfs der Speicherzelle und dem Nachteil des Zerstörens der Speicherinformation beim Auslesen von Zellen wie auch durch
Leckströme. Der Speicherinhalt muss deshalb lokal
bei jedem Auslesen und zusätzlich global, d. h. der gesamte Bausteininhalt, in einem vorgegebenen Zeitintervall „aufgefrischt“ werden (refresh). Die Steuerung
hierfür übernimmt der DRAM-Controller.
7.2.2 Speicherverwaltungseinheiten
Bei Rechnersystemen mit Mehrprogrammbetrieb
(siehe 8) werden Programme und ihre Daten (Prozesse) mit Unterbrechungen ausgeführt und dazu ggf.
zwischenzeitlich aus dem Hauptspeicher entfernt
und bei Bedarf wieder geladen. Eine dabei evtl.
notwendige Änderung des Ladeorts erfordert, dass
7 Rechnersysteme
bination aus Segmentierung mit Seitenverwaltung
(paged segmentation).
Die folgenden Darlegungen beziehen sich auf jene
Aspekte der Speicherverwaltung, die mit der Hardware von MMUs zusammenhängen. Zu den softwaretechnischen Aspekten sei auf 8.3.3 verwiesen.
Bild 7-8. Rechnerstruktur mit Speicherverwaltungseinheit
MMU mit Segmentverwaltung (segmentation).
(MMU)
Das Bilden von Segmenten erfordert eine Strukturierung des virtuellen wie auch des realen Adressraums
und somit des Hauptspeichers. Im Virtuellen wird
dazu (als eine Möglichkeit) das virtuelle Adresswort
unterteilt: in eine Segmentnummer als Kennung eines
Segments, bestimmt durch die n höchstwertigen
Adressbits (z. B. n = 8), und in eine Bytenummer
als Abstand zum Segmentanfang (offset), festgelegt
durch die verbleibenden m niederwertigen Adressbits
(z. B. m = 24 bei 32-Bit-Adressen). Das heißt, der
virtuelle Adressraum wird in Bereiche der Größe
2m (hier 16 Mbyte) eingeteilt, die die maximal
mögliche Segmentgröße haben. Er wird daher bei
kleineren Segmenten nur lückenhaft genutzt, was
jedoch ohne Nachteil ist, da die eigentliche Speicherung auf einem Hintergrundspeicher erfolgt und
dort Lücken durch eine weitere Adressumsetzung
durch das Betriebssystem vermieden werden. Bei
der Strukturierung des realen Adressraums ist man
allerdings auf eine möglichst gute Hauptspeicherausnutzung angewiesen. Ein lückenloses Speichern
von Segmenten erreicht man dadurch, dass man der
virtuellen Segmentnummer eine Byteadresse (hier
32-Bit-Adresse) als Segmentbasisadresse zuordnet,
zu der bei der Adressumsetzung die virtuelle Bytenummer addiert wird. Segmente können dadurch an
jeder beliebigen Byteadresse beginnen. Eine solche
Adressumsetzung zeigt Bild 7-9. Die Umsetztabelle
einschließlich der Schutz- und Statusangaben, die
sog. Segmenttabelle, hat hier den geringen Umfang
von bis zu 256 Deskriptoren (n = 8) und wird deshalb
insgesamt in einem „schnellen“ Registerspeicher der
MMU gehalten.
Bei einer anderen Möglichkeit der Strukturierung im
Virtuellen wird das Adresswort um die Segmentnummer erweitert. Hierfür hat die MMU mehrere Segmentnummerregister, die durch den Zugriffsstatus des
Prozessors, nämlich Code- (Programm-), Daten- oder
Stack-Zugriff angewählt werden. Der Vorteil gegen-
Programme und Daten im Hauptspeicher verschiebbar (relocatable) sein müssen. Diese Verschiebbarkeit
wird in flexibler Weise durch eine Speicherverwaltungseinheit (memory management unit, MMU)
erreicht, die als Bindeglied zwischen dem Adressbus
des Prozessors und dem Adresseingang des Hauptspeichers wirkt (Bild 7-8; zu anderen Techniken
siehe 8.3.3). Sie setzt die vom Prozessor während
der Programmausführung erzeugten sog. virtuellen,
logischen Programmadressen in reale, physische
Speicheradressen um und führt dabei zusätzlich eine
Zugriffsüberwachung durch. Die hierfür erforderliche Information wird vom Betriebssystem in sog.
Umsetztabellen bereitgestellt. Um deren Umfang
gering zu halten, bezieht man die Adressumsetzung
und den Speicherschutz nicht auf einzelne Adressen,
sondern auf zusammenhängende Adressbereiche,
und zwar auf Segmente oder Seiten.
Segmente und Seiten. Bei einer Speicherverwaltung mittels Segmenten werden die Bereiche
so groß gewählt, dass sie logische Einheiten, wie
Programmcode, Daten oder Stack, vollständig
umfassen; sie haben dementsprechend variable
Größe. Bei einer Speicherverwaltung mittels Seiten
(pages) wird eine logische Einheit in Bereiche
einheitlicher Länge unterteilt. Für den Zugriffsschutz
(Speicherschutz) werden den Segmenten bzw. Seiten
Schutzattribute zugeordnet, die von der MMU bei
der Adressumsetzung unter Bezug auf die vom
Prozessor erzeugten Statussignale ausgewertet werden. Adressumsetzinformation, Schutzattribute und
zusätzliche Statusangaben eines Bereichs bilden
dessen Deskriptor. Deskriptoren werden wiederum
in Umsetztabellen zusammengefasst. – In heutigen
Prozessoren findet man im Allgemeinen eine Kom-
91
92
Technische Informatik / Rechnerorganisation
Bild 7-9. Adressumsetzung für Segmente, deren Segmentnummern aus einem Teil der virtuellen Adresse gebildet werden. Virtueller Adressraum von 4 Gbyte (32-BitAdressen), aufgeteilt in 256 Segmente mit jeweils bis zu
16 Mbyte
über einer MMU mit Unterteilung des Adressworts
ist, dass Segmente in ihrer Größe nicht beschränkt
sind und in größerer Anzahl verwaltet werden können (abhängig von der Breite der Segmentnummerregister). Die dementsprechend größere Segmenttabelle
wird nicht mehr in der MMU, sondern im Hauptspeicher untergebracht. Um dennoch schnell auf die aktuellen Deskriptoren zugreifen zu können, werden diese
in Pufferregister der MMU, die den Segmentnummerregistern zugeordnet sind, kopiert.
Nachteilig an der Segmentverwaltung ist, dass der
beim Mehrprogrammbetrieb erforderliche Austausch
von Speicherinhalten (swapping) abhängig von der
Segmentgröße sehr zeitaufwändig sein kann und
dass ein zu ladendes Segment immer einen zusammenhängenden Speicherbereich benötigt, somit freie
Speicherbereiche ggf. nicht genutzt werden können.
Dieser Nachteil tritt bei der Seitenverwaltung nicht
auf.
MMU mit Seitenverwaltung (paging).
Bei der Seitenverwaltung wird der virtuelle Adressraum in Seiten
(pages) als relativ kleine Bereiche einheitlicher Größe (typischerweise 4 Kbyte, aber auch 4 Mbyte oder
1 Gbyte konfigurierbar) unterteilt, die im Speicher auf
Seitenrahmen (page frames) gleicher Größe abgebildet werden (Bild 7-10a). Dazu werden bei der Adressumsetzung die n höchstwertigen Adressbits der virtuellen Adresse (Seitennummer) durch n Bits der realen
Adresse (Rahmennummer) ersetzt (z. B. n = 20). Die
verbleibenden m Bits der virtuellen Adresse (Byte-
Bild 7-10. Seitenverwaltung. a Abbildung der Seite 2 auf
den Speicherrahmen 0 und der Seiten 1 und 4 auf den Speicherrahmen 2 (shared memory, siehe auch im Folgenden:
MMU mit zweistufiger Seitenverwaltung); b Adressumsetzung bei einer Seitengröße von 4 Kbyte
nummer, Relativadresse der Seite) werden als Relativadresse des Rahmens unverändert übernommen (z. B.
m = 12 bei 32-Bit-Adressen,
Bild 7-10b). Der virtuelle Adressraum wird somit
in Seiten unterteilt, die in beliebige freie, nicht
notwendigerweise zusammenhängende Seitenrahmen
geladen werden können und so eine bessere Nutzung
des Speichers erlauben. Diese Technik ermöglicht
es auch, nur die aktuellen Seiten eines Prozesses
(working set) im Speicher zu halten. Das Betriebssystem muss dann jedoch so ausgelegt sein, dass es
bei einem Zugriffsversuch auf eine nicht geladene
Seite die Zugriffsoperation unterbricht, den Prozess
blockiert, die fehlende Seite in den Speicher lädt und
danach die Zugriffsoperation und damit den Prozess
wieder aufnimmt (demand paging). Da bei diesem
Vorgehen der verfügbare Hauptspeicherplatz geringer
sein kann als der insgesamt benötigte, spricht man
auch von virtuellem Speicher.
Die Seitentabelle wird wegen ihres großen Umfangs
(im obigen Beispiel bis zu 220 Seitendeskriptoren) in
Teilen im Hauptspeicher und auf dem Hintergrund-
7 Rechnersysteme
Bild 7-11. Zweistufige Adressumsetzung für
Segment- und Seitenverwaltung. Virtueller
Adressraum von 4 Gbyte (32-Bit-Adressen),
aufgeteilt in 1024 Segmente mit jeweils bis zu
1024 Seiten von je 4 Kbyte. Von 1024 möglichen Seitentabellen ist nur eine dargestellt
speicher gehalten, wobei sie u. U. selbst der Seitenverwaltung unterworfen wird. Um einen schnellen
Deskriptorzugriff zu ermöglichen, werden die aktuellen Seitendeskriptoren in einen speziellen Cache der
MMU, den sog. Translation-look-aside-Buffer (TLB)
kopiert. In einer MMU-Variante lässt sich die Seitentabelle als sog. invertierte Seitentabelle auf die Größenordnung der Anzahl an Seitenrahmen reduzieren,
indem sie mittels Hash-Adressierung verwaltet wird
(Umcodieren der Seitennummern; siehe auch 10.7).
MMU
mit zweistušger Seitenverwaltung. Um
Segmente einerseits als logische Einheiten verwalten und andererseits in kleineren Einheiten
speichern zu können, kombiniert man die Verwaltung von logischen Einheiten (Segmenten) mit der
Seitenverwaltung und erhält so eine zweistufige
Adressabbildung, wie Bild 7-11 für die Segmentverwaltung durch Unterteilung des Adressworts
zeigt. Da hier die Segmente als Zusammenfassungen von Seiten gebildet werden, spricht man von
zweistufiger Seitenverwaltung. Ausgangspunkt der
Adressabbildung ist die Segmenttabelle (das Seitentabellenverzeichnis), die für jedes Segment einen
Deskriptor mit den Schutzattributen, dem Status
und einem Zeiger auf eine Seitentabelle enthält. Die
Seitentabelle wiederum enthält für jede Seite des
Segments einen Deskriptor mit der Rahmennummer,
dem Status (z. B. Seite geladen) und den seitenspezifischen Schutzattributen. Weicht ein Schutzattribut
einer Seite von dem des Segments ab, so gilt z. B.
das strengere Attribut. Diese Technik erlaubt es u. a.,
Segmente verschiedener Benutzer sich in Teilen
(Seiten) überlappen zu lassen und den Benutzern
unterschiedliche Zugriffsrechte für den gemeinsamen
Speicherbereich (shared memory) einzuräumen.
– Als Erweiterung dieser Technik gibt es MMUs mit
mehr als zwei Tabellenebenen, z. B. mit dreistufiger
Adressumsetzung.
7.2.3 Caches
Systemstrukturen. Ein Cache ist ein schneller
Speicher, der in der Speicherhierarchie als Puffer
zwischen dem Hauptspeicher und dem Prozessor
angeordnet ist (Bild 7-12). Er ist üblicherweise in
den Prozessorbaustein integriert (on-chip cache)
und meist in zwei Caches für Befehle und Daten
getrennt (split caches). Ferner gibt es prozessorexterne Caches (off-chip caches), üblicherweise mit
gemeinsamer Speicherung von Befehlen und Daten
(unified cache) und mit einer Kapazität von mehreren
Mbyte. Bei der Kombination von On-chip-Cache und
Off-chip-Cache wird der erste als First-level- und
Bild 7-12. Struktur einer Prozessor-Cache-Hauptspeicher-
Hierarchie. Schnittstelle 1: Off-chip-Cache, Schnittstelle 2:
On-chip-Cache
93
94
Technische Informatik / Rechnerorganisation
der zweite als Second-level-Cache bezeichnet (L1bzw. L2-Cache). Häufig ist auch der L2-Cache in den
Prozessorbaustein integriert, dann gibt es ggf. einen
Off-chip-L3-Cache.
Ein On-chip-Cache hat bei begrenzter räumlicher
Ausdehnung auf dem Halbleitersubstrat, d. h. bei
begrenzter Speicherkapazität, üblicherweise dieselbe
kurze Zugriffszeit wie die Prozessorregister, d. h.
einen Taktschritt mit der hohen prozessorinternen
Taktfrequenz. Bei einen Off-chip-Cache hingegen
kommt es darauf an, wie er mit dem Prozessor
verbunden ist. Ist er an die Prozessorbusschnittstelle
angeschlossen (frontside cache, Bild 7-1b), so
kommt die sehr viel niedrigere Taktfrequenz des
Prozessorbusses zur Wirkung. Hat er einen eigenen
Prozessoranschluss, der dann als Punkt-zu-PunktVerbindung ausgelegt ist (backside cache, Bild 7-1c),
kann er mit z. B. halber oder auch ungeteilter Internfrequenz des Prozessors getaktet sein, benötigt ggf.
aufgrund seiner Größe aber mehr als einen Takt pro
Zugriff. Grundsätzlich erfolgt der Datenaustausch
mit dem On-chip-Cache durch Blockbuszyklen (im
günstigsten Fall als 2-1-1-1-Bursts, Bild 7-6b).
Laden und Aktualisieren. Da ein Cache eine we-
sentlich geringere Kapazität als der Hauptspeicher
hat, sind besondere Techniken für das Laden und Aktualisieren sowie für das Adressieren seiner Inhalte erforderlich. Bei Lesezugriffen des Prozessors wird zunächst geprüft, ob sich das zur Hauptspeicheradresse
gehörende Datum im Cache befindet. Bei einem Treffer (cache hit) wird es von dort gelesen; bei einem
Fehlzugriff (cache miss) wird es aus dem Hauptspeicher gelesen und dabei gleichzeitig in den Cache geladen. Dieses Laden umfasst nicht nur das eigentlich
adressierte Datum, sondern einen Block von 16, 32
oder 64 Bytes (data prefetch) entsprechend 4 oder 8
Übertragungen auf einem 32- oder 64-Bit-Datenbus
(Blockbuszyklus, Bild 7-6b).
Schreibzugriffe auf den Cache gibt es in der Regel nur auf Daten, nicht auf Befehle. Sie erfordern
immer auch das Aktualisieren des Hauptspeichers.
Beim Write-through-Verfahren erfolgt dies bei jedem
Schreibzugriff, wobei der Cache für Schreibzugriffe
seine Vorteile einbüßt. Teilweise Abhilfe schafft hier
ein sog. Write-Buffer, ein Register, in dem die Daten
für die Speicherschreibvorgänge zwischengespeichert
werden, damit der Cache sofort wieder für Prozessorzugriffe frei wird. Beim Write-back-Verfahren hingegen erfolgt das Rückschreiben erst dann, wenn ein
Block im Cache überschrieben werden muss. Dieses
Verfahren ist aufwändiger in der Verwaltung, gewährt
jedoch den Zugriffsvorteil auch für einen Großteil der
Schreibzugriffe. Auch hier wird für das Schreiben in
den Hauptspeicher ein Write-Buffer verwendet.
Eine besonders hohe Trefferquote (hit rate) bei
Cache-Zugriffen erhält man bei wiederholtem
Zugriff, z. B. bei Befehlszugriffen in Programmschleifen, die sich vollständig im Cache befinden
sowie bei Operationen, die sich auf eine begrenzte
Anzahl von Operanden beziehen. Trefferquoten bis
zu 95% werden genannt.
Adressierung. Die Adressierung eines Cache er-
folgt entweder durch virtuelle Adressen mit dem
Vorteil einer zur MMU parallelen, d. h. unverzögerten
Adressauswertung oder, heute vorwiegend, durch
reale Adressen mit dem Vorteil, dass die CacheSteuerung – auch bei Hauptspeicherzugriffen anderer
Master – immer in der Lage ist, Hauptspeicher und
Cache gemeinsam zu aktualisieren. Bei virtueller
Adressierung muss u. U. nach dem Schreiben in den
Hauptspeicher, z. B. durch einen DMA-Controller,
der gesamte Cache-Inhalt als ungültig, da nicht
aktualisiert, verworfen werden. Grundsätzlich ist bei
beiden Adressierungsarten eine Adressumsetzung
von einem großen Adressraum auf den kleineren des Cache erforderlich, wozu sich assoziative
Speicherstrukturen anbieten.
Bei einem vollassoziativen Cache wird zusätzlich
zum Datenblock dessen Blockadresse als Blockkennung (tag) gespeichert, und das Auffinden
eines Datums erfolgt durch parallelen Vergleich
des Blockadressteils der anliegenden Adresse mit
allen gespeicherten Blockkennungen. Der Vorteil
ist hierbei, dass ein Block an beliebiger Position im
Cache stehen kann, dafür ist allerdings der Hardwareaufwand mit je einem Vergleicher pro Cache-Block
sehr hoch.
Weniger Vergleicher erfordern teilassoziative Caches,
bei denen jeweils zwei, vier, sechs oder mehr Blöcke
im Cache zu einem Satz (set) zusammengefasst werden und nur ein Teil der Blockadresse als satzbezogene Blockkennung gespeichert wird. Der andere Teil
7 Rechnersysteme
der Blockadresse wird als Index bezeichnet und zur
direkten Anwahl der Sätze benutzt (decodiert). Nachteilig ist, dass die Positionen der Speicherblöcke im
Cache jetzt nicht mehr beliebig sind. Bei nur einem
Block pro Satz, wo jede Blockadresse über ihren Indexteil auf eine bestimmte Position abgebildet wird,
dafür aber auch nur ein einziger Vergleicher benötigt wird, spricht man von einem einfach assoziativen oder direkt zuordnenden Cache (direct mapped
cache), hingegen bei mehreren (n) Blöcken pro Satz
und damit n möglichen Positionen für jeden Speicherblock (bei n Vergleichern) von einem n-fach assoziativen Cache (n-way set associative cache).
Bild 7-13 zeigt einen zweifach assoziativen Cache
mit 1024 Sätzen zu je zwei Blöcken mit je 16 Bytes
(32 Kbyte). Der mittlere Teil der am Cache anliegenden Hauptspeicheradresse (Index) adressiert die Sätze
in herkömmlicher Weise und wählt jeweils zwei Datenblöcke (Cache-Zeilen) mit ihren Tag-Feldern aus.
Der höherwertige Adressteil wird mit den Einträgen
der beiden Tag-Felder verglichen. Bei Übereinstimmung mit einem der Einträge wird der niederwertige Adressteil (Offset) zur Byteadressierung im Datenblock herangezogen. – Zwei Statusbits für jeden
Block, Valid und Dirty (im Bild nicht dargestellt), zeigen an, ob ein Block insgesamt gültig ist bzw. ob er
seit dem Laden verändert wurde. Vom Valid-Bit hängt
die endgültige Trefferaussage (cache hit), vom DirtyBit das Rückschreiben beim Copy-back-Verfahren ab.
– Leistungswerte von Caches bezüglich Assoziativität
und Dimensionierung finden sich in [4].
Vollassoziative Caches und teilassoziative Caches mit
wenigstens 2 Blöcken pro Satz benötigen für das Laden von Blöcken eine in Hardware realisierte Ersetzungsstrategie, die bei vollem Cache bzw. Satz vor-
Bild 7-13. Adressierung eines zweifach assoziativen Cache
mit 1024 Sätzen
gibt, welcher Eintrag überschrieben werden soll. Das
kann z. B. der am längsten nicht mehr adressierte
(least recently used) oder auch ein zufällig ausgewählter Eintrag sein (random).
7.2.4 Hintergrundspeicher
Hintergrundspeicher dienen zur Speicherung großer
Datenmengen sowohl zur Bereithaltung für die aktuelle Verarbeitung als auch zu deren Sicherung und
Archivierung. Die wichtigsten Datenträger sind Magnetplatten, optische und magnetooptische Platten sowie Magnetbänder (Streamer-Kassetten). Kennzeichnend für diese Datenspeicher sind ihre (verglichen
mit Hauptspeichern) großen Kapazitäten bei wesentlich geringeren Kosten pro Bit. Nachteilig sind jedoch
die durch die mechanische Wirkungsweise bedingten
langen Zugriffszeiten. Diese liegen um Größenordnungen über denen von Halbleiterspeichern und erst
recht über den Taktzeiten von Prozessoren. Die Datenspeicherung erfolgt stets blockweise mit sequentiellem Zugriff; der beim Hauptspeicher übliche direkte
Zugriff auf Einzeldaten ist nicht möglich. – Die Einbaugröße eines solchen Speichers wird durch den sog.
Formfaktor bestimmt. Dieser basiert auf kreisscheibenförmigen Speichermedien und entspricht den Abmessungen ihrer Hüllen, z. B. 3,5 oder 5,25 Zoll. – Zu
Hintergrundspeichern siehe z. B. [12].
Magnetplattenspeicher.
Bei Magnetplattenspeichern wird die Information bitseriell in konzentrischen Spuren (tracks) einer rotierenden, auf einer
oder beiden Oberflächen magnetisierbaren kreisförmigen Scheibe (disk) gespeichert. Der Zugriff auf die
Spuren erfolgt mit einem auf einen radial bewegbaren
Arm montierten Schreib-/Lesekopf. Die Spuren
sind in Sektoren einheitlicher Größe eingeteilt,
wovon jeder aus einem Datenfeld (z. B. 256 oder
512 Datenbytes) und einem vorangehenden Erkennungsfeld (identifier field, ID-Feld) mit der für den
Zugriff benötigten Information besteht: Spurnummer, Oberflächenbezeichnung, Sektornummer und
Datenfeldlänge. Ergänzt werden beide Felder durch
vorangestellte Kennungsbytes (address marks) und
angehängte Blocksicherungsbytes (CRC-Bytes, siehe 6.2). Der Zugriff auf einen Magnetplattenspeicher
erfordert dementsprechend zwei Schritte: zunächst
95
96
Technische Informatik / Rechnerorganisation
die Spur- und Sektoranwahl und dann das Schreiben bzw. Lesen der Bytes des Sektors. – Vor der
ersten Benutzung muss eine Magnetplatte formatiert
werden. Dazu wird sie in allen Spuren beschrieben,
wobei die Datenfelder mit Platzhalter-Information
gefüllt werden.
Magnetplattenspeicher existieren in unterschiedlichen Ausführungen. Festplattenspeicher bestehen
aus einer oder mehreren übereinander angeordneten
starren Magnetscheiben mit Speicherkapazitäten (bei
Nutzung beider Oberflächen) von einigen hundert
Gbyte (Formfaktor 2,5 bzw. 1, 8 Zoll, in PCs und
Notebooks) bis zu 4 Tbyte (Formfaktor 3, 5 Zoll,
in Servern). Die Scheiben als Speichermedium
und das eigentliche Laufwerk bilden dabei eine
untrennbare Einheit, was hohe Umdrehungszahlen
(derzeit bis zu 15 000 U/min) und damit hohe
Zugriffsraten ermöglicht (ca. 100 Mbyte/s). Der
Vorteil mehrerer Scheiben ist, dass nach Positionierung der übereinanderliegenden Schreib-/Leseköpfe
(Schreib-/Lesekamm) ein ganzer „Zylinder“, d. h.
mehrere übereinanderliegende Spuren, erreichbar
ist (Bild 7-14). Festplattenspeicher dienen zur ständigen Programm- und Datenbereithaltung für den
Hauptspeicher und sind deshalb feste Komponenten
von Rechnern mit IDE/ATA- oder SCSI-Anschluss
(ältere Ausführungen) oder mit SATA- oder SASAnschluss (neuere Ausführungen). Sie können aber
auch als transportable Speichermedien eingesetzt
werden, z. B. als Rechnereinschübe oder als prozessorexterne Geräte mit z. B. SCSI-, FireWire- oder
USB-Kabelanschluss.
Bild 7-14. Zugriff auf den Plattenstapel eines Festplatten-
speichers
Bei Wechselplattenspeichern ist das Speichermedium eine einzelne, in einer starren Kunststoffhülle
untergebrachte, meist biegsame Scheibe/Folie, die
als sog. Diskette vom Laufwerk trennbar ist. Beispiele sind die Floppy-Disk, die Super-Disk und die
Zip-Diskette. Mittlerweile sind diese Wechselplattenspeicher durch die sog. Flash-Speicher weitgehend
vom Markt verdrängt worden, insbesondere durch
den USB-Stick (siehe unten).
Optische Plattenspeicher. Optische Plattenspeicher
nutzen als Informationsträger eine starre, rotierende Scheibe, die in Durchmesser (12 cm) und
Handhabung (Wechselmedium ohne Hülle) der
Audio-Compact-Disc (Audio-CD) entspricht. Auch
der Zugriff erfolgt wie bei ihr, berührungslos mit
Laserlicht. Zu unterscheiden sind im zeitlichen
Auftreten und mit zunehmenden Speicherkapazitäten
(1.) Speicher mit herkömmlicher CD-Technik, (2.)
die DVD und (3.) die Blu-ray Disc in Konkurrenz
zur High Definition DVD, jeweils mit Varianten
hinsichtlich Nurlesbarkeit (ROM, Read-Only Memory), Einmalbeschreibbarkeit (R, Recordable) und
Wiederbeschreibbarkeit (RW, ReWriteable).
Compact Disc (CD). Die CD-ROM ist ein nur lesbares Medium mit Kapazitäten von 650 Mbyte und
darüber. Wie bei Magnetplattenspeichern werden die
Daten in Sektoren und Spuren abgelegt, jedoch spiralförmig. Die Datendarstellung erfolgt durch Übergänge zwischen der Oberfläche (land) des Mediums
und in sie eingeprägte Vertiefungen (pits). Die CDROM dient zur Bereitstellung großer Datenmengen,
z. B. von Softwarepaketen, Lexika, Versandkatalogen
usw.
Die CD-R entspricht im Aufbau der CD-ROM, ist jedoch einmal (ggf. in mehreren Sitzungen) beschreibbar. Dabei werden die Vertiefungen durch den Laserstrahl in eine organische Schicht eingebrannt. Sie hat
Kapazitäten zwischen 650 und 900 Mbyte und dient
zur Datenarchivierung und auch zur Erstellung von
Audio-CDs.
Die CD-RW mit 700 Mbyte Speicherkapazität ist
wiederholt (ca. 1000-mal) beschreibbar. Gespeichert
werden die Daten mittels des Laserstrahls durch
unterschiedlich starke Erhitzung der Bitpositionen,
wodurch sich die Bitmuster als amorphe (un-
7 Rechnersysteme
strukturierte) bzw. kristalline Oberflächenbereiche
realisieren lassen, die den Laserstrahl beim Lesen
unterschiedlich gut reflektieren (Phase-ChangeVerfahren). Die CD-RW dient zur Sicherung von
Daten, die von Zeit zu Zeit aktualisiert werden, d. h.
als sog. Backup-Medium.
Die Übertragungsraten dieser Speicher werden als
Vielfache von 150 kbyte/s, der Übertragungsrate
einer Audio-CD, angegeben (z. B. Lesen einer
CD-ROM 52-fach, d. h. 7, 8 Mbyte/s).
Digital Versatile Disc (DVD). Die DVD-Speicherung
basiert auf der CD-Technik, hat aber eine sehr viel höhere Speicherkapazität von zunächst 4, 7 Gbyte. Diese wird durch geringeren Spurabstand und geringere
Pit-Abmessung erreicht. Sie kann durch Verwendung
von zwei übereinander liegenden Informationsschichten oder/und durch zusätzliche Nutzung der Rückseite der Scheibe auf 8,5 und 17 Gbyte erhöht werden. Entwickelt wurde die DVD zur Speicherung von
Video/Audio-Daten in komprimierter Form, sie wird
aber auch allgemein als Datenträger eingesetzt.
Bei den einmal- und den wiederbeschreibbaren
DVDs hat sich kein einheitlicher Standard durchgesetzt. Zwar gibt es Aufzeichnungsgeräte, die
alle vorhandenen Formate beherrschen; aber nicht
alle Formate sind mit allen Wiedergabegeräten
abspielbar. Die DVD-ROM ist wie die CD-ROM
nur lesbar. Sie wird bevorzugt zum Vertrieb von
Filmen eingesetzt. Die DVD-R, DVD+R, DVD-RW
und DVD+RW sind wie die CD-R bzw. CD-RW
nur einmal- bzw. wiederbeschreibbar. Sie werden
bevorzugt zur Aufzeichnung von Filmen genutzt und
lösen die VHS-Videoaufzeichnung ab. Die Datenraten für die Aufzeichnung und Wiedergabe werden in
Vielfachen von 1, 35 Mbyte/s angegeben, derzeit in
der Größenordnung 4-, 8-, 16- und 22-fach, abhängig
vom Format und von Aufzeichnung/Wiedergabe. –
DVD-Geräte übernehmen zusätzlich die Funktion
von CD-Geräten und lösen diese ab.
Blu-ray Disc (BD), High Definition DVD (HD
DVD). Beide Speicher arbeiten mit einem gegenüber
der DVD geänderten (blauen) Laserlicht, sodass
sie zu dieser inkompatibel sind. Hierdurch und
durch andere Veränderungen lassen sich jedoch die
Speicherkapazitäten bei einlagigen Medien auf ca.
30 Gbyte erhöhen. Bis zu acht Lagen sind derzeit
in der Erprobung. Die Datenrate wird in Vielfachen von 36 Mbit/s, d. h. 4, 5 Mbyte/s angegeben.
Eingesetzt werden diese Medien insbesondere für
Filmaufzeichnungen hoher Bildqualität.
Magnetooptische Plattenspeicher.
Magnetooptische Plattenspeicher (magneto-optical disks, MOs,
MODs) haben als Wechselmedium eine in einer
Hülle gelagerte Scheibe mit magnetisierbarer Oberfläche und einer Datenspeicherung in konzentrischen
Spuren. Für das Schreiben wird der Effekt genutzt,
dass ein ferromagnetischer Stoff bei Überschreitung
der sog. Curie-Temperatur seine Magnetisierbarkeit
verliert. Die Speicherstelle wird dazu mit einem
Laserstrahl punktuell erhitzt, und es wird eine magnetische Ausrichtung durch einen Elektromagneten
erzeugt. Für das Lesen wird der Kerr-Effekt genutzt,
wobei polarisiertes Licht unter Einfluss eines Magnetfeldes in seiner Polarisationsebene gedreht wird.
MOs haben derzeit Kapazitäten von 230 Mbyte bis
zu 2, 3 Gbyte (3,5 Zoll) und von 650 Mbyte bis zu
17,3 Gbyte (5,25 Zoll). Sie zeichnen sich insbesondere durch ihre häufige Wiederbeschreibbarkeit
(ca. 1 Million mal) bei großer Datensicherheit aus.
Sie werden zur Datensicherung und -archivierung
eingesetzt.
Magnetbandspeicher, Streamer.
Magnetbandspeicher verwenden als Datenträger ein flexibles
Kunststoffband, das auf einer Seite eine magnetisierbare Schicht trägt und zum Beschreiben und
Lesen an einem Schreib-/Lesekopf mit (bei manchen
Geräten ohne) Berührung vorbeigezogen wird. Das
Band – früher als sog. Langband auf einer offenen
Spule aufgewickelt – ist heute kompakt in einer
Kassette untergebracht. Während beim Langband
die Speicherung parallel erfolgte, z. B. 8 Bits plus
1 Paritätsbit (Bild 7-15a), gibt es bei den Kassetten zwei bitserielle Aufzeichnungstechniken: das
Längsspurverfahren (linear recording) mit spurweiser, serpentinenartiger Aufzeichnung (ggf. auch
mehrere Spuren parallel, z. B. 8), bei dem das Band
wechselweise in beiden Laufrichtungen betrieben
wird (Bild 7-15b), und das Schrägspurverfahren
(helical scan recording), bei dem die Schreib- und
Leseköpfe auf einer rotierenden, schräg zum Band
gestellten Trommel untergebracht sind (wie bei
97
98
Technische Informatik / Rechnerorganisation
chend ihrer Leistungsdaten (und Kosten) bei kleinen
Rechnersysteme (PCs) bis hin zu Hochleistungssystemen (Servern) eingesetzt. Da Streamer insbesondere
für die Datensicherung (backup) eingesetzt werden,
ist darauf zu achten, dass ihre Speicherkapazitäten
denen der zu sichernden Festplatten genügen.
Flash-Speicher. Im Gegensatz zu allen bisher ge-
Bild 7-15. Datenspeicherung auf Magnetband. a Langband
mit z. B. 9 parallelen Spuren; b Streamer-Band bei Längsspurverfahren mit serpentinenartigen Spuren; c StreamerBand bei Schrägspurverfahren
Video-Recordern, Bild 7-15c). Einige Kassetten
(cartridges) haben zwei Bandwickel (Spulen). Beim
Längsspurverfahren wird dann der Schreib-/Lesekopf
in einer Kassettenaussparung an das Band angelegt.
Beim Schrägspurverfahren wird hingegen das Band
aus der Kassette herausgeführt und mittel Führungsrollen um die Schreib-/Lesetrommel gelegt. Es gibt
aber auch Kassetten mit nur einem Wickel, sodass
mehr Platz für das Band zur Verfügung steht (STK-,
DLT- und LTO-Streamer). Hier ist der zweite Wickel
im Laufwerk untergebracht, und das Band wird
im Betrieb aus der Kassette herausgezogen, über
mehrere Rollen am Schreib-/Lesekopf vorbeigeführt
und am externen Wickel festgeklemmt. – Die Schrägspurtechnik bewirkt eine höhere Bandabnutzung als
die Längsspurtechnik.
Sowohl beim Längsspur- als auch beim Schrägspurverfahren erfolgt die blockweise Speicherung
– anders als beim Langband – nicht im Start-StoppBetrieb mit seiner aufwändigen Bandsteuerung,
sondern bei geringerem gerätetechnischen Aufwand mit kontinuierlichem Datenstrom, woraus die
Gerätebezeichnung Streamer resultiert.
Die Speicherkapazitäten von Streamern unterscheiden sich grundsätzlich nach der StreamerTechnologie aber auch innerhalb einer Technologie.
Heute (2012) sind Speicherkapazitäten von bis zu
fünf Tbyte erhältlich; durch Datenkompression
können diese Werte noch verdoppelt werden. Die Datenübertragung (Lese- und Schreibgeschwindigkeit)
liegt bei über 120 Mbyte/s. Streamer werden entspre-
nannten Hintergrundspeichern haben Flash-Speicher
als rein elektronische Speicher keine beweglichen
Teile. Ähnlich den DRAM-Speicherbausteinen wird
ein Bit durch eine elektrische Ladung dargestellt,
hier mittels eines Feldeffekttransistors (FET) mit
spezieller Struktur. Die besondere Eigenschaft dieses
Transistors ist, dass er die Ladung nach Abschalten
der Versorgungsspannung nicht verliert, also die
gespeicherte Information behält. Die Speicherinformation kann durch entsprechendes Ansteuern des
Transistors geschrieben und gelesen werden.
Die Art des Speichermediums, ein Halbleitersubstrat, hat unterschiedliche Erscheinungsformen von
Flash-Speichern zur Folge. So gibt es diese einerseits
als in Rechner und Peripheriegeräte integrierte
Flash-Bausteine (Flash ROMs) und andererseits als
externe Speicher, z. B. in Form von Steckkarten
kleiner Baugröße (Flash Card, Memory Card),
wie man sie insbesondere im Multimediabereich
findet (Audio, Bild, Video), und als sog. USBSticks (USB-Geräte) in Form eines verlängerten
USB-Steckers, wie man sie als Hintergrundspeicher
zur Datensicherung verwendet. Es gibt sie außerdem als Pufferspeicher in sog. Hybrid-Festplatten.
– Die Kapazitäten von Flash-Speichern reichen bis in
den mehrstelligen Gbyte-Bereich, wobei ggf. mehrere Flash-Bausteine zusammengeschaltet werden.
Die Lebensdauer der Speicherzellen liegt je nach
Technologie bei 10 000 bis zu mehreren 100 000
Lösch-/Schreibzyklen.
7.3 Ein-/Ausgabeorganisation
Die Ein-/Ausgabeorganisation umfasst die Hardware
und Software, um Daten zwischen Hauptspeicher
und Peripherie (Geräten, devices) zu übertragen.
Zur Peripherie gehören die Hintergrundspeicher
und die Ein-/Ausgabegeräte. Hinzu kommen anwendungsspezifische Ein-/Ausgabeeinheiten, z. B.
7 Rechnersysteme
zur Übertragung von Steuer-, Zustands-, Mess- und
Stellgrößen in der Prozessdatenverarbeitung. – Zur
Ein-/Ausgabeorganisation siehe vertiefend z. B. [2].
Funktionen. Abhängig vom Peripheriegerät um-
fasst ein Ein-/Ausgabevorgang meist
Aktionen, wie
mehrere
– Starten des Vorgangs, z. B. durch Starten des Geräts,
– Ausführen spezifischer Gerätefunktionen, z. B. Positionieren des Schreib-/Lesearms bei einem Magnetplattenspeicher,
– Übertragen von Daten, meist in Blöcken fester oder
variabler Byteanzahl,
– Lesen und Auswerten von Statusinformation, z. B.
zur Fehlererkennung und -behandlung,
– Stoppen des Vorgangs, z. B. durch Stoppen des Geräts.
Systemstrukturen. Die
Unterschiede der Arbeitsgeschwindigkeiten und Datendarstellungen
zwischen dem Systembus oder dem Ein-/AusgabeHub (Bild 7-1) und den Peripheriegeräten erfordern
eine Anpassung der Geräte an den Bus bzw. Hub
(Bild 7-16). Bei einfachen Geräteschnittstellen
geschieht dies durch passive Anpassbausteine (interfaces, i/o ports). Die Geräteinitialisierung, die
Übertragungssteuerung und die Statusauswertung
übernimmt dabei der Prozessor. Er kann durch eine
zusätzliche Steuereinheit mit Busmasterfunktion,
einen DMA-Controller (DMAC), unterstützt werden,
der ihn von der Datenübertragung entlastet. Bei
Geräteschnittstellen mit komplexeren Steuerungsabläufen werden aktive Anpassbausteine (host adapter,
hubs) eingesetzt. Sie entlasten den Prozessor, da
die Steuerung auf einer höheren Kommandoebene
erfolgt. Die Datenübertragung selbst wird auch hier
meist von einem DMA-Controller durchgeführt.
Sowohl die passiven als auch die aktiven Systembusbzw. Hub-Anschlüsse erfordern auf der Geräteseite
eine Elektronik gleicher Art. Diese bildet zusammen
mit der gerätespezifische Steuerungselektronik die
Gerätesteuereinheit (device controller).
Synchronisation. Da bei einem Ein-/Ausgabevorgang zwei oder mehr Steuereinheiten gleichzei-
Bild 7-16. Rechner mit unterschiedlichen Geräteanbindungen, jeweils parallel oder seriell und mittels Kabel übertragend. a Peripheriebus, b Punkt-zu-Punkt-Verbindung für
ein Einzelgerät, c kettenförmige und d baumförmige Struktur mit Punkt-zu-Punkt-Verbindungen zwischen den Geräten
tig aktiv sind, müssen die in ihnen ablaufenden
Prozesse, die entweder als Programm oder als
Steuerung realisiert sind, miteinander synchronisiert
werden. Synchronisation bedeutet hier „aufeinander warten“; das gilt sowohl auf der Ebene der
Einzeldatenübertragung (z. B. zwischen Interface
und Device-Controller) als auch auf der Ebene der
Block- oder Gesamtübertragung (z. B. zwischen
Prozessor und DMA-Controller). Die Synchronisation einer Einzeldatenübertragung erfordert
abhängig vom Zeitverhalten der Übertragungspartner
verschiedene Techniken. Unter der Voraussetzung,
dass der eine Partner immer vor dem anderen für
eine Übertragung bereit ist, genügt es, wenn der
langsamere Partner seine Bereitschaft signalisiert.
Haben beide Partner variable Reaktionszeiten, so
ist vor jeder Datenübertragung ein Signalaustausch
in beiden Richtungen erforderlich. Man bezeichnet
dieses Aufeinander-Warten auch als HandshakeSynchronisation (handshaking).
99
100
Technische Informatik / Rechnerorganisation
7.3.1 Prozessorgesteuerte Ein-/Ausgabe
Bei der prozessorgesteuerten Ein-/Ausgabe übernimmt der Prozessor im Zusammenwirken mit einem
(passiven) Interface die gesamte Steuerung eines
Ein-/Ausgabevorgangs, d. h. das Ausgeben und
Einlesen von Steuer- und Statusinformation sowie
das Übertragen und Zählen der einzelnen Daten.
Bild 7-17 zeigt dazu eine Konfiguration mit einem
Interface für byteweise Datenübertragung. Sie erfolgt über ein Pufferregister (data register, DR),
unterstützt durch zwei Steuerleitungen (ReadySignale RDY1, RDY2). Ein ladbares Steuerregister
(control register, CR) erlaubt das Programmieren unterschiedlicher Interface-Funktionen, z. B.
die Einsignal-Synchronisation, die HandshakeSynchronisation oder das Sperren und Zulassen
von Interruptanforderungen an den Prozessor. Ein
lesbares Statusregister (SR) zeigt dem Prozessor den
Interface-Status an, z. B. den Empfang des Signals
RDY2.
Der Ablauf einer Einzeldatenübertragung sei anhand
einer Byteausgabe an ein Peripheriegerät demonstriert. Der Prozessor schreibt das Byte in DR, sodass
die Bits auf dem peripheren Datenweg anliegen. Das
wird dem Gerät durch das (Bereitstellungs-)Signal
RDY1 angezeigt. Das Gerät übernimmt das Datum
in ein eigenes Register oder einen Pufferspeicher und
bestätigt die Übernahme durch das (Quittungs-)Signal
RDY2. Das Interface setzt daraufhin ein Ready-Bit in
SR und nimmt sein RDY1-Signal zurück; daraufhin
setzt auch das Gerät sein RDY2-Signal zurück. Das
Ready-Bit signalisiert dem Prozessor den Abschluss
Bild 7-17. Byteorientiertes Interface mit HandshakeSynchronisation
der Übertragung und kann von diesem entweder programmgesteuert, d. h. durch wiederholtes Lesen von
SR und Abfragen des Ready-Bits (busy waiting, polling), oder interruptgesteuert, d. h. durch Freigeben
des Ready-Bits als Interrupt-Request-Signal, ausgewertet werden.
7.3.2 DMA-Controllergesteuerte Ein-/Ausgabe
Ein DMA-Controller entlastet den Prozessor von
der Datenübertragung, indem er, von diesem einmal
initialisiert und gestartet, die Übertragung eines
Datenblocks oder mehrerer, miteinander verketteter
Datenblöcke selbstständig und parallel zur Verarbeitung im Prozessor durchführt. Man bezeichnet
diese Organisationsform, bei der die Daten ohne
Prozessoreingriff direkt zum/vom Speicher fließen, als Ein-/Ausgabe mit Direktspeicherzugriff
(direct memory access, DMA). Da die Übertragungssteuerung des DMA-Controllers (DMAC) in
Hardware realisiert ist, werden höhere Übertragungsgeschwindigkeiten als bei der prozessorgesteuerten
Ein-/Ausgabe erreicht.
Die für die Übertragung erforderlichen Parameter,
wie Speicheradresse, Interface-Adresse, Datenformat, Blockgröße und Übertragungsrichtung,
werden vom Prozessor bei der Initialisierung in
dafür vorgesehene Register des Controllers geladen.
Weitere Register speichern den Status. Ist ein solcher
Registersatz mehrfach vorhanden, so spricht man
von mehreren DMA-Kanälen. Sie benutzen das
Steuerwerk des Controllers im Multiplexbetrieb.
Die einzelnen Daten eines Blocks werden entweder
direkt übertragen, d. h. in jeweils einem Buszyklus,
indem der Controller gleichzeitig den Speicher über
den Adressbus und das Interface-Datenregister über
ein Anwahlsignal adressiert, oder indirekt, d. h. in
zwei aufeinanderfolgenden Buszyklen, indem der
Controller nacheinander beide Einheiten adressiert
und jedes einzelne Datum zwischenspeichert. Die
indirekte Technik ist zeitaufwändiger, ermöglicht
aber Speicher-zu-Speicher-Übertragungen, z. B. zur
Durchführung des Kompaktifizierens (siehe 8.3.3).
Die Synchronisation von Prozessor und DMAController auf der Ebene der Blockübertragung
erfolgt mit denselben Techniken wie bei der prozessorgesteuerten Einzeldatenübertragung, d. h.,
7 Rechnersysteme
der Controller signalisiert den Abschluss seiner
Übertragung entweder durch eine vom Prozessor
abzufragende Statusinformation oder durch eine
Unterbrechungsanforderung.
Während der Initialisierungsphase ist der DMAController Slave des Prozessors, sonst eigenständiger
Busmaster, der sich in einem Einbussystem den
Systembus mit dem Prozessor teilt. Bezüglich des
Buszugriffs hat er höhere Priorität als der Prozessor.
Arbeitet er im Cycle-stealing-Modus, so verdrängt
er den Prozessor jeweils für die Übertragung eines
Datums vom Bus und gibt danach den Bus bis zur
nächsten Übertragung frei (für langsame Übertragungen geeignet). Arbeitet er im Burst-Modus, so
belegt er den Bus für die Dauer einer Blockübertragung, wodurch der Prozessor für längere Zeit vom
Bus verdrängt wird (für schnelle Übertragungen
erforderlich). Siehe dazu auch 7.1.3, Busarbitration.
7.3.3 Ein-/Ausgabeprozessor
Ergänzt man den DMA-Controller-Baustein durch
einen eigenen Prozessor, der ein im Hauptspeicher
oder in einem lokalen Speicher stehendes Ein/Ausgabeprogramm ausführen kann, so erhält man
einen Ein-/Ausgabeprozessor. Dieser kann außer der
eigentlichen Datenübertragung auch eine Datenvorund Datennachverarbeitung vornehmen, z. B. eine
Datentransformation oder -formatierung. Ferner
kann er Statusmeldungen auswerten und so z. B.
bei fehlerhafter Übertragung eines Datenblocks
dessen Übertragung nochmal veranlassen. Für einen
Ein-/Ausgabevorgang stellt der Zentralprozessor
dabei nur noch ein Parameterfeld bereit, und der
Ein-/ Ausgabeprozessor führt diesen Vorgang anhand
dieser Angaben selbstständig durch.
Frühere Einheiten dieser Art mit einem auf die Ein/Ausgabe zugeschnittenen Kommandosatz wurden
als Ein-/Ausgabekanäle bezeichnet. Heutige Ein/Ausgabeprozessoren haben universelle Befehlssätze.
Sie sind vielfach zu Ein-/Ausgaberechnern ausgebaut,
indem sie zusätzlich zum DMA-Controller mit einem
lokalen Bus und lokalen Buskomponenten ausgestattet sind, z. B. mit einem Speicher für Programme
und Daten und mit verschiedenen Interfaces. Der
lokale Bus und die Systembusschnittstelle sind dabei
häufig standardisiert (z. B. PCI) und durch eine On-
chip-Bridge (PCI-zu-PCI) miteinander verbunden,
wodurch sich der Systementwurf vereinfacht. Modernere Konzepte sehen Punkt-zu-Punkt-Verbindungen
an der Bausteinschnittstelle vor.
7.3.4 Schnittstellen
Der Anschluss von Hintergrundspeichern und
Ein-/Ausgabegeräten an einen Rechner erfolgt auf
der Grundlage von Schnittstellenvereinbarungen,
d. h. mit standardisierten Geräteschnittstellen. Als
Vermittler fungieren Steuereinheiten (Host-Adapter,
Hubs, Bild 7-16); übertragen wird seriell oder
parallel.
Hinsichtlich der eingesetzten Standards hat sich in
den letzten Jahren ein vollständiger Wandel vollzogen. So wurden die inzwischen in die Jahre gekommenen, insbesondere bei PCs eingesetzten Schnittstellen PS/2 und RS-232-C (seriell) sowie Centronics
und IEEE-1284 alias Parallel Port (parallel) durch
serielle Schnittstellen, nämlich USB (d. h. USB 2.0
oder 3.0) und FireWire abgelöst. Desgleichen werden
die parallelen Geräteverbindungen IDE/ATA und SCSI durch deren serielle Nachfolger SATA und SAS
ersetzt. Zu diesen modernen Schnittstellen, die alle als Punkt-zu-Punkt-Verbindungen ausgelegt sind,
ggf. aber Busfunktion haben, siehe 7.1.6.
Für spezielle Geräteanbindungen in technischen
Umgebungen gibt es jedoch weiterhin herkömmliche serielle und parallele Schnittstellen, die es
erlauben, einfache Übertragungsprotokolle mit
geringem Hardwareaufwand zu realisieren. Als
serielle Schnittstellen mit Busfunktion seien hier I 2C
(Inter-Integrated Circuit) und das Serial Peripheral
Interface (SPI) erwähnt, die zur Kommunikation
zwischen Digitalbausteinen eingesetzt werden.
Sie arbeiten mit nur zwei bzw. drei Signal- und
Taktleitungen und erlauben Übertragungsraten bis
hin zu einigen Mbit/s. Sie sind zu ergänzen um
den große Bereich der Feldbusse (7.1.6). Als „universelle“ parallele Schnittstellen gibt es die sog.
Ein-/Ausgabetore (i/o ports), meist mit mehreren
8 Bit breiten Datenverbindungen, die aber auch
bitweise für die Übertragung von Steuersignalen
genutzt werden können. Sie sind als einfache, passive Interfaces realisiert und eignen sich sowohl
für parallele Übertragungen, synchronisiert durch
101
102
Technische Informatik / Rechnerorganisation
Steuersignale, als auch für die davon unabhängige
Übermittlung bitweiser Steuerinformation.
7.3.5 Ein-/Ausgabegeräte
Zu den Ein-/Ausgabegeräten zählen zum einen
die Hintergrundspeicher (7.2.4), zum anderen jene
Geräte, die die Mensch-Maschine-Schnittstellen für
das Ein- und Ausgeben von Zeichen (Programme,
Daten, Text) und von Grafik bilden. Dargestellt wird
die Information vorwiegend nach dem Rasterprinzip,
d. h. durch spalten- und zeilenweises Aufteilen der
Darstellungsfläche in Bildpunkte (picture elements,
pixels), die Schwarzweiß-, Grau- oder Farbwerte repräsentieren. Daneben gibt es das Vektorprinzip, d. h.
die Bilddarstellung durch Linien, deren Anfangsund Endpunkte durch x,y-Koordinaten festgelegt
sind. Hinzu kommen Multimediageräte mit digitalen
Daten für Sprache, Musik, Fotos und Filme.
Terminal. Ein Terminal besteht aus einer Tastatur
(keyboard) zur Eingabe von Zeichen im ASCII oder
EBCDIC und einer Bildschirmeinheit (Monitor, video
display) zur Ausgabe von Zeichen und Grafik. Als
Tastatur ist bei der deutschen ASCII-Version die sog.
MF2-Tastatur (Multifunktionstastatur) mit 102 Tasten
gebräuchlich.
Ältere Bildschirmeinheiten, sog. Röhrenmonitore,
haben eine Elektronenstrahlröhre zur Bilddarstellung
(cathode ray tube, CRT). Sie arbeiten nach dem
Punktrasterprinzip und stellen die Bildpunkte üblicherweise in Farbe, aber auch schwarzweiß oder in
Graustufen dar. Aufgebaut wird das Farbbild durch
drei Elektronenstrahlen (für Rot, Grün und Blau:
RGB), die zeilenweise über den Schirm geführt
werden und dabei eine Phosphorschicht beschreiben.
Die Positionen der Bildpunkte werden dabei durch
eine Loch- oder Streifenmaske vorgegeben. Wichtig
ist hier eine Bildwiederholfrequenz von wenigstens 75 Hz, um ein flimmerfreies Bild zu erhalten
(besser 85 Hz). Wichtig ist auch die Anzahl an
Bildpunkten, da sie die Bildauflösung bestimmt. Als
gering auflösend gelten die älteren Maße 640 × 480
(video graphics array, VGA) und 800 × 600 (super
VGA, SVGA); heute gebräuchlich sind 1024 × 768
(extended GA, XGA), 1280 × 1024 (super XGA,
SXGA) und 1600 × 1200 (ultra XGA, UXGA). Die
Bildflächengröße wird durch das Diagonalenmaß
bezeichnet, das einen nicht nutzbaren Bildrand mit
einschließt. Üblich sind hier Größen zwischen 15
und 22 Zoll. Der Vorteil von Röhrenmonitoren liegt
in der weitgehenden Farbkonstanz bei unterschiedlichen Blickwinkeln. Nachteile sind das Flimmern
(unbewusst wahrgenommen) und eine eingeschränkte
Bildschärfe.
Die Röhrenmonitore wurden inzwischen vielfach
durch die platz- und energiesparenden Flachbildschirme verdrängt. Ihre Leuchtfähigkeit basiert
entweder auf chemischen Substanzen, die unter Einfluss eines elektrischen Feldes Licht emittieren (wie
beim Elektrolumineszenz-Display) oder, als heute
vorherrschende Technik, Licht absorbieren (wie
beim Flüssigkristall-Display, liquid crystal display,
LCD), hier das Licht einer Hintergrundbeleuchtung.
Bei den heute weniger gebräuchlichen, passiven
LCDs (double super twisted nematic, DSTN) werden
dazu die Kristalle durch gitterförmig angeordnete
Leiterbahnen angesteuert. Bei den vorherrschenden
aktiven LCDs hat jeder Bildpunkt einen eigenen
Steuertransistor (thin film transistor, TFT), was eine
sehr gute (und schnelle) Bilddarstellung erlaubt.
Die Bildschirmdiagonalen liegen bei Notebooks
derzeit bei bis zu 17 Zoll mit Auflösungen von bis zu
1920 × 1200 Bildpunkten, bei Standgeräten sind auch
größere Diagonalen mit bis zu 23 Zoll (1920 × 1200)
oder auch 30 Zoll (2560 × 1600) gebräuchlich.
Gegenüber Röhrenmonitoren ist die durch das Diagonalmaß bezeichnete Bildfläche vollständig genutzt,
ein Bildflimmern ist nicht wahrnehmbar, und die
Bilddarstellung ist sehr scharf. Je nach Bildschirm
kann es jedoch eine starke Farbabhängigkeit vom
Blickwinkel geben. Außerdem ist das röhrenübliche 4:3-Format zugunsten der Bildschirmbreite
verändert.
Bildschirme werden durch Grafikeinheiten angesteuert. Diese speichern Bildinformation und unterstützen den rechenintensiven Bildaufbau durch Spezialhardware (Grafik-Controller). Moderne Grafikeinheiten weisen eine hohe Leistungsfähigkeit in der 3DDarstellung von Bewegtbildern auf.
Beamer. Beamer dienen zur Präsentation von
Bildschirminhalten (Festbilder, Bewegtbilder) mittels
Lichtstrahl auf einer Projektionsfläche, z. B. einer
7 Rechnersysteme
Leinwand. Dabei werden zwei Projektionstechniken
unterschieden. Bei der (älteren) LCD-Technik wird
mittels einer Metalldampflampe und monochromatischen Spiegeln das auf drei kleinen TFT-Schirmen
dargestellte Bild in drei Teilbilder in den Farben Rot,
Grün und Blau (RGB) erzeugt, diese dann in einem
Prisma zusammengefügt und über eine Linsenoptik
als Farbbild projiziert. Als Nachteil dieser Technik
können aufgrund der Bildzusammenfügung Konvergenzprobleme (Unschärfe) und bei Verschmutzen der
TFT-Schirme Farbstiche auftreten.
Bei der (neueren) DLP-Technik (digital light processing) wird das Bild mittels eines optischen Halbleiterbausteins erzeugt, der für jeden darzustellenden Bildpunkt einen sehr kleinen Spiegel hat. Diese Spiegel
reflektieren das Licht einer Lichtquelle und projizieren es über ein Linsensystem. Sie werden dabei abhängig von der digital vorliegenden Bildinformation
(Bildpunkthelligkeit) bzgl. des Lichteinfalls gekippt
und erzeugen so ein Helligkeitsspektrum zwischen
Weiß und Schwarz. Für die Farbdarstellung wird das
Licht nicht direkt, sondern durch ein Farbrad (RGB)
hindurch auf die Spiegel gelenkt, wodurch das Farbbild durch drei aufeinanderfolgende Teilbilder (RGB)
entsteht. Bei scharfem und kontrastreichem Bild können hier als Nachteil Farbschlieren auftreten. Beamer
der höheren Leistungsklasse, wie sie auch für die Kinoprojektion eingesetzt werden, verwenden für jeden
Farbanteil einen eigenen Spiegelbaustein und fassen
die so erzeugten drei Teilbilder wiederum durch ein
Prisma zusammen.
Beamer gibt es mit Bildauflösungen von VGA bis
UXGA (siehe oben: Terminal); gebräuchlich sind
1024×768 (XGA) Bildpunkte sowie zunehmend auch
Breitformate mit bis zu 1920 × 1200 Bildpunkten.
Die Güte der Bildprojektion hängt außerdem von der
Bildhelligkeit (gängig 4000 bis 8000 ANSI Lumen)
und vom Kontrast (gängig 400:1 bis 1000:1) ab.
Maus. Eine sog. Maus besteht aus einem handlichen Gehäuse, das auf dem Tisch (mechanische Maus
mit Rollkugel) bzw. auf einer reflektierenden Unterlage (optische Maus) verschoben wird und dessen Position auf einem Bildschirm durch eine Marke angezeigt wird. Über Tasten und ggf. ein Rändelrad
am Mausgehäuse können – im Zusammenwirken mit
der unterstützenden Software – bestimmte Funktio-
nen ausgelöst werden, z. B. das Anwählen von Feldern einer menügesteuerten Benutzeroberfläche, das
Fixieren von Bezugspunkten für grafische Objekte eines Zeichenprogramms oder das Scrollen bei Bildschirmfenstern (Verschieben des Ausschnitts).
Tablett (tablet). Das Tablett ist ein grafisches Ein-
gabegerät, das eine entsprechende Arbeitsweise wie
mit Bleistift und Papier erlaubt. Es erfasst die Position
eines mit der Hand geführten Stiftes auf einer rechteckigen Fläche und überträgt dessen x,y-Koordinaten
zum Rechner, der diese ggf. auf einem Bildschirm anzeigt. Die Erfassung der Koordinaten geschieht durch
galvanische, akustische, kapazitive, magnetische oder
magnetostriktive Kopplung von Stift und Fläche.
– Bei den sog. Tablet-PCs ist der Flachbildschirm
(eines Notebooks) um die Funktion des Tabletts
erweitert, d. h., der Bildschirm dient als Anzeige- und
als Eingabemedium, wobei beide Darstellungsebenen
gleichzeitig zur Anzeige kommen.
Scanner.
Ein Scanner tastet eine zweidimensionale Vorlage mittels Helligkeits- (Reflexionsgrad-)
Messung zeilenweise Punkt für Punkt ab und
speichert die Abtastwerte der Bildpunkte in PixelDarstellung. Diese kann auf einem Rechner weiterverwendet werden, entweder als Pixel-Grafik
(Grafik oder Text) oder nachbearbeitet als ASCIIoder EBCDIC-Textdarstellung. Bei der Nachbearbeitung wird die Pixel-Darstellung von einem
Segmentierungsprogramm in die Pixel-Bereiche
der einzelnen Zeichen zerlegt, die dann von einem
Zeichenerkennungsprogramm klassifiziert und im
ASCII oder EBCDIC dargestellt werden (optical
character recognition, OCR).
Drucker (printer).
Drucker dienen zur Ausgabe von
Text, codiert im ASCII oder EBCDIC, sowie von Grafik.
Nadeldrucker (Matrixdrucker) arbeiten als reine
Textausgabegeräte mit Zeichenmatrizen von z. B. 9
vertikalen Punkten (dots) bei ca. 3 mm Schriftzeichenhöhe. Die vertikalen Bildpunkte werden durch
„Nadeln“, die in einem horizontal bewegten Schreibkopf untergebracht sind, mittels eines Farbbandes
auf das Papier übertragen. Höher auflösende Nadeldrucker haben ein feineres Raster von z. B. 2 × 12
103
104
Technische Informatik / Rechnerorganisation
gegeneinander versetzten vertikalen Bildpunkten,
womit ein wesentlich besseres Schriftbild erreicht
wird. Darüber hinaus erlauben sie bei punktweisem
Walzenvorschub auch die Ausgabe von Grafik.
Nadeldrucker sind vergleichsweise langsam und laut
und liefern ein verhältnismäßig schlechtes Druckbild.
Sie haben jedoch den Vorteil eines dokumentenechten
Drucks mit Durchschlägen und werden nur noch
dafür eingesetzt.
Tintenstrahldrucker arbeiten wie Nadeldrucker mit
horizontal bewegtem Druckkopf und mit punktweiser Darstellung von Zeichen und Grafik. Anstelle der
Nadeln haben sie jedoch bis zu 24 oder 48 feine Düsen, über die Tintentröpfchen gezielt auf das Papier
gespritzt werden. Getrennte Tintenstrahlsysteme für
Schwarz, Gelb („Yellow“), Cyan und Magenta erlauben den Farbdruck. Tintenstrahldrucker zeichnen sich
durch hohe Auflösung und geringe Geräuschentwicklung aus.
Laserdrucker sind die aufwändigsten Drucker. Bei
ihnen wird die Information einer Druckseite durch
einen Laserstrahl punktweise auf eine photoleitende
Selenschicht auf einer rotierenden Trommel geschrieben. Ähnlich wie bei einem Kopiergerät wird diese Information durch Tonerpartikel auf Papier übertragen
und fixiert. Farb-Laserdrucker verwenden schwarzen
Toner sowie Toner in den drei Standardfarben Gelb
(„Yellow“), Cyan und Magenta. Laserdrucker zeichnen sich durch hohe Auflösung, große Präzision in
der Darstellung sowie durch verhältnismäßig hohe
Druckgeschwindigkeiten aus.
7.4 Parallelrechner
Die Geschwindigkeitssteigerungen der Rechner
basieren einerseits auf technologischen Fortschritten,
wie sie sich in steigenden Speicherkapazitäten und
Taktfrequenzen niederschlagen, und andererseits auf
strukturellen Entwicklungen, wie z. B. Parallelisierung von Abläufen in Prozessoren und Rechnern.
Nach einer groben, 1972 von Flynn eingeführten
Klassifizierung [3] gibt es hinsichtlich der Parallelität
von gleichzeitig wirkenden Befehls- und Datenströmen drei grundsätzliche Strukturformen. Mit
SISD (single-instruction, single-data, Bild 7-18a)
bezeichnet er Prozessoren, bestehend aus einer
Befehlseinheit I und einer Verarbeitungseinheit D,
Bild 7-18. Grobe Einteilung von Rechnern nach einfacher
(S) und mehrfacher (M) Befehls- (I) und Datenverarbeitung (D). a SISD; b SIMD; c MIMD. Die Pfeile symbolisieren den Steuerungsfluss
die die Befehle eines Programms nacheinander
ausführen. Solche Prozessoren arbeiten durchaus
auch parallel, aber nur „intern“, z. B. im Pipelining,
ggf. verbunden mit der Superskalar- oder der VLIWTechnik (siehe hierzu 5.5). Man bezeichnet sie aber
nicht als Parallelrechner.
Mit SIMD (single-instruction, multiple-data,
Bild 7-18b) bezeichnet Flynn Spezialprozessoren
(proprietäre Prozessoren) mit nur einer Befehlseinheit (I), aber mehreren Verarbeitungseinheiten (D)
mit lokalen Speichern, die von der Befehlseinheit
einheitlich gesteuert werden. Diese Prozessoren
werden bereits als Parallelrechner bezeichnet. Zu
ihnen zählen die Vektorrechner und die Feldrechner.
Als MIMD (multiple-instruction, multiple-data,
Bild 7-18c) bezeichnet er die echten Parallelrechner
mit mehreren eigenständigen, heute meist standardisierten Prozessoren. Je nachdem, ob es sich dabei
um gleichartige oder unterschiedliche Prozessoren
handelt, spricht man von homogenen bzw. inhomogenen Mehrprozessorsystemen. Hinsichtlich der
Kommunikation zwischen den Prozessoren unterscheidet man zwischen speichergekoppelten und
nachrichtengekoppelten Mehrprozessorsystemen.
– Zu den SIMD- und MIMD-Systemen siehe
vertiefend z. B. [10].
7.4.1 Vektorrechner
Vektorrechner (genauer: Vektorprozessoren) sind
charakterisiert durch Vektorbefehle, das sind Befehle, die eine ganze Reihe von Operandenpaaren
adressieren und in gleicher Weise verknüpfen. Unter
einem „Vektor“ wird hier ganz allgemein eine
geordnete Menge gleichartig zu behandelnder Ope-
7 Rechnersysteme
randen verstanden. Der Flynn’schen Klassifizierung
entsprechend müssten die Einzeloperationen eines
Vektorbefehls – üblicherweise Gleitpunktoperationen
– von entsprechend vielen Gleitpunktrecheneinheiten
ausgeführt werden (SIMD), tatsächlich erfolgt die
Ausführung jedoch effizienter in Fließbandverarbeitung in einer sog. Vektor-Pipeline. Im Gegensatz
zu einer „skalaren“ Gleitpunktrecheneinheit mit
Pipelining, die in Vektorrechnern meist zusätzlich
vorhanden ist und die pro Befehl nur ein Operandenpaar verarbeitet, werden in die Vektor-Pipeline
nach der Initiierung eines Befehls nacheinander
sämtliche Vektorelemente eingespeist. Da dies
taktweise geschieht, muss eine genügend schnelle
Versorgung der Pipeline gewährleistet sein, z. B.
durch Vektorregister (Registerspeicher für einen
gesamten Vektor) und durch breite Verbindungswege
zum Hauptspeicher, unterstützt durch parallele
Zugriffsmöglichkeiten auf diesen (ggf. durch dessen
Aufteilung in Speicherbänke).
Vektorprozessoren haben meist mehrere, parallel arbeitende Vektor-Pipelines, die ggf. auch miteinander verkettet werden können (vector chaining). Dabei werden die Elemente eines Resultatvektors einem
nachfolgenden Vektorbefehl als Elemente zugeführt,
noch bevor die Ausführung des ersten Vektorbefehls
abgeschlossen ist.
Vektorrechner bestehen üblicherweise aus einer
Vielzahl von Vektorprozessoren (z. B. als MIMDStruktur), womit sich sehr hohe Rechenleistungen
erzielen lassen. Sie eignen sich insbesondere für
Spezialaufgaben, beispielsweise zur Lösung großer
Differenzialgleichungssysteme, wie sie bei der
Simulation kontinuierlicher Vorgänge auftreten (z. B.
Wettervorhersage, Strömungssimulation).
7.4.2 Feldrechner
Feldrechner (array computers) sind wie Vektorrechner für eine hohe Rechenleistung ausgelegt. Anders
als diese führen sie jedoch aufeinanderfolgende Operationen nicht überlappend aus, sondern führen gemäß dem „reinen“ SIMD-Prinzip einen Befehl gleichzeitig mit vielen Operandenpaaren aus. Feldrechner
haben dementsprechend zahlreiche gleiche Verarbeitungseinheiten, die zentral und taktsynchron gesteuert
werden. Diese sind als Zeile, Gitter oder Quader re-
gelmäßig angeordnet und haben üblicherweise lokale
Speicher. Nachbarschaftsbeziehungen werden für den
Datenaustausch genutzt. Die Verarbeitung erfolgt im
Format der Wortlänge, z. B. mit 64 Bit, aber auch mit
nur 1 Bit.
Feldrechner dienen hauptsächlich zur Lösung numerischer Probleme. Das Prinzip des Feldrechners ist auch
in heutigen Standardprozessoren als Zusatz zur Unterstützung von Multimedia-Anwendungen (MMX,
SSE) zu finden.
7.4.3 Speichergekoppelte Mehrprozessorsysteme
Ein
speichergekoppeltes
Mehrprozessorsystem
(SMP, shared memory processed system) besteht
aus mehreren, meist gleichen Prozessoren, die sich
den Adressraum des Hauptspeichers teilen. Die
Verbindungsstruktur ist entweder ein Bus oder
ein Kreuzschienenverteiler. Darüber hinaus gibt es
Varianten dieser Strukturen, basierend auf Punktzu-Punkt-Verbindungen wie HyperTransport, Quick
Path Interconnect und PCI-Express (7.1.5).
Der Bus kann ein für diese Struktur ausgelegter
Prozessorbus sein (Bild 7-19a), der es erlaubt, die
Adressbus- und Datenbuszuteilung voneinander zu
trennen und Adress- und Datenübertragungen für
mehrere Prozessoren zeitlich überlappend auszuführen mit dem Ziel einer effizienten Busnutzung (split
transactions, siehe auch 7.1.4).
Bei einem Kreuzschienenverteiler (crossbar switch,
Bild 7-19b) ist der Hauptspeicher in Module gegliedert, die mit den Prozessormodulen über eine Matrix
von Schaltern verbunden sind.
Beim Bus wie beim Kreuzschienenverteiler wird die
Anzahl der direkten Speicherzugriffe durch den Einsatz von L2-Caches großer Kapazität gering gehalten
(siehe 7.2.3). Um dabei dennoch die Datenkohärenz
zu gewährleisten, müssen sich die Cache-Controller
bei Cache- und Speicherzugriffen untereinander
verständigen, z. B. mittels des sog. MESI-Protokolls.
Dieses steuert übergreifend die Zugriffe auf den
gemeinsamen Adressraum, wobei es die jeweiligen
Zustände ggf. adressierter Cache-Einträge auswertet
(MESI: modified, exclusive, shared, invalid; siehe
z. B. [2, 4]). – Da der Speicherzugriff in solchen
Systemen im Prinzip von jedem Prozessor aus gleich
105
106
Technische Informatik / Rechnerorganisation
Bild 7-20. Speichergekoppeltes Mehrprozessorsystem mit
NUMA-Architektur (als über Links verbundene UMASysteme gemäß Bild 7-19 a). Verteilter gemeinsamer
Speicher mit unterschiedlich schnellen Zugriffen für die
Prozessoren
Bild 7-19. Speichergekoppelte Mehrprozessorsysteme mit
UMA-Architektur bei unterschiedlichen Verbindungsstrukturen. a Gemeinsamer Bus (z. B. erweitertes Einprozessorsystem nach Bild 7-1 b); b Kreuzschienenverteiler. Je-
weils gemeinsamer Speicher mit gleich schnellen Zugriffen für alle Prozessoren
schnell ist, spricht man auch von UMA-Architektur
(uniform memory access).
Der Vorteil eines speichergekoppelten Mehrprozessorsystems liegt in dem relativ einfachen Übergang
vom Ein- zum Mehrprozessorsystem, bei dem im Wesentlichen das Betriebssystem dafür zu sorgen hat,
dass die bisher auf einem einzigen Prozessor quasiparallel ausgeführten Prozesse auf die nun mehrfach vorhandenen Prozessoren verteilt werden. Dabei
sind alle Prozessoren gleichrangig, auch bei der Entscheidung, auf welchem Prozessor das Betriebssystem läuft. Der Nachteil der UMA-Architektur ist die
eingeschränkte Skalierbarkeit, d. h. die begrenzte Erweiterbarkeit um zusätzliche Prozessoren.
Mit dem Einsatz von Mehrkernprozessoren (multicore processors) entstehen kompakte UMAStrukturen, bei denen die Prozessoren bzw. Prozessorkerne und deren Caches sowie die Verbindungsstruktur und ggf. der DRAM-Controller
innerhalb eines einzigen Chips aufgebaut sind.
Die Skalierbarkeit läßt sich verbessern, indem man
Rechnerknoten mit lokalen Speichern bildet und sie
über spezielle Interfaces (links) miteinander verbindet (Bild 7-20). Dabei haben die Prozessoren wieder
einen gemeinsamen Adressraum, der Hauptspeicher
ist jetzt jedoch auf die Knoten verteilt. Man bezeichnet ein solches System deshalb auch als DSM-System
(distributed shared memory) oder, da die lokalen bzw.
nichtlokalen Speicherzugriffe unterschiedlich schnell
sind, als NUMA-Architektur (non-unified memory access). Ein NUMA-Knoten kann z. B. aus mehreren
Prozessoren in UMA-Architektur bestehen.
7.4.4 Nachrichtengekoppelte
Mehrprozessorsysteme
Ein nachrichtengekoppeltes (lose gekoppeltes)
Mehrprozessorsystem ähnelt in seiner Struktur der
NUMA-Architektur (Bild 7-21), besteht also aus
Rechnerknoten (Prozessor-Speicher-Paaren), die über
ein Verbindungsnetz miteinander gekoppelt sind.
Hier gibt es jedoch keinen gemeinsamen Adressraum,
vielmehr sind die lokalen Speicher nur von ihren
jeweiligen lokalen Prozessoren aus zugreifbar. Die
Kommunikation zwischen den Rechnerknoten ist
daher nur durch Versenden von Nachrichten mittels
Bild 7-21. Serielle Datenübertragung, a asynchron; b syn-
chron
7 Rechnersysteme
des Verbindungsnetzes möglich. Sie wird, um die
Prozessoren zu entlasten, durch spezielle Kommunikationshardware unterstützt. Die Unabhängigkeit
der Rechnerknoten wird durch ein jeweils eigenes
Betriebssystem unterstrichen.
Der Vorteil nachrichtengekoppelter Systeme liegt in
ihrer fast unbegrenzten Skalierbarkeit. Gebräuchlich
sind Systeme mit bis zu hunderttausend Rechnerknoten (häufig auch SMP-Systeme als Knoten)
unter Verwendung von standardisierten Mikroprozessoren. Wegen der damit erreichbaren hohen
Parallelität spricht man auch von MPP-Systemen
(massive parallel processing). Ihr Nachteil liegt in
der Programmierung, da die Parallelisierung der
Anwendersoftware individuell vorzunehmen ist.
Eingesetzt werden MPP-Systeme zur Lösung von
Spezialaufgaben hoher Komplexität, z. B. im Bereich
der Simulation.
7.5 Rechnernetze
Rechner sind heute in hohem Maße in Rechnernetzen miteinander verbunden, mit der Möglichkeit der
Kommunikation zwischen den Rechnern wie auch deren Benutzern. Gegenüber dem isolierten Einsatz von
Rechnern reicht das Spektrum der zusätzlichen Möglichkeiten vom „Resource-Sharing“, d. h. der gemeinsamen Benutzung von Geräten und Dateien, bis hin
zum weltweiten Informationsaustausch im Internet.
Grundlegend unterscheidet man zwischen lokalen Netzen (inhouse nets, local area networks,
LANs) und Weitverkehrsnetzen (Telekommunikationsnetzen, wide area networks, WANs). LANs sind
Verbunde von geringer räumlicher Ausdehnung meist
innerhalb von Gebäuden oder Grundstücken, die
von einzelnen Nutzern (Unternehmen, Institutionen)
betrieben werden. WANs haben landesweite Ausdehnung (üblicherweise flächendeckend) und werden
meist von den Telekommunikationsgesellschaften
betrieben. GANs haben mittels Überseekabel und
Satelliten globale Reichweiten und stehen üblicherweise im Verbund mit den regionalen WANs. Als
Zwischenform von LAN und WAN gibt es die sog.
Metronetze (metropolitan area networks, MANs).
Sie sind Stadtnetze (Regionalnetze) und wirken
innerhalb oder zwischen Ballungsräumen, oft als
Hochgeschwindigkeitsnetze. – Zu Rechnernetzen
siehe z. B. [9].
7.5.1 Serielle Datenübertragung
Um den Leitungsaufwand in den Netzen gering zu
halten, werden Daten grundsätzlich bitseriell übertragen, wobei die Bitfolge einem definierten Zeitraster
zugeordnet wird.
Bei asynchron-serieller Übertragung werden wahlweise fünf bis acht Datenbits eines Zeichens (ggf.
plus Paritätsbit) in einem zur Synchronisation benötigten Rahmen (frame), bestehend aus einem Startbit und einem oder mehreren Stoppbits, zusammengefasst (Bild 7-21a). Bei der Übertragung aufeinanderfolgender Zeichen synchronisieren sich Sender und
Empfänger bei jedem Zeichen anhand der fallenden
Flanke des Startbits, sodass der Zeitabstand zwischen
zwei Zeichen beliebig variieren darf.
Bei der synchron-seriellen Übertragung bilden
die Datenbits aufeinanderfolgender Zeichen einen
lückenlosen Bitstrom (Bild 7-21b) und die Synchronisation geschieht laufend anhand der Pegelübergänge
im Datensignal. Um auch bei längeren 0- oder
1-Folgen Pegelwechsel zu erzwingen, werden besondere Signalcodierungen verwendet, oder es werden
vom Sender zusätzliche Synchronisationszeichen
oder -bits eingefügt (character/bit stuffing). Diese
Einfügungen werden vom Empfänger erkannt und
eliminiert. Die Übertragung erfolgt blockweise, wozu
auf logischer Ebene Rahmen mit ggf. zusätzlicher
Adressierungs- und Steuerinformation gebildet
werden.
Die reziproke Schrittweite des Zeitrasters bezeichnet man als Schrittgeschwindigkeit (Einheit
Baud, Bd). Die Übertragungsgeschwindigkeit oder
Übertragungsrate in bit/s kann ein Vielfaches der
Schrittgeschwindigkeit betragen, nämlich dann,
wenn pro Taktschritt mehrere Bits codiert übertragen
werden. Die erreichbare Übertragungsrate hängt
u. a. von der Leitungsart ab. Sie wächst in folgender
Reihenfolge: unverdrillte Doppelader (Telefonleitung, Kupfer), nichtabgeschirmte oder abgeschirmte
verdrillte Doppelader (unshielded/shielded twisted
pair, UTP/STP, Kupfer), Koaxialkabel (Kupfer),
Lichtwellenleiter (Glasfaser).
7.5.2 Weitverkehrsnetze (WANs)
Strukturen. Bei Weitverkehrsnetzen besteht das
Übertragungssystem aus Übertragungsleitungen und
107
108
Technische Informatik / Rechnerorganisation
Vermittlungseinrichtungen, sog. Knotenrechnern
(Vermittlungsrechner, interface message processor,
IMP), die für die Durchschaltung der Übertragungsleitungen zuständig sind. Die Datenübertragung zwischen zwei Anwendungsrechnern erfolgt jeweils über
die beiden ihnen zugeordneten Knotenrechner. Sind
diese zwei Knotenrechner nicht direkt miteinander
verbunden, so müssen die Daten über andere Knotenrechner geleitet werden (Punkt-zu-Punkt-Netz).
Bei den paketvermittelnden Netzen erfolgt dieser
Transport in Datenpaketen, die in den Knotenrechnern zunächst zwischengespeichert und dann
weitergeleitet werden (store-and-forward network,
packet switching, connectionless). Zusammengehörige Pakete können dabei unterschiedliche Wege
mit unterschiedlichen Laufzeiten durchlaufen, je
nachdem welche Verbindungsleitungen zwischen
den Netzknoten jeweils verfügbar sind (Wegewahl,
routing). Sie werden dabei nummeriert und können
so dem Empfänger in der korrekten Reihenfolge
zugestellt werden.
Bei den leitungsvermittelnden Netzen hingegen wird
für die Dauer der Datenübertragung ein Verbindungsweg fest durchgeschaltet (connection-oriented).
Den Punkt-zu-Punkt-Netzen stehen die sog.
Broadcast-Netze gegenüber. Bei ihnen werden
die Datenpakete vom Sender gleichzeitig an mehrere
oder an alle Netzteilnehmer übertragen, und der
gemeinte Empfänger erkennt an der mitgelieferten
Adressinformation, dass die Nachricht für ihn bestimmt ist. Typisch sind hier Funkübertragungen,
z. B. mittels Satelliten.
Datenfernübertragung.
Als Datenfernübertragung
(DFÜ) bezeichnet man die Datenübertragung
zwischen sog. Datenendgeräten (Datenendeinrichtungen, DEE), z. B. zwei Rechnern, unter Benutzung
von WANs, d. h. von Übertragungsleitungen und
Vermittlungseinrichtungen der Telekommunikationsgesellschaften. Die Anpassung der Datenendgeräte
an die Signaldarstellung und Übertragungsvorschriften dieser Unternehmen erfordert an der
Teilnehmerschnittstelle Datenübertragungseinrichtungen (DÜE), die bei analoger Übertragung als
Modems und bei digitaler Übertragung als Datenanschlussgeräte oder als Adapter bezeichnet werden
(Bild 7-22).
Bild 7-22. Datenfernübertragung zwischen zwei Datenend-
einrichtungen (DEE) mittels zweier Datenübertragungseinrichtungen (DÜE). Als Übertragungssystem wurde das analoge Fernsprechnetz angenommen, dementsprechend sind
als DÜE Modems eingesetzt
Protokolle. Die Kommunikation zwischen zwei
Rechnern bzw. Anwenderprozessen umfasst zahlreiche Funktionen, die von den übertragungstechnischen
Voraussetzungen bis zu den logischen und organisatorischen Vorgaben auf der Anwenderebene reichen.
Hierzu gehören: Aufbau, Aufrechterhaltung und Abbau einer Verbindung, Übertragen eines Bitstroms,
Aufteilen eines Bitstroms in Übertragungsblöcke,
Sichern der Datenübertragung sowie Fehlerbehandlung, Wegewahl im Netz, Synchronisieren der
Übertragungspartner, Herstellen einer einheitlichen
Datenrepräsentation, Aufteilen der zu übertragenden
Information in logische und physikalische Abschnitte
usw. Zur Beherrschung dieser Komplexität wurde von
der ISO das OSI-Referenzmodell entwickelt (Open
Systems Interconnection), das die Kommunikation in
sieben hierarchischen Schichten (layers) beschreibt.
Analoges Fernsprechnetz. Das analoge Fernsprech-
netz (Telefonnetz) ist das älteste öffentliche Telekommunikationsnetz und hat eine relativ geringe
Übertragungsleistung entsprechend dem für Sprachsignale erforderlichen schmalen Frequenzbereich
von 300 bis 3400 Hz. Der Netzzugang erfolgt über
ein Modem (Modulator/Demodulator), welches das
zu sendende digitale Signal in ein analoges und
das zu empfangene Analogsignal in ein digitales
umsetzt. Das Analogsignal wird mit einer in diesem
Frequenzbereich liegenden sinusförmigen Trägerschwingung erzeugt, auf den das digitale Signal
aufmoduliert wird. Man benutzt hier die aus der
Nachrichtentechnik bekannten Modulationsarten
Amplituden-, Frequenz- und Phasenmodulation.
Setzt man zur Signalcodierung mehr als zwei Amplituden, zwei Frequenzen oder zwei Phasenwinkel ein
oder kombiniert man die Modulationsverfahren, so
7 Rechnersysteme
lassen sich pro Übertragungstaktschritt mehr als nur
ein Bit codieren.
Mit solchen Verfahren erreicht man bei der für das
analoge Fernsprechnetz kennzeichnenden Schrittgeschwindigkeit von 2400 Baud Übertragungsraten von
bis zu 9600 bit/s (V.32-Modem). Durch Erhöhung
der Bitanzahl pro Taktschritt erreicht man bis zu
14 400 bit/s (V.32bis) und bei zusätzlicher Erhöhung
der Schrittgeschwindigkeit bis zu 33 600 bit/s (V.34).
Mit einer anderen Technik, bei der die Daten nicht
analog durch modulierte Schwingungen, sondern digital als diskrete Spannungspegel dargestellt werden,
erreicht man bis zu 56 000 bit/s (56k-Modem, V.90).
Digitales Fernsprechnetz, ISDN. Das analoge Fern-
sprechnetz wird durch das jüngere, digitale Netz
ISDN (Integrated Services Digital Network) ergänzt
bzw. ersetzt. Das ISDN erlaubt den direkten digitalen
Netzzugang und bietet dazu für jeden Anschluss
zwei (logische) Übertragungskanäle (B-Kanäle)
zur gleichzeitigen Nutzung mit Übertragungsraten
von je 64 kbit/s (ca. 8 000 Zeichen pro Sekunde)
sowie einen Signalisierkanal (D-Kanal) mit 16 kbit/s,
an die in einer Art Busstruktur bis zu acht Datenendgeräte, einschließlich Telefon, angeschlossen
werden können. Die höhere Übertragungsleistung
des ISDN ergibt sich durch eine Erweiterung des
nutzbaren Frequenzbereichs auf bis zu 120 kHz
bei digitaler Datendarstellung (digital subscriber
line, DSL; Subscriber: Fernmeldeteilnehmer). Für
die Teilnehmeranbindung an das Netz werden wie
beim analogen Fernsprechnetz Zweidrahtleitungen
verwendet.
Mit beiden Fernsprechnetzen steht eine Vielzahl von
Diensten für die Übertragung von Sprache, Daten,
Text, Festbildern und bedingt auch von Bewegtbildern zur Verfügung, so u. a. das Fernsprechen (Telefon, auch Bildtelefon) und das Fernkopieren (Telefax). Generell handelt es sich hierbei um relativ geringe Datenaufkommen.
Reine Datennetze. Für höhere Datenaufkommen
gibt es reine Datennetze, bei denen der Zugang
entweder über analoge oder digitale Fernsprechanschlüsse oder über spezielle Netzanschlüsse
(Twisted-pair-, Koaxial- oder Glasfaserkabel) erfolgt.
Im Gegensatz zu der bei den beiden Fernsprechnet-
zen gebräuchlichen Leitungsvermittlung arbeiten sie
üblicherweise mit Paketvermittlung. Die hier verfügbaren Übertragungsraten reichen bis zu einigen
Gbit/s. – Technisch möglich und erprobt sind bei
Lichtwellenleitern Übertragungsgeschwindigkeiten
im THz-Bereich.
xDSL-Techniken. Die Übertragungsraten von analo-
gen und von ISDN-Teilnehmeranschlüssen können
wesentlich erhöht werden, wenn auf der Leitungsverbindung zwischen Teilnehmer und Vermittlungsstelle
keine zusätzliche Frequenzbandbegrenzung durch
Signalverstärker auftritt und somit breitere bzw.
mehr Frequenzbänder genutzt werden können (d. h.
oberhalb von 3400 Hz bzw. 120 kHz). Hierfür
kommen verschiedene Verfahren digitaler Datendarstellung zum Einsatz, die unter dem Begriff xDSL
zusammengefasst werden (Tabelle 7-3). Sie sind
eine Weiterentwicklung des für ISDN genutzten
DSL-Verfahrens (x steht als Platzhalter für das
jeweilige Verfahren) und dienen insbesondere dem
Internet-Zugang, was eine ggf. höhere Datenübertragungsrate für den Rückkanal (Daten aus dem Netz,
download) als für den Hinkanal (upload) rechtfertigt
(asymmetrische Übertragung). Die Signaldarstellung
erfolgt entweder im Basisbandverfahren (unmoduliertes Digitalsignal) oder im Breitbandverfahren
(modulierter Träger).
In Deutschland ist das asymmetrische ADSL am weitesten verbreitet, mit Übertragungsraten von bis zu
Tabelle 7-3. xDSL-Techniken und ihre Übertragungsra-
ten. Die tatsächlich erreichbaren Werte hängen von der
Leitungsdämpfung ab (zu überbrückende Entfernung, Leitungsqualität). Benötigt wird jeweils ein Adernpaar, bei
HDSL ggf. zwei oder drei (zur Verringerung von Störeinflüssen). Im Vergleich dazu die maximalen Modem- und
ISDN-Datenübertragungsraten
xDSL-Technik
ADSL (asymmetric)
ADSL2 (asymmetric)
HDSL (high data rate)
SDSL (single line)
VDSL2 (very high data rate)
Modem
ISDN
Übertragungsrate/Mbit/s
Hinkanal
Rückkanal
0,512
6
1
16 (20)
1,54 oder 2 1,54 oder 2
3
3
5 (10)
25 (50)
0,033
0,056
0,144
0,144
109
110
Technische Informatik / Rechnerorganisation
6 Mbit/s im Rückkanal, bei den neueren Varianten
ADSL2 mit bis zu 12 und ADSL2+ mit bis zu 16 und
20 Mbit/s. Es erlaubt Datenübertragung und Telefonie gleichzeitig; zur Signaltrennung ist zum ADSLModem ein sog. Splitter erforderlich. VDSL2 schließt
sich in der Leistung an ADSL2+ an, mit sowohl symmetrisch als auch asymmetrisch spezifizierten Übertragungsraten. Als erreichbarer Spitzenwert werden
200 Mbit/s angegeben; derzeitige Realisierungen sehen bis zu 50 Mbit/s vor. Der Bedarf wird im Übertragen von hochaufgelöstem Fernsehen (HDTV) gesehen. – DSL-Modems werden entweder direkt mit
dem PC verbunden (USB, Punkt-zu-Punkt-Ethernet),
oder sie sind als Router eines lokalen Netzes ausgelegt (WLAN, Ethernet-Switch mit mehreren Ports).
7.5.3 Lokale Netze (LANs)
Lokale Netze sind in ihrem Ursprung BroadcastNetze mit Bus- oder Ringstruktur, die im Gegensatz
zu Weitverkehrsnetzen keine Knotenrechner enthalten. Statt dessen hat jeder Anwendungsrechner eine
Netzsteuereinheit (network controller) in Form einer
Interface-Karte (siehe LAN-Controller in Bild 7-1b),
und das Übertragungssystem besteht im einfachsten
Fall (Bus) nur aus einer Leitung. Davon abweichend
haben heute Stern- und Baumstrukturen eine große
Verbreitung (strukturierte Verkabelung). Hier kommen dann Vermittlungseinheiten zum Einsatz. – Zu
beachten ist, dass die logische Struktur eines LAN
von den im Folgenden betrachteten physikalischen
Strukturen durchaus verschieden sein kann.
Ring. Bei der Ringstruktur (Token Ring) sind alle
Teilnehmer punkt-zu-punkt in einem Ring miteinander verbunden. Die Kommunikation erfolgt
in fest vorgegebenem Umlaufsinn, wobei freie
Übertragungskapazitäten durch umlaufende Marken
(tokens) gekennzeichnet werden (Bild 7-23a). Der
sendende Teilnehmer übergibt einem freien Token
seine Nachricht zusammen mit seiner Adresse und
der des Empfängers. Der durch die Empfängeradresse angesprochene Netzteilnehmer übernimmt die
Nachricht und übergibt dem Token eine Empfangsbestätigung für den Sender, nach deren Empfang dieser
das Token wieder freigibt. Vorteil der Ringstruktur
ist die kollisionsfreie Übertragung, was eine gute
Bild 7-23. LAN-Strukturen. a Ring; b Bus; c Stern
Nutzung der Übertragungsraten erlaubt, sowie der
geringe Aufwand. Sie hat jedoch den Nachteil, dass
bei nur einer Unterbrechung im Ring der gesamte
Ring funktionsunfähig wird.
Ein Beispiel für die Ringstruktur ist der Token-Ring
von IBM mit Übertragungsraten von 4 oder 16 Mbit/s
(IEEE 802.5). Ein weiterer Token-Ring, das FDDI
(Fiber Distributed Data Interface), vermeidet den
oben geschilderten Nachteil durch Verdoppelung des
Rings (ANSI X3T9.5). Die maximale Länge für den
„primären“ wie auch für den „sekundären“ Ring beträgt hier 200 km, die Übertragungsrate 100 Mbit/s.
FDDI gilt als Hochgeschwindigkeits-LAN und wird
insbesondere auch als sog. Backbone eingesetzt, um
LANs miteinander zu verbinden.
Bus. Bei
der Busstruktur kommunizieren die
Teilnehmer über einen Bus als passiven Vermittler
(Bild 7-23b). Das Übertragungsmedium ist z. B. ein
Koaxialkabel, an das auch während des Betriebs des
Netzes neue Teilnehmer angeschlossen werden können. Der Ausfall eines Teilnehmers beeinträchtigt die
Funktionsfähigkeit des Netzes nicht. Die Möglichkeit
des gleichzeitigen Zugriffs auf den Bus macht jedoch
eine Strategie zur Vermeidung von Kollisionen
erforderlich. So muss (ähnlich der Busarbitration in
Rechnern) ein Sender vor Sendebeginn anhand des
Signalpegels auf dem Bus zunächst prüfen, ob der
Bus frei ist; und er muss mit Sendebeginn erneut
prüfen, ob nicht gleichzeitig ein zweiter Sender
zu senden begonnen hat. Im Konfliktfall muss er
die Übertragung aufschieben bzw. abbrechen, um
sie z. B. nach einer zufallsbestimmten Wartezeit
erneut zu versuchen. Ein Verfahren mit dieser
Organisationsform ist das CSMA/CD-Verfahren
(Carrier Sense Multiple Access/Collision Detection),
nach dem das weit verbreitete Ethernet von Xerox
mit einer Übertragungsrate von 10 Mbit/s arbeitet
7 Rechnersysteme
(IEEE 802.3). Derzeit sind Ethernet-Varianten mit
Übertragungsraten von 100 Mbit/s (Fast-Ethernet),
1 Gbit/s (Gigabit-Ethernet) und 10 Gbit/s (10Gigabit-Ethernet) mit üblicherweise Stern- oder
Baumstruktur im Einsatz.
Eine andere Organisationsform hat der Token-Bus, bei
dem wie beim Token-Ring ein Token zyklisch weitergereicht wird, wodurch sich eine eindeutige Zuteilung
der Sendeberechtigung ergibt (IEEE 802.4). Ein Beispiel ist das MAP (manufacturing automation protocol) mit Übertragungsraten von 1, 5 bis zu 20 Mbit/s.
Stern und Baum. Bei der Sternstruktur sind alle
Teilnehmer über einen zentralen Knoten (als passive
oder aktive Vermittlungseinrichtung) miteinander
verbunden, über den die gesamte Kommunikation
läuft (Bild 7-23c), z. B. einen Hub oder einen Switch
(siehe unten). Diese Technik birgt zwar eine größere
Ausfallgefahr des gesamten Netzes, erleichtert aber
den Netzaufbau und die Netzerweiterung wesentlich.
Sie wird deshalb z. B. bei heutigen Ethernet-LANs
bevorzugt eingesetzt. Die Baumstruktur entsteht
durch Zusammenschluss von Bussen oder Sternstrukturen, wobei je nach Anpassungsbedarf zwischen den
Teilnetzen unterschiedliche Netzverbinder eingesetzt
werden.
Netzverbinder. Für den Aufbau von LANs und
für den Übergang zwischen Netzen oder Netzsegmenten werden unterschiedliche Netzverbinder
eingesetzt, abhängig davon, auf welcher Ebene des
ISO/OSI-Referenzmodells der Übergang stattfindet:
Repeater. Ein Repeater verbindet zwei gleichartige
Netze in der untersten Schicht 1, z. B. zwei 10-Mbit/sEthernets. Er wirkt als Zwischenverstärker und führt
eine reine Bitübertragung durch, d. h., er nimmt keine
Protokollumsetzung vor.
Hub. Ein Hub („Nabe“, „Zentrum“) ist eine Verteilereinrichtung in der Schicht 1, die einen zentralen Anschluss sternförmig auf mehrere Anschlüsse aufteilt
bzw. in der umgekehrten Richtung diese Anschlüsse
zusammenfasst (Konzentratorfunktion). Hubs haben
entweder Verstärkerfunktion (aktiver Hub) oder nicht
(passiver Hub). Erstere wirken wie Repeater. Aufgrund der bei Hubs zentralisiert aufgebauten Netzsteuerung eignen sie sich insbesondere auch für die
Realisierung logischer Bus- und Ringstrukturen.
Bridge. Eine Bridge ist ein Netzverbinder in der
Schicht 2. Sie verbindet zwei unterschiedliche Netze
mit jedoch gleicher Realisierung in der Schicht 1,
z. B. ein Ethernet und einen Token-Bus. Sie entscheidet anhand der den Daten mitgegebenen
Adressinformation, was weitergeleitet werden soll,
hat also Filterfunktion.
Switch. Ein Switch („Schalteinrichtung“) ist ähnlich
einem Hub ein sternförmiger Verteiler/Konzentrator.
Im Gegensatz zum Hub, der die ankommenden Daten grundsätzlich an alle Netzteilnehmer weiterleitet
und bei dem sich die Netzteilnehmer die Netzkapazität teilen müssen, schaltet ein Switch die Datenpakete anhand ihrer Adressen gezielt an einzelne Netzteilnehmer durch. Dabei kann er die Übertragungskapazität eines Anschlusses hoher Übertragungsrate
(z. B. 1 Gbit/s) auf mehrere Anschlüsse mit geringeren Übertragungsraten (z. B. 100 oder 10 Mbit/s) aufteilen, wodurch ein hoher Datendurchsatz im Netz erreicht wird. Hinsichtlich der adressspezifischen Weiterleitung ist er der Schicht 2 (und ggf. darüber) zuzuordnen.
Router. Ein Router stellt eine Netzverbindung in der
Schicht 3 her. Er fungiert als Verbinder unterschiedlicher Netze mit unterschiedlichen Übertragungsmedien und ist mit entsprechenden Schnittstellen ausgestattet. Ankommende Datenpakete werden von ihm
auf den richtigen Zielweg geschickt. Die Wegewahl
bestimmt er anhand gespeicherter Routing-Tabellen.
Er führt keine Filterung durch.
Brouter. Ein Brouter ist eine Kombination aus Bridge
und Router (Schichten 2 und 3).
Gateway. Ein Gateway schließlich stellt eine Netzverbindung auf der jeweils obersten Netzebene her.
Es ist erforderlich, wenn die Netze nicht dem Referenzmodell entsprechen und es somit keine gemeinsame Schicht gibt. Dementsprechend führt ein Gateway neben der Routing-Funktion auch Protokoll- und
Codeumwandlungen durch.
Funknetze. Funknetze als drahtlose Verbindungen
lösen kabelgebundene Verbindungen ab. Derzeit am
weitesten verbreitet ist das sog. WLAN (wireless
LAN). Es folgt dem Basisstandard IEEE 802.11
mit Varianten, die durch eine zusätzliche Buchstabenkennung bezeichnet sind. Sein Netzaufbau im
111
112
Technische Informatik / Rechnerorganisation
sog. Infrastruktur-Modus erfolgt über die WLANAdapter der einzelnen Netzteilnehmer und durch
einer vermittelnde Basistation (access point, AP),
die die Verbindung zu einem drahtgebundenen
lokalen Netz, z. B. einem Ethernet herstellt. In
einer einfacheren Version, dem Ad-hoc-Modus,
können die Netzteilnehmer auch direkt mittels der
WLAN-Adapter (und ohne eine Netzanbindung)
miteinander kommunizieren (Peer-to-Peer-Netz).
Die maximalen Übertragungsraten betragen 1 und
2 Mbit/s (802.11), 11 Mbit/s (802.11b), 54 Mbit/s
(802.11g, 802.11a) sowie 600 Mbit/s (802.11n).
Die Funkreichweiten liegen innerhalb von Gebäuden bei 30 bis 80 m, im Freien bei mehreren 100 m.
Für die drahtlose Vernetzung von Geräten, z. B.
Computer und Peripheriegeräte, Mobiltelefone,
PDAs (personal digital assistant), hat das sog. Bluetooth (IEEE 802.15.1) eine weite Verbreitung. Seine
Übertragungsraten liegen bei 723 kbit/s bis maximal
2,1 Mbit/s für den Download und 57 kbit/s für den
Upload. Überbrückt werden geringe Entfernungen
von bis zu 10 m in Gebäuden.
7.6 Leistungskenngrößen
von Rechnersystemen und ihre Einheiten
Die Leistungsfähigkeit eines Rechners wird bei
pauschaler Beschreibung durch eine Reihe von
Leistungskenngrößen seiner Komponenten gekennzeichnet, die nachfolgend mit den üblichen Einheiten
alphabetisch geordnet aufgelistet sind.
Dabei ist zu beachten, dass die Vorsätze M (Mega),
G (Giga) und T (Tera) je zwei verschiedene Bedeutungen haben: bei Speicherkapazitäten als Potenzen
von 2 (M = 220 , G = 230 , T = 240 ) und für zeitbezogene Angaben (z. B. Frequenz, Übertragungsrate,
Durchsatz) als Potenzen von 10 (M = 106 , G = 109 ,
T = 1012 ). Beim Vorsatz Kilo sind die beiden Bedeutungen durch die Schreibweise unterschieden (K =
210 = 1024, k = 103 ).
Aufzeichnungsdichte (bit/recording density): bpi
(bit per inch), bpi2 (bit per square inch); bei
Speichermedien mit beschreibbaren Oberflächen,
z. B. Magnetbändern und Magnetplatten.
Bildauflösung von Bildschirmen (resolution): n × m
Pixel (picture elements, Bildpunkte, Spaltenanzahl
n mal Zeilenanzahl m).
Bildwiederholfrequenz (refresh rate): Hz; bei Röhrenmonitoren; Werte zwischen 50 und 100 Hz, annähernd flimmerfrei ab 85 Hz.
bpi, bpi2 : siehe Aufzeichnungsdichte.
bps: siehe Übertragungsrate bei serieller Übertragung.
Busbandbreite (bus bandwidth): byte/s (bytes per
second); maximal mögliche Übertragungsrate
bei parallelen Bussen/Verbindungen; ermittelt
aus Bustaktfrequenz, multipliziert mit der Datenbusbreite in Bytes, geteilt durch die für eine
Übertragung erforderliche Anzahl von Taktschritten.
Bustaktfrequenz (bus clock frequency): MHz, GHz;
Frequenz der Taktschritte bei Datenübertragungen
auf einem Bus.
Buszykluszeit (bus cycle time): bei parallelen Bussen
Maß für die Dauer eines Buszyklus; üblicherweise
als Anzahl der Taktschritte für die Durchführung
des Buszyklus angegeben.
cpi: siehe Zeichendichte, horizontale.
cps: siehe Geschwindigkeit der Zeichendarstellung.
Dhrystone: D/s (dhrystones per second); IntegerBenchmark, bestehend aus einem synthetischen
Mix von z. B. C-Anweisungen für Ganzzahloperationen. Gemessen wird die Anzahl der
Schleifendurchläufe pro Sekunde. Für die Ermittlung des Prozessordurchsatzes (in MIPS)
herangezogen.
dpi, dpi2 : siehe Punktdichte.
Geschwindigkeit der Zeichendarstellung (character
rate): cps (characters per second); auf Bildschirmen und durch Drucker.
Linpack: MFLOPS, GFLOPS (millions/billions of
floating-point operations per second); FloatingPoint-Benchmark mit z. B. in Fortran geschriebenen Matrizenrechnungen. Heute auch in Java
verfügbar.
MFLOPS: siehe Prozessordurchsatz für Gleitpunktoperationen.
MIPS: siehe Prozessordurchsatz.
MWIPS: siehe Whetstone.
Prozessorbustaktfrequenz (processor bus clock
frequency): MHz, GHz; Frequenz der Taktschritte
bei Datenübertragungen über die Datenanschlussleitungen eines Prozessors. Bei Prozessoren mit
8 Betriebssysteme
hoher Prozessortaktfrequenz ist sie technisch
bedingt um einen Teiler geringer als diese.
Prozessordurchsatz (throughput, processing speed):
MIPS (millions of instructions per second). Einigermaßen vergleichbare Aussagen sind nur bei im
Befehlssatz ähnlichen Prozessoren und bei gleichen Programmen, z. B. Benchmark-Programmen
oder einheitlichem Befehlsmix sinnvoll.
Prozessordurchsatz für Gleitpunktoperationen:
MFLOPS (millions of floating-point operations per second, Megaflops), oft auch mit den
Vorsätzen G (Gigaflops), T (Teraflops) und P
(Petaflops); entweder als theoretischer Höchstwert
(Ausführungszeit von Gleitpunktmultiplikation
und -addition) oder als Durchschnittswert (siehe
Linpack) angegeben.
Prozessortaktfrequenz (processor clock frequency):
MHz, GHz; Frequenz der prozessorinternen
Operationsschritte.
Punktdichte (resolution): dpi (dots per inch), dpi2
(dots per square inch); bei Text- und Grafikausdrucken.
Schrittgeschwindigkeit bei serieller Datenübertragung (baud/modulation rate): Bd (Baud);
Anzahl der Übertragungsschritte pro Sekunde
(häufig inkorrekt als „Baudrate“ bezeichnet). Bei
Übertragung von nur einem Bit pro Schritt ist die
Schrittgeschwindigkeit gleich der Übertragungsrate.
SPEC CPU2006: Leistungsangaben, basierend auf
Benchmarks der Organisation SPEC (Standard Performance Evaluation Corporation) zur Bewertung
der Integer- und Floating-Point-Leistungsfähigkeit
eines Rechners unter Berücksichtigung des Prozessors, Hauptspeichers und Compilers. Hierzu
werden die Laufzeiten von 12 (SPECint2006) bzw.
17 (SPECfp2006) ausgesuchten Programmen in
Relation zu vorgegebenen Bezugswerten gesetzt
und aus diesen 12 bzw. 17 Verhältniszahlen jeweils
das geometrische Mittel gebildet. – Benchmarks
für andere Bereiche, z. B. für Grafikeinheiten und
Server, sind verfügbar.
Speicherkapazität von Haupt- und Hintergrundspeichern (memory/storage capacity): byte, B (Byte);
wird in Kbyte (KB), Mbyte (MB), Gbyte (GB) oder
Tbyte (TB) angegeben.
Spurdichte (track density): tpi (tracks per inch); bei
Speichermedien mit beschreibbaren Oberflächen,
z. B. bei Magnetbändern und Magnetplatten.
Suchzeit, mittlere (average seek time): ms; bei
Sekundärspeichern mit mechanischen Zugriffsmechanismen: die Zeitdauer von der Speicheranwahl
bis zur Bereitschaft, Daten zu liefern (Lesen) bzw.
Daten zu übernehmen (Schreiben).
tpi: siehe Spurdichte.
Transferrate (transfer rate): T/s (transfers per
second); Anzahl der maximal möglichen Datenübertragungen pro Sekunde; wird in MT/s oder
GT/s angegeben.
Übertragungsgeschwindigkeit (transmission speed):
synonym mit Übertragungsrate.
Übertragungsrate bei paralleler Übertragung (data
transfer rate): Bps, byte/s, B/s (bytes per second);
wird in kbyte/s, Mbyte/s oder Gbyte/s angegeben.
Übertragungsrate bei serieller Übertragung (data
transfer rate): bps, bit/s, b/s (bits per second); wird
in kbit/s, Mbit/s oder Gbit/s angegeben.
Wartezyklen (wait cycles): zusätzliche Taktschritte
eines Busmasters, z. B. Prozessors, bei Überschreiten der minimalen Buszykluszeit (häufig auch als
wait states bezeichnet).
Whetstone: MWIPS (millions of whetstone instructions per second); Floating-Point-Benchmark mit
Schwerpunkt auf Gleitpunktoperationen in mathematischen Funktionen.
Zeichendichte, horizontale (character density): cpi
(characters per inch); bei Textausgabe auf Bildschirmen und durch Drucker.
Zugriffszeit (access time): ns; bei Halbleiterspeichern: die Zeitdauer von der Speicheranwahl bis
zur Datenbereitstellung (Lesen) bzw. Datenübernahme (Schreiben).
Zykluszeit (cycle time): ns; bei Halbleiterspeichern:
die Zeitdauer von der Speicheranwahl bis zur Bereitschaft für den nächsten Zugriff.
8 Betriebssysteme
Die Software eines Rechners wird grob unterteilt
in die Systemsoftware zur Verwaltung der Rechner-
113
114
Technische Informatik / Rechnerorganisation
8.1.1 Stapelbetrieb
Bild 8-1. Systemstruktur bei zwei Softwareebenen
funktionen und in die Anwendersoftware zur Lösung
von Anwenderproblemen. Basis der Systemsoftware
ist das Betriebssystem (operating system). Es „sitzt“
unmittelbar auf der Hardware und bietet der übrigen
Systemsoftware sowie der Anwendersoftware eine
von der Hardware abstrahierte, leichter handhabbare Schnittstelle in der Form von Systemaufrufen
(Bild 8-1). Die übrige Systemsoftware unterstützt
den Anwender beim Erstellen, Verwalten und Ausführen von Programmen. Zu ihr zählen z. B. die sog.
Benutzerschnittstelle (über die der Benutzer mit dem
Rechner kommuniziert), z. B. ein Kommandointerpreter oder eine grafische Oberfläche, weiterhin ein
einfacher Editor sowie Assembler und Compiler
für unterschiedliche Programmiersprachen. Die Anwendersoftware umfasst Programme wie Text- und
Grafikprogramm (desktop publishing), Datenbankanwendungen, E-Mail-Programm, Internet-Browser
usw. – Zur Vertiefung des Themas Betriebssysteme
siehe z. B. [1, 4, 5].
In den Anfängen der Rechnertechnik wurde die
Abwicklung eines Rechenauftrags (job) durch den
Bediener (operator) der Rechenanlage manuell über
ein Bedienpult gesteuert. Dazu mussten das in Lochkarten gestanzte Programm und seine Daten in einem
Lochkartenlesegerät bereitgestellt und das Programm
eingelesen, übersetzt und gestartet werden. Mit den
ersten Betriebssystemen wurde dieser Ablauf automatisiert und durch eingefügte Steueranweisungen einer
Jobsteuersprache (job control language) gesteuert
(Bild 8-2). Hinzu kam der automatische Jobwechsel, indem mehrere Jobs zu einem Stapel (batch)
zusammengefasst und nacheinander abgearbeitet
wurden (Stapelbetrieb, batch processing).
Nachteilig für den Benutzer waren die langen
Wartezeiten von der Abgabe eines Programms bis
zum Erhalt der Ergebnisse. Durch den Einsatz
von Magnetbandgeräten konnten die Wartezeiten
reduziert und „gerechtere“ Strategien eingeführt werden, indem die Jobs nach ihrer Laufzeit vorsortiert
und kürzere Jobs zuerst ausgeführt wurden. Eine
weitere Verbesserung wurde durch die Überlappung
von zentraler Verarbeitung und der Ein-/Ausgabe
erreicht, indem für die Eingabe der Jobs und die
Ausgabe ihrer Ergebnisse selbstständig arbeitende
Ein-/Ausgabeeinheiten (DMA-Kanäle) eingesetzt
8.1 Betriebssystemarten
Die Begriffe für Betriebssysteme sind stark durch die
Entwicklung der Rechnertechnik geprägt, beginnend
mit dem Großrechner (main frame) als der zentralen
Recheneinheit für viele Benutzer, anfangs ohne, später mit der Möglichkeit des Dialogbetriebs. Es folgte der Kleinrechner als persönlicher bzw. lokaler Arbeitsplatzrechner für den einzelnen Benutzer (Personal Computer, Workstation) bzw. in der Prozessdatenverarbeitung zur Steuerung technischer Systeme
(Prozessrechner). Hinzu kamen die sog. eingebetteten Systemen (embedded control systems) als Steuerungen in z. B. Automobilen und Geräten allgemein.
Heute dominiert die Vernetzung von Rechnern jeder Größenordnung mit ausgeprägter Kommunikation zwischen diesen und ihren Benutzern.
Bild 8-2. Lochkartenstapel mit Anweisungen zum Assemb-
lieren, Laden und Starten eines Programms im Stapelbetrieb
8 Betriebssysteme
wurden, unterstützt durch Magnetplattenspeicher für
die Speicherung der Programme und Daten des Jobs
und deren Ergebnisse.
Heutzutage wird der Stapelbetrieb hauptsächlich auf
paralellen Hochleistungsrechnern (vgl. Abschnitt 7.4)
eingesetzt, um den Job-Durchsatz zu erhöhen und eine gute Auslastung der paralellen Systemkomponenten (Prozessoren, Speicher, Ein/Ausgabekanäle) zu
erzielen. Durch Zuweisung von Prioritäten können
niedrig priorisierte Jobs in den Hintergrund gedrängt
werden und in Zeiten schwacher Beanspruchung ausgeführt werden.
8.1.2 Dialogbetrieb
Beim Dialogbetrieb teilen sich wie beim Stapelbetrieb viele Benutzer einen zentralen Rechner. Sie sind
jedoch über eigene Terminals mit diesem verbunden und arbeiten interaktiv. Sie erhalten dazu den
Rechner reihum für eine kurze Zeitscheibe zugeteilt
(time slicing), sodass jeder Benutzer den Eindruck
hat, den Rechner für sich allein zur Verfügung zu
haben. Man sagt, jeder Benutzer habe seine eigene
„virtuelle“ Maschine, und bezeichnet das Betriebssystem als Teilnehmersystem (time sharing system).
Eine typische Anwendung des Dialogbetriebs sind
die Transaktionssysteme, Teilnehmersysteme für die
Verwaltung von Datenbanken mit sich laufend verändernden Datenbasen, wie sie etwa bei Kontoführungssystemen oder Buchungssystemen vorkommen.
Sie müssen besonders die Konsistenz der Datenbasis
gewährleisten und dementsprechend Simultanzugriffe auf einen Datensatz sicher verhindern.
8.1.3 Einbenutzer- und Netzsysteme
Die gesunkenen Hardwarekosten haben es ermöglicht, anstelle des Einsatzes eines gemeinsamen
„Zentralrechners“ jedem Benutzer einen eigenen
Rechner als Personal Computer oder Workstation
zur Verfügung zu stellen. Die Betriebssysteme
solcher Rechner sind zunächst Einbenutzersysteme
(single-user systems), die sich durch eine komfortable
Benutzeroberfläche auszeichnen.
Innerhalb gemeinsamer Arbeitsumgebungen ist es üblich, solche Rechner miteinander zu vernetzen, um
Ressourcen gemeinsam nutzen zu können. Am gebräuchlichsten sind hier Netze, in denen die gemeinsamen Ressourcen durch speziell ausgestattete Rechner, sog. Server, bereitgestellt werden, auf die die
Benutzer als Klienten zugreifen: sog. Client-ServerSysteme.
Ihnen gegenüber stehen die sog. Peer-to-peerSysteme, bei denen es keine Server gibt, sondern
sich die Benutzer ihre Ressourcen über das Netz
gegenseitig und mit gleichberechtigtem Zugriff
zur Verfügung stellen. Jeder der Rechner hat bei
dieser Vernetzung sein eigenes Betriebssystem, das
jedoch um die für die Netzanbindung erforderliche
Kommunikationssoftware erweitert ist.
Bemerkung. Ein Client-Server-System ist ggf. durch
ein Speichernetz, ein sog. Storage Area Network
(SAN) ergänzt, das Server und Hintergrundspeicher
(Festplatten, Streamer) unabhängig vom vorhandenen Rechnernetz miteinander verbindet (siehe 7.1.6:
Fibre Channel, InfiniBand).
8.1.4 Mehrbenutzer- und Mehrprogrammsysteme
Rechnernetze werden vielfach auch dazu genutzt,
dem einzelnen Benutzer Rechenleistung auf anderen
Rechnern im Netz zur Verfügung zu stellen. Dazu
wählt sich der Benutzer von seinem Rechner aus in
einen solchen anderen Rechner ein (remote log-in)
und arbeitet dann unter dessen Betriebssystem. Dieses muss dementsprechend als Mehrbenutzersystem
(multiuser system) ausgelegt sein, d. h., es muss
gleichzeitig das Arbeiten des eigenen Benutzers
wie auch das von externen Benutzern erlauben.
Grundlage hierfür ist der Mehrprogrammbetrieb
(multiprogramming), der üblicherweise auf der Basis
des Prozesskonzeptes realisiert wird (siehe 8.3.1).
Hierbei können mehrere Programme in Form sog.
Prozesse (tasks) gleichzeitig aktiv sein, wobei die
Aktivitäten voreinander geschützt sind (multitasking). Steht dem einzelnen Rechner nur ein Prozessor
zur Verfügung, so werden die Prozesse quasiparallel,
d. h. zeitlich ineinander verzahnt ausgeführt; bei
Mehrprozessorsystemen werden sie vom Betriebssystem auf die Prozessoren aufgeteilt. – Bemerkung:
Das Multitasking wird auch bei Einbenutzersystemen
realisiert, um kürzere Wartezeiten durch quasiparallele bzw. echt parallele Ausführung von Anwenderund Systemprozessen zu erreichen.
115
116
Technische Informatik / Rechnerorganisation
8.1.5 Verteilte Systeme
8.2.1 Privilegierungsebenen
Eine gegenüber dem Mehrbenutzerbetrieb verbesserte Nutzung der Leistungsfähigkeit eines Rechnernetzes erreicht man durch verteiltes Rechnen. Hierbei
werden freie Rechenkapazitäten des Netzes in die
Problembearbeitung eines Benutzers mit einbezogen,
und zwar in der Weise, dass diese Aktivitäten für
den Benutzer unsichtbar, man sagt transparent, bleiben. Die Rechner haben dabei keine individuellen Betriebssysteme mehr, vielmehr gibt es nur noch ein gemeinsames Betriebssystem, dessen Funktionen über
die einzelnen Rechner verteilt sind. Man bezeichnet
dies als ein verteiltes System.
In einem System mit zwei Privilegierungsebenen
laufen die elementaren Betriebssystemfunktionen im
privilegierten Supervisor-Modus und die Benutzeraktivitäten im nichtprivilegierten User-Modus
ab. Den Benutzeraktivitäten rechnet man hierbei
auch diejenigen Systemprogramme zu, die als Betriebssystemerweiterungen gelten, z. B. Compiler.
Bestimmt wird die jeweilige Betriebsart durch ein
Modusbit im Statusregister des Prozessors. Der
Schutzmechanismus besteht zum einen in der prozessorexternen Anzeige der jeweiligen Betriebsart durch
die Statussignale des Prozessors. Dies kann von einer
Speicherverwaltungseinheit dazu genutzt werden,
Zugriffe von im User-Modus laufenden Programmen
auf bestimmte Adressbereiche einzuschränken (z. B.
nur Lesezugriffe erlaubt) oder ganz zu unterbinden
(siehe 7.2.2). Andererseits können der SupervisorEbene die vollen Zugriffsrechte eingeräumt werden.
Der Schutzmechanismus besteht zum andern in der
Aufteilung der Stack-Aktivitäten in einen Supervisorund einen User-Stack. Dazu sieht die Hardware oft
zwei Stackpointerregister vor, die abhängig von der
Betriebsart gültig sind.
Einen weiteren Schutz bieten die sog. privilegierten Befehle, die nur im Supervisor-Modus ausführbar sind. Zu ihnen zählen alle Befehle, mit denen
die sog. Modusbits im Statusregister verändert werden können, so auch die Befehle für die Umschaltung
in den User-Modus. Programmen, die im User-Modus
laufen, ist neben der Einschränkung des Zugriffs auf
die privilegierten Adressbereiche auch die Ausführung privilegierter Befehle verwehrt. Versuche, dies
zu durchbrechen, führen zu Programmunterbrechungen in Form von sog. Fallen (traps, siehe 8.2.2).
Der Übergang vom Supervisor- in den User-Modus
erfolgt durch einen der privilegierten Befehle (z. B.
rte, return from exception), der Übergang vom Userin den Supervisor-Modus durch sog. Systemaufrufe
(system calls, supervisor calls), realisiert durch TrapBefehle.
8.1.6 Echtzeitsysteme
Eine gewisse Sonderstellung nehmen Rechnersysteme zur Steuerung technischer Systeme ein, da die
Kommunikation mit ihnen primär nicht durch einen
Benutzer, sondern durch ein technisches System erfolgt. Sie erfordern Betriebssysteme hoher Zuverlässigkeit, die in der Lage sind, auf kritische Zustände,
wie sie z. B. durch Interruptsignale angezeigt werden, in vom technischen System diktierten Zeiträumen, d. h. in „Echtzeit“ zu reagieren. Die maximal
zulässigen Reaktionszeiten liegen dabei, je nach System, im Bereich von Milli- oder Mikrosekunden. Diese sog. Echtzeitsysteme (real-time operating systems)
sehen eine hierfür ausgewiesene Interrupt- und Prozessverwaltung vor (präemptives Multitasking, siehe 8.3). Sie müssen außerdem für eine effektive Fehlerbehandlung, d. h. Fehlerentdeckung und möglichst
auch Fehlerbehebung ausgelegt sein.
8.2 Prozessorunterstützung
Wesentlich für die Betriebssicherheit eines Rechners
ist der Schutz der für den Betrieb erforderlichen
Systemsoftware vor unerlaubten Zugriffen durch die
Anwendersoftware (Benutzerprogramme). Grundlage hierfür sind die in der Prozessorhardware
verankerten Betriebsarten (Modi) zur Vergabe
von Privilegierungsebenen. Hier gibt es Systeme
mit zwei oder mehr Ebenen. Eng verknüpft mit
den Betriebsarten ist die Ausnahmeverarbeitung
(exception processing), d. h. die Verwaltung von
Programmunterbrechungen durch den Prozessor.
8.2.2 Traps und Interrupts
Programmunterbrechungen resultieren aus Anforderungen an den Prozessor, die Programmausführung
zu unterbrechen und die Verarbeitung mit einer
8 Betriebssysteme
Unterbrechungsroutine fortzusetzen. Solche Anforderungen treten als Traps und Interrupts auf.
Traps. Traps werden immer durch eine Befehls-
ausführung verursacht, lösen eine Anforderung also
„synchron“ mit der Programmausführung aus. Sie
entstehen dementsprechend vorwiegend prozessorintern, es gibt aber auch von außen kommende
Trap-Signale. Typische Trap-Ursachen sind:
– Division durch null (zero-divide trap),
– Bereichsverletzung bei Operationen mit ganzen
Zahlen in 2-Komplement-Darstellung (overflow
trap),
– Bereichsunterschreitung oder -überschreitung bei
Gleitpunktoperationen (floating-point underflow
bzw. floating-point overflow trap),
– Aufruf eines privilegierten Befehls im User-Modus
(privilege violation trap),
– Aufruf eines Befehls mit nicht anwendbarem Operationscode (illegal instruction trap),
– Fehler im Buszyklus, z. B. Ausbleiben des BereitSignals (bus error trap),
– Zugriff auf eine nicht geladene Speicherseite (page
fault trap; ermöglicht das Nachladen der Seite),
– Ausführen eines Trap-Befehls (trap instruction trap
als supervisor call, d. h. Betriebssystemaufruf),
– Ausführen eines beliebigen Befehls bei gesetztem
Trace-Bit im Prozessorstatusregister (trace trap;
erlaubt das schrittweise Durchlaufen eines Programms für Testzwecke; die Trace-Trap-Routine
dient dabei zur Statusanzeige).
Interrupts.
Interrupts werden durch prozessorexterne Ereignisse erzeugt, z. B. durch Anforderungen von
Ein-/Ausgabeeinheiten oder von externen (technischen) Prozessen. Daher treten diese Anforderungen
unvorhersehbar, d. h. „asynchron“ zur Programmausführung auf. Sie werden dem Prozessor als
Interruptsignale zugeführt, die von ihm üblicherweise
nach jeder Befehlsabarbeitung abgefragt werden.
Maskierbare Interrupts bewirken bei Annahme einer
Anforderung durch den Prozessor das Blockieren
weiterer Anforderungen, indem dieser in seinem Prozessorstatusregister eine sog. Interruptmaske setzt.
Abhängig davon, ob der Prozessor nur einen einzigen
Interrupteingang hat oder ob er Interruptanforderungen codiert über mehrere Eingänge entgegennimmt,
besteht diese Maske aus einem oder aus mehreren
Bits. Bei z. B. einem 3-Bit-Interruptcode und einer
3-Bit-Interruptmaske können sieben Prioritätsebenen
unterschieden werden (das übrige Codewort besagt,
dass keine Anforderung vorliegt). Den 3-Bit-Code
erzeugt ein externer Prioritätencodierer. Die Interruptmaske wird mit Abschluss der Interruptroutine
(Befehl rte, siehe unten) durch Laden des ursprünglichen Prozessorstatus (Befehlszähler, Statusregister)
wieder zurückgesetzt. Ein explizites Zurücksetzen
kann, da die Maske zu den Modusbits des Statusregisters gehört, nur durch einen privilegierten Befehl
erfolgen (8.2.1).
Nichtmaskierbare Interrupts sind nicht blockierbar. Dies gewährleistet eine schnelle Reaktion bei
nichtaufschiebbaren Anforderungen, z. B. das Retten
des Rechnerstatus auf einen Hintergrundspeicher,
wenn die Versorgungsspannung einen kritischen
Grenzwert unterschreitet (power fail save). Ein
spezieller Interrupt (Reset-Signal) dient zur Systeminitialisierung. Er setzt u. a. im Prozessorstatusregister
den Supervisor-Modus (8.2.1), lädt den Befehlszähler und ein ggf. für den Supervisor-Modus
eigenes Stackpointerregister mit zwei vorgegebenen
Adressen des Betriebssystems und leitet dann die
Programmausführung ein.
Allen Interruptsignalen oder Interruptcodes sind Prioritäten zugeordnet, die für die Unterbrechbarkeit von
Interruptroutinen maßgeblich sind. Höchste Priorität
hat dabei das Reset-Signal. Interrupts können prozessorextern weiter nach Prioritätsebenen klassifiziert
werden, womit sich mehrere Interruptquellen pro Signaleingang verwalten lassen (siehe 7.1.3). Bei der
oben beschriebenen Mehrebenenstruktur für maskierbare Interrupts bezieht sich diese Maßnahme auf die
einzelnen Eingänge des Prioritätencodierers.
8.2.3 Ausnahmeverarbeitung
(exception processing)
Eine Unterbrechungsanforderung bewirkt, sofern sie
nicht durch den Prozessorstatus blockiert wird, eine
Unterbrechung des laufenden Programms und führt
zum Aufruf einer Unterbrechungsroutine (Bild 8-3).
Damit verbunden sind das Retten des aktuellen
117
118
Technische Informatik / Rechnerorganisation
8.3 Betriebssystemkomponenten
Zu den wesentlichen Aufgaben eines Betriebssystems
gehören:
Bild 8-3. Aufruf einer Unterbrechungsroutine, ausgelöst
durch einen maskierbaren Interrupt
Prozessorstatus (Befehlszähler, Statusregister) auf
den Supervisor-Stack, das Setzen der Interruptmaske
bei einer maskierbaren Anforderung, das Umschalten
in den Supervisor-Modus, das Lesen der Startadresse
der Unterbrechungsroutine aus einer sog. Vektortabelle, das Laden der Adresse in den Befehlszähler
und das Starten der Unterbrechungsroutine.
Die Rückkehr erfolgt durch einen privilegierten
Rücksprungbefehl, der den alten Status wieder lädt
(return from exception, rte).
Wurde die Unterbrechung durch einen maskierbaren
Interrupt ausgelöst, so ist sie wegen der dabei gesetzten Interruptmaske durch eine Anforderung gleicher
Art nicht unterbrechbar, es sei denn, die Maske wurde
innerhalb der Routine explizit zurückgesetzt. Das automatische Rücksetzen erfolgt erst mit dem Laden des
alten Prozessorstatus durch den Rücksprungbefehl.
Die Vektortabelle (Trap- und Interrupttabelle) ist eine vom Betriebssystem verwaltete Tabelle im Speicher für die Startadressen aller Trap- und Interruptroutinen. Bei der Unterbrechungsbehandlung erfolgt
der Zugriff auf die Tabelle über eine der Anforderung
zugeordnete Vektornummer, die der Prozessor als Tabellenindex benutzt. Bei einem Trap oder einem sog.
nichtvektorisierten Interrupt wird die Vektornummer,
abhängig von der Trap-Ursache bzw. der aktivierten
Interruptleitung, prozessorintern generiert. Bei einem
sog. vektorisierten Interrupt wird sie von der anfordernden Interruptquelle bereitgestellt und vom Prozessor in einem Lesezyklus übernommen. Die der
Quelle zugeordnete Vektornummer ist hierbei aus einem größeren Vorrat wählbar, d. h., es können bei nur
einer Anforderungsleitung viele unterschiedliche Anforderungen verwaltet werden. Zu deren Priorisierung
siehe 7.1.3.
– die Abstraktion (als grundsätzliche, übergeordnete
Aufgabe) durch Verbergen der Hardware und Herstellen des Rechnerzugangs auf einer höheren, logischen Ebene (siehe Bild 8-1),
– das Verwalten der Betriebsmittel (resources,
Hardware und Software), d. h. des Prozessors (oder
der Prozessoren), des Hauptspeichers, der Hintergrundspeicher und der Ein-/Ausgabegeräte wie
auch von Programmen (z. B. Editoren, Compiler,
Textsysteme) und Daten,
– das Gewährleisten der Systemsicherheit durch
Schutzmechanismen, einerseits zwischen dem Betriebssystem und den übrigen Systemprogrammen
sowie den Anwenderprogrammen, andererseits
zwischen den Programmen verschiedener Benutzer,
– das Bereitstellen einer Schnittstelle für die nicht
zum Betriebssystem gehörende Systemsoftware
sowie für die Anwendersoftware in Form von
Systemaufrufen (system calls).
Zudem unterstützt das Betriebsystem die Benutzerschnittstelle durch die Bereitstellung entsprechender
Funktionen. – Die wichtigsten Komponenten zur
Durchführung dieser Aufgaben werden im Folgenden
beschrieben.
8.3.1 Prozessverwaltung
Heutige Rechnersysteme sind durch parallel
ablaufende Aktivitäten charakterisiert (multiprogramming/-tasking). Hierbei handelt es sich entweder
um echte Parallelität, z. B. bei der gleichzeitigen Ausführung eines Programms durch den Prozessor sowie
der Abwicklung eines Ein-/ Ausgabevorgangs durch
eine Ein-/Ausgabeeinheit (DMA-Controller) oder bei
gleichzeitiger Ausführung mehrerer Programme, die
auf mehrere Prozessoren oder mehrere Prozessorkerne verteilt sind (Mehrprozessor-/Mehrkernsystem).
Oder es handelt sich um eine Quasiparallelität, z. B.
wenn verschiedene Programme den Prozessor im
Wechsel für eine begrenzte Zeit zugeteilt bekom-
8 Betriebssysteme
8.3.2 Interprozesskommunikation
Bild 8-4. Prozesszustände
men. „Von weitem“ betrachtet, kann auch diese
Arbeitsweise als parallel angesehen werden.
Um von den relativ komplizierten zeitlichen und örtlichen Abhängigkeiten dieser Abläufe zu abstrahieren, beruhen moderne Betriebssysteme auf dem Prozesskonzept. Als Prozess bezeichnet man im Prinzip ein Programm in seiner Ausführung [5], genauer,
den zeitlichen Ablauf einer Folge von Aktionen, beschrieben durch ein Programm und die zu seiner Ausführung erforderliche Information. Diese umfasst den
Befehlszähler, den Stackpointer und weitere Prozessorregister sowie die vom Programm benutzten Daten, z. B. die Variablenbereiche, Pufferadressen für
die Ein-/Ausgabe und den aktuellen Status benutzter
Dateien. Zur Verwaltung von Prozessen wird jedem
ein Prozesskontrollblock zugeordnet, der dessen aktuellen Ausführungszustand (Prozessstatus) anzeigt.
Während seiner „Lebenszeit“ befindet sich ein
Prozess stets in einem von drei Zuständen (Bild 8-4).
Er ist aktiv (running), wenn ihm ein Prozessor
zugeteilt ist und sein Programm ausgeführt wird; er
ist blockiert (blocked), wenn seine Weiterführung
von einem Ereignis, z. B. der Beendigung eines Ein-/
Ausgabevorgangs, abhängt; und er ist bereit (ready,
runnable), wenn dieses Ereignis eingetreten ist und er
auf die Zuteilung eines Prozessors wartet. Prozesse
im Zustand bereit sind in eine Warteschlange (process queue) eingereiht. Die Zuteilung des Prozessors
übernimmt der Scheduler des Betriebssystems.
Bei einfachen Betriebsystemen leiten die Prozesse
einen erforderlichen Prozesswechsel (Abgabe und
Neuvergabe des Prozessors) selbst ein, indem sie eine
entsprechende Systemfunktion aufrufen (kooperatives Multitasking). Bei Betriebssystemen mit höheren
Anforderungen, insbesondere mit Echtzeitfähigkeit,
erfolgt der Prozesswechsel zeitscheibengesteuert
(time slicing), d. h. zentral durch eine Zeitgeberfunktion gesteuert (präemptives Multitasking).
Die parallele bzw. quasiparallele Ausführung von
Prozessen erfordert eine Kommunikation zwischen
Prozessen, z. B. hinsichtlich des Wartens auf den
Abschluss eines Ein-/Ausgabevorgangs oder des
Konkurrierens zweier Prozesse um dasselbe Betriebsmittel. Abstrakt betrachtet handelt es sich
dabei um die Synchronisation, d. h. das AufeinanderAbstimmen von Prozessaktionen. Beim kooperativen
Multitasking kann diese Synchronisation wegen des
von den Prozessen selbst gesteuerten Prozesswechsels problemlos über einen ihnen gemeinsam (global)
zugänglichen Speicherbereich erfolgen. Beim präemptiven Multitasking hingegen genügt ein solches
Vorgehen nicht. Hier besteht nämlich die Gefahr,
dass durch den zentral gesteuerten Prozesswechsel
eine logisch zusammenhängende Zugriffsfolge eines
Prozesses vorzeitig abgebrochen wird und dadurch
Daten in einem inkonsistenten Zustand hinterlassen
werden, z. B. bei Schreibzugriffen auf eine Datei.
Eine solche Inkonsistenz wäre für einen später aktivierten Prozess, der lesend auf diese Datei zugreift,
nicht erkennbar. Gelöst wird dieses Problem z. B.
durch Synchronisation mit Hilfe von Semaphoren [3]
als einer von mehreren gebräuchlichen Techniken.
Beim Zugriff mehrerer Prozesse auf ein gemeinsames Betriebsmittel ist der Semaphor eine binäre Größe; mit 0 zeigt er den Belegt- und mit 1 den Freizustand des Betriebsmittels an. Bevor ein Prozess, der
dieses Betriebsmittel benötigt, in den kritischen Programmabschnitt (critical section) des Zugriffs eintritt,
prüft er den Semaphor. Ist dieser 0, so wird der Prozess in eine dem Betriebsmittel zugeordnete Warteschlange eingereiht; ist er 1, so betritt der Prozess den
kritischen Abschnitt und setzt den Semaphor auf 0.
Damit werden andere Prozesse vom Zugriff ausgeschlossen (gegenseitiger Ausschluss, mutual exclusion). Beim Verlassen des kritischen Abschnitts setzt
der Prozess den Semaphor wieder auf 1, wodurch ggf.
ein in der Warteschlange befindlicher Prozess aktiviert wird.
Voraussetzung für das Funktionieren des Semaphorprinzips ist die Nichtunterbrechbarkeit der geschilderten Semaphoroperation. In Einprozessorsystemen
lässt sich dies durch Blockieren des Interruptsystems,
in speichergekoppelten Mehrprozessorsystemen
119
120
Technische Informatik / Rechnerorganisation
Bild 8-5. Verklemmung zweier Prozesse A und B durch gegenseitiges Festhalten der Betriebsmittel 1 und 2
durch Blockieren des Arbitrationssystems erreichen.
Letzteres wird von der Hardware unterstützt, z. B.
durch einen atomaren Befehl, der das Abfragen und
Ändern eines Semaphors in einer nichtunterbrechbaren Folge von Buszyklen durchführt.
Zusätzlich besteht bei konkurrierenden Zugriffen auf
mehrere Betriebsmittel die Gefahr einer Verklemmung (deadlock), wenn Prozesse auf Betriebsmittel
warten, die sie wechselseitig festhalten (Bild 8-5).
8.3.3 Speicherverwaltung
Zu den wichtigsten Aufgaben jedes Betriebssystems
gehört die Speicherverwaltung, d. h. über freie und
belegte Speicherbereiche Buch zu führen, Prozessen
Speicherplatz zuzuweisen (allocation) und wieder freizugeben (deallocation), die Übertragung
zwischen Hintergrundspeicher und Hauptspeicher
durchzuführen, Tabellen für die Adressumsetzungen bereitzustellen und Speicherbereiche gegen
unzulässige Zugriffe zu schützen.
Statische Speicherverwaltung. Bei einfachen Sys-
temen mit Einprogrammbetrieb entfallen viele dieser Teilaufgaben, da bei ihnen immer nur ein Programm in den Speicher geladen ist, d. h. immer nur
ein Prozess vorliegt. Die Speicherplatzzuweisung erfolgt dementsprechend statisch, weshalb der Hauptspeicher von vornherein in je einen Bereich für das
Betriebssystem und für den Benutzer aufgeteilt werden kann. Beide Bereiche werden ggf. mittels einer einfachen Erweiterung der Speicheranwahllogik
zur Auswertung der aktuellen Privilegierungsebene
des Programms gegeneinander geschützt (siehe 8.2.1:
Supervisor-/User-Modus).
Dynamische Speicherverwaltung.
Bei Systemen
mit Mehrprogrammbetrieb werden Prozesse dyna-
misch erzeugt, sodass jeweils Speicherplatz für den
Programmcode, den Stack und die Daten bereitgestellt werden muss, der nach Ablauf der „Lebenszeit“
eines Prozesses wieder freigegeben wird. Ein Prozess
seinerseits kann, sobald er aktiv ist, Speicherplatz innerhalb des für ihn bereitgestellten Speicherbereichs
anfordern und auch wieder freigeben, wozu ihm
Systemaufrufe zur Verfügung stehen (z. B. allocate,
free).
Für die Speicherplatzvergabe an die Prozesse ist eine dynamische Verwaltung des Hauptspeichers erforderlich, d. h., Programme und ihre Daten müssen an
beliebige Speicherorte ladbar (verschiebbar, relocatable) sein. Grundsätzlich wird die Verschiebbarkeit
dadurch erreicht, dass zu sämtlichen (relativen) Programmadressen die aktuelle Ladeadresse (Basisadresse) addiert wird. Bei sehr einfachen Systemen kann
diese Addition vor oder bei dem Ladevorgang vorgenommen werden. Effizienter und flexibler ist es jedoch, die Addition zur Laufzeit eines Programmes
durchzuführen, z. B. mittels der Adressierungsarten
des Prozessors, indem bei der Programmerstellung
für Datenzugriffe nur die basisrelative Adressierung
und für Sprungbefehle nur die befehlszählerrelative Adressierung verwendet werden. Beide Adressierungsarten entsprechen der registerindirekten Adressierung mit Displacement (siehe 6.4.4). Im ersten Fall
hält eines der allgemeinen Register die Datenbasisadresse, im zweiten Fall fungiert der Befehlszähler als
Sprungbasis. Die Verschiebbarkeit erfordert lediglich
das Initialisieren des Basisadressregisters und des Befehlszählers (Laden der Startadresse) vor dem Programmstart.
Eine aufwändigere, heute aber gängige Lösung
bietet der Einsatz einer Speicherverwaltungseinheit
(MMU). Sie ermöglicht neben einer flexiblen Adressumsetzung insbesondere auch den Speicherschutz.
Dabei wird der Hauptspeicher in Bereiche einheitlicher fester Länge (Seiten, genauer Seitenrahmen),
oder variabler Länge (Segmente) aufgeteilt. Bezüglich
der Realisierung von Seiten- und Segmentverwaltung
einschließlich der Technik des virtuellen Speichers
sowie zu den entsprechenden MMUs sei auf 7.2.2
verwiesen.
Freispeicherverwaltung. Ein mit der Seitenverwal-
tung und mit der Segmentverwaltung verknüpftes
8 Betriebssysteme
Problem, das weitgehend durch Software gelöst
wird, ist das Verwalten der aktuell freien bzw.
belegten Bereiche des Hauptspeichers, die sog.
Freispeicherverwaltung.
Bei der Seitenverwaltung (paging) mit ihren Bereichen einheitlicher Größe ist die Freispeicherverwaltung relativ einfach, da sich jeder der freien Seitenrahmen des Hauptspeichers in gleicher Weise für eine Speicherplatzzuweisung eignet. Ein Problem ergibt sich dann, wenn beim Laden einer Seite alle Seitenrahmen belegt sind und entschieden werden muss,
welche der vorhandenen Seiten überschrieben werden
soll. Hier muss ggf. der Inhalt des ausgewählten Rahmens, falls er nach dem Laden verändert wurde, zuvor
auf den Hintergrundspeicher zurückgeschrieben werden. Entscheidungshilfe bieten hier verschiedene Alterungsstrategien (wie sie z. T. auch bei Caches üblich sind), die entweder Statusbits in den Deskriptoren (R: referenced, d. h. lesend zugegriffen, M: modified, d. h. schreibend zugegriffen) oder eine Liste der
belegten Seiten (z. B. verkettet nach der Reihenfolge
ihres Ladens) oder beides auswerten.
Bei der Segmentverwaltung (segmentation) hingegen
ist die Freispeicherverwaltung relativ aufwändig.
Hier führen die unterschiedlich großen Bereiche
bei ihrer Freigabe ggf. zu einer Fragmentierung
(fragmentation) des Hauptspeichers, indem Lücken
entstehen, die nicht zusammenhängend nutzbar sind.
Diese Zerstückelung kann gemindert werden, indem
freiwerdende Bereiche mit ggf. direkt benachbarten
freien Bereichen vereinigt werden. Daneben gibt es
das sehr viel zeitaufwändigere, dafür aber wirkungsvollere Verfahren des Kompaktifizierens, bei dem
die belegten Bereiche in gewissen Zeitabständen so
verschoben werden, dass ein zusammenhängender
Freispeicherbereich entsteht (garbage collection).
Von einer alleinigen Segmentierung wird deshalb
meist abgesehen, der Vorteil der Segmentierung
jedoch bei der zweistufigen Seitenverwaltung genutzt
(siehe 7.2.2). Bei ihr reduziert sich das Freispeicherproblem auf das der Seitenverwaltung.
Die Möglichkeit von Prozessen, zur Programmlaufzeit Speicherplatz für Daten mittels Systemaufrufen
anzufordern und diesen auch wieder freizugeben,
führt auf eine weitere Ebene der Speicherverwaltung.
Diese findet jeweils innerhalb des von der MMU
verwalteten Gesamtdatenbereichs eines Prozesses
statt und wird allein von der Software durchgeführt.
Da die Speicherplatzanforderungen variabel sind,
kann es bei der Freigabe solcher Bereiche ebenfalls
zu einer Zerstückelung des Speichers kommen. Das
Betriebssystem verwaltet die Bereiche, indem es z. B.
je eine verkettete Liste der freien und der belegten
Bereiche führt. Die Bereiche werden dazu mit einem
„Kopfeintrag“ (header) versehen, der ihren Zustand
(frei, belegt), ihre Größe sowie einen Zeiger auf
den nächsten Bereich in der Liste angibt. Bei einer
Speicherplatzanforderung wird ein genügend großer
Bereich aus der Freiliste ausgewählt und in die
Belegtliste eingehängt. Ein Überhang wird ggf. abgetrennt und in der Freiliste weitergeführt. Die Auswahl
des Bereichs wird hinsichtlich der Suchzeit und einer
möglichst guten Bereichsnutzung optimiert, wofür es
verschiedene Verfahren gibt. Beim First-fit-Verfahren
wird der erste ausreichend große Bereich in der Liste
gewählt (kurze Suchzeit), beim Best-fit-Verfahren der
Bereich mit dem kleinsten Restbereich (geringsten
Verschnitt), beim Worst-fit-Verfahren der Bereich mit
dem größten Restbereich (beste Weiternutzbarkeit).
Ferner wird auch hier die Technik des Verschmelzens
benachbarter Freibereiche angewandt.
8.3.4 Dateiverwaltung
Programme und Daten sind auf Hintergrundspeichern
als Dateien (files) abgelegt und werden vom Betriebssystem verwaltet. Dateien abstrahieren von den physischen und funktionellen Eigenschaften der Hintergrundspeicher und ermöglichen dem Anwender einen
einheitlichen Zugriff mit symbolischer Dateiadressierung. Bei einigen Betriebssystemen, z. B. UNIX [2],
werden Ein-/Ausgabegeräte in das Dateikonzept mit
einbezogen und Zugriffe auf diese wie Dateizugriffe
behandelt. Details wie blockweiser oder zeichenweiser Zugriff sind dabei dem Benutzer verborgen.
Aufgrund der Ähnlichkeit der Abläufe bei Zugriffen
auf Hintergrundspeicher und auf Ein-/Ausgabegeräte
können diese einheitlich unter dem Begriff der Ein/Ausgabe betrachtet werden (siehe 8.3.5).
Dateioperationen.
Das Filesystem des Betriebssystems stellt für die Dateiverwaltung ( file handling)
Operationen in Form von Systemaufrufen bereit, z. B.
create, remove, open, close, read und write (hier an-
121
122
Technische Informatik / Rechnerorganisation
gelehnt an Aufrufe in der unter dem Betriebssystem
UNIX verwendeten Programmiersprache C). Create
erzeugt eine Datei, indem der Dateiname in ein Dateiverzeichnis ( file directory) eingetragen wird und
diesem Eintrag dateispezifische Angaben zugeordnet
werden: die erlaubte Zugriffsart (read, write, execute, ggf. spezifiziert nach Dateiinhaber, Benutzergruppe und globaler Benutzung), die Gerätebezeichnung
des zugeordneten Hintergrundspeichers und ein Zeiger auf den ersten Bytespeicherplatz der Datei in diesem Speicher. Mit create wird die Datei gleichzeitig
für den Zugriff geöffnet. Remove hebt die Wirkung
von create wieder auf.
Lese- und Schreibzugriffe durch read bzw. write
erfolgen bytesequenziell, wobei auf dem aktuellen
Stand des Bytezeigers aufgesetzt und der Zeiger
jeweils aktualisiert wird. Zuvor werden die Zugriffsrechte des Aufrufers überprüft. Mit close wird eine
Datei geschlossen. Das Öffnen einer geschlossenen
Datei erfolgt mit open unter Berücksichtigung der
erlaubten Zugriffsart, z. B. read. Hierbei wird geprüft,
ob der Aufrufer das erforderliche Zugriffsrecht hat
und ob die Datei etwa bereits geöffnet ist. Erlaubt
eine Datei den gemeinsamen Zugriff mehrerer
Benutzer (shared program, shared data), so kann sie
zwar gleichzeitig von mehreren lesenden, aber nur
von einem schreibenden Benutzer geöffnet werden.
Dateiverzeichnisse. Die Verwaltung von Dateien
erfolgt mittels Verzeichnissen von Verweisen auf
andere Verzeichnisse oder unmittelbar auf Dateien. Ausgehend von einem Hauptverzeichnis (root
directory) erhält man so, bei mehrfacher Stufung,
eine Baumstruktur (Bild 8-6). Diese ist nützlich
Bild 8-6. Hierarchische Dateiverwaltung. Beispiel eines
Pfadnamens: PROJECT_2/MEYER/SCRIPT/TEXT_1
für eine hierarchische Vergabe von Zugriffsrechten
an Benutzergruppen und Einzelbenutzer und ermöglicht es dem Benutzer, ausgehend von seinem
individuellen Benutzerverzeichnis (home directory) seine Dateisammlung zu strukturieren. Die
hierarchische Struktur zeigt sich auf dem Bildschirm in Form ineinandergeschachtelter Fenster,
als tabellen-/listenähnliches Verzeichnis oder als
Textzeile aneinandergereihter Verzeichnis- und
Dateibezeichnungen (Pfadname, Bild 8-6).
8.3.5 Ein-/Ausgabeverwaltung
Ein-/Ausgabevorgänge (Dateizugriffe) sind hardwarenahe Abläufe, bei denen Daten zwischen dem
Hauptspeicher und Hintergrundspeichern oder Ein-/
Ausgabegeräten (devices) mittels Steuereinheiten
(device controllers) übertragen werden, häufig
mit Unterstützung eines DMA-Controllers. Dem
Betriebssystem obliegt es, diese Abläufe zu organisieren, sie jedoch vor dem Benutzer in ihren
physischen Details zu verbergen und diesem eine
von der Hardware abstrahierte logische Schnittstelle
zur Verfügung zu stellen. Dementsprechend besteht
die Systemsoftware aus einer unteren Schicht gerätespezifischer Routinen, den sog. Gerätetreibern
(device drivers), und aus einer höheren Schicht
geräteunabhängiger Routinen, als der eigentlichen
Betriebssystemschnittstelle in Form von Systemaufrufen.
Bibliotheksroutinen. Die
Schnittstelle der Systemaufrufe ist für den Benutzer üblicherweise durch
Bibliotheksroutinen verdeckt, die er als vom System
vorgegebene Dienstprogramme in sein Programm
einbinden muss. Sie erlauben ihm Funktionsaufrufe,
wie create, remove, open, close, read und write
(siehe 8.3.4), unter Angabe der für den Vorgang
erforderlichen Ein-/Ausgabeparameter (z. B. bei
read und write: Datei- bzw. Gerätebezeichnung,
Adresse eines Ein-/Ausgabepuffers, Anzahl der zu
übertragenden Bytes). Aufgabe der entsprechenden
Bibliotheksroutine ist es dann, den Funktionsaufruf
dem zuständigen Systemaufruf in Form eines TrapBefehls zuzuordnen und diesem die Parameter an
vorgegebenem Ort bereitzustellen. Darüber hinaus
wertet eine solche Routine ggf. Formatierungsinfor-
9 Algorithmen
mation aus und wandelt diese mit darzustellenden
Werten in eine Zeichenkette um (und umgekehrt). Als
Rückgabewert einer solchen Funktion wird z. B. die
Anzahl der gelesenen Bytes oder ggf. ein Fehlercode
geliefert.
Geräteunabhängige Software. Die hinter den Systemaufrufen stehende geräteunabhängige Software des Betriebssystems prüft die Zugiffsberechtigung des Benutzers und weist dessen Ein/Ausgabeanforderung ggf. zurück. Sie prüft, ob
das angeforderte Gerät verfügbar ist, wenn nicht,
reiht sie den Auftrag in eine Warteschlange ein.
Sie stellt Pufferbereiche für die einzelnen Ein-/
Ausgabevorgänge bereit. Bei blockweise arbeitenden Geräten abstrahiert sie dabei von deren physischer Blockgröße (z. B. von den unterschiedlichen
Sektorlängen von Hintergrundspeichern) und arbeitet
stattdessen mit einer für alle Geräte einheitlichen logischen Blockgröße. In gleicher Weise abstrahiert sie
bei zeichenweise arbeitenden Geräten von der Anzahl
gleichzeitig zugreifbarer Bytes (z. B. Byte, Halbwort,
Wort), d. h., sie arbeitet immer byteweise.
Dem Benutzer wird die Möglichkeit gegeben, auf Teile eines Blocks zuzugreifen, selbst dann, wenn der
Datenaustausch mit dem Gerät nur blockweise möglich ist. Schließlich obliegt es dieser Software, Fehlermeldungen der Gerätetreiber auszuwerten und auf
sie zu reagieren.
Geräteabhängige Software. Die geräteabhängige
Software sieht für jeden Gerätetyp einen Gerätetreiber vor. Diesem sind die Merkmale und der Zustand
der Gerätehardware bekannt, d. h., er kennt die Register der Steuereinheit und ihre Funktion, die Kommandos, die die Steuereinheit ausführen kann, die technischen Eigenschaften eines Geräts, z. B. die Sektorgröße, die Oberflächen- und die Zylinderanzahl einer
Festplatte, sowie den aktuellen Status eines Geräts,
z. B. Motor läuft oder Gerät beschäftigt.
Die Aufgaben eines Gerätetreibers sind dementsprechend vielfältig. Er nimmt Ein-/Ausgabeaufträge von
der über ihm liegenden geräteunabhängigen Software entgegen, ermittelt bei einer blockweisen Übertragung aus der logischen Blocknummer den physischen
Speicherort (z. B. Oberflächen-, Zylinder- und Sektornummer einer Festplatte), bereitet den Datentransport
vor (z. B. Laden eines Kommandos für das Positionieren des Schreib-/Lesearms) und initiiert dann den
eigentlichen Datenzugriff (Laden eines Kommandos
für den Lese- oder Schreibzugriff). Die erforderliche
Synchronisation zwischen Treiber mit Gerätehardware erfolgt meist über Interrupts und entsprechende
Interruptroutinen. Dabei kann es ggf. zu Wartezeiten
kommen, in denen der Treiber dann blockiert ist. Der
Treiber hat weiterhin die Aufgabe, Fehler zu erkennen
und möglichst zu beheben. Dazu wertet er z. B. bei
einem Lesevorgang die den Daten beigefügte Prüfinformation aus und veranlasst im Fehlerfall z. B. das
Wiederholen des Ein-/Ausgabevorgangs.
Programmierung
P. Rechenberg, H. Mössenböck
Programmieren im Sinne der Informatik heißt, ein
Lösungsverfahren für eine Aufgabe so zu formulieren, dass es von einem Computer ausgeführt werden kann. Der Programmierer muss dazu verschiedene Lösungsverfahren (Algorithmen) und Datenstrukturen kennen, mit denen sich die Lösung am günstigsten beschreiben lässt, und er muss eine Programmiersprache beherrschen. Je größer die Programme werden, umso mehr spielen zusätzlich Methoden der Projektplanung, Projektorganisation, Qualitätssicherung
und Dokumentation eine Rolle, die man unter dem
Begriff Softwaretechnik zusammenfasst.
9 Algorithmen
9.1 Begri−e
Die Bezeichnung Algorithmus ist von dem Namen des arabischen Mathematikers Al-Chwarizmi
(Al-Khorezmi, etwa 780–850) abgeleitet. Definition:
Ein Algorithmus ist ein endliches schrittweises Verfahren zur Berechnung gesuchter aus
gegebenen Größen, in dem jeder Schritt aus
einer Anzahl eindeutig ausführbarer Opera-
123
124
Technische Informatik / Programmierung
tionen und gegebenenfalls einer Angabe über
den nächsten Schritt besteht.
Zur mathematischen Präzisierung siehe z. B.
[8, 10].
Ein Algorithmus hat i. Allg. einen Namen; die
gegebenen Größen heißen Eingangsparameter, die
gesuchten Ausgangsparameter. Man beschreibt
den Aufruf (d. h. die Ausführung) des Algorithmus Q mit dem Eingangsparameter x und dem
Ausgangsparameter y durch Q(x, y) oder deutlicher
durch Q(↓ x ↑ y).
Wenn ein Algorithmus in einer Programmiersprache
abgefasst ist, sodass er (nach Übersetzung in eine
rechnerinterne Darstellung) von einer Maschine ausgeführt werden kann, nennt man ihn Programm.
Wegen der engen Verwandtschaft von Algorithmus
und Programm werden beide Begriffe oft synonym
gebraucht.
Ablaufstrukturen. Die Anordnung der Schritte in
Algorithmen folgt wenigen Mustern: Sequenz, Verzweigung und Schleife.
Die Sequenz entspricht der sukzessiven Ausführung
von Schritten:
Ausführung von Schritt 1
Ausführung von Schritt 2
Ausführung von Schritt 3
Die Verzweigung entspricht der Auswahl von einer
unter mehreren Möglichkeiten:
Falls Bedingung B erfüllt ist,
führe Schritt X aus,
sonst
führe Schritt Y aus.
9.2 Darstellungsarten
Algorithmen lassen sich auf verschiedene Weisen darstellen, die jeweils ihre Vor- und Nachteile haben.
Stilisierte Prosa. Jeder Schritt wird nummeriert und
unter Benutzung von Umgangssprache halbformal
beschrieben.
Programmablaufplan (Ablaufdiagramm). Eine Darstellung mit grafischen Elementen zur Hervorhebung
der Ablaufstrukturen. Die wichtigsten genormten
Symbole zeigt Bild 9-1. Die Norm ist allerdings
veraltet.
Struktogramm (Nassi-Shneiderman-Diagramm).
Benutzt noch einfachere grafische Symbole und beschränkt sich auf wenige bewährte Grundmuster für
Sequenz, Verzweigung und Schleife. Die wichtigsten
genormten Symbole zeigt Bild 9-2.
Algorithmenbeschreibungssprache (Pseudocode).
Programmiersprachenähnliche Darstellung, jedoch
frei vom syntaktischen Ballast einer echten Programmiersprache (damit der Algorithmus klar
hervortritt).
Programmiersprache. Das präziseste Instrument zur
Darstellung eines Algorithmus.
Beispiel für die verschiedenen Darstellungen sei das
einfache Suchproblem: Gegeben ist eine Liste von
Zahlen z0 , z1 , . . . , zn , mit n ≥ 0 und eine Zahl x. Es soll
festgestellt werden, ob x in der Liste enthalten ist und
wenn ja, an welcher Stelle es steht. Ergebnis soll eine
Zahl y sein, deren Wert der Index des gesuchten Listenelements ist oder −1, wenn x nicht in der Liste enthalten ist. Die Ausführungsreihenfolge der einzelnen
Schritte ist, wenn nicht anders angegeben, sequentiell.
Die Schleife entspricht der wiederholten Ausführung
eines Schritts:
Solange Bedingung B erfüllt ist,
wiederhole Schritt X.
Hier wird Schritt X wiederholt ausgeführt, bis die Bedingung B nicht mehr erfüllt ist (Schritt X muss dazu
den Wert von B ändern).
Die Abläufe aller Algorithmen setzen sich aus diesen wenigen Grundstrukturen und einigen Modifikationen davon zusammen. Näheres siehe 11.3.
Bild 9-1. Die wichtigsten Symbole für Ablaufdiagramme
(DIN 66 001)
9 Algorithmen
Bild 9-3. Suchen in einer Liste (Ablaufdiagramm)
Bild 9-2. Die wichtigsten Symbole für Struktogramme
(DIN 66261)
Stilisierte Prosa.
S. 1 Initialisiere. Setze y auf n.
S. 2 Prüfe. Wenn zy = x ist, ist der Algorithmus zu
Ende, sonst vermindere y um 1.
S. 3 Ende? Wenn y ≥ 0 ist, gehe nach S. 2 zurück,
andernfalls ist der Algorithmus zu Ende.
Ablaufdiagramm s. Bild 9-3.
Struktogramm s. Bild 9-4.
Algorithmenbeschreibungssprache. In der Java ähnlichen Algorithmenbeschreibungssprache Jana [1] lautet der Algorithmus wie folgt (int[0:n] z bedeutet dabei, dass z eine Folge ganzer Zahlen (integer) ist, die
von 0 bis n indiziert werden kann):
Such (↓int[0:n] z ↓int n ↓int x ↑int y) {
y = n;
while (y ≥ 0 && z[y] != x) {
y = y - 1;
}
}
(1)
Programmiersprache. Nur in dieser Darstellung sind
Algorithmen einer direkten maschinellen Ausführung
zugänglich. Eine Formulierung in C# lautet:
void Such (int[] z, int n, int x, out int y) {
y = n;
while (y >= 0 && z[y] != x) {
y = y - 1;
}
}
(2)
Bild 9-4. Suchen in einer Liste (Struktogramm)
Tabelle 9-1 zeigt die Vor- und Nachteile der verschiedenen Darstellungsarten.
9.2.1 Abstraktionsschichten
Je nach dem Zweck kann man einen Algorithmus
in verschiedenen Abstraktionsschichten darstellen. In
der abstraktesten, höchsten Schicht, wenn sein innerer
Aufbau nicht interessiert, besteht er nur aus seinem
Namen und den Parametern. Eine Verfeinerung davon
kann seinen inneren Aufbau auf eine Weise darstellen, die noch nicht alle Einzelheiten enthält, weitere
Verfeinerungen können diese hinzufügen.
Beispiel. Algorithmus zur aufsteigenden Sortierung
einer Zahlenliste list[0] bis list[n] in verschiedenen
Abstraktionsschichten:
125
126
Technische Informatik / Programmierung
Tabelle 9-1. Charakteristik der Darstellungsarten von Algorithmen
Darstellungsart
Anwendungsbereich
Vorteile
Nachteile
Stilisierte Prosa
Übersichts- und Detaildarstellung
Durch Abwesenheit von Formalismus für jeden verständlich. Programmiersprachenunabhängig.
Erläuterungen und Kommentare
leicht hinzuzufügen.
Unübersichtlich. Struktur tritt
schlecht hervor. Gefahr der
Mehrdeutigkeit.
Ablaufdiagramm
Übersichtsdarstellung
Anschaulich. Unübertroffen gute
(da zweidimensionale) Darstellung von Verzweigungen und
Schleifen.
Nur für Steuerfluss. Deklarationen schlecht unterzubringen.
Undisziplinierte Anwendung
beim Entwurf fördert unklare
Programmstrukturen.
Struktogramm
Übersichtsdarstellung
Beschränkung auf wenige Ablaufstrukturen. Fördert dadurch
einfache Programmstrukturen.
Struktur weniger klar hervortretend als in Ablaufdiagrammen. Deklarationen und
Kommentare schwer unterzubringen.
Algorithmenbeschreibungssprache
(Pseudocode)
Übersichts- und Detaildarstellung
Flexibel und (meist) genügend
präzise.
Linear und unanschaulich.
Setzt Kenntnis der Sprache
voraus.
Programmiersprache
Detaildarstellung
Größte Präzision. Nach Übersetzung unmittelbar für eine Maschine verständlich.
Sprachabhängig. Mit Details
überladen. Setzt Kenntnis
der Sprache voraus.
Abstrakteste Schicht:
Sort(list ↓n)
Da list verändert wird, ist es ein Übergangsparameter
(sowohl Eingangs- wie Ausgangsparameter).
Die erste Verfeinerung zeigt, dass das Austauschverfahren zum Sortieren benutzt wird: Man sucht das
kleinste Element in der gesamten Liste und vertauscht
es mit list[0]. Dann sucht man das kleinste Element
in der Teilliste list[1] bis list[n] und vertauscht es mit
list[1], usw. (hier in der Algorithmenbeschreibungssprache Jana dargestellt):
Sort(int[0:n] list ↓int n) {
int i, min;
for (i = 0 .. n-1) {
min = Index des kleinsten Elements von
list[i..n];
Vertausche list[i] mit list[min];
}
}
Die zweite Verfeinerung detailliert die Prosatexte der
ersten:
Sort(int[0:n] list ↓int n) {
int i, k, min, h;
for (i = 0 .. n-1){
// Suche Minimum aus list[i..n]
min = i;
for (k = i + 1 .. n){
if (list[k] < list[min]) min = k;
}
// Vertausche list[i] mit list[min]
h = list[i];
list[i] = list[min];
list[min] = h;
}
}
9.3 Einteilungen
Algorithmen können z. B. nach ihrer Struktur, nach
den von ihnen verwendeten Datenstrukturen und nach
ihrem Aufgabengebiet charakterisiert werden.
9 Algorithmen
9.3.1 Einteilung nach Strukturmerkmalen
Algorithmen als Funktionen. Diese Algorithmen
kommunizieren mit ihrer Umgebung nur über ihre Parameter. Variablen, die nur innerhalb des Algorithmus
benutzt werden (lokale Variablen) bleiben nicht über
eine Ausführung des Algorithmus hinaus erhalten.
Solche Algorithmen haben kein „Gedächtnis“, d. h.
keinen Zustand, der von einem Aufruf zum nächsten
erhalten bleibt. Ihre Ausgangsparameter sind allein
eine Funktion der Eingangsparameter. Beispiele sind
die meisten mathematischen Funktionen, wie sin(x)
und die Algorithmen Such und Sort aus 9.2.
Algorithmen mit Gedächtnis. Diese Algorithmen
besitzen einen Zustand, der von ihrer Vorgeschichte
abhängt. Ihre Ergebnisse sind eine Funktion ihrer
Eingangsparameter und ihres Zustands. Der Zustand
wird durch Variablen repräsentiert, deren Werte
von Aufruf zu Aufruf erhalten bleiben (statische
Variablen). Beispiel: ein Algorithmus, der bei jedem
Aufruf die Position eines Roboters verändert und sich
diese merkt. Algorithmen mit Gedächtnis werden
vor allem in der objektorientierten Programmierung
verwendet.
Rekursive Algorithmen. Die Lösung eines Problems
lässt sich oft rekursiv auf die Lösung gleichartiger
Teilprobleme zurückführen (z. B. die Suche in einer
Liste auf die Suche in zwei halb so großen Teillisten). Ein Algorithmus heißt rekursiv, wenn er sich
bei seiner Ausführung selbst aufruft (möglicherweise
über andere Algorithmen als Zwischenstufen). Rekursion ist ein wohlbekanntes Mittel der Mathematik. In
der Programmierung kommt sie überall dort vor, wo
Aufgaben (z. B. Suchprobleme) oder Datenstrukturen
(z. B. Bäume, siehe Kap. 10.5) auf natürliche Weise
rekursiv definiert sind. 10.5 enthält ein Beispiel.
Exhaustionsalgorithmen. Bei manchen Problemen
kann man die Lösung nur durch erschöpfendes Ausprobieren aller Möglichkeiten finden. Es handelt sich
hier typischerweise um Suchprobleme (z. B. Suche
eines Auswegs aus einem Labyrinth) oder um Optimierungsprobleme (z. B. Auswahl einer Teilmenge
von Gegenständen, sodass ihr Gewicht minimal und
ihr Wert maximal wird). Durch Speicherung von bereits als optimal erkannten Zwischenergebnissen kann
man manchmal die Anzahl der zu prüfenden Möglichkeiten reduzieren (dynamische Programmierung). Ex-
Tabelle 9-2. Schnittgeschwindigkeit in m/min bei spanender Bearbeitung x1 : Bearbeitungsart; x2 : Werkstoffbeschaffenheit
x2
x1
Drehen
Hobeln
Bohren
weich
1500
2500
200
hart
300
400
150
haustionsalgorithmen sind oft rekursiv und aufgrund
der vielen zu prüfenden Lösungsmöglichkeiten langsam.
Tabellengesteuerte (interpretative) Algorithmen.
Oft lassen sich die Ergebnisse eines Algorithmus mithilfe einer Tabelle aus den Argumenten
berechnen. Tabelle 9-2 zeigt z. B., wie die Schnittgeschwindigkeit bei spanender Bearbeitung von
der Bearbeitungsart (Drehen, Hobeln, Bohren) und
der Werkstoffbeschaffenheit (weich, hart) abhängt.
Ein Algorithmus zur Berechnung der Schnittgeschwindigkeit kann die Tabelle als Matrix speichern
und braucht dann nur zu den Argumentwerten den
entsprechenden Tabelleneintrag zu suchen. Das Spezifische der Aufgabe ist hier in der Tabelle enthalten,
nicht im Algorithmus. Tabellen einer bestimmten
Art (sog. Entscheidungstabellen) sind in DIN 66 241
genormt.
Wenn man in einer Tabelle Operationen statt Zahlen
speichert und der Algorithmus diese Operationen ausführt, nennt man den Algorithmus interpretativ. Da
Algorithmen als Schaltwerke (siehe Kap. 2) mit Zuständen und Übergängen angesehen werden können
und Schaltwerke als Tabellen darstellbar sind, lassen sich auch Algorithmen als Tabellen verschlüsseln. Ein Algorithmus, der solche Tabellen interpretiert, ist folglich ein universeller Algorithmus, weil
er jeden in Tabellenform codierten Algorithmus ausführen kann. Interpretation und universeller Algorithmus haben dementsprechend in der Informatik weit
reichende Bedeutung.
Weitere Klassen von Algorithmen sind nichtdeterministische (die bei mehrfacher Ausführung womöglich verschiedene Schritte durchlaufen, aber immer
dasselbe Ergebnis liefern), probabilistische (die bei
mehrfacher Ausführung womöglich verschiedene Ergebnisse liefern), parallele (bei denen mehrere Schrit-
127
128
Technische Informatik / Programmierung
te gleichzeitig ablaufen können), verteilte (deren Abschnitte auf verschiedene Computer verteilt sind) und
genetische (die Methoden der Evolution wie Mutation, Selektion und Kreuzung benutzen).
9.3.2 Einteilung nach Datenstrukturen
Die von Algorithmen verwendeten Datenstrukturen
(z. B. Listen, Bäume, Graphen) sind in mancher Hinsicht charakteristisch für sie und ermöglichen deshalb
eine nützliche Einteilung (siehe Kap. 10).
9.3.3 Einteilung nach Aufgabengebiet
Ohne die Berücksichtigung der Aufgaben spezieller
Fachgebiete, wie Physik, Chemie usw., ergibt sich etwa folgende Einteilung der Algorithmen.
Numerische Algorithmen betreffen Methoden der
numerischen Mathematik, wie die Lösung von Gleichungssystemen, Approximation von Funktionen,
numerische Integration, Lösung von Differentialgleichungssystemen [7, 15, 16].
Seminumerische Algorithmen. Dieser Begriff
bezeichnet Algorithmen, die zwischen numerischem
und symbolischem Rechnen stehen. Hierzu gehört
die Erzeugung von Zufallszahlen, die Gleitpunktarithmetik, das Rechnen mit mehrfacher Genauigkeit,
mit Brüchen und Polynomen [9].
Symbolisches Rechnen. Hierher gehören Algorithmen, die mathematische Formeln einem Kalkül gemäß transformieren (z. B. Differenzieren und Integrieren komplizierter Formelausdrücke) sowie Algorithmen zum automatischen Beweisen (z. B. von Formeln des Prädikatenkalküls) [2, 3, 6].
Such- und Sortieralgorithmen treten in vielen Formen auf. Suchen und Sortieren im Arbeitsspeicher
ist Teil fast aller größeren Programmsysteme, Suchen
und Sortieren in externen Speichern ist eine zentrale
Operation in Datenbanken.
Kombinatorische Algorithmen suchen Lösungen in
einem vorgegebenen Lösungsraum aus endlich vielen
diskreten Punkten; wichtig in Künstlicher Intelligenz,
Operations Research, Analyse von Netzen aller Art,
Optimierung.
Algorithmen zur Textverarbeitung oder syntaktische Algorithmen verarbeiten lange Zeichenfolgen,
wie sie bei der maschinellen Bearbeitung von Doku-
menten auftreten. Typische Anwendungen sind Mustersuche in Editoren, Syntaxanalyse in Sprachübersetzern, Datenkompression und -expansion.
Algorithmen der digitalen Signal- und Bildverarbeitung analysieren und transformieren Signale,
die bei der digitalen Signalübertragung auftreten.
Typische Anwendungen: digitale Filterung, Schnelle
Fourier-Transformation (FFT) [11].
Geometrische Algorithmen lösen Aufgaben im Zusammenhang mit Punkten, Linien und anderen einfachen geometrischen Objekten, z. B. Bildung der konvexen Hülle eines Punkthaufens, Feststellen, ob und
wo sich Objekte schneiden, zweidimensionales Suchen [4].
Algorithmen der grafischen Datenverarbeitung
schließen sich an geometrische Algorithmen an
und behandeln die Darstellung zwei- und dreidimensionaler Objekte auf Bildschirmen und in
Zeichnungen (z. B. Auffinden verdeckter Kanten,
schnelle Rotation, realistische Oberflächengestaltung
durch Schattierung und Reflexion) [5, 14].
9.4 Komplexität
Für jede Aufgabe der Datenverarbeitung gibt es meist
mehrere Lösungsalgorithmen, die sich in bestimmten
Merkmalen unterscheiden, wie z. B. Laufzeit, Speicherplatzbedarf, statische Länge des Algorithmus und
Schachtelungsstruktur. Um Algorithmen miteinander
vergleichen oder für sich allein kennzeichnen zu können, versucht man, die Merkmale in Abhängigkeit
von geeigneten Messgrößen zu quantifizieren. Die
Messgrößen geben Auskunft über die „Komplexität“
eines Algorithmus. Ihre Berechnung nennt man
„Komplexitätsanalyse“.
Eines der wichtigsten Merkmale eines Algorithmus
ist seine Laufzeit in Abhängigkeit vom Umfang der
Eingabedaten. Sie wird „Zeitkomplexität“ genannt.
Zeitkomplexität:
Laufzeit = f (Umfang der Eingabedaten)
Für den Umfang n der Eingabedaten gibt es kein einheitliches, vom betrachteten Algorithmus unabhängiges Maß. Tabelle 9-3 zeigt natürliche Maße für verschiedene Aufgabenklassen.
Die Laufzeit eines Algorithmus lässt sich durch
Laufzeitberechnung (analytisch auf dem Papier) oder
10 Algorithmen
Tabelle 9-3. Maße für den Umfang von Eingabedaten
Aufgabenklasse
Sortieren, Suchen
Matrixoperationen
Textverarbeitung
Mehrfachgenaue
Multiplikation
Natürliches Maß für den
Umfang der Eingabedaten
Elementeanzahl
Zeilen- mal Spaltenanzahl
Länge des Eingabetextes
Gesamtstellenanzahl der
Operanden
durch Laufzeitmessung (unmittelbar auf der Maschine) bestimmen. Meist begnügt man sich aber mit viel
gröberen Angaben, insbesondere damit, dass sich die
Laufzeit des einen Algorithmus „für große n“ wie
die Funktion n2 verhält, die eines anderen dagegen
wie n3 ; d. h., mit der Eingabegröße 2n läuft der erste
viermal, der zweite achtmal so lange wie mit der
Eingabegröße n. Diese sog. asymptotische Zeitkomplexität beschreibt man durch die O-Notation. Man
schreibt f (n) = O(g(n)), gelesen: „ f (n) ist von der
Ordnung g(n)“, wenn es eine positive Konstante c
gibt, sodass
lim |g(n)/ f (n)| ≤ c
n→∞
oder, ohne Limes ausgedrückt, wenn es positive Konstanten c und n0 gibt, sodass gilt:
f (n) ≤ c · g(n) für alle n ≥ n0
Das heißt: „Mit unbeschränkt wachsendem n wächst
f (n) nicht schneller als g(n).“
Typische asymptotische Komplexitäten und ihre Eigenschaften sind (in Anlehnung an [13]):
O(1) Konstante Komplexität. Die Laufzeit ist unabhängig von der Eingabegröße. Das ist der Idealfall,
bedeutet aber möglicherweise, dass die Eingabegröße
unpassend gewählt wurde.
O(log n) Logarithmische Komplexität. Sehr günstig.
Verdopplung von n bedeutet nur einen Anstieg der
Laufzeit um log 2, d. h. um eine Konstante. Erst bei
der Quadrierung von n wächst log n auf das Doppelte.
O(n) Lineare Komplexität. Immer noch günstig. Tritt
auf, wenn auf jedes Eingabeelement eine feste Verar-
Bild 9-5. Wachstum einiger Funktionen, die bei der
Komplexitätsanalyse benutzt werden (doppeltlogarithmische Darstellung!)
beitungszeit entfällt. Verdopplung von n bedeutet Verdopplung der Laufzeit.
O(n log n) Leicht überlineare Komplexität. Nicht viel
schlechter als die lineare Komplexität, weil der Logarithmus von n klein gegen n ist. Tritt oft auf, wenn ein
Problem fortgesetzt in Teilprobleme zerlegt wird, die
unabhängig voneinander gelöst werden.
O(n2 ) Quadratische Komplexität. Ungünstig. Tritt
z. B. auf, wenn der Algorithmus auf alle n2 Paare
der n Eingabedaten angewandt wird oder zwei
geschachtelte Schleifen enthält, deren Ausführungshäufigkeit mit n wächst. Verdopplung von n bedeutet
Vervierfachung der Laufzeit.
O(n3 ) Kubische Komplexität. Sehr ungünstig und nur
bei kleinen n akzeptabel. Verdopplung von n bedeutet
Verachtfachung der Laufzeit.
O(2n ) Exponentielle Komplexität. Katastrophal für
große n. Tritt auf, wenn zur Lösung eines Problems
alle Kombinationen der n Eingabedaten exhaustiv
geprüft werden müssen. Eine Erhöung von n um 1
verdoppelt bereits die Laufzeit. Bild 9-5 zeigt das
Wachstum der verschiedenen Funktionen.
129
130
Technische Informatik / Programmierung
10 Datentypen
und Datenstrukturen
Algorithmen arbeiten mit Daten, die in verschiedener Form vorliegen können (z. B. als Zahlen, Zeichen,
Listen, Tabellen). Einfache Daten können zu komplexeren Datenstrukturen gruppiert werden. Die Wahl
der für eine Problemlösung am besten geeigneten Datenstrukturen ist ebenso wichtig wie die Wahl der am
besten geeigneten Algorithmen. Beide hängen eng zusammen und müssen zusammen gesehen werden.
10.1 Begri−e
10.1.1 Datentyp
Ein Datentyp definiert eine Menge von Werten und
die damit ausführbaren Operationen. So beschreibt
z. B. der Datentyp int in der Sprache C# die Menge
der ganzen Zahlen (in einem von der Sprache festgelegten Bereich) samt den Operatoren + (Addition), −
(Subtraktion), * (Multiplikation), / (Division) sowie
den Vergleichsoperatoren (==, !=, >, >=, <, <=).
Datentypen sind Eigenschaften von Variablen und
Werten. Eine Variable ist ein Behälter für Werte. Der
Datentyp einer Variablen bestimmt die Art der Werte,
die in dieser Variablen gespeichert werden dürfen.
In einer int-Variablen dürfen z. B. nur ganze Zahlen
gespeichert werden und keine Gleitpunktzahlen oder
Zeichen.
Ähnlich wie Variablen haben auch Werte einen Datentyp. Der Wert 17 hat z. B. in C# den Datentyp int,
der Wert ’a’ hat den Datentyp char (Zeichen). Ein
Ausdruck berechnet aus den Werten seiner Operanden einen neuen Wert, der ebenfalls einen Datentyp
hat, welcher nach den Regeln der Sprache aus den
Operandentypen ermittelt wird.
Bei der Übersetzung von Programmiersprachen spielen Datentypen eine wichtige Rolle. Der Compiler
kann prüfen, ob die Operandentypen in Ausdrücken
miteinander kompatibel sind oder ob in einer Zuweisung der Typ der rechten Seite zum Typ der linken
Seite passt. Die Typregeln einer Programmiersprache
gestatten es dem Compiler, viele Programmierfehler
bereits zur Übersetzungszeit zu entdecken.
Die Speicherzellen einer Maschine enthalten lediglich
eine Folge von Bits und haben keine von der Maschine vorgegebene Bedeutung. Indem man einer Spei-
cherzelle (Variablen) einen Typ gibt, versieht man ihre Bitfolge mit einer Bedeutung. Dieselbe Bitfolge
kann z. B. je nach Datentyp als Zahl, als Zeichen oder
als Adresse aufgefasst werden.
10.1.2 Datenstruktur
Eine Datenstruktur besteht aus mehreren Elementen
mit gleichem oder unterschiedlichem Datentyp. Die
gebräuchlichsten Datenstrukturen sind Arrays (Folgen gleichartiger Elemente) und Strukturen (Folgen
unterschiedlicher Elemente), die in 10.3 näher beschrieben werden. Arrays und Strukturen sind so häufig, dass sie in den meisten Programmiersprachen als
eigene Datentypen vorkommen. Es gibt jedoch auch
anwendungsspezifische Datenstrukturen, die z. B. ein
Straßennetz, eine Zeichnung in einem Grafikeditor
oder eine Stückliste in einem Fertigungsprogramm
modellieren können. Indem man die Details einer Datenstruktur vor dem Benutzer verbirgt und den Zugriff nur über einige wenige genau definierte Operationen gestattet, gelangt man von einer konkreten Datenstruktur zu einer abstrakten Datenstruktur oder zu
einem abstrakten Datentyp (10.10, 12.3.1).
Man unterscheidet ferner zwischen statischen und
dynamischen Datenstrukturen. Eine statische Datenstruktur wird ausschließlich durch ihre Deklaration
festgelegt. Ihre Größe ist zur Übersetzungszeit
bekannt, und ihr Speicherplatz wird zur Laufzeit
automatisch reserviert. Die Größe der Datenstruktur
kann sich anschließend nicht mehr ändern. Eine
dynamische Datenstruktur besteht hingegen aus einer
nicht festgelegten Anzahl von Objekten, die über
Referenzen (Zeiger) miteinander verknüpft sind. Die
Objekte werden durch spezielle Anweisungen erzeugt
und zur Datenstruktur hinzugefügt. Eine dynamische
Datenstruktur kann also zur Laufzeit wachsen und
schrumpfen. Die häufigsten dynamischen Datenstrukturen sind verkettete Listen, Bäume und Graphen.
10.2 Elementare Datentypen
Die meisten Programmiersprachen stellen elementare
Datentypen für ganze Zahlen, Gleitpunktzahlen,
Wahrheitswerte und Zeichen zur Verfügung (sog.
Standardtypen). Tabelle 10-1 zeigt den Namen sowie
die Schreibweise für Konstanten dieser Standardtypen für einige bedeutende Programmiersprachen.
10 Datentypen und Datenstrukturen
Tabelle 10-1. Elementare Datentypen in Programmiersprachen
Ganze Zahlen
Konstanten
Gleitpunktzahlen
Konstanten
Wahrheitswerte
Konstanten
Zeichen
Konstanten
Fortran
INTEGER
123
REAL
3.14, 314E-2
LOGICAL
.FALSE., .TRUE.
CHARACTER
’A’
Pascal
integer
123
real
3.14, 314E-2
boolean
false, true
char
’A’
Ganze Zahlen. Zur Darstellung siehe 6.3.3. Ganze
Zahlen werden im Rechner immer exakt dargestellt,
und arithmetische Operationen mit ihnen (mit Ausnahme der Division) liefern immer exakte Ergebnisse
(sofern der Wertebereich nicht überschritten wird).
Gleitpunktzahlen. Über die Darstellung durch Mantisse und Exponent siehe 6.3.4. Man beachte, dass
Gleitpunktzahlen im Rechner nur als Approximation
der wirklichen Zahlenwerte dargestellt sind. Bei Folgen von Rechenoperationen können sich dadurch Abweichungen vom wahren Resultat ergeben. Die Abfrage, ob eine berechnete Gleitpunktvariable x den
Wert a hat, sollte deshalb niemals auf Gleichheit stattfinden, sondern immer eine Toleranz ε berücksichtigen, nicht if (x == a) . . . , sondern if (abs(x−a) < eps)
. . . (die Funktion abs liefert den Betrag ihres Arguments). Um die Auswirkungen der approximativen
Zahlendarstellung zu verringern, verwendet man oft
Gleitpunktzahlen sog. doppelter Genauigkeit.
Wahrheitswerte (Boole’sche oder logische Werte)
sind die beiden Werte „wahr“ (true) und „falsch“ (false). Sie treten besonders in den Bedingungsteilen von
If- und Schleifenanweisungen auf. Zum Beispiel kann
eine Boole’sche Variable looping zum Abbruch einer
Schleife so benutzt werden:
bool looping = true;
...
while (looping) {... looping = false; ...}
Damit die Schleife verlassen wird, muss looping in
der Schleife auf den Wert false gesetzt werden.
Zeichen. Zeichen werden meist als Byte im sog. ASCII, in neueren Sprachen wie Java oder C# auch als
zwei Bytes im sog. Unicode verschlüsselt.
Java
int
123
float
3.14, 314E-2
boolean
false, true
char
’A’
C#
int
123
float
3.14, 314E-2
bool
false, true
char
’A’
Aufzählungstypen. Ein Aufzählungstyp (Enumerationstyp) definiert eine Menge benannter Werte durch
erschöpfende Aufzählung dieser Werte, z. B. in C#:
enum Season {spring, summer, fall, winter}
enum Color {red, blue, green}
Hier sind spring, summer, fall, winter Konstanten
vom Typ Season, die man Variablen dieses Typs zuweisen und auf die man Variablen dieses Typs abfragen kann, z. B.:
Season season;
Color dressColor;
...
if (season == Season.summer)
dressColor = Color.blue;
Aufzählungstypen erweisen sich zur sprechenden Bezeichnung von Werten als nützlich.
10.3 Zusammengesetzte Datentypen
Aus elementaren Datentypen lassen sich neue Datentypen zusammensetzen. Man unterscheidet dabei
zwischen Arrays (bestehend aus Elementen gleichen
Typs) und Strukturen (bestehend aus Elementen unterschiedlichen Typs). Die meisten Programmiersprachen bieten auch Referenztypen an, deren Werte auf
Objekte anderer Datentypen verweisen und mit denen
sich dynamische Datenstrukturen bilden lassen.
10.3.1 Arrays
Ein eindimensionales Array (auch Vektor oder Feld
genannt) ist die geordnete Folge von Elementen, die
131
132
Technische Informatik / Programmierung
alle denselben Typ haben. Das Array hat einen Namen; seine Elemente werden über Indizes angesprochen. Die Indizes sind ganze Zahlen zwischen einem
unteren Grenzindex u (in manchen Programmiersprachen immer 0) und einem oberen Grenzindex o. Zum
Beispiel wird ein Array a aus 5 Elementen des Typs
int mit u = 0, o = 4 in der Sprache C so deklariert:
int[5] a;
a[0] ist sein erstes, a[4] sein letztes Element. Die Elemente werden auch als indizierte Variablen bezeichnet und in aufeinander folgenden Speicherzellen gespeichert (Bild 10-1b). Indizierte Variablen kann man
wie einfache Variablen in Ausdrücken verwenden und
ihnen Werte zuweisen. Charakteristisch für Arrays ist:
ein gemeinsamer Name für alle Elemente, alle Elemente haben denselben Typ, die Elemente sind geordnet, gleich schneller Zugriff zu allen Elementen,
die Indizes sind meist Zahlen, d. h., man kann mit ihnen rechnen.
Besondere Bedeutung haben Arrays aus Zeichen, die
zur Speicherung von Texten benutzt werden. Da Arrays in den meisten Programmiersprachen eine feste, durch Deklaration bestimmte Länge haben, Texte
in ihrer Länge aber stark variieren können, gibt es in
einigen Programmiersprachen einen besonderen Datentyp (String), der einem Zeichenarray mit unspezifiziertem oberen Grenzindex entspricht.
In den meisten Programmiersprachen sind auch mehrdimensionale Arrays mit mehreren Indizes zugelassen. Die C-Deklarationen
float[10][20] m;
float[2][10][20] p;
dreidimensionales Array p mit 2 Matrizen zu 10
Zeilen zu 30 Spalten. Die Elemente sind hier jeweils
Gleitpunktzahlen (float). Die Speicherung einer
Matrix geschieht entweder zeilenweise (C, Pascal,
Ada) oder spaltenweise (Fortran). Dadurch besteht
eine lineare Ordnung zwischen allen Elementen, die
es gestattet, mehrdimensionale auf eindimensionale
Arrays zurückzuführen.
10.3.2 Strukturen
Strukturen (records, Verbunde) sind geordnete Folgen
von Elementen unterschiedlichen Typs (Bild 10-2).
Zum Beispiel wird in C eine Struktur x aus Elementen
si mit den Typen T i so deklariert:
struct{T1 s1 ; ...; Tn sn ;} x;
Die Elemente einer Struktur heißen Felder (leider
werden auch Arrays manchmal als Felder bezeichnet); sie sind durch die „Punktschreibweise“ über
ihren Namen ansprechbar. Für Bild 10-2 gilt: person.name bezeichnet das Feld name, person.name[0]
bezeichnet den ersten Buchstaben des Namens.
struct {
enum {Mr, Mrs, Dr}
char[30]
char[30]
int
char[20]
} person;
a
definieren z. B. ein zweidimensionales Array m
(Matrix) mit 10 Zeilen zu je 20 Spalten und ein
person
title name street zip
city
b
person
int[5] a;
+0 +1
Adr(a) +4
title;
name;
street;
zip;
city;
+8
+12
+16
+31
+61 +65
+85
+20
a[0] a[1] a[2] a[3] a[4]
title name
street
zip city
c
Bild 10-1. Eindimensionales Array. a Deklaration in C,
Bild 10-2. Struktur. a Deklaration in C; b Darstellung als
b Repräsentation im Speicher (Adressen in Byte unter der
Annahme: 1 Arrayelement = 4 Byte)
„Baum“, c Repräsentation im Speicher (Adressen in Byte
unter der Annahme dichtester byteweiser Speicherung)
10 Datentypen und Datenstrukturen
10.3.3 Zeiger und Referenzen
Zeiger (pointer) verweisen auf Objekte anderer Datentypen. Ihre Werte sind Adressen. Die so referenzierten Objekte liegen in einem besonderen Speicherbereich (Halde, heap) und müssen vom Programmierer zur Laufzeit durch spezielle Anweisungen dynamisch erzeugt werden.
Manche Sprachen fassen Zeiger und die durch sie
referenzierten Objekte zu sog. Referenztypen zusammen. Eine Variable eines Referenztyps enthält einen
Verweis auf das referenzierte Objekt.
In Java und C# sind zum Beispiel Arrays Referenztypen. Die Deklaration int[] a beschreibt dort eine Variable a, die auf ein int-Array verweist. Die Länge des
Arrays wird erst bei der Erzeugung des Arrayobjekts
angegeben (z. B. a = new int[5];).
Referenzen auf Strukturen werden in Java und C#
durch sog. Klassen deklariert (siehe auch 11.3.5). Folgendes Beispiel deklariert eine Klasse Node mit den
Feldern val und next.
class Node {
int val;
Node next;
}
(1)
Eine Variable p vom Typ Node wird durch Node p;
deklariert. Die Anweisung p = new Node(); erzeugt
ein neues Objekt vom Typ Node und weist dessen
Adresse der Variablen p zu. Auf die Felder dieses Objekts kann man mit p.val und p.next zugreifen.
Das Fled next des Typs Node verweist wieder auf ein
Node-Objekt. Auf diese Weise kann man eine beliebig lange Kette (Liste) von Node-Objekten (Knoten)
bilden. Bild 10-3 zeigt eine solche Liste mit vier Knoten. Ihr erster Knoten wird durch eine Variable first
vom Typ Node referenziert. Das next-Feld des letzten
Knotens hat den Wert null, d. h. es zeigt auf keinen
weiteren Knoten.
first
val next
null
Bild 10-3. Liste aus vier Knoten gemäß Deklaration (1)
In älteren Programmiersprachen wie C oder Pascal,
müssen dynamisch erzeugte Objekte vom Programmierer freigegeben werden, sobald sie nicht mehr benötigt werden. Dies geschieht z. B. in Pascal mit dispose(p). Der Speicherplatz des durch p referenzierten
Objekts wird dadurch freigegeben und steht dann wieder für die Erzeugung neuer Objekte zur Verfügung.
Da die explizite Freigabe von Objekten fehleranfällig
ist, benutzen neuere Sprachen wie Java oder C# eine sog. automatische Speicherbereinigung (garbage
collection). Dabei handelt es sich um ein Systemprogramm, das Objekte automatisch freigibt, sobald sie
nicht mehr referenziert werden.
Zeiger und Referenzen sind die Grundlage für dynamische Datenstrukturen wie verkettete Listen, Bäume
oder Graphen, die in den nächsten Abschnitten behandelt werden.
10.4 Verkettete Listen
Eine verkettete Liste ist eine Folge von Objekten
(Knoten), die über Referenzen (Kanten) derart
verknüpft sind, dass jeder Knoten außer dem letzten
genau einen Nachfolger hat (Bild 10-3). Eine verkettete Liste ähnelt aus logischer Sicht einem Array,
hat jedoch den Vorteil, dass sie beliebig wachsen
und schrumpfen kann und dass das Einfügen und
Löschen von Knoten an jeder Listenposition gleich
effizient ist; dafür hat sie den Nachteil, dass nicht
auf alle Knoten gleich schnell zugegriffen werden
kann. Ein Zugriff auf den Knoten an Position i
erfordert das Durchlaufen der i − 1 Vorgängerknoten.
Typische Listenoperationen sind Einfügen, Löschen
und Suchen von Werten.
Wenn list eine Variable vom Typ Node aus 10.3.3 ist,
die auf den ersten Knoten der Liste zeigt, so kann man
einen Wert val wie folgt als ersten Knoten der Liste
einfügen (Beispielcode in C#; ein mit ref bezeichneter Parameter ist ein Übergangsparameter):
void Insert (ref Node list, int val) {
Node p = new Node();
p.val = val;
p.next = list; list = p;
}
133
134
Technische Informatik / Programmierung
a
b
list
p
list
7
2
anrede
3
7
q
c
list
anschrift
null
7
2
null
a
p
2
3
Am Ende der Schleife zeigt q auf den letzten Knoten der Liste, außer die Liste ist leer. Wenn die Liste
leer ist (list == null) wird der neue Knoten, auf den p
zeigt, zum ersten und einzigen Knoten der Liste. Andernfalls wird er zum Nachfolger des Knotens auf den
q zeigt. Bild 10-4 zeigt die Auswirkungen von Insert
und Append an einem Beispiel. Die Laufzeitkomplexität der meisten Listenoperationen ist O(n), d. h. die
Laufzeit ist proportional zur Anzahl n der Listenknoten.
Neben einfach verketteten Listen, bei denen jeder
Knoten lediglich einen Zeiger auf seinen Nachfolger
hat, gibt es auch doppelt verkettete Listen, bei denen
jeder Knoten sowohl einen Zeiger auf seinen Nachfolger als auch einen Zeiger auf seinen Vorgänger
hat. Doppelt verkettete Listen können in beiden
Richtungen durchlaufen werden.
10.5 Bäume
Ein Baum ist wie eine verkettete Liste eine dynamische Datenstruktur aus Knoten und Kanten. Im Gegensatz zu einer verketteten Liste kann jeder Knoten
mehrere Nachfolger (Söhne) haben. Hingegen hat jeder Knoten genau 1 Vorgänger (Vater), mit Ausnahme
vorname zuname strasse staat plz ort
b
c
anschrift (anrede, name (vorname, zuname),
adresse (strasse, staat, plz, ort)
Bild 10-4. Einfügen in eine verkettete Liste. a Ursprüngliche Liste; b nach Insert(ref list, 3); c nach Append(ref list,
3)
void Append (ref Node list, int val) {
Node p = list;
Node q;
while (p != null) { q = p; p = p.next; }
p = new Node();
p.val = val; p.next = null;
if (list == null) list = p; else q.next = p;
}
adresse
anschrift
anrede
name
vorname
zuname
adresse
strasse
staat
plz
ort
null
Das Einfügen am Listenende erfordert hingegen einen
Durchlauf der gesamten Liste bis zum letzten Knoten:
name
anschrift
name
vorname,
zuname
anrede
adresse
strasse, staat,
plz, ort
d
Bild 10-5. Darstellungsarten von Bäumen. a baumartig;
b Zeilen mit Einrückungen; c Zeichenfolge mit Klammern;
d geschachtelte Mengen
des ersten Knotens, der Wurzel, die keinen Vorgänger
hat. Die Söhne eines Knotens sind zueinander Brüder. Knoten, die keine Söhne haben, nennt man Blätter. Die Söhne eines Knotens können als Wurzeln von
Unterbäumen dieses Knotens betrachtet werden.
Ein Baum beschreibt eine Hierarchie und kann aus
logischer Sicht auf vielerlei Art dargestellt werden
(Bild 10-5). Da hierarchische Strukturen in vielen Anwendungen vorkommen (z. B. Gliederung einer Formel, eines Programmsystems, eines Schriftstücks, einer Firma), sind Bäume von großer Bedeutung.
Ein Baum, in dem alle Knoten höchstens zwei Söhne
haben, heißt binärer Baum, alle anderen Bäume heißen Vielwegbäume.
Binäre Bäume
Binäre Bäume sind von besonderer Bedeutung, weil
sie einfach und regulär gebaut sind und besonders oft
vorkommen. Auch Vielwegbäume lassen sich auf bi-
10 Datentypen und Datenstrukturen
Bild 10-6. Transformation eines Vielwegbaums in einen binären Baum. a Vielwegbaum mit Zeigern vom Vater zu allen Söhnen; b binärer Baum mit Zeigern zum ersten Sohn
und zum nächsten Bruder
näre Bäume zurückführen. Bild 10-6 zeigt das Verfahren. Es beruht auf der Idee, dass ein Knoten im
transformierten Baum nicht Zeiger auf alle seine Söhne hat, sondern nur einen Zeiger auf den ersten Sohn,
während alle Brüder über einen weiteren Zeiger verkettet sind. Auf diese Weise benötigt man in jedem
Knoten nur zwei Zeiger.
Repräsentation. Die Knoten eines binären Baums
bestehen aus Daten beliebigen Typs und zwei Zeigern left und right zu seinen Unterbäumen. Der Baum
selbst wird durch einen Zeiger tree auf seine Wurzel
repräsentiert. Bild 10-7 zeigt ein Beispiel.
Baumdurchwandern. Um festzustellen, ob ein gegebener Baum einen Knoten mit einem bestimmten
Datenwert enthält, muss man ihn durchsuchen. Eine wichtige Operation im Zusammenhang mit Bäumen ist deshalb das „Durchwandern“ eines Baums,
so dass jeder Knoten „besucht“ wird. Dazu gibt es
zwei Vorgangsweisen: Beim Breitensuchen (breadthfirst search) besucht man zuerst die Wurzel, dann alle Söhne, dann die Söhne der Söhne usw. Beim Tiefensuchen (depth-first search) versucht man, von der
Wurzel aus möglichst schnell „in die Tiefe“ zum ersten Blatt vorzustoßen. Hier lassen sich nach der Reihenfolge, in der man die Wurzel und ihre beiden Unterbäume besucht, drei Varianten unterscheiden: Präordnung (preorder): Wurzel – linker Unterbaum –
rechter Unterbaum; Postordnung (postorder): Linker
Unterbaum – rechter Unterbaum – Wurzel; Symmetrische Ordnung (inorder): linker Unterbaum – Wurzel –
rechter Unterbaum. Bild 10-8 zeigt die verschiedenen
Besuchsreihenfolgen. Jede ist eine linearisierte Darstellung des Baums (aus Darstellung d kann der Baum
jedoch nicht rekonstruiert werden).
Das Tiefensuchen ist die einfachere Vorgehensweise,
da es rekursiv formuliert werden kann. Ein in C# abgefasster Algorithmus für den Durchlauf eines Baumes in Präordnung sieht so aus:
void DepthFirst (Node p) {
Process(p); // Verarbeitung der Wurzel
if (p.left != null) DepthFirst(p.left);
if (p.right != null) DepthFirst(p.right);
}
Dieser Algorithmus hat die Laufzeitkomplexität O(n),
wenn n die Knotenanzahl des Baums ist.
tree
data
left right
null
null null
null null
null null
Bild 10-7. Repräsentation eines binären Baums als Knoten
mit Zeigern
Bild 10-8. Besuchsreihenfolge beim Durchwandern binärer
Bäume. a Breitensuche; b Tiefensuche in Präordnung;
c Tiefensuche in Postordnung; d Tiefensuche in symmetrischer Ordnung
135
136
Technische Informatik / Programmierung
Binäre Suchbäume. Außer zur Darstellung hierarchischer Beziehungen werden Bäume auch zur geordneten Speicherung von Daten verwendet. Für jeden
Knoten K eines solchen Baums gilt, dass alle Werte
im linken Unterbaum von K kleiner und alle Werte
im rechten Unterbaum von K größer oder gleich dem
Wert von K sind. Bild 10-9 zeigt einen solchen binären Suchbaum, der so entstanden ist, dass folgende
Namen bedeutender Informatiker in den anfangs leeren Baum eingefügt wurden: Gries, Floyd, Dijkstra,
Conway, Knuth, Wirth, Hoare, Earley, Giloi, Naur.
Der erste Name, Gries, ergibt die Wurzel; der zweite
Name, Floyd, steht alphabetisch vor Gries und bildet
deshalb die Wurzel des linken Unterbaums, usw.
So organisierte Bäume heißen Suchbäume, da sie sich
gut zum schnellen Suchen eignen, besonders dann,
wenn der Suchbaum „ausgeglichen“ ist, d. h. wenn
seine Höhe (die Anzahl seiner Schichten) minimal ist.
Ein ausgeglichener binärer Suchbaum mit n Knoten
enthält höchstens log2 n + 1 Schichten. Man kann
in ihm jeden Wert durch den Besuch von maximal
log2 n + 1 Knoten finden, also in logarithmischer
Zeit. Der folgende rekursive Algorithmus zeigt das
Suchen in binären Suchbäumen:
void TreeSearch (Node root, AnyData x, out Node y) {
if (root == null) y = null;
else if (x == root.data) y = root;
else if (x < root.data)
TreeSearch(root.left, x, out y);
else TreeSearch(root.right, x, out y);
}
Das Einfügen und Löschen von Knoten in ausgeglichenen Bäumen erfordert ebenfalls nur logarithmische Zeit. Dabei geht die Ausgeglichenheit verloren,
Bild 10-9. Binärer Suchbaum
es gibt jedoch Verfahren, bei denen ausgeglichene
Bäume beim Einfügen und Löschen wieder in ausgeglichene Bäume transformiert werden (siehe Allg.
Literatur).
10.6 Graphen
Ein Graph ist die allgemeinste und flexibelste dynamische Datenstruktur. Im Gegensatz zu Bäumen kann
ein Knoten eines Graphen nicht nur mehrere Nachfolger, sondern auch mehrere Vorgänger haben. Auch
Zyklen sind nicht ausgeschlossen, d. h. man kann von
einem Knoten über Kanten zu anderen Knoten und
wieder zurück gelangen. Ein zyklenfreier Graph kann
mehrere Wurzeln (Knoten ohne Vorgänger) haben,
wogegen ein zyklenbehafteter Graph u. U. keinen einzigen Knoten ohne Vorgänger hat. Bild 10-10 zeigt
ein Beispiel eines zyklenfreien und eines zyklenbehafteten Graphen.
Mit Graphen lassen sich komplexe Zusammenhänge
diverser Anwendungsgebiete beschreiben, z. B.
Abhängigkeiten zwischen Aufgaben, Wege zwischen
Orten, Leiterbahnen auf Halbleiterplatinen usw.
Graphen sind daher in vielen Aufgabengebieten
nützlich. Typische Anwendungen sind das Auffinden
kürzester Wege, die Prüfung auf Zyklenfreiheit, das
Feststellen der Erreichbarkeit von Knoten und viele
weitere graphentheoretische Eigenschaften.
Repräsentation. Zur Darstellung von Graphen gibt
es verschiedene Datenstrukturen (Bild 10-11): Die
Adjazenzmatrix enthält in Zeile i und Spalte j den
Wert 1, wenn es eine Kante zwischen dem Knoten i
und dem Knoten j gibt, sonst den Wert 0. Die Adjazenzliste speichert für jeden Knoten eine Liste seiner
Nachfolger.
Eine spezielle Form der Adjazenzliste liegt vor, wenn
der Knotentyp Felder mit Referenzen auf mögliche
Nachfolger enthält, wie das im folgenden Beispiel der
Bild 10-10. Graphen. a zyklenfrei; b zyklenbehaftet
10 Datentypen und Datenstrukturen
index = Hashfunktion(schluessel);
0
"Kunz" 34567
1
"Meier" 12345
"Huber" 23456
2
Bild 10-11. Darstellungsarten des Graphen aus Bild 10-10b.
a Adjazenzmatrix; b Adjazenzliste
3
"Hinz" 54321
4
Fall ist, in dem Node zwei Referenzen (left und right)
auf mögliche Nachfolgerknoten hat:
class Node {
bool visited;
AnyData data;
Node left, right;
}
Node graph;
Bild 10-12. Verzeichnis als Hashtabelle
void Visit (Node p) {
if(p != null && ! p.visited) {
Prozess(p); //verarbeite Knoten
p.visited = true;
Visit(p.left);
Visit(p.right);
}
}
Repräsentation. Ein Verzeichnis wird meist als
Hashtabelle in Form eines Arrays tab[0..n] implementiert, wobei jedes Element des Arrays auf
eine Liste von Schlüssel/Wert-Paaren verweist.
Der Schlüssel (z. B. ein Name) wird mit Hilfe
einer sog. Hashfunktion (to hash = zerhacken) in
eine ganze Zahl i im Bereich 0..n abgebildet. Das
Schlüssel/Wert-Paar wird dann in der Liste, auf die
tab[i] zeigt, eingefügt oder gesucht. Bild 10-12 zeigt
ein Beispiel einer Hashtabelle, wobei angenommen
wird, dass „Kunz“ auf 0, „Meier“ und „Huber“ auf 1
und „Hinz“ auf 3 abgebildet werden.
Wenn man die Hashfunktion so wählt, dass sie sämtliche Schlüssel gleichmäßig in den Bereich 0..n abbildet, werden alle Listen kurz und etwa gleich lang.
Dadurch ergeben sich kurze Einfüge- und Suchzeiten.
Wenn jede Liste weniger als k Knoten enthält (wobei
k eine kleine Konstante ist), beträgt die Laufzeitkomplexität für das Einfügen und Suchen O(1), d. h. der
Zeitaufwand für alle Paare ist konstant. Techniken zur
Implementierung der Hashfunktion können der Allg.
Literatur entnommen werden.
10.7 Verzeichnisse
10.8 Mengen
Ein Verzeichnis (dictionary) ist eine Sammlung von
Schlüssel/Wert-Paaren. Ein Wert (z. B. eine Telefonnummer) kann unter einem Schlüssel (z. B. einem
Personennamen) im Verzeichnis abgelegt und wieder
gesucht werden. Die Operation Insert( d ↓ key ↓ val)
fügt den Wert val unter dem Schlüssel key in das Verzeichnis d ein. Die Operation Search( d ↓ key ↑ val)
liefert den unter key gespeicherten Wert val aus d. Alle Schlüssel in einem Verzeichnis müssen eindeutig
sein.
Obwohl der Mengenbegriff grundlegend für die Mathematik ist, gibt es in keiner der in Kap. 11 behandelten Programmiersprachen einen vordefinierten
Datentyp, der dem allgemeinen Mengenbegriff entspricht. Man kann sich aber einen abstrakten Datentyp (siehe 10.10) bauen, der eine Menge, wie in 10.7
gezeigt, als Hashtabelle verwaltet, die nur Schlüssel,
aber keine Werte enthält.
Mengen kleiner ganzer Zahlen kann man auch als
Bitarrays in einem Maschinenwort speichern. Ist die
// bereits besucht?
// Referenzen auf Nachfolger
// Wurzel des Graphen
(1)
Beim Durchlaufen eines Graphen unterscheidet man
wie bei Bäumen zwischen Breitensuche und Tiefensuche, wobei man allerdings berücksichtigen muss,
dass ein Knoten wegen Zyklen oder mehreren einmündenden Kanten u. U. mehrmals besucht wird. Um
Doppelbesuche zu vermeiden, merkt man sich in jedem Knoten, ob er bereits besucht wurde (visited).
Der Durchlauf durch einen wie in (1) deklarierten
Graphen mittels Tiefensuche sieht in C# so aus:
137
138
Technische Informatik / Programmierung
Zahl i in der Menge enthalten, hat das Bit i im Maschinenwort den Wert 1, sonst 0. Mit einem Wort aus
32 Bits lassen sich also 232 verschiedene Mengen der
Zahlen 0..31 darstellen. Sprachen wie C oder C# bieten Operationen zum Setzen und Abfragen einzelner
Bits. Die Mengenoperationen ∪ und ∩ können als bitweise Oder- bzw. Und-Operation implementiert werden.
10.9 Dateien
Eine Datei ( file) ist eine Sammlung von Daten, die auf
einem externen Speichermedium gehalten wird. Sie
ist damit eine externe Datenstruktur, die sich von den
bisher behandelten (internen) Datenstrukturen in folgenden Punkten unterscheidet: (1) Eine Datei bleibt
über das Ende des Programms, das sie erzeugt hat,
hinaus erhalten; (2) eine Datei ist meist so umfangreich, dass nur ein Teil von ihr im Hauptspeicher gehalten werden kann; (3) der Transport eines solchen
Dateiteils vom oder zum Hauptspeicher ist eine Ein-/
Ausgabe-Operation und ist um mehrere Zehnerpotenzen langsamer als ein Hauptspeicherzugriff.
Ältere Programmiersprachen wie Fortran oder Pascal besitzen eigene Sprachmittel zur Handhabung von
Dateien. Neuere Sprachen wie C++, Java oder C#
lagern diese Aufgabe hingegen in Bibliotheken aus,
was den Vorteil hat, dass man auf diese Weise unterschiedliche Zugriffsmechanismen für Dateien anbieten kann, ohne die Sprache zu ändern.
Die Verwaltung von Dateien und ihre Übertragung
vom und zum Arbeitsspeicher obliegt dem Betriebssystem, das dem Programmierer eine vereinfachte
Sicht auf Dateien bietet: Abstrakt gesehen ist eine
Datei eine lineare Folge von Bytes, ähnlich einem
eindimensionalen Bytearray variabler Länge.
Dateiname, Datei öffnen und Datei schließen. Dateien haben einen programmexternen Namen, unter
dem sie das Betriebssystem kennt, und einen programminternen Namen in Form einer Variablen, die
die Datei repräsentiert. Eine Datei muss vor dem Lesen oder Schreiben des ersten Bytes geöffnet und nach
dem Lesen oder Schreiben des letzten Bytes geschlossen werden. Durch das Öffnen macht das Betriebssystem die Datei dem Programm zugänglich, durch das
Schließen wird dieser Zugang wieder aufgehoben.
Zugriffsarten. In der Regel werden die Bytes einer Datei sequentiell gelesen und geschrieben. Dabei
wird eine Dateiposition mitgeführt, die nach dem Öffnen der Datei auf den Dateianfang verweist und bei
jeder Lese- und Schreiboperation weitergesetzt wird.
Meist kann man die Dateiposition auch explizit an eine bestimmte Stelle der Datei setzen und dann von
dort weiter lesen oder schreiben. In älteren Programmiersprachen wie PL/I gibt es noch wesentlich vielfältigere Zugriffsarten. Zum Beispiel kann man dort
die Daten einer Datei in sog. Sätze gliedern und darauf über Suchbegriffe (Schlüssel) direkt zugreifen.
Heute benutzt man für diese Art von Aufgaben Datenbanken.
Textdateien und binäre Dateien. Der Wert einer Variablen kann entweder als Bitmuster (so wie es im Arbeitsspeicher steht) auf eine Datei geschrieben werden oder als Zeichenfolge, die der textuellen Darstellung des Werts entspricht. Eine int-Variable mit
dem Wert 12345 kann z. B. entweder als Bitfolge
0011000000111001 oder als Zeichenfolge „12345“
(in ASCII codiert durch fünf Bytes) geschrieben werden.
Man nennt Dateien aus speicherinternen Bitmustern
binäre Dateien (binary files). Das Schreiben und Lesen binärer Dateien geht schnell und ist sparsam im
externen Speicherplatzverbrauch, aber die binäre Darstellung ist i. Allg. programm- und maschinenabhängig und nicht druckbar. Man benutzt sie zur Speicherung von Objektprogrammen und für Zwischenergebnisse. Dateien, die nur aus druckbaren Zeichen bestehen (z. B. druckbaren ASCII-Zeichen), nennt man
Textdateien. Bei der Ausgabe auf eine Textdatei muss
jede Variable vor dem Schreiben entsprechend einem
vorgegebenen Format in eine Zeichenkette konvertiert
werden; bei der Eingabe muss die externe Zeichenkette in die maschineninterne Binärdarstellung konvertiert werden. Das kostet Zeit, ist aber erforderlich,
wenn die externe Darstellung gedruckt oder auf andere Maschinen übertragen werden soll.
Dateioperationen. Bibliotheksmodule zur Dateiverarbeitung bieten üblicherweise folgende Operationen
an. Die Variable f ist dabei vom Bibliothekstyp
File, die Variable x ist vom Typ byte (in manchen
Bibliotheken auch von einem beliebigen elementaren
Typ).
10 Datentypen und Datenstrukturen
Open(↓ fn ↑ f ):
Öffne eine Datei f mit dem
Dateinamen fn.
Write(↓ f ↓ x):
Schreibe x auf die Datei f (füge
x an das Ende von f an).
Read(↓ f ↑ x):
Lies x von der Datei f .
Seek(↓ f ↓ pos): Setze die Lese-/Schreibposition
der Datei f auf pos.
Close(↓ f ):
Schließe die Datei f (beende die
Ein- /Ausgabe mit der Datei f ).
Um beim Lesen feststellen zu können, wann das Dateiende erreicht ist, bedient man sich einer DateiendeMarkierung (eof = end of file), deren Erreichen man
(bei den einzelnen Programmiersprachen in unterschiedlicher Weise) feststellen kann.
10.10 Abstrakte Datentypen
Die von einer Programmiersprache angebotenen Datentypen nennt man konkrete Datentypen. Sie können entweder elementar sein (int, float, char, usw.)
oder zusammengesetzt (Arrays, Strukturen, Referenzen). Darüber hinaus kann man sich in vielen Sprachen seine eigenen abstrakten Datentypen bauen. Ein
abstrakter Datentyp besteht aus einer (meist für den
Benutzer verborgenen) Datenstruktur und den darauf
ausführbaren Operationen. In objektorientierten Sprachen werden abstrakte Datentypen durch sog. Klassen
dargestellt (siehe 11.3.5).
Ein abstrakter Datentyp bildet eine Abstraktion, die
aus logischer Sicht genau so verwendet werden kann
wie ein konkreter Datentyp. Die Daten des konkreten Typs int sind z. B. die Bits, mit denen die Zahl
dargestellt wird; die Operationen sind +, −, * und /.
Die Daten eines abstrakten Datentyps File sind der
Dateiname, die Dateiposition, Datenpuffer, usw.; Die
Operationen sind Open, Close, Read, Write und Seek.
Manche Sprachen enthalten konkrete Datentypen, die
in anderen Sprachen fehlen und dort durch abstrakte Datentypen nachgebaut werden müssen. In Fortran gibt es z. B. für komplexe Zahlen den konkreten Datentyp COMPLEX. Man kann Variablen dieses Typs deklarieren (z. B. COMPLEX x, y, z) und auf
sie alle arithmetischen Operationen anwenden (z. B.
z = x + y). In Sprachen wie Java oder C# muss man
sich für komplexe Zahlen hingegen eine Klasse Complex bauen, die als Daten den Realteil und den Imaginärteil einer komplexen Zahl speichert und als Opera-
tionen Add(↓ x ↓ y ↑ z), Subtract(↓ x ↓ y ↑ z), Multiply(↓ x ↓ y ↑ z) und Divide(↓ x ↓ y ↑ z) anbietet, wobei
x, y und z Variablen vom Typ Complex sind.
Im Folgenden werden als Beispiele zwei abstrakte
Datentypen vorgestellt, die in vielen Problemstellungen der Informatik nützlich sind, aber von keiner Programmiersprache als konkrete Datentypen angeboten
werden: der Keller und die Schlange.
Keller. Ein Keller (auch Stapel, stack) ist eine Folge
von Datenobjekten mit zwei charakteristischen Operationen: Man kann ein Objekt an das Ende des Kellers anfügen (einkellern, push), und man kann ein
Objekt vom Ende des Kellers lesen und entfernen
(auskellern, pop). Das zuletzt eingekellerte Objekt
wird immer als erstes ausgekellert. Man denke an
einen Bücherstapel, bei dem man ebenfalls nur an das
oberste Buch bequem heran kann. Keller werden deshalb auch LIFO-Speicher (last in f irst out) genannt.
Ein Keller wird in einfachster Weise durch ein eindimensionales Array repräsentiert (siehe Bild 10-13).
Einkellern heißt, ein Objekt an das Ende des belegten
Teils des Arrays anfügen, Auskellern heißt, das Objekt vom Ende des belegten Teils entfernen.
Folgende Operationen werden meist auf Keller angewandt (s ist vom abstrakten Datentyp Stack):
Push( s ↓ x): Kellert das Element x als oberstes
in Keller s ein.
Pop( s ↑ x): Kellert das oberste Element x aus
Keller s aus.
Full(↓ s):
Liefert den Funktionswert true,
wenn Keller s voll ist, sonst false.
Empty(↓ s):
Liefert den Funktionswert true,
wenn Keller s leer ist, sonst false.
Keller lassen sich überall da einsetzen, wo die
Reihenfolge der Bearbeitung von Datenobjekten
durch eine Klammerstruktur im weitesten Sinn
festgelegt ist, z. B. bei der Übersetzung geklammerter Ausdrücke, bei der Ausführung geschachtelter
Prozeduraufrufe und bei der Unterbrechung eines
laufenden Rechenprozesses durch einen anderen
höherer Dringlichkeit.
Schlangen. Eine Schlange (queue, Puffer) ist eine
lineare Datenstruktur, an deren Ende Datenelemente angefügt und von deren Anfang Datenelemente
entnommen werden. Schlangen werden auch FIFOSpeicher ( f irst in f irst out) genannt, da das als erstes
139
140
Technische Informatik / Programmierung
Bild 10-13. Der abstrakte Datentyp Keller. a Leerer Keller, b nach Einkellern von x; c nach Einkellern von y; d nach Einkellern von z; e nach
Auskellern von z; f nach Auskellern von y
eingetragene Element als erstes entnommen wird. Die
für Schlangen typischen Operationen sind (q ist vom
abstrakten Datentyp Queue):
Enqueue( q ↓ x):
Fügt das Element x an das
Ende der Schlange q an.
Dequeue( q ↑ x): Holt das Element x vom
Anfang der Schlange q.
Full und Empty analog zum Keller.
Schlangen werden zur Datenpufferung benutzt, d. h.,
wenn Datenelemente zwar in der Reihenfolge ihres
Eintreffens, aber nicht Schritt haltend damit, bearbeitet werden sollen.
11 Programmiersprachen
Programmiersprachen gestatten die Beschreibung von
Algorithmen und Datenstrukturen in einer so präzisen
Weise, dass die Algorithmen von einer Maschine ausgeführt werden können. Sie sind damit das wichtigste
Verbindungsglied zwischen Mensch und Maschine.
Bei niederen Programmiersprachen bestehen die
Programme aus den Befehlen einer bestimmten
Maschine. Bei höheren oder „problemorientierten“
Programmiersprachen bestehen sie aus maschinenunabhängigen Anweisungen und müssen vor der
Ausführung von einem Übersetzer (Compiler) in die
Maschinensprache übersetzt werden. Hier werden
nur höhere Programmiersprachen behandelt (über
Maschinen- und Assemblersprachen siehe 6.4).
11.1 Begri−e und Einteilungen
Seit dem Erscheinen der ersten höheren Programmiersprache (Fortran, 1957) sind viele hundert Programmiersprachen entstanden, von denen aber nur wenige größere Verbreitung erfahren haben. Von diesen
sind wiederum, wenn man von Sprachen für spezielle Zwecke absieht, heute 10 bis 20 von größerer Bedeutung (Tabelle 11-1). Über die Geschichte der Programmiersprachen orientiert [18, 49].
11.1.1 Universal- und Spezialsprachen
Universalsprachen sind für ein breites Anwendungsgebiet konzipiert, etwa für technischwissenschaftliche Probleme (Fortran-, Pascalund C-Familie) oder für kommerzielle Probleme
(Cobol). Spezialsprachen sind auf ein engeres
Anwendungsgebiet zugeschnitten, etwa auf die
Simulation (GPSS), auf Datenbanken (SQL) oder
auf die Berechnung elektrischer Netze. Einige Sprachen stehen dazwischen; sie arbeiten mit speziellen
Datenstrukturen, wie Vektoren (APL) oder Bäumen
(Lisp, Prolog), sind aber nicht auf ein schmales
Anwendungsgebiet spezialisiert.
Eine häufige Form von Spezialsprachen sind sog.
Skriptsprachen. Sie sind aus Kommandosprachen
entstanden, mit dem Zweck, Programme aufzurufen,
zu parametrisieren und miteinander zu verknüpfen.
Oft werden sie dazu verwendet, kurze Berechnungen
oder Steuerungsaufgaben außerhalb des eigentlichen
Programms zu erledigen. Skriptsprachen werden
üblicherweise nicht in Maschinencode übersetzt,
sondern interpretiert, d. h. unmittelbar während
der Analyse ihres Quellcodes ausgeführt. Sie sind
oft dynamisch getypt, d. h. Variablen werden nicht
mit einem Datentyp deklariert, sondern können im
Laufe der Programmausführung Werte unterschiedlicher Typen enthalten. Meist sind sie imperativ
und auf einen bestimmten Anwendungsbereich
zugeschnitten.
Man unterscheidet Batchsprachen (z. B. csh,
Windows Cmd), die den Ablauf einer Folge von
Programmen steuern, anwendungsspezifische Skriptsprachen (z. B. VBA, Emacs Script), mit denen man
auf Elemente einer Programmoberfläche (z. B. auf
Zellen einer Tabellenkalkulation) zugreifen und diese
verändern kann, Textverarbeitungssprachen (z. B.
awk, Perl), die Befehle enthalten, um Muster in
Texten zu suchen und zu manipulieren sowie WebSkriptsprachen (z. B. JavaScript, PHP), mit denen
man Inhalte von Webseiten erzeugen, verarbeiten und
ändern kann. Einige Skriptsprachen wie JavaScript,
11 Programmiersprachen
Tabelle 11-1. Bedeutende Programmiersprachen
Name
Fortran
Algol 60
Cobol
Lisp
Pascal
Abkürzung von
formula
translation
algorithmic
language
common business
oriented language
list processing
language
–
Erscheinungsjahr
≈1957
1960
≈1960
1962
Literatur
[30, 37]
Bemerkungen
Siehe 11.4.2
[4]
Ursprung der Algol-Familie. Bahnbrechende
Ideen.
Für kommerzielle Anwendungen auch heute noch
die am meisten verwendete Sprache.
Hauptsprache der „Künstlichen Intelligenz“. Einzige Datenstruktur ist der binäre Baum (= Liste).
Siehe 11.4.3
[14]
[46, 47]
1971
[15, 31]
1972
[8, 10, 28]
Prolog
programming
in logic
C
Modula-2
Ada
Smalltalk
–
–
–
–
≈1973
≈1980
≈1980
≈1980
[16, 32]
[27, 50]
[2, 17]
[19]
C++
Haskell
–
–
≈1982
1990
[29, 48]
[1, 42]
Java
C#
–
–
1995
2001
[20, 40]
[21, 41]
PHP oder Python nähern sich in ihrem Sprachumfang
universellen Sprachen an.
11.1.2 Sequenzielle und parallele Sprachen
Ein auf einer Maschine (Prozessor) ablaufendes
Programm wird auch „Prozess“ genannt. Ältere
imperative Sprachen gestatten nur die Formulierung
eines einzigen Prozesses, der für sich allein abläuft,
ohne Kommunikation mit anderen Prozessen auf
demselben Prozessor oder parallel arbeitenden
Prozessoren. Einige Programmiersprachen bieten
jedoch Möglichkeiten zur Formulierung paralleler
Prozesse (z. B. Ada, Java, C#). Die hierbei auftretenden Probleme betreffen die Synchronisation und
den Informationsaustausch zusammenarbeitender
Prozesse [22]. Von diesen Sprachen zu unterscheiden
sind solche für Parallel- und Vektorrechner, bei denen
umfangreiche, meist mathematisch-physikalische Berechnungen (z. B. Matrixoperationen) in Teile zerlegt
Modelliert das logische Schließen. Besonders für
„Künstliche Intelligenz“ geeignet. Einzige Datenstruktur ist der binäre Baum.
Siehe 11.4.4
Siehe 11.4.3
Siehe 11.4.3
Erste konsequent objektorientierte Sprache mit
großer Klassenbibliothek.
Siehe 11.4.4
Moderne funktionale Sprache mit statischer Typenprüfung
Siehe 11.4.4
Siehe 11.4.4
und auf vielen Prozessoren gleichzeitig ausgeführt
werden oder bei denen bestimmte Rechenoperationen
gleichzeitig auf einen ganzen Vektor von Werten
angewendet werden. Hier gibt es bis heute fast nur
Erweiterungen von Fortan [9, 43].
11.1.3 Imperative und nichtimperative Sprachen
(Denkmodelle)
Nach dem einer Programmiersprache zugrunde liegenden Denkmodell (d. h. der Denkweise, der der
Programmierer folgt) unterscheidet man imperative
und nichtimperative Sprachen.
Imperative Sprachen
Sie beruhen auf dem Denkmodell, dass ein Programm
aus einer Folge von Anweisungen besteht und Variablen im Sinne von Behältern (Speicherplätzen) benutzt, in die man in zeitlicher Folge verschiedene
141
142
Technische Informatik / Programmierung
Programmiersprachen
imperativ
nichtimperativ
prozedurorientiert
objektorientiert
funktional
deklarativ
Fortran
C
Pascal
Ada
Smalltalk
C++
Java
C#
LISP
ML
Miranda
Haskell
Prolog
Bild 11-1. Imperative und nichtimperative Programmier-
sprachen
Datenwerte legen kann. Sie lassen sich in prozedurorientierte und objektorientierte Sprachen gliedern
(Bild 11-1).
Prozedurorientierte (prozedurale) Sprachen. Hier
konzentriert man sich auf die Operationen und
betrachtet die Daten als passiv. Um z. B. ein Element x in eine Liste L einzufügen, schreibt man
Insert(L, x). Man ruft die Prozedur Insert auf und
übergibt ihr L und x als Parameter. Ältere Programmiersprachen (wie Fortran, C, Pascal) sind
prozedurorientiert, einige haben Erweiterungen für
die Objektorientierung.
Objektorientierte Sprachen. Hier werden Daten und
die auf sie anwendbaren Operationen zu einem Ganzen zusammengefasst, das man Objekt nennt:
Objekt = Daten + Operationen
Objekte sind aktiv in dem Sinn, dass man sie auffordern kann, gewisse „Aufträge“ auszuführen, wodurch
eine ihrer Prozeduren aufgerufen wird. Im objektorientierten Jargon sagt man „man sendet einem Objekt
eine Nachricht (message)“. Die dadurch aufgerufene Prozedur nennt man Methode. Um ein Element x
in eine Liste L einzufügen, schreibt man L.Insert(x).
Man gibt dem Objekt L den Auftrag, das Element x
einzufügen. Das führt zum Aufruf der Methode Insert. Einzelobjekte mit gleichem Verhalten werden
zu Klassen zusammengefasst (z. B. mehrere ListenObjekte zu einer Klasse List). Abschnitt 11.3.5 enthält
Näheres über Klassen.
Man unterscheidet zwischen rein objektorientierten
Sprachen wie Smalltalk [19], bei denen alle Daten
(auch Zahlen und Zeichen) Objekte sind und alle
Operationen (auch + und −) Methoden, und hybriden
objektorientierten Sprachen wie C++ [48], Java [20]
oder C# [21], bei denen nur komplexe Daten wie Listen Klassen sind, einfache Daten wie Zahlen oder Zeichen jedoch nicht. Hybride Sprachen ergeben effizientere Programme als rein objektorientierte Sprachen,
sind aber nicht so flexibel.
Im Gegensatz zu prozedurorientierten Sprachen gestatten objektorientierte Sprachen eine bessere Strukturierung von Programmen, da man zusammengehörige Daten und Operationen als Klassen modellieren
kann, die Dinge der realen Welt abbilden (z. B. Personen, Konten, Maschinen).
Nichtimperative Sprachen
Sie beruhen auf Denkmodellen, in denen ein Programm beschrieben wird, ohne die explizite Reihenfolge seiner Operationen anzugeben. Sie sind dadurch
abstrakter als imperative Sprachen. Man unterscheidet funktionale und deklarative Sprachen (Bild 11-1).
Funktionale (applikative) Sprachen beruhen auf dem
Denkmodell der mathematischen Funktion. Jeder Algorithmus kann als Funktion aufgefasst werden, die
Argumente in Ergebnisse abbildet. Die schrittweise
Ausführung ist dabei implizit in der Schachtelung von
Funktionsaufrufen und rekursiven Funktionsdefinitionen enthalten. Der Wert von x * y + z wird hier durch
den geschachtelten Funktionsaufruf Plus(Mal(x, y), z)
ausgedrückt. Hauptvertreter der funktionalen Sprachen (mit vielen imperativen Unreinheiten) ist Lisp
[47]. Rein funktionale Sprachen wie Miranda [23]
und Haskell [1] kommen ohne Variablen aus (x, y
und z sind im Beispiel als Konstanten aufzufassen, die
man nicht verändern kann – wie in der Mathematik).
Deklarative Sprachen beschreiben nur Daten und Beziehungen zwischen ihnen; der Algorithmus ist in der
Semantik der Sprache verborgen. Die z. Z. einzige
deklarative Sprache von Bedeutung, Prolog [10], beruht auf der Semantik der Prädikatenlogik. Eine Gleichung, wie u = x * y, wird als Beziehung (Relation,
Prädikat) zwischen den Variablen x, y und u angesehen, die je nach der Belegung mit Werten wahr oder
falsch sein kann. Man schreibt dafür mal(X, Y, U).
mal ist ein Prädikat, das eine Relation zwischen X, Y
und U bezeichnet, X, Y, U sind Terme (Parameter).
mal(2, 4, 8) ist wahr, mal(2, 4, 10) ist falsch. Das Prädikat mal(2, 4, U) bedeutet: „Suche alle Belegungen
11 Programmiersprachen
der Variablen U derart, dass 2 * 4 = U ist“; U bekommt dadurch den Wert 8. Der Wert von x * y + z
wird in Prolog als „Regel“ geschrieben:
malplus(X, Y, Z, H) := mal(X, Y, U), plus(U, Z, H)
Gelesen: „Das Prädikat malplus (X, Y, Z, H) ist dann
wahr, wenn es eine Variable U gibt, sodass mal (X, Y,
U) und plus (U, Z, H) beide wahr sind“. Ein Algorithmus, der für gegebene Werte von X, Y und Z solche Werte von U und H berechnet, dass die Regel erfüllt wird, ist in Prolog eingebaut. Man beachte, dass
durch die Regel das Problem nur spezifiziert, aber
kein Lösungsalgorithmus beschrieben wird. Die Ausführungsreihenfolge mehrerer Regeln (mit Abfragen
und Schleifen) wird in deklarativen Sprachen implizit
beschrieben und tritt deshalb in den Hintergrund.
Für technische Anwendungen werden fast nur imperative Sprachen eingesetzt, nichtimperative können
aber auf Spezialgebieten (z. B. Künstliche Intelligenz)
vorteilhaft sein.
11.2 Beschreibungsverfahren
Wer Programmiersprachen benutzt, muss die Form
(Syntax) und die Bedeutung (Semantik) ihrer Konstruktionen genau kennen. Beides soll in einem Dokument, der Sprachdefinition, möglichst vollständig
und eindeutig niedergelegt sein. Während man für die
Beschreibung der Syntax zufrieden stellende formale Verfahren gefunden hat, ist das für die Beschreibung der Semantik bisher nicht gelungen, weshalb
man sich hierzu meist der Umgangssprache bedient.
Grundkenntnisse der Beschreibungstechnik von Programmiersprachen sind für jeden, der eine Programmiersprache erlernen will, unerlässlich.
11.2.1 Syntax
Programmiersprachen setzen sich syntaktisch aus
zwei Schichten zusammen. In der lexikalischen (unteren) Schicht besteht ein Programm aus einer Folge
von Symbolen, die sich ihrerseits aus Zeichen zusammensetzen. In den meisten Programmiersprachen
finden sich folgende Symbolarten:
• Schlüsselwörter (if, while, class, . . . ) sind Buchstabenfolgen fester Bedeutung, die den Charakter
einer Erweiterung des Zeichenvorrats haben;
• Bezeichner (i, x, result, ...) sind vom Programmierer vergebene Namen zur Bezeichnung von Variablen, Konstanten, Typen, Prozeduren;
• Zahlen (1, 3.14, . . . );
• Zeichenketten (“abracadabra”) sind Zeichenfolgen, meist in Anführungszeichen eingeschlossen;
• Einzelzeichen (+, *, [, ], . . . ) und Verbundzeichen
(==, <=, . . . ).
Neben Symbolen gehören zur lexikalischen Schicht
auch Kommentare (z. B. /*Kommentar*/ oder //
Kommentar). Kommentare sind Erläuterungen für
den Programmierer und haben keine Auswirkungen
auf die Arbeitsweise des Programms.
Über der lexikalischen Schicht liegt die eigentliche
syntaktische Schicht, in der man die Symbole als atomar betrachtet und ihre Gruppierung zu Ausdrücken,
Anweisungen und Deklarationen beschreibt. Die
Menge der Syntaxregeln einer Sprache nennt man
ihre Grammatik.
Als Grammatik-Schreibweise benutzt man die
Backus-Naur-Form (BNF) oder eine ihrer Erweiterungen (EBNFs). Die Syntax arithmetischer Ausdrücke wird z. B. durch folgende drei BNF-Regeln
beschrieben:
Expr → Term | Expr + Term | Expr - Term
Gelesen: „Ein arithmetischer Ausdruck Expr ist definiert als Term oder als die Folge von Expr, Pluszeichen, Term oder als die Folge Expr, Minuszeichen,
Term.“ Der senkrechte Strich trennt Alternativen.
Term → Fact | Term * Fact | Term / Fact
Gelesen: „Ein Term ist definiert als Fact oder als die
Folge Term, Malzeichen, Fact oder als die Folge Term,
Divisionszeichen, Fact.“
Fact → ident | number | ( Expr )
Gelesen: „Ein Faktor Fact ist definiert als ein Bezeichner oder eine Zahl oder ein Ausdruck in Klammern.“
Durch diese drei Regeln ist die syntaktische Struktur aller arithmetischen Ausdrücke aus Bezeichnern,
Zahlen, den Operatoren +, −, *, / und Klammern eindeutig beschrieben (Bild 11-2). Die rekursiven Alternativen beschreiben Wiederholungen und Schachtelungen. Manche Autoren schreiben die Symbole der
143
144
Technische Informatik / Programmierung
BNF in spitzen Klammern, also Expr statt Expr.
Eine empfehlenswerte moderne EBNF ist die von
Wirth [50]. Bei ihr wird das Gleichheitszeichen anstelle des Pfeils benutzt, jede Regel wird durch einen
Punkt beendet, und Zeichenfolgen, die sich selbst bedeuten, werden in Anführungszeichen gesetzt. Runde
Klammern werden zur Zusammenfassung, eckige als
Optionssymbol und geschweifte als Wiederholungssymbol benutzt. a[b] bedeutet a oder ab, a{b} bedeutet a oder ab oder abb oder abbb . . . Die geschweiften
Klammern gestatten den weitgehenden Verzicht auf
Rekursion und machen die Grammatik leichter lesbar.
Die Grammatik der arithmetischen Ausdrücke lautet
in dieser EBNF:
Expr = Term {(”+” | ”-”) Term}.
Term = Fact {(”*” | ”/”) Fact}.
Fact = ident | number | ”(” Expr ”)”.
11.2.2 Semantik
Die Bedeutung der Konstruktionen einer Programmiersprache nennt man ihre Semantik (im
engeren Sinn). Zum Beispiel bedeutet die Anweisung
a = b + c*d, dass der Wert des Ausdrucks b + c*d
nach den Vorrangregeln der Mathematik berechnet
und anschließend der Variablen a zugewiesen wird.
Die hier verwendeten Begriffe Berechnung, Wert,
Ausdruck, Zuweisung, Variable werden dabei als
bekannt vorausgesetzt (ihre Semantik muss also
schon vorher erklärt worden sein). Die Semantik
mancher Konstruktionen ist umgangssprachlich nur
ungenau beschreibbar; eine exakte formale Beschreibung lässt sich dagegen entweder nur unvollständig
Expr
Expr
Term
Term
Term
Fact
Fact
Fact
ident
number
ident
a
+
3
*
c
Bild 11-2. Syntaxbaum des Ausdrucks a + 3 * c gemäß der
im Text angegebenen Grammatik
durchführen, oder sie wird so unhandlich und nur für
Spezialisten verständlich, dass man bei der Sprachdefinition auf sie verzichtet. Techniken zur formalen
Semantikbeschreibung sind Forschungsgegenstand
der Informatik. Im weiteren Sinn umfasst der Begriff
Semantik alle Sprachregeln, die sich nicht durch eine
formale Syntaxbeschreibung wie BNF ausdrücken
lassen. Zum Beispiel ist der Text
class Nonsense {
int x = ”drei”;
void M() { a = 1; }
}
ein syntaktisch korrektes C#-Programm, aber er verstößt gegen die Sprachregeln: „Die Typen der linken
und rechten Seite einer Zuweisung müssen miteinander kompatibel sein“ und „Alle in Anweisungen
vorkommenden Namen müssen zuvor deklariert worden sein“. Solche die Syntax ergänzenden Sprachregeln nennt man Kontextbedingungen (auch statische
Semantik) und rechnet sie traditionell zur Semantik,
da sie sich nicht in BNF ausdrücken lassen, obwohl
sie mit der eigentlichen Semantik weniger als mit der
Syntax zu tun haben.
Algol60 [4] war die erste Programmiersprache, die
mittels BNF und umgangssprachlicher Semantik definiert wurde. Ähnliche Sprachdefinitionen (language
reports) gibt es von Pascal [31], Modula-2 [50], Ada
[17], Java [20] und C# [21], aber leider von vielen anderen Sprachen nicht.
11.3 Konstruktionen imperativer Sprachen
Die wichtigsten Konstruktionen imperativer Programmiersprachen sind Deklarationen zur Definition
von Konstanten, Variablen, Typen und Prozeduren,
Anweisungen zur Ausführung von Aktionen, Ausdrücke zur Berechnung von Werten und Klassen bzw.
Module zur Gliederung von Programmen.
11.3.1 Deklarationen
Deklarationen beschreiben die in den Anweisungen
eines Programms verwendeten Elemente. Sie legen
für jedes Element einen Namen und gewisse Eigenschaften (z. B. Typ, Größe, Wert) fest. Der Compiler
benutzt die so definierten Eigenschaften, um die statische Korrektheit des Programms zu prüfen. Beispiel
eines Deklarationsteils in C#:
11 Programmiersprachen
const int max = 100;
class String {...}
int i, j, k; String name;
void Sort (int[] a) {...}
//
//
//
//
Konstante
Typ
Variablen
Prozedur bzw. Methode
Das Prinzip, jedem Programmelement durch Deklaration einen (und nur einen) Typ zuzuordnen, sodass
der Compiler die korrekte Verwendung dieser Elemente gemäß den Typregeln der Sprache überprüfen
kann, nennt man „statische Typisierung“ (static typing). Einige ältere Programmiersprachen gestatten
implizite Deklarationen; z. B. werden in Fortran alle
nichtdeklarierten Variablen, die mit I bis N anfangen,
als ganzzahlige Größen, alle anderen als GleitpunktGrößen angesehen. In manchen Sprachen werden Variablen ohne Typ deklariert (z. B. in Smalltalk). Ihr
Typ wird erst zur Laufzeit aus dem Typ des zugewiesenen Wertes bestimmt (dynamic typing).
11.3.2 Ausdrücke
Ausdrücke setzen sich in den meisten Sprachen wie
in der Mathematik aus Konstanten, Variablen, Operatoren und Klammern in beliebig tiefer Schachtelung
zusammen. Zur Indizierung von Arrays werden in
manchen Sprachen runde Klammern verwendet: a(i)
(Fortran, Ada), in anderen eckige: a[i] (Pascal, C,
Java, C#). Die Typen der Operanden müssen nach
den Regeln der Sprache miteinander kompatibel sein.
Zum Beispiel ist es nicht erlaubt, eine Zahl mit einer Zeichenkette zu multiplizieren. Der Typ eines
Ausdrucks wird aus den Typen seiner Operanden bestimmt.
11.3.3 Anweisungen
Die wichtigsten Anweisungsarten sind Zuweisung, Verzweigung, Schleife, Prozeduraufruf und
Ein-/Ausgabe-Anweisung.
Zuweisung. Einfachste und häufigste Anweisung,
von der Form
Variable := Ausdruck
Variable = Ausdruck;
(in Pascal, Ada)
(in Fortran, C, Java, C#)
Das Zuweisungssymbol „=“ bedeutet hier eine Operation und darf nicht mit der Gleichheitsrelation der
Mathematik verwechselt werden!
Verzweigungsanweisungen. Die unbedingte Verzweigung zu der Anweisung mit der Marke m
lautet meist goto m. Bessere Programmstrukturen
(siehe 12.3.2) ergeben Verzweigungsanweisungen
der Form
if (Bedingung) {Anweisungsfolge}
if (Bedingung) {
Anweisungsfolge1
} else {
Anweisungsfolge2
}
Diese Anweisungen ermöglichen nur binäre Verzweigungen. Moderne Sprachen bieten aber auch eine
Vielwegverzweigung an, die in C# folgende Form
hat:
switch (Ausdruck) {
case 1: Anweisungsfolge1; break;
case 2: Anweisungsfolge2; break;
case 3: Anweisungsfolge3; break;
default: Anweisungsfolge4; break;
}
Abhängig davon, ob der Ausdruck den Wert 1, 2 oder
3 hat, wird hier die Anweisungsfolge 1, 2 oder 3 ausgeführt. Wenn der Ausdruck einen anderen Wert hat,
wird der Default-Zweig betreten und Anweisungsfolge 4 ausgefürt. Jeder Zweig muss durch eine BreakAnweisung abgeschlossen werden, die zum Ende der
Switch-Anweisung springt.
Schleifenanweisungen. Eine Schleife dient zum wiederholten Durchlaufen einer Anweisungsfolge. Bei
der iterativen Schleife wird eine Anweisungsfolge
wiederholt ausgeführt, solange die Bedingung am Anfang jedes Durchlaufs erfüllt ist. Das folgende Programmstück berechnet die Anzahl i der Dezimalstellen der positiven ganzen Zahl n, wobei es n so oft
ganzzahlig durch 10 dividiert, bis n zu 0 geworden
ist:
i = 0;
while (n > 0) {
n = n / 10; i = i + 1;
}
Manche Sprachen bieten auch eine iterative Schleife
mit der Bedingungsprüfung am Schleifenende, z. B.
i = 0;
do {
n = n / 10; i := i + 1;
} while (n > 0);
145
146
Technische Informatik / Programmierung
Die induktive Schleife (Zählschleife) ist eine Kurzform der iterativen Schleife mit Bedingungsprüfung
vor jedem Durchlauf. Bei ihr wird die Initialisierung
einer Laufvariablen, die Prüfung der Abbruchbedingung und die Erhöhung der Laufvariablen nach jedem
Durchlauf im Schleifenkopf zusammengefasst, z. B.:
for (i = 0; n > 0; i = i + 1) {
n = n / 10;
}
Ein-/Ausgabe-Anweisungen sind sehr unterschiedlich ausgebildet. Ältere Sprachen bieten eigene
Anweisungen zur Ein- und Ausgabe für die verschiedenen Speichermedien, mit oder ohne Zusatzangaben
für die Formatierung, mit einem oder mehreren
Datenelementen pro Anweisung und vielen anderen
Wahlmöglichkeiten. Neuere Sprachen haben keine
Ein-/Ausgabe-Anweisungen in der eigentlichen Sprache, sondern benutzen Bibliotheksprozeduren dafür.
11.3.4 Prozeduren (Methoden)
Prozeduren (in objektorientierten Sprachen Methoden
genannt) sind benannte Anweisungsfolgen, die mit
Parametern versehen werden können und lokale Deklarationen enthalten dürfen. Man unterscheidet zwischen der Prozedurdeklaration (Text aus Name, Parameter, lokale Deklarationen, Anweisungen) und dem
Prozeduraufruf, bei dem eine Prozedur über ihren Namen aufgerufen, d. h. ausgeführt, wird.
Beispiel. Deklaration einer Prozedur zur Berechnung
von xn in C# (die Operation i++ ist äquivalent zu
i = i + 1):
void Power (int x, int n, out int y) {
y = 1;
for (int i = 0; i < n; i++) { y = y * x; }
}
Die Prozedur ist die wichtigste Programmstruktur in
prozeduralen Sprachen. Sie erfüllt folgende Aufgaben: Gliederung eines Programms in überschaubare Teile; Codeersparnis, da der Anweisungsteil der
Prozedur nur einmal gespeichert wird, aber beliebig
oft aufgerufen werden kann; Abstraktion des Anweisungsteils durch den Prozedurnamen (beim Aufruf).
Parameter. Prozeduren haben i. Allg. Parameter, die
zur Beschreibung der Eingangs- und Ausgangsgrößen
dienen und somit die Schnittstelle zwischen der Prozedur und ihrem Benutzer (Rufer) bilden. Die im Prozedurkopf deklarierten Parameter können in der Prozedur wie gewöhnliche Variablen verwendet werden.
Sie heißen formale Parameter und werden beim Aufruf durch aktuelle Parameter ersetzt.
Man unterscheidet Eingangsparameter, deren Werte
vom Rufer an die Prozedur übergeben werden, Ausgangsparameter, deren Werte von der Prozedur an
den Rufer zurückgegeben werden und Übergangsparameter, deren Werte vom Rufer an die Prozedur
übergeben, dort u.U. verändert und anschließend an
den Rufer zurückgegeben werden. Eingangsparameter werden durch Zuweisungen der aktuellen an die
formalen Parameter implementiert (call by value). Bei
Ausgangs- und Übergangsparametern bezeichnet der
Name des aktuellen und des formalen Parameters dieselbe Variable; wenn man daher den Wert des formalen Parameters ändert, ändert sich dadurch auch
der Wert des aktuellen Parameters (call by reference).
Wenn man die oben beschriebene Prozedur Power
wie folgt aufruft
Power(3, 4, out result);
werden die aktuellen Eingangsparameter 3 und 4
den formalen Eingangsparametern x und n zugewiesen. Die Prozedur berechnet ihr Ergebnis im
formalen Ausgangsparameter y und ändert dadurch
auch den aktuellen Ausgangsparameter result, der
anschließend den Wert 81 (34 ) hat.
Funktionen. Eine Funktionsprozedur (oder einfach
Funktion) ist eine Prozedur, deren Aufruf einen Wert
liefert und diesen Wert repräsentiert, wie in der Mathematik, wo f (x) auch die Berechnungsvorschrift
und zugleich den Funktionswert bedeutet. Die oben
beschriebene Prozedur Power kann in C# wie folgt
als Funktion implementiert werden:
int Power (int x, int n) {
int y = 1;
for (int i = 0; i < n; i++) { y = y * x; }
return y;
}
Diese Funktion gibt eine ganze Zahl zurück und hat
daher den Rückgabetyp int (anstelle von void, was
„kein Rückgabetyp“ bedeutet). Ihr Rückgabewert
11 Programmiersprachen
(hier y) muss durch eine Return-Anweisung an den
Rufer übergeben werden. Funktionsaufrufe treten
meist als Operanden von Ausdrücken auf. Der Aufruf
result = 10 + Power(3,4);
liefert in result das Ergebnis 91 (10 + 81).
Gültigkeitsbereiche. Jede Prozedur bildet einen eigenen Gültigkeitsbereich für Namen. Eine in der Prozedur deklarierte Variable x (sog. lokale Variable) ist
nur innerhalb der Prozedur ab ihrer Deklarationsstelle gültig (sichtbar) und bezeichnet ein anderes Objekt
als ein x, das außerhalb der Prozedur deklariert wurde. Eine in einem umschließenden Gültigkeitsbereich
deklarierte Variable y (globale Variable) kann aber in
der Prozedur angesprochen werden, sofern sie dort
nicht neu deklariert wurde. Bild 11-3 zeigt ein Beispiel: In der Prozedur P sind die lokalen Variablen x p ,
z, der formale Parameter a und die globale Variable y
gültig. Die globale Variable xC wird in P durch die
lokale Variable x p verdeckt. Getrennte Gültigkeitsbereiche erhöhen die Freiheit bei der Wahl von Namen
und ermöglichen erst die unabhängige Arbeit mehrerer Personen an einem Programmsystem. Sie sind
deshalb von großer Bedeutung für die Entwicklung
größerer Programme.
Lebensdauer. Lokale Variablen leben nur während
der Ausführung der Prozedur, in der sie deklariert
sind. Zu Beginn der Prozedur wird Speicherplatz für
sie angelegt, am Ende der Prozedur wird er wieder
freigegeben. Wenn eine Prozedur rekursiv aufgerufen
wird (wie P in Bild 11-3), hat jede Aktivierung dieser
Prozedur einen eigenen Satz ihrer lokalen Variablen.
Gültigkeit von
xC y
a xP z
class C {
int x, y;
void P (int a) {
int x, z;
... // benutze x, z, a, y
if (a > 0) P(a–1);
}
}
Bild 11-3. Gültigkeitsbereiche von Variablen (hier in C#).
xC bezeichnet das x in Klasse C, xP das x in Prozedur P
Variablen, die in einer Klasse deklariert sind, leben so
lange wie das Objekt der Klasse, zu dem sie gehören. Objekte einer Klasse leben von ihrer Erzeugung
bis zum Zeitpunkt, ab dem sie nicht mehr referenziert
werden.
11.3.5 Klassen
In älteren Programmiersprachen bilden Prozeduren
die einzige Strukturierungsmöglichkeit für Programme. Große Programmsysteme bestehen aber aus vielen hundert Prozeduren und werden rasch unübersichtlich. Man braucht daher noch weitere Strukturierungsmöglichkeiten. Moderne Sprachen bieten zu
diesem Zweck Klassen an.
Eine Klasse ist ein Baustein, der zusammengehörige
Prozeduren (Methoden) und deren globale Daten (Felder) zu einem größeren Ganzen zusammenfasst. Sie
dient zur Implementierung abstrakter Datentypen, mit
denen man fehlende Typen der Programmiersprache
nachbauen kann. Klassen helfen, Programme besser
zu gliedern und bilden die Grundlage der objektorientierten Programmierung mit ihren Konzepten wie
Vererbung und dynamische Bindung.
Folgendes Beispiel zeigt eine Klasse Figure zur Modellierung grafischer Figuren in C#. Sie kapselt die
Daten einer Figur (ihre Koordinaten x und y) und stellt
einfache Zugriffsmethoden wie Move und Draw sowie einen Konstruktor (s. u.) zur Verfügung:
class Figure {
private int x, y; // Position
public Figure (int x, int y) {
this.x = x; this.y = y;
}
public void Move (int dx, int dy) {
x = x + dx; y = y + dy;
}
public virtual void Draw() { ... }
}
Figure ist ein Datentyp und kann zur Deklaration von
Variablen verwendet werden:
Figure f1, f2;
Bevor man mit Figuren arbeiten kann, muss man Objekte der Klasse Figure erzeugen:
f1 = new Figure(10, 20);
f2 = new Figure(50, 60);
147
148
Technische Informatik / Programmierung
Die Operation f 1 = new Figure(10, 20); erzeugt ein
neues Figure-Objekt, auf das dann f 1 verweist, und
ruft anschließend den Konstruktor der Klasse Figure
auf (eine spezielle Methode mit dem gleichen Namen
wie die Klasse). Der Konstruktor initialisiert im obigen Beispiel die Felder x und y des erzeugten Objekts
mit den Werten 10 und 20. Neben den expliziten Parametern (hier x und y) hat jede Methode auch einen impliziten Parameter namens this, der das Objekt referenziert, auf das die Methode angewendet wird. Jedes
Objekt hat, wie in Bild 11-4 gezeigt, einen eigenen
Satz von Feldern und somit einen eigenen Zustand.
Sobald ein Objekt existiert, kann man auf seine
Felder zugreifen und seine Methoden aufrufen.
f 1.Move(2, 3); ruft z. B. die Move-Methode des
durch f 1 referenzierten Objekts auf und erhöht
dadurch f 1.x um 2 und f 1.y um 3.
Eine Klasse bildet einen eigenen Gültigkeitsbereich
für die in ihr deklarierten Felder und Methoden. Alles, was in der Klasse deklariert ist, ist außerhalb der
Klasse unsichtbar; alles was außerhalb der Klasse deklariert ist, kann hingegen auch in der Klasse benutzt
werden. Die Einschränkung der Sichtbarkeit ist nützlich, da man oft die konkrete Implementierung der
Daten einer Klasse vor ihren Benutzern verbergen
und ihnen den Zugriff lediglich über einige wohl definierte Methoden gestatten möchte (Geheimnisprinzip, information hiding). Das macht die Benutzung
der Klasse einfacher und erlaubt es, ihre Implementierung später zu ändern, ohne dass die Benutzer davon
betroffen sind.
Um eine Methode oder ein Feld auch außerhalb einer
Klasse sichtbar zu machen, muss man es exportieren.
Dies geschieht in C#, indem man es mit dem Zusatz
public deklariert, während eine Deklaration mit dem
Zusatz private (oder ohne jeglichen Zusatz) die Sichtbarkeit der Methode oder des Feldes auf die deklarierende Klasse beschränkt. Bei der Klasse Figure sind
z. B. der Konstruktor und die Methoden Move und
f1
Move
Draw
x 10 y 20
f2
Move
Draw
x 50 y 60
Bild 11-4. Figure-Objekte mit separatem Zustand
Draw auch außerhalb der Klasse sichtbar, während
die Felder x und y nur innerhalb von Figure verwendet werden dürfen.
Klassen können getrennt voneinander übersetzt werden, wodurch mehrere Programmierer gleichzeitig an
unterschiedlichen Klassen arbeiten können.
Klassen modellieren Dinge der realen Welt wie Figuren, Personen oder Konten. Objektorientierte Programme bestehen aus Objekten solcher Klassen, wobei jede Klasse für einen eigenen Aufgabenbereich
(z. B. Figurenbehandlung, Personenbehandlung) zuständig ist und alle dafür nötigen Daten und Operationen enthält. Auf diese Weise kann man Programme
sauber nach Aufgabenbereichen strukturieren.
Einige Programmiersprachen wie Modula-2 kennen
keine Klassen, dafür aber Module. Ein Modul ist wie
eine Klasse ein Baustein bestehend aus Variablen und
Prozeduren. Im Gegensatz zu einer Klasse ist ein Modul aber kein Datentyp und unterstützt auch nicht die
Konzepte der objektorientierten Programmierung wie
Vererbung und dynamischen Bindung. Ein Modul bildet wie eine Klasse einen eigenen Gültigkeitsbereich,
aus dem Namen exportiert und in dem Namen aus anderen Modulen importiert werden können.
Namensräume
Für sehr große Programmsysteme gibt es in C# die
Möglichkeit, mehrere zusammengehörige Klassen zu
sog. Namensräumen (in Java Paketen) zusammenzufassen. Ein Namensraum bildet einen eigenen Gültigkeitsbereich: Die in ihm deklarierten Klassen sind außerhalb des Namensraums nicht bekannt. Man kann
aber eine Klasse aus ihrem Namensraum exportieren,
indem man sie mit dem Zusatz public deklariert. Ein
Namensraum B kann alle exportierten Klassen eines
Namensraums A durch using A; importieren und somit in B bekannt machen, z. B.:
namespace A {
public class C1 { ... }
public class C2 { ... }
class C3 { ... }
}
namespace B {
using A; // C1 und C2 sind somit hier bekannt
...
}
11 Programmiersprachen
Vererbung
Von einer bestehenden Klasse (Oberklasse) kann man
neue Klassen (Unterklassen) ableiten, die alle Felder und Methoden der Oberklasse erben und somit
als eine Erweiterung der Oberklasse betrachtet werden können. Folgende Klasse Rectangle ist eine Unterklasse von Figure:
class Rectangle : Figure {
// erbt x, y, Move, Draw
public int width, heigth; // neue Felder
public Rectangle (int x, int y, int w, int h) { ... }
public override void Draw() { ... }
// überschreibt geerbtes Draw
public void Fill (Pattern pat) { ... }
// neue Methode
...
}
Rectangle erbt x und y von Figure und deklariert zusätzliche Felder width und height; ebenso erbt es Move und Draw und deklariert eine neue Methode Fill
sowie einen eigenen Konstruktor. Geerbte Methoden
wie Draw können in einer Unterklasse überschrieben
werden (Neudeklaration mit gleicher Parameterliste
aber unterschiedlicher Implementierung).
Die Vererbung fördert die Wiederverwendung, da
man auf bestehenden Klassen aufbauen und diese
erweitern kann. Noch wichtiger ist aber die Kompatibilität zwischen Unterklasse und Oberklasse: Eine
Unterklasse ist eine Spezialisierung ihrer Oberklasse.
Rectangle-Objekte sind spezielle Figuren, die alle
Felder und Methoden von Figuren aufweisen. Daher
kann man Rectangle-Objekte in Figure-Variablen
speichern und sie wie Figure-Objekte behandeln.
Dynamische Bindung
In objektorientierter Sprechweise sagt man zum Aufruf f .Draw() „man gibt f den Auftrag Draw“. Je
nachdem, welches Objekt in f gespeichert ist, wird
dieser Auftrag anders ausgeführt. Enthält f ein Figure-Objekt, wird die Draw-Methode von Figure aufgerufen; enthält f ein Rectangle-Objekt, wird das Draw
von Rectangle aufgerufen. Der Auftrag Draw wird also dynamisch (zur Laufzeit) an eine von mehreren
möglichen Implementierungen gebunden.
Dynamische Bindung erlaubt es, mit verschiedenen
Unterklassen zu arbeiten, ohne sie zu unterscheiden.
Hätte Figure neben Rectangle auch die Unterklassen
Circle und Line, so könnte man Objekte all dieser
Klassen in f speichern und mit f .Draw() zeichnen,
ohne Fallunterscheidungen zu benötigen.
Objektorientierte Programme benutzen oft Klassenhierarchien mit einer Oberklasse wie Figure als Abstraktion und Unterklassen wie Rectangle oder Circle
als Ausprägungen davon. Da manche Methoden in
der Oberklasse noch nicht sinnvoll implementierbar
sind, kann man ihre Implementierung weglassen und
nur ihren Namen und ihre Parameter angeben. Man
spricht dann von einer abstrakten Klasse, deren Methoden in Unterklassen überschrieben werden müssen. Eine vollständig abstrakte Klasse (alle Methoden
ohne Anweisungsteil) nennt man in C# ein Interface.
Eine Klasse kann von mehreren Interfaces erben und
wird damit zu ihnen kompatibel.
Ein Programm, das mit abstrakten Klassen oder Interfaces arbeitet, die man später durch konkrete Unterklassen ersetzen kann, nennt man ein Rahmenprogramm (framework). Es ist ein Halbfabrikat, das man
durch „Einstecken“ konkreter Klassen zu einem Endfabrikat ausbauen kann.
Zusammenfassend kann man die objektorientierte
Programmierung folgendermaßen charakterisieren:
Objektorientierte Programmierung
= Klassen + Vererbung + dynamische Bindung
11.3.6 Ausnahmebehandlung
In jedem nichttrivialen Programm treten Fehler oder
Ausnahmesituationen auf, auf die man reagieren
muss. Wenn z. B. ein Benutzer einen falschen Dateinamen eingibt, kann die Datei nicht geöffnet werden.
Oft kann man solche Fehler aber nicht an der Stelle
ihres Auftretens behandeln, sondern muss sie an
eine rufende Methode melden, da erst diese sinnvoll
reagieren kann. In älteren Programmiersprachen
verwendete man dazu Fehlernummern, die an den
Rufer zurückgegeben wurden. Der Rufer musste die
Fehlernummer prüfen und geeignet reagieren.
Neuere Sprachen wie C++, Java oder C# bieten einen
speziellen Ausnahmebehandlungs-Mechanismus (exception handling), dessen Ziel es ist, den Regelfall
eines Programms von der Fehlerbehandlung zu tren-
149
150
Technische Informatik / Programmierung
nen. Kern der Ausnahmebehandlung ist die sog. TryAnweisung, die in C# folgendermaßen aussieht:
try {
... // Code für den Regelfall
} catch (Exception1 e) {
... // Fehlerbehandlung: Reaktion auf Exception1
} catch (Exception2 e) {
... // Fehlerbehandlung: Reaktion auf Exception2
}
Wenn im Try-Block oder in einer von dort aufgerufenen Methode eine Ausnahme (exception) auftritt,
wird der Block abgebrochen und es wird zum passenden Catch-Block verzweigt. Passt kein Catch-Block,
wird die Suche im Rufer fortgesetzt. Nach Ausführung des Catch-Blocks setzt das Programm hinter der
dazugehörigen Try-Anweisung fort.
Eine Ausnahme der Art Exception1 wird in C# durch
die Anweisung throw new Exception1(); ausgelöst.
Exception1 ist eine Klasse mit Informationen über die
Fehlerart. Man kann eigene Ausnahme-Klassen für
spezifische Fehlerarten implementieren.
11.3.7 Parallelität
Programme bestehen oft aus Aktionen, die gleichzeitig stattfinden können. Während ein Teil des
Programms z. B. Berechnungen durchführt, kann
ein anderer Teil Benutzereingaben verarbeiten.
Gleichzeitig ablaufende Aktionen nennt man
parallele Prozesse. Man unterscheidet zwischen
schwergewichtigen und leichtgewichtigen Prozessen.
Schwergewichtige Prozesse sind unabhängig voneinander ablaufende Programme; zwischen ihnen
gibt es kaum Wechselwirkungen. Leichtgewichtige
Prozesse (sog. Threads) sind parallele Aktivitäten
innerhalb eines Programms; sie kommunizieren über
gemeinsame globale Variablen.
Neuere Programmiersprachen wie Java oder C# haben Mechanismen zum Starten und Beenden eines
Threads sowie zur Synchronisation des Zugriffs auf
gemeinsam benutzte Variablen. In anderen Sprachen
wie C++ sind diese Mechanismen über Bibliotheken
realisiert.
Ein Thread wird gestartet, indem man eine Prozedur P angibt, die parallel zum gerade laufenden Programm ausgeführt werden soll. Alle von P aufgerufenen Prozeduren gehören ebenfalls zum Thread
von P. Ein Thread endet, wenn seine Prozedur P endet. Threads laufen quasiparallel, d. h. verzahnt; das
Betriebssystem entzieht in kurzen Intervallen dem jeweils aktiven Thread den Prozessor und gibt ihn dem
nächsten Thread. Der Benutzer des Programms merkt
davon nichts und hat den Eindruck als liefen alle
Threads gleichzeitig.
Wenn Threads gemeinsame globale Variablen benutzen, muss man sicherstellen, dass sie nicht gleichzeitig schreibend darauf zugreifen. Würde inmitten eines Schreibzugriffs ein Thread-Wechsel stattfinden,
wäre der Wert der Variablen unvorhersagbar (race
condition). Daher erlauben Sprachen wie Java oder
C#, eine Anweisungsfolge zu sperren, in der auf gemeinsame Variablen schreibend zugegriffen wird. So
wird sichergestellt, dass sie nie von mehreren Threads
gleichzeitig ausgeführt wird. Nur ein einziger Thread
wird eingelassen, die anderen müssen warten (Wechselseitiger Ausschluss, mutual exclusion).
11.4 Programmiersprachen
für technische Anwendungen
Dieser Abschnitt porträtiert die für Anwendungen in
den Ingenieurwissenschaften wichtigsten imperativen
Programmiersprachen.
11.4.1 Sprachfamilien
Manche Programmiersprachen sind miteinander verwandt (bilden eine Sprachfamilie), weil sie vom selben Entwickler stammen, Weiterentwicklungen älterer Sprachen sind oder auch, weil sie nach ihrem Erscheinungsbild (Syntax) mit anderen zusammengehören. Bei den Sprachen für technische Anwendungen
lassen sich etwa folgende Familien unterscheiden:
• Fortran-Familie mit Fortran 77, Fortran 90, Fortran
95, Fortran 2003 und Fortran 2008.
• Pascal-Familie mit Pascal, Modula-2, Modula-3,
Oberon, Oberon-2, Ada 83 und Ada 95.
• C-Familie mit C, C++, Java und C#.
Tabelle 11-2 zeigt, welche dieser Sprachen folgende
wünschenswerte Eigenschaften haben:
• abstrakte Datentypen;
• Objektorientiertheit durch Klassen, Vererbung und
dynamische Bindung;
11 Programmiersprachen
Tabelle 11-2. Bedeutende Sprachen für technische Anwendungen
Sprache
Fortran 77
Fortran 90/95
Fortran 2008
Abstrakte
Datentypen
nein
ja
ja
Klassen +
Vererbung
nein
nein
ja
Dynamische
Bindung
nein
nein
ja
Autom. Speicherbereinigung
nein
nein
nein
Pascal
Modula-2
Modula-3
Oberon
Oberon-2
Ada 83
Ada 95
C
C++
Java
C#
nein
ja
ja
ja
ja
ja
ja
ja
Ja
ja
ja
nein
nein
ja
ja
ja
nein
ja
nein
ja
ja
ja
nein
nein
ja
nein
ja
nein
ja
nein
wahlweise
ja
ja
nein
nein
ja
ja
ja
nein
nein
nein
nein
ja
ja
Ausnahmebehandlung
nein
nein
f. Gleitpunktoperationen
nein
nein
ja
nein
nein
ja
ja
nein
ja
ja
ja
Parallelität
nein
mit Modul
Vektorrechnung
nein
mit Modul
ja
mit Modul
mit Modul
ja
ja
ja
ja
ja
ja
• automatische Speicherbereinigung, die den Programmierer davon befreit, einmal angelegte Objekte selbst wieder freigeben zu müssen, wenn sie
nicht mehr gebraucht werden;
• eine in die Sprache integrierte Ausnahmebehandlung;
• die Möglichkeit, miteinander kommunizierende
parallele Prozesse zu formulieren.
einige ausdrücklich als gleich (Pascal) und wieder andere lassen diese Frage offen. Der Kommentar in jedem Beispiel zeigt, wie Kommentare in der betreffenden Sprache geschrieben werden. Die Zeilennummern am Rand gehören nicht mit zum Programm.
Im Folgenden wird jede Sprachfamilie kurz charakterisiert. Um einen Eindruck vom Aussehen einer Prozedur in den betreffenden Familien zu geben, wird die
Berechnung eines Polynoms mit dem Hornerschema
in einer Sprache aus jeder Familie gezeigt. Die Prozedur berechnet den Wert des Polynoms
Fortran ist die älteste prozedurale Programmiersprache und ist besonders für technisch-wissenschaftliche
Berechnungen geeignet. Fortran wurde von der Firma
IBM entwickelt, erstmals 1957 bekannt gemacht und
später als FORTRAN II, FORTRAN IV, FORTRAN
66, FORTRAN 77 und Fortran 95 in vielen Dialekten
(auch für die Echtzeitverarbeitung) weiterentwickelt.
Die letzte genormte Fassung heißt Fortran 2008; sie
hat sich in vielen Punkten modernen Programmiersprachen angenähert. Aus historischen Gründen wird
hier jedoch FORTRAN 77 beschrieben, in dem noch
immer viele Programme existieren. Die Syntax von
FORTRAN 77 ist veraltet, die Möglichkeiten der Bildung von Datenstrukturen sind auf ein- und mehrdimensionale Arrays beschränkt. Die als Standard vorhandenen Datentypen Double Precision und Complex
sind jedoch ein Komfort, den man anderswo oft vergebens sucht. Fortran-Programme setzen sich aus unabhängig voneinander übersetzten Prozeduren (Sub-
y = a 0 + a 1 x + a 2 x2 + . . . + a n xn
Gegeben sind die Koeffizienten a[0] bis a[n] als Array a und die Variable x; gesucht ist der Polynomwert y.
Zum besseren Verständnis sind in jedem Programm
die Schlüsselwörter und sog. Standardnamen (Namen
mit festgelegter Bedeutung) fett gedruckt. Alle übrigen Namen wurden frei gewählt. Die Schreibweise mit großen oder kleinen Buchstaben folgt dem
überwiegenden Gebrauch. Einige Sprachdefinitionen
sehen a und A ausdrücklich als verschieden an (C#),
11.4.2 Die Fortran-Familie
151
152
Technische Informatik / Programmierung
routinen genannt), die nicht ineinander geschachtelt
werden können, bausteinartig zusammen und ermöglichen damit die Anlage von Programmbibliotheken.
Die Gültigkeit der Namen ist auf eine Prozedur beschränkt. Durch die Common-Anweisung können jedoch prozedurübergreifende Namen eingeführt werden. Fortran hat keinen Deklarationszwang für einfache Variablen. Es gibt keine dynamisch angelegten
Objekte, demzufolge auch keine Zeiger und keine rekursiven Prozeduren. Die Konstruktionen von Fortran sind relativ einfach und maschinennah, woraus
sich kurze Übersetzungszeiten und schnelle Objektprogramme ergeben. Programm (1) zeigt die Prozedur
Horner in FORTRAN 77.
1
2
3
4
5
6
7
8
SUBROUTINE HORNER (A, N, X, Y)
REAL A(0:N)
C Keine globalen Groessen vorhanden
Y = A(N)
DO 100 I = N - 1, 0, -1
100 Y = Y * X + A(I)
RETURN
END
(1)
In Zeile 2 wird der Parameter A, das Koeffizientenarray, deklariert, die übrigen Parameter und die Laufvariable I sind implizit deklariert: I und N als IntegerVariablen, da alle Namen, die mit I bis N anfangen,
automatisch vom Typ INTEGER sind, X und Y vom
Typ REAL, da alle übrigen nichtdeklarierten Namen
vom Typ REAL sind. Zeile 5 zeigt die Zählschleife.
100 ist die Marke der letzten Anweisung, die zur
Schleife gehört. Der Ablauf einer Fortran-Prozedur
endet mit der letzten Anweisung vor dem END oder
kann schon vorher mit einer Return-Anweisung beendet werden. Programm (1) kann für sich allein und
unabhängig von den Programmen, in denen es aufgerufen wird, übersetzt werden.
Fortran 2008 ist eine Obermenge von FORTRAN 77
und bietet viele neue Sprachmittel: wählbare Genauigkeit numerischer Datentypen; dynamische Daten
und Zeiger; die Ablaufstrukturen Fallunterscheidung, Durchlauf- und Endlos-Schleife; Rekursion;
Module; Objektorientierung; parallele Abarbeitung
von Arrays. Eine (nichtgenormte) Variante von
Fortran ist HPF (High-Performance Fortran). Es
enthält Sprachelemente für Parallelverarbeitung,
Objektorientierung und Ausnahmebehandlung.
Literatur: [30, 37].
11.4.3 Die Pascal-Familie
Pascal wurde um 1970 von N. Wirth entwickelt. Sein
Ziel war Einfachheit und Klarheit der Prinzipien, Einführung der von Hoare vorgeschlagenen Konstruktionen zur Erzeugung neuer Datenstrukturen und Sicherheit durch weitgehende Typisierung. Pascal war
zuerst mehr für didaktische Zwecke als für die Herstellung großer Programmsysteme gedacht und kannte deshalb keine getrennte Übersetzbarkeit. Erweiterungen der Sprache mit getrennter Übersetzbarkeit
sind aber heute üblich, wenn auch für jede Implementierung verschieden. Pascal gestattet die Deklaration von Konstanten, Typen, Variablen und Prozeduren; es gibt Zeigertypen, dynamische Speicherplatzverwaltung und rekursive Prozeduren.
Modula-2 stammt ebenfalls von N. Wirth, ist eine
Weiterentwicklung von Pascal (um 1980) und besitzt
u.a. folgende über Pascal hinausgehende Eigenschaften: (1) Systematischere Syntax, (2) Module mit
getrennter Übersetzbarkeit und Schnittstellenprüfung, (3) Variablen, die Prozeduren enthalten können,
(4) bei Bedarf Zugriff auf Maschineneigenschaften
(Adressen, Bytes, Wörter), Durchbrechung der
Typisierung, Coroutinen als Grundlage der Programmierung paralleler Prozesse. (1) bis (3) machen
Modula-2 zu einer Sprache, in der sich Algorithmen
sehr gut maschinenunabhängig formulieren lassen.
(4) macht Modula-2 zu einer Systemprogrammierungssprache, in der man maschinennahe Software
schreiben kann (z. B. Betriebssysteme), ohne auf
eine Assemblersprache zurückgreifen zu müssen.
Trotz dieser Vielseitigkeit ist der Sprachumfang
relativ klein, Compiler sind ebenfalls klein und
schnell. Programm (2) zeigt die Prozedur Horner in
Modula-2.
1
2
3
4
5
6
7
8
9
10
11
PROCEDURE Horner(a: ARRAY OF REAL;
n: INTEGER:
x: REAL;
VAR y: REAL);
VAR i: INTEGER; (*Keine glob.Groessen*)
BEGIN
y := a [n];
FOR i := n - 1 TO 0 BY -1 DO
y := y * x + a[i]
END
END Horner;
(2)
11 Programmiersprachen
Die Zeilen 1 bis 4 enthalten die Deklaration aller
Parameter. Der Array-Parameter a ist ein „offenes
Array“, dessen Länge unspezifiziert bleibt. Die ForSchleife wird durch ein eigenes END abgeschlossen.
Alles übrige gleicht Pascal und Fortran. Eine ReturnAnweisung ist nicht erforderlich. Programm (2)
ist Bestandteil eines größeren Programms, kann
aber leicht zu einem separat übersetzbaren Modul
erweitert werden.
Die Sprache Oberon wurde um 1990 von N. Wirth
als Nachfolger von Modula-2 entwickelt. Sie verzichtet auf einige entbehrliche Konstruktionen von
Modula-2, bringt aber die Neuheit der sog. Typerweiterungen, die die objektorientierte Programmierung
ohne Klassen ermöglichen. Oberon-2 ist eine Weiterentwicklung von Oberon, die die objektorientierte
Programmierung in Oberon erleichtert.
Die Sprache Ada wurde um 1980 aufgrund einer Ausschreibung des amerikanischen Verteidigungsministeriums entwickelt und 1983 standardisiert (Ada 83).
Sie war als die zentrale Sprache für militärische Projekte gedacht, wird inzwischen aber auch für nichtmilitärische Aufgaben eingesetzt. Ada wurde 1995 revidiert und auf Objektorientierung hin erweitert (Ada
95). Ada ist eine der umfangreichsten Sprachen, da
die Bedürfnisse verschiedener Benutzerkreise (parallele Prozesse, Echtzeitanwendungen, Zugriff auf Maschineneigenschaften u. a.) befriedigt werden sollten.
Aus diesem Grund ist die Sprache nicht leicht zu erlernen, und die Compiler für sie sind groß und langsam. Ada baut auf Pascal auf, weicht aber weit von
ihm ab. Es hat u. a. folgende Eigenschaften: (1) Gleitpunktarithmetik mit definierten Genauigkeitsschranken, (2) Module, hier Pakete ( packages) genannt, in
verschiedenen Varianten, (3) Ausnahmebehandlung,
(4) parallele Prozesse mit einem SynchronisationsMechanismus (dem Rendezvous-Konzept). Programm
(3), Zeilen 4 bis 13, zeigt die Prozedur Horner in Ada.
Um die Entstehung von Ada-Dialekten zu vermeiden,
werden Ada-Compiler validiert (d. h. geprüft, ob sie
die Sprache normenkonform übersetzen) und bekommen daraufhin ein Zertifikat.
1
2
3
4
5
6
n: constant Integer := 10;
type Coeff is array(Integer <>) of Float;
a: Coeff (0..n);
procedure Horner ( a: in Coeff; n: in Integer;
x: in Float;
y: out Float) is
7
8
9
10
11
12
13
14
15
result: Float;
Coeff ist global
begin
result := a(n);
for i in reverse 0 .. n-1 loop
result := result * x + a(i);
end loop;
y := result;
end Horner;
(3)
Der Parametertyp Coeff muss in dem umschließenden
Programm als „unconstrained array“ deklariert sein
(Zeile 2). Die formalen Parameter von Horner werden
durch in und out als Eingangs- und Ausgangsparameter gekennzeichnet (Zeilen 4–6). Die Schleifenanweisung in Zeile 11 besagt, dass i die Werte von 0 bis
n − 1 in umgekehrter Folge durchlaufen soll. y kann
nicht im Prozedurrumpf zum Rechnen benutzt werden, weil es als Ausgangsparameter nicht in wertliefernder Position verwendet werden darf.
Literatur: [5, 7, 39, 44, 50].
11.4.4 Die C-Familie
Die Sprache C wurde von D. Ritchie am Anfang der
siebziger Jahre zur Programmierung des Betriebssystems Unix entwickelt. Große Teile von Unix und viele
Unix-Bibliotheksprogramme sind in C geschrieben,
sodass der Erfolg von Unix zugleich der von C war.
C enthält außer den Konstruktionen höherer Sprachen
(Datentypen, Abfragen, Schleifen, Prozeduren) auch
solche niederer Sprachen (Bit-Operationen, RegisterVariablen). Datentypen sind ganze und Gleitpunktzahlen, Zeichen und Zeiger; Boole’sche Daten fehlen,
d. h. werden durch die Zahlen 0 und 1 ausgedrückt.
Datenstrukturen sind Arrays und Strukturen. Prozeduren dürfen rekursiv aufgerufen, aber textlich nicht
geschachtelt werden. Die einzige Parameterübergabeart ist „call by value“. Durch Übergabe einer Adresse
als Parameter kann man allerdings Übergangsparameter simulieren. C hat sehr lockere Typregeln: der Typ
eines Werts kann z. B. beliebig geändert werden, Indexüberschreitungen bei Arrays werden nicht geprüft,
mit Zeigern kann auf beliebige Speicherzellen zugegriffen werden. Das macht C-Programme fehleranfällig, aber auch effizient, weil zur Laufzeit kaum Prüfungen stattfinden.
C++ ist eine objektorientierte Erweiterung von C und
kann als sein Nachfolger angesehen werden. C++ er-
153
154
Technische Informatik / Programmierung
möglicht das Arbeiten mit Klassen, Vererbung und
dynamischer Bindung. Dazu kommen Sprachmittel
für Ausnahmebehandlung und parametrisierte Typen
(templates). C++ bietet größere Typsicherheit als C,
ist aber wesentlich umfangreicher und komplexer. Im
Gegensatz zu den meisten anderen objektorientierten
Sprachen hat C++ keine automatische Speicherbereinigung.
Java ist eine objektorientierte Programmiersprache
mit automatischer Speicherbereinigung, Ausnahmebehandlung, Parallelität und parametrisierbaren
(generischen) Typen. Java ist zwar syntaktisch
ähnlich zu C, geht aber in den Konzepten weit über
C hinaus, weshalb es nur bedingt zur C-Familie zu
zählen ist. Java ist statisch typisiert und im Gegensatz
zu C oder C++ typsicher: Der Compiler und das
Laufzeitsystem garantieren, dass die Typregeln der
Sprache nicht verletzt werden, dass keine Indexüberschreitungen stattfinden und dass Zeiger nur auf
Objekte erlaubter Typen verweisen.
Java-Programme werden nicht in Maschinencode
übersetzt, sondern in sog. Bytecode-Befehle einer
virtuellen Maschine, die interpretativ ausgeführt
werden. Dadurch sind Java-Programme auf jeder Maschine lauffähig, auf der es einen Java-Interpretierer
gibt. Sie können sogar über das Internet verschickt
und auf der Empfängermaschine ausgeführt werden.
Manche Java-Systeme übersetzen den Bytecode
unmittelbar vor der Ausführung ( just in time) in
Maschinencode, was seine Geschwindigkeit steigert.
Java ist einfacher und moderner als C++, allerdings
auch weniger effizient. Für maschinennahe Programmierung ist es nicht geeignet.
C# (sprich „see sharp“) ist eine Weiterentwicklung
von Java mit starken syntaktischen Ähnlichkeiten.
Wie Java ist C# objektorientiert, hat automatische
Speicherbereinigung, Ausnahmebehandlung, Parallelität und generische Typen. Darüber hinaus hat es
Strukturen, Eingangs-, Ausgangs- und Übergangsparameter, Mechanismen zur Ereignisbehandlung,
Variablen, die Methoden enthalten können (delegates) sowie einige Merkmale, die das Programmieren
bequemer machen (properties, indexer, selbst definierbare Operatoren). C# ist wie Java absolut
typsicher, man kann aber gewisse Typprüfungen
in systemnahen Programmteilen ausschalten. Auch
C#-Programme werden in einen Bytecode übersetzt,
der aber vor der Ausführung immer in den Maschinencode der aktuellen Maschine transformiert wird.
C# ist Teil der Microsoft-Plattform .NET (sprich
„dot net“), die auf dem Betriebssystem Windows
aufbaut. C#-Programme laufen nur unter .NET oder
damit kompatiblen Systemen. Programm (4) zeigt
das Hornerschema (als Methode einer Klasse) in C#.
1
2
3
4
5
6
7
8
public void Horner(float[] a, int n, float x,
out float y) {
// Keine globalen Groessen
y = a[n];
for (int i = n - 1; i >= 0; i--) {
y = y * x + a[i];
}
}
(4)
Der Teil i − − im Kopf der Schleife ist eine Abkürzung für i = i − 1 und wird erst am Ende jedes Schleifendurchlaufs ausgeführt.
Literatur: [16, 20, 21, 32, 40, 41, 48]
11.5 Programmbibliotheken
für numerisches Rechnen
Es gibt eine Fülle von Programmbibliotheken zur Lösung numerischer Aufgaben. Sie sind in vielen Bearbeiterjahren entstanden, verwenden sehr effiziente Algorithmen, die auch Sonderfälle im Problem berücksichtigen, und sind weitgehend fehlerfrei. Die beiden
umfangreichsten und bedeutendsten sind:
NAG [38]: Eine in England entstandene Bibliothek
zur Lösung numerischer und statistischer Aufgaben
mit über 1400 Unterprogrammen. Es gibt sie für
verschiedene Betriebssysteme und Programmiersprachen (z. B. für Fortran, C, C++, Java, C#) in
getrennten Fassungen für einfache und doppelte
Genauigkeit.
IMSL [26]: Das amerikanische Gegenstück zu NAG.
Für Teilgebiete der numerischen Mathematik gibt es
spezielle Fortran-Softwarepakete, z. B. Linpack zum
Lösen von linearen Gleichungssystemen, Eispack zur
Lösung von Eigenwertproblemen, Lapack für lineare
Algebra (Nachfolger von Linpack und Eispack; auch
für C++ erhältlich), Quadpack zur numerischen Integration, Fitpack zur Approximation von Kurven und
Flächen. Über Details und Beschaffung dieser und
vieler weiterer Pakete siehe [6].
11 Programmiersprachen
11.6 Programmiersysteme
für numerisches und symbolisches Rechnen
Von immer größerer Bedeutung für den Ingenieur
werden Softwarepakete, mit denen man (oft ohne eigentliche Programmierung) numerische und algebraische Aufgaben lösen kann. Sie haben eine grafische
Benutzeroberfläche, zeigen mathematische Formeln
gut lesbar an, visualisieren numerische Ergebnisse
durch Grafiken und gestatten auch symbolische Rechnungen. Die „Programmierung“ ist ein Frage- und
Antwortspiel zwischen Benutzer und Maschine. Diese Programmiersysteme wirken sich auf die Arbeit
des praktisch tätigen Ingenieurs ebenso dramatisch
aus wie seinerzeit die Einführung des Taschenrechners. Besondere Eigenschaften:
1. Rationale Arithmetik beliebiger Genauigkeit
(Rechnen mit Brüchen und beliebig langen ganzen
Zahlen).
2. Visualisierung von Kurven und Raumflächen.
3. Lösung numerischer Aufgaben (z. B. Nullstellenbestimmung, Interpolation, numerisches Differenzieren und Integrieren, Anfangswertproblem von
Differentialgleichungen).
4. Symbolisches Rechnen (Multiplizieren, Dividieren, Differenzieren, Integrieren und mehr) mit Polynomen, rationalen Ausdrücken, Matrizen, transzendenten Ausdrücken.
5. Vereinfachen von symbolischen Ausdrücken.
Verbreitete Programmiersysteme dieser Art sind Mathematica [52], Maple [11], MATLAB [12], Derive
[34] und Reduce [13].
11.7 Web-Programmierung
Programme werden heute oft über das Internet
benutzt, d. h. sie laufen nicht auf dem Rechner
des Benutzers (Client), sondern auf einem anderen
Rechner (Server), der über das Internet erreichbar
ist. Die Benutzerschnittstelle bildet häufig ein sog.
Web-Browser, der Programmausgaben anzeigt und
Benutzereingaben entgegennimmt.
Ein Web-Browser zeigt Webseiten an, die in HTML
[24] codiert sind, einer Beschreibungssprache, die
Inhalt und Formatierung der Webseiten festlegt.
Man unterscheidet zwischen statischen und dynamischen Webseiten. Statische Webseiten sind als
HTML-Dateien am Server gespeichert und werden
vom Browser über einen Uniform Resource Locator
(URL, z. B. http://www.springer.de) angesprochen.
Wenn eine statische Webseite angefordert wird, wird
die entsprechende Datei vom Server zum Client übertragen und vom Web-Browser angezeigt (Bild 11-5a).
Dynamische Webseiten enthalten nicht nur HTMLBeschreibungen, sondern auch Codestücke, die
bei Anforderung der Seite am Server ausgeführt
werden und HTML-Teile generieren, die anstelle
der Codestücke in die Datei eingefügt werden. Auf
diese Weise lassen sich Webseiten erzeugen, die
speziellen Benutzeranfragen entsprechen (z. B. eine
Liste aller Bücher zu einem bestimmten Stichwort).
Die Codestücke sind häufig in einer Skriptsprache
wie PHP [35] codiert. Server-Technologien wie
JavaServer Pages [51] oder ASP.NET [45] erlauben
es, solche Codestücke auch in Java oder C# zu
schreiben. Bei Anforderung einer dynamischen
Webseite generiert ein Anwendungsprogramm am
Server ein HTML-Dokument und schickt es an den
Client, wo es vom Web-Browser angezeigt wird
(Bild 11-5b).
Webseiten können auch Dialogelemente wie Buttons, Checkboxen oder Auswahllisten enthalten.
Benutzereingaben in diesen Dialogelementen führen
üblicherweise zu einer neuen Seitenanforderung,
wobei die Eingabe am Server interpretiert und ein
neues HTML-Dokument zurückgeschickt wird.
Da dies zeitaufwändig ist, können Benutzereingaben auch durch clientseitige Skriptstücke (z. B. in
Benutzerrechner
(Client)
a
Anforderung von
Static.html
Static.html
Anforderung von
Dynamic.aspx
b
Web-Browser
Anwendungsrechner
(Server)
Static.html
Dynamic.aspx
generiertes HTML
Anwendungs- Datenprogramm
bank
Internet
Bild 11-5. Anforderung einer a statischen Webseite (Static.html); b dynamischen Webseite (Dynamic.aspx)
155
156
Technische Informatik / Programmierung
Tabelle 12-1. Einteilung von Programmsystemen nach ihrer Größe. Annahme: 1 Seite Programmtext = 50 Programmzeilen
Codezeilen
Seiten
Beispiele
klein
<1000
<20
Programmierübungen,
kurzlebige
Hilfsprogramme
Programmgröße
mittel
groß
1000–10 000
10 000–100 000
20–200
200–2000
kleine Werkzeuge und
Compiler, große
Echtzeitsysteme
Werkzeuge und
Echtzeitsysteme, Programmierumgebungen,
größere
Echtzeitsysteme
JavaScript [33]) direkt im Web-Browser behandelt
werden, wodurch das HTML-Dokument verändert
und oft eine neuerliche Seitenanforderung vermieden
werden kann. Allerdings kann dabei nicht auf Daten
zugegriffen werden, die am Server liegen (z. B. in
einer Datenbank).
Eine weitere Möglichkeit der Web-Programmierung
bieten sog. Applets [3]. Applets sind JavaProgramme, die zusammen mit einer Webseite
vom Server zum Client übertragen und dort ausgeführt werden. Jedem Applet wird ein Bereich
der Webseite im Browser zugewiesen, in dem das
Applet Informationen anzeigen und Benutzereingaben entgegennehmen kann. Mit Applets lassen sich
komplexe Benutzeroberflächen realisieren, die mit
HTML nur schwer darstellbar sind. Aus Sicherheitsgründen ist es Applets meist verboten, auf Daten
des Benutzerrechners zuzugreifen oder beliebige
Netzwerkverbindungen zu öffnen. Ähnliche Ziele
wie Applets verfolgt auch die Silverlight-Technologie
von Microsoft [25].
Während ein Web-Browser zur Kommunikation zwischen einem Menschen und einem Programm dient,
kann auch ein Programm selbst mit anderen Programmen über das Internet kommunizieren. Neben einfachen Netzwerkverbindungen, über die meist nur Daten fließen, gibt es dazu sog. Web Services [36]. Dabei handelt es sich um Dienstprogramme am Server,
die in einer beliebigen Programmiersprache geschrieben und deren Methoden von einem Anwendungsprogramm am Client aufgerufen werden können. Der
Aufruf wird dabei in eine Textnachricht verpackt, die
über das Internet übertragen und am Server decodiert wird. Der Server führt die gewünschte Methode
sehr groß
>100 000
>2000
Betriebssysteme,
Datenbanksysteme,
Entwicklungsumgebungen,
Auskunftssysteme
aus und schickt eventuelle Rückgabewerte wieder als
Textnachricht an den Rufer zurück.
12 Softwaretechnik
12.1 Begri−e, Aufgaben und Probleme
12.1.1 Eigenschaften großer Programme
Programmsysteme können von sehr unterschiedlicher
Größe sein. Tabelle 12-1 zeigt eine mögliche Einteilung in 4 Klassen; im folgenden wird aber nur
zwischen „kleinen“ und „großen“ Programmen unterschieden.
Wenn ein Programmsystem mit 500 Anweisungen
von einem Programmierer ohne den Einsatz besonderer Techniken geschrieben werden kann, darf man
nicht daraus schließen, dass die Herstellung eines
Programmsystems mit 50 000 Anweisungen nur ein
Vielfaches an Personal und einige zusätzliche Koordination erfordert. Ein quantitativer Unterschied von
1 : 100 schlägt sich vielmehr auch in qualitativen Unterschieden nieder. Während bei kleinen Programmen
meist Hersteller und Benutzer dieselbe Person und
die Hauptkriterien für die Qualität des Programms
seine Korrektheit und Effizienz sind, liegen die Verhältnisse bei großen Programmen völlig anders. Viele
Software-Ingenieure müssen zusammenarbeiten, Hersteller und Benutzer sind verschiedene Personengruppen, und es gibt viele Benutzer. Große Programme haben eine lange Lebensdauer (5 bis 20 Jahre) und werden häufig geändert. Zuverlässigkeit, Flexibilität und
Übertragbarkeit auf andere Maschinen können hier
12 Softwaretechnik
Tabelle 12-2. Merkmale kleiner und großer Programme
„Kleine“ Programme
Hersteller = Benutzer
Seltene Benutzung
Kaum Änderungen
Fehler führen zum
Programmabbruch
und richten keinen
Schaden an
Qualitätsmerkmale:
• Korrektheit
• Effizienz
Kaum Qualitätssicherung
Kaum Wartung
Keine oder einfache
Dokumentation
Kein Management
„Große“ Programme
Mehrere Hersteller, viele
Benutzer
Oftmalige Benutzung
(5–20 Jahre Lebensdauer)
Laufende Änderungen
Fehler dürfen oft nicht zum
Programmabbruch führen
und keinen Schaden
anrichten
Qualitätsmerkmale:
• Korrektheit
• Effizienz
• Zuverlässigkeit
• Robustheit
• Benutzerfreundlichkeit
• Wartbarkeit
• Portabilität
Qualitätssicherung ist
wichtiger Bestandteil der
Herstellung
Vieljährige Wartung erforderlich, Wartungskosten
können Herstellungskosten übersteigen
Umfangreiche, schwer
aktuell zu haltende
Dokumentation
Umfangreiches und
schwieriges Management
wichtigere Qualitätskriterien sein als die Effizienz. In
Tabelle 12-2 sind Unterschiede zwischen kleinen und
großen Programmen zusammengestellt.
12.1.2 Begri− der Softwaretechnik
Herstellung, Qualitätssicherung, Wartung, Dokumentation und Management großer Programmsysteme,
die von mehreren für viele geschrieben werden,
erfordern besondere Techniken, Methoden und Werkzeuge, die man unter dem Namen Softwaretechnik
(software engineering) zusammenfasst. Einige Facetten dieses Begriffs ergeben sich aus den folgenden
Definitionen:
• Das Ziel der Softwaretechnik ist die wirtschaftliche
Herstellung zuverlässiger und effizienter Software.
• Softwaretechnik ist die praktische Anwendung wissenschaftlicher Erkenntnisse auf den Entwurf und
die Konstruktion von Computerprogrammen, verbunden mit der Dokumentation, die zur Entwicklung, Benutzung und Wartung der Programme erforderlich ist.
Softwaretechnik hat den Charakter einer Ingenieurdisziplin: Es werden Produkte von mehreren für viele
produziert, die sich in der Praxis bewähren müssen;
das Kostendenken spielt eine Rolle, die Bearbeitung
eines Projekts muss mit durchschnittlich Befähigten
stattfinden können, das Ergebnis darf nicht vom Talent einiger abhängen. Im Gegensatz zu Projekten in
anderen Ingenieurdisziplinen sind für Softwareprojekte folgende Eigenschaften spezifisch:
• Jedes Programm wird nur einmal entwickelt,
eine Serienfertigung gibt es nicht. Planung und
Aufwandabschätzungen sind darum besonders
schwierig.
• Die technischen Bedingungen (Schnittstellen zur
Hardware und Systemsoftware) ändern sich besonders schnell.
• Software als immaterielles Produkt unterliegt
nicht den üblichen Schranken der Technik. Sie ist
„im Prinzip“ jeder Situationsveränderung leicht
anpassbar; in Wirklichkeit zerstören nachträgliche
Anpassungen mehr und mehr die Systemarchitektur und führen dadurch zu Fehlern und
Chaos.
Das Hauptproblem der Softwaretechnik ist der Kampf
mit der logischen Komplexität großer Programme.
Wenn n Prozeduren oder n Mitarbeiter Informationen
austauschen (jeder mit jedem), ergeben sich n(n−1)/2
Verbindungen zwischen ihnen, d. h., die Anzahl der
Verbindungen wächst quadratisch mit der Anzahl der
verbundenen Objekte. Viele Methoden der Softwaretechnik, insbesondere Entwurfsmethoden, laufen deshalb darauf hinaus, durch Einschränkung der erlaubten Verbindungen und durch Abstraktion die Komplexität herabzusetzen.
12.1.3 Software-Qualität
Die Qualität eines Programms hängt von vielen Eigenschaften ab, zu denen u. a. die folgenden gehören:
157
158
Technische Informatik / Programmierung
Korrektheit. Es soll sich seiner Spezifikation gemäß
verhalten, also für korrekte Eingaben korrekte Ergebnisse liefern. Es soll außerdem über einen festgelegten
Zeitraum hinweg fehlerfrei funktionieren, was man
als Zuverlässigkeit bezeichnet.
Robustheit. Es soll sich auch bei fehlerhaften Eingaben „angemessen“ verhalten. Auf keinen Fall sollen Fehler zum Programmabbruch führen. Fehlerhafte Eingaben und Programmzustände müssen erkannt
werden. Fehlerhafte Ergebnisse müssen auf einfache
Weise rückgängig gemacht werden können.
Benutzerfreundlichkeit. Es soll einfach zu erlernen
und intuitiv zu bedienen sein. Bedienungsfehler sollen weitgehend ausgeschlossen werden.
Effizienz. Es soll statisch kurz sein und zur Laufzeit
sparsam mit Speicherplatz und Rechenzeit umgehen.
Wartbarkeit. Änderungen und Erweiterungen sollen
einfach vorgenommen werden können und möglichst
lokal bleiben. Dazu muss der Programmcode klar
strukturiert und lesbar sein. Das Programm soll in
Bausteine gegliedert sein, die abgeschlossene Aufgabengebiete bearbeiten und bei Bedarf gegen andere
Bausteine mit gleicher Schnittstelle ausgetauscht werden können.
Portabilität. Es soll einfach auf andere Rechner
oder Betriebssysteme übertragen und an andere
Programmbibliotheken angepasst werden können.
Man erreicht das durch Vermeidung systemspezifischer Sprachen, Operationen und Datenformate
sowie durch Trennung portabler von nichtportablen
Programmteilen.
Je größer ein Programmsystem ist, desto wichtiger
sind seine Qualitätsanforderungen. Besteht ein Programm z. B. aus 100 voneinander abhängigen Komponenten, die jeweils zu 99% korrekt sind, so beträgt
die Korrektheit des Gesamtsystems nur noch 37%
(0,99100 = 0,37).
Die einzelnen Qualitätskriterien widersprechen sich
teilweise. Bei Maximierung der Effizienz eines Programms kann zum Beispiel seine Wartbarkeit und
Portabilität leiden. Hier gilt es, einen vernünftigen
Kompromiss zu finden.
• Phase 1: Problemanalyse. Das zu lösende Problem
wird in Zusammenarbeit mit dem Auftraggeber
definiert und analysiert. Das Ergebnis ist die Anforderungsdefinition (Pflichtenheft).
• Phase 2: Entwurf. Das Softwaresystem wird in
Klassen zerlegt (Grobentwurf) und diese anschließend in Methoden (Feinentwurf). Dabei werden
die Schnittstellen der einzelnen Teile sowie ihr
Zusammenspiel mit anderen Systemteilen spezifiziert. Das Ergebnis ist die Systemarchitektur.
• Phase 3: Implementierung. Die Klassen werden
programmiert und für sich getestet.
• Phase 4: Test. Die Klassen werden zusammengesetzt, und das Gesamtsystem wird getestet. Das Ergebnis ist die Abnahme durch den Auftraggeber.
• Phase 5: Wartung. Im Betrieb entdeckte Fehler
werden beseitigt, und das Programmsystem wird
den sich verändernden Anforderungen angepasst.
Hinzu kommen Qualitätssicherung, Dokumentation,
und Management, die sich über alle Phasen erstrecken.
Man unterscheidet verschiedene Vorgehensmodelle,
die sich im Ablauf der Phasen und im Grad ihrer Verzahnung unterscheiden.
Wasserfall-Modell. Die Phasen laufen wie ein Wasserfall strikt sequenziell ab. Jede Phase produziert
ein Dokument, das zum Ausgangspunkt der nächsten Phase wird (Bild 12-1). Wenn in einer Phase Fehler entdeckt werden, muss man zurückgehen und eine oder mehrere Vorgängerphasen erneut durchlaufen. Dadurch ergibt sich ein Kreislauf, den man den
Lebenszyklus des Softwaresystems nennt.
12.1.4 Vorgehensmodelle
Die Arbeit an einem Softwareprojekt gliedert sich in
Phasen, wobei sich folgende Einteilung bewährt hat:
Bild 12-1. Software-Lebenszyklus (Wasserfall-Modell)
12 Softwaretechnik
Prototyping-Modell. Es hat sich gezeigt, dass es in
der Praxis kaum möglich ist, eine Phase vollständig abzuschließen, bevor die nächste beginnt. Manche Anforderungen treten erst nach der Implementierung zu Tage, und die Richtigkeit von Entwurfsentscheidungen kann oft erst am laufenden System geprüft werden. Daher ist man dazu übergegangen, bereits bei der Problemanalyse oder beim Entwurf sog.
Prototypen zu entwickeln, d. h. vereinfachte Vorversionen des Softwareprodukts, an denen man frühzeitig die Erfüllung der Anforderungen sowie die Eignung von Entwurfsentscheidungen überprüfen kann
[11,12]. Man unterscheidet zwischen experimentellem Prototyping, bei dem der Prototyp anschließend
weggeworfen wird, und evolutionärem Prototyping,
bei dem der Prototyp schrittweise zum endgültigen
Produkt ausgebaut wird.
Spiralmodell. Das Spiralmodell vereinigt WasserfallModell und Prototyping und bezieht auch eine Risikoanalyse mit ein [3]. Die Entwicklung eines Softwaresystems vollzieht sich dabei in spiralförmigen Zyklen,
wobei in jedem Zyklus ein Prototyp entwickelt wird,
an dem die Anforderungen überprüft und die verbleibenden Risiken für die weitere Entwicklung analysiert werden.
Agile Modelle. Als Gegensatz zum WasserfallModell mit seiner strikten Phasentrennung wurden in
letzter Zeit Vorgehensmodelle entwickelt, die durch
kurze Entwicklungszyklen, geringe Formalisierung
und hohe Flexibilität bei Änderungen gekennzeichnet
sind [1, 13]. Sie werden daher auch Agile Modelle genannt. Die gewünschten Teile der Software
werden dabei inkrementell in der Reihenfolge ihrer
Priorität entwickelt, wobei jeder Zyklus alle oben
geschilderten Phasen durchläuft. Die Zyklen sind
kurz und enden immer mit einer lauffähigen Version
des Produkts. Die Entwicklung wird durch ständiges
Testen des Gesamtsystems und durch laufende
Einbeziehung des Auftraggebers begleitet. Dadurch
wird sichergestellt, dass der Entwicklungsstand des
Produkts zu jedem Zeitpunkt korrekt ist und den
Anforderungen entspricht.
12.2 Problemanalyse
und Anforderungsdešnition
Am Anfang eines Softwareprojekts müssen Aufgabenstellung und Leistungsumfang vom Auftraggeber
und Auftragnehmer gemeinsam festgelegt werden.
Die hierfür in Frage kommenden Verfahren treten
bei der Planung anderer technischer Projekte ebenso
auf und gehören damit mehr zur Systemtechnik als
zur Softwaretechnik (Zustandsanalyse, Systemabgrenzung, Systembeschreibung, Machbarkeits- und
Risikoanalyse u. a.).
Methoden und Hilfsmittel, um Anforderungen zu
erheben, sind Interviews, Fragebögen, die Analyse bestehender Abläufe und Formulare sowie die
Mitarbeit im Betrieb. Beschreibungsmittel sind
Zeichnungen und Tabellen aller Art, insbesondere
Hierarchiediagramme, Ablaufdiagramme, Datenflusspläne und Entscheidungstabellen. Die erhobenen
Anforderungen sollten benannt und nach einem
hierarchischen Klassifizierungsschema nummeriert
werden, damit man sich in späteren Phasen des
Projekts darauf beziehen kann.
Man unterscheidet zwischen funktionalen und
nichtfunktionalen Anforderungen. Funktionale Anforderungen beziehen sich auf die Aufgaben, die
das Softwaresystem erfüllen soll (z. B. Roboterarm
positionieren, Geschwindigkeit einstellen, Druck
anzeigen). Nichtfunktionale Anforderungen sind
Eigenschaften wie Zuverlässigkeit, Effizienz oder
Benutzerfreundlichkeit.
Eine bewährte Methode zur Erhebung der funktionalen Anforderungen ist die sog. Use-Case-Analyse, bei
der die verschiedenen Benutzungsszenarien des Systems und die verschiedenen Benutzergruppen identifiziert und aufgelistet werden. Ein Benutzungsszenario (use case) ist eine abgeschlossene Interaktion
zwischen dem Benutzer und dem System (z. B. Buch
entleihen, Buch suchen, Mahnung verschicken). Sie
wird als Folge von Aktionen beschrieben, wobei sich
jeweils Benutzeraktionen und Aktionen des Softwaresystems abwechseln. Zur Beschreibung eines Benutzungsszenarios gehört außerdem die Spezifikation der
möglichen Fehlerfälle, der Vorbedingungen und der
Nachbedingungen.
Die Erfahrung zeigt, dass Anforderungen selten bereits zu Beginn des Projekts vollständig erfasst werden können und dass sie sich oft im Laufe der Zeit
ändern. Der Ansatz, die endgültigen Anforderungen
aufgrund eines Prototyps der zu entwickelnden Software zu ermitteln, hat sich daher in der Praxis bewährt.
159
160
Technische Informatik / Programmierung
Das Ergebnis der Problemanalyse ist ein Dokument,
die „Anforderungsdefinition“. Sie stellt das Pflichtenheft dar, also die Vereinbarung zwischen Auftraggeber und Auftragnehmer über das zu liefernde Produkt.
Eine ausführliche Behandlung dieses Themas findet
man in der Allgemeinen Literatur.
12.3 Entwurf und Implementierung
Mit dem Entwurf wird die Architektur eines Softwaresystems festgelegt. Dazu wird das Gesamtsystem in Teilsysteme zerlegt, und die Teilsysteme und
ihr Zusammenwirken werden spezifiziert. Je nachdem, ob die Zerlegung geschickt oder ungeschickt gewählt wird, ergibt sich später ein strukturell gutes,
leicht verstehbares und leicht änderbares Softwareprodukt oder das Gegenteil. Da die Entwurfsentscheidungen zu einem Zeitpunkt getroffen werden müssen,
wo man nur wenig über die Zusammenhänge weiß, ist
der Entwurf eine schwierige, Erfahrung voraussetzende, schöpferische Tätigkeit. Entwurfsmethoden können ihn unterstützen, garantieren aber nicht ein hochwertiges Softwareprodukt. Die Frage, wie man eine
softwaretechnische Aufgabe in Teilaufgaben zerlegen
muss, nimmt eine zentrale Stellung in der Softwaretechnik ein. Man unterscheidet zwischen dem Grobentwurf (der Zerlegung eines Systems in Klassen)
und dem Feinentwurf (der Zerlegung einzelner Methoden in Untermethoden und Abläufe).
12.3.1 Grobentwurf
Beim Grobentwurf geht es um die Zerlegung eines
Softwaresystems in seine Grundbausteine, d. h. in seine Klassen oder Module.
Modularität. Grundlegend für die Konstruktion technischer Geräte ist die Modularität (Bausteinprinzip).
Jeder Baustein führt eine in sich abgeschlossene Aufgabe aus und kann als Ganzes gegen andere Bausteine
ausgetauscht werden. Dazu muss jeder Baustein eine festgelegte Schnittstelle zu den anderen Bauteilen
des Gerätes besitzen, und alle Informationen müssen
über diese Schnittstelle laufen. Man überträgt diesen
Bausteinbegriff auf die Softwaretechnik und modelliert auch ein Programm als eine Menge in sich abgeschlossener Klassen, die mit anderen Klassen über ei-
ne festgelegte Schnittstelle kommunizieren und voneinander so weit unabhängig sind, dass sie durch verschiedene Bearbeiter programmiert werden können.
Ein Klasse besteht aus zusammengehörigen Daten
und Methoden und bildet einen für sich verständlichen Baustein (einen abstrakten Datentyp), der verwendet werden kann, ohne sein Inneres zu kennen
und der implementiert werden kann, ohne zu wissen,
wo er später verwendet wird. Das Innere einer Klasse besteht aus Daten und Hilfsmethoden, die außerhalb der Klasse nicht sichtbar sind und nur über Zugriffsmethoden angesprochen werden können, die die
Schnittstelle der Klasse bilden (Bild 12-2).
Das Verstecken der Klassenimplementierung hinter
einer einfachen Methodenschnittstelle entspricht dem
Geheimnisprinzip (information hiding) [10] und hat
folgende Vorteile:
• Abstraktion. Eine Klasse stellt eine Abstraktion
dar (z. B. eine Liste, eine Maschine, ein Konto)
und bietet alle Operationen an, um mit dieser Abstraktion zu arbeiten.
• Einfache Bedienung. So wie ein technisches Gerät
(z. B. ein Telefon) an seiner Oberfläche nur wenige Tasten und Regler hat, über die es bedient werden kann, hat auch eine Klasse nur wenige einfache Methoden, über die sie angesprochen werden
kann. Die Implementierung des Inneren interessiert den Benutzer nicht, genauso wenig wie einen
Benutzer das Innere eines Telefons interessiert.
• Änderbarkeit. Solange die Schnittstelle unverändert bleibt, kann das Innere einer Klasse geändert
werden, ohne dass benutzende Klassen adaptiert
werden müssen. Änderungen bleiben dadurch lokal und sind einfach durchzuführen.
Zugriffsmeth. 1
Zugriffsmeth. 2
Zugriffsmeth. 3
Hilfsmethoden
Daten
Klasse
Bild 12-2. Klasse mit öffentlichen Zugriffsmethoden und
privaten Daten und Hilfsmethoden
12 Softwaretechnik
• Sicherheit. Da private Daten nur über Zugriffsmethoden zugänglich sind, ist es einfach, ihre Konsistenz zu garantieren. Fremde Klassen haben keinen
direkten Zugriff auf private Daten und können sie
somit auch nicht zerstören.
Das Geheimnisprinzip wird nicht immer strikt eingehalten. In Ausnahmefällen kann es sinnvoll sein, den
direkten Zugriff zu gewissen Daten zu erlauben, z. B.
aus Effizienzgründen oder um die Klassenschnittstelle nicht mit Zugriffsmethoden zu überladen.
So gut wie alle modernen Programmiersprachen bieten Klassen als eigenes Sprachkonstrukt an (11.3.5).
Ältere Sprachen wie Modula-2 oder Ada verwenden
statt Klassen Module, die keinen Datentypen sind,
sondern lediglich eine Zusammenfassung von Daten
und Prozeduren darstellen.
Bindung und Kopplung. Die Gliederung eines Softwaresystems gilt als gelungen, wenn der innere Zusammenhang der Klassen (Bindung) groß und die Abhängigkeit zwischen den Klassen (Kopplung) klein
ist. Ein Maß für die Bindung ist die Anzahl der Zugriffe auf klasseninterne Daten und Hilfsmethoden.
Ein Maß für die Kopplung ist die Anzahl der Zugriffe
auf klassenfremde Daten und Methoden.
Der Zugriff auf fremde Klassen sollte in der Regel
über Methodenaufrufe erfolgen. Das ist die leichtgewichtigste Kopplungsart, da Methodenaufrufe meist
unverändert bleiben, wenn sich die Daten einer Klasse
ändern. Der direkte Zugriff auf klassenfremde Daten
stellt hingegen eine stärkere Kopplung dar: Wenn sich
die Daten ändern, müssen auch alle Zugriffe angepasst werden. Am stärksten sind zwei Klassen gekoppelt, wenn eine davon implizite Annahmen über die
Daten der anderen macht (z. B. über die Länge eines
Arrays). Solche Kopplungen sollten unbedingt vermieden werden, da sie nicht explizit im Programmtext
sichtbar sind und bei Änderungen der Daten leicht
übersehen werden.
Schnittstellenentwurf. Die Benutzbarkeit einer
Klasse hängt stark von der Eleganz ihrer Schnittstelle
ab, also von der Art, wie die Zugriffsmethoden und
ihre Parameter gewählt wurden. Folgende Kriterien
sind für einen guten Schnittstellenentwurf wichtig:
• Einfachheit. Vermeide ausgefallene oder kompliziert zu benutzende Operationen. Je kleiner die
Schnittstelle, desto einfacher ist eine Klasse zu benutzen.
• Vollständigkeit. Biete alle aus abstrakter Sicht nötigen Operationen für die Klasse an.
• Redundanzfreiheit. Vermeide es, gleiche Dienste
auf verschiedene Weise anzubieten.
• Elementarität. Fasse Operationen nicht zusammen, wenn sie auch einzeln benötigt werden.
• Konsistenz. Halte dich konsequent an Regeln
(z. B. betreffend Namensgebung, Groß-/Kleinschreibung, Parameterreihenfolge).
Einige dieser Kriterien widersprechen sich (z. B. Einfachheit und Vollständigkeit). Die Kunst eines erfahrenen Softwaretechnikers besteht darin, einen ausgewogenen Kompromiss zwischen ihnen zu finden.
Modelle. Bevor man ein technisches System implementiert, baut man meist ein Modell, an dem man
die Eigenschaften des Systems erproben und seine
Vollständigkeit und Zweckmäßigkeit überprüfen
kann. Elektrotechniker benutzen dazu Schaltpläne,
Architekten Baupläne. Auch in der Softwaretechnik gibt es grafische Notationen, mit denen man
Modelle von Softwaresystemen beschreiben kann.
Als Standardnotation für diesen Zweck hat sich die
Unified Modeling Language (UML) etabliert [7].
Es handelt sich dabei um eine Sammlung von
Diagrammarten, mit denen man verschiedene Sichtweisen auf ein (objektorientiertes) Softwaresystem
darstellen kann. Zu den wichtigsten Diagrammarten
gehören Klassendiagramme, Sequenzdiagramme und
Zustandsdiagramme.
Klassendiagramme beschreiben die Klassen eines
Programms mit ihren Feldern und Methoden sowie
die Beziehungen zwischen den Klassen (Benutzung und Vererbung). Bild 12-3 zeigt ein Beispiel:
Klassen werden durch Kästchen beschrieben, in
denen ihr Name, ihre Felder und ihre Methoden
Window
x, y
width
height
Resize(dx, dy)
Move(dx, dy)
* Figure
x, y
Draw()
Rectangle
width, height
Draw()
Bild 12-3. Klassendiagramm
Circle
radius
Draw()
161
162
Technische Informatik / Programmierung
e: Editor
w: Window
r: Rectangle
c: Circle
Resize(x, y)
Draw()
Draw()
Bild 12-4. Sequenzdiagramm
dargestellt werden. Ein Pfeil zwischen Klassen deutet
eine Benutzt-Beziehung an, ein hohler Pfeil eine
Vererbungs-Beziehung (Rectangle und Circle sind
Unterklassen von Figure).
Sequenzdiagramme beschreiben die Interaktion
zwischen Objekten in Form einer zeitlich geordneten Folge von Nachrichten. Bild 12-4 zeigt ein
einfaches Beispiel: Objekte werden durch senkrechte
Striche dargestellt, Nachrichten durch Pfeile. Das
Editor-Objekt e sendet die Nachricht Resize(x, y) an
das Window-Objekt w, welches seinerseits Draw()Nachrichten an ein Rectangle- und ein Circle-Objekt
sendet.
Zustandsdiagramme beschreiben die Zustände, in denen sich ein Objekt befinden kann sowie die Ereignisse, die einen Übergang zwischen den Zuständen bewirken. Zustandsdiagramme ähneln Automaten, wie
sie in 2.3 beschrieben wurden.
12.3.2 Feinentwurf
Nachdem man ein Softwaresystem in Klassen zerlegt
und deren Schnittstellen durch Zugriffsmethoden spezifiziert hat, geht man daran, das Innere der Klassen zu entwerfen. Dabei werden die Zugriffsmethoden schrittweise verfeinert und schließlich in einer
Programmiersprache implementiert.
Schrittweise Verfeinerung. Die schrittweise Verfeinerung ist eine Entwurfsmethode, bei der man eine
gegebene Aufgabe in Teilaufgaben zerlegt und diese
wieder in kleinere Teilaufgaben, bis die Teilaufgaben
so klein und klar sind, dass man sie direkt in einer
Programmiersprache formulieren kann. Der Entwurf
schreitet also vom Groben zum Detail oder von oben
nach unten (top-down) fort. Das Ergebnis ist ein hierarchischer Graph von Methoden und Untermethoden.
Ausführliche Beispiele findet man in [16,17].
Bild 12-5. Die Komplexität eines Problems wird durch Hal-
bierung seiner Größe mehr als halbiert
Obwohl man die schrittweise Verfeinerung im Prinzip
auch beim Grobentwurf anwenden kann, eignet sie
sich doch eher für den Feinentwurf, bei dem es darum
geht, einzelne Methoden in Untermethoden und Anweisungsfolgen zu zerlegen. Beim Grobentwurf geht
es hingegen um die Modellierung eines größeren Programmsystems, das meist viele gleichwertige Aufgaben hat. Man findet dort schwerer einen Startpunkt,
bei dem die Zerlegung beginnen kann.
Einer der Vorteile der schrittweisen Verfeinerung liegt
darin, dass die Komplexität eines Problems durch seine Zerlegung reduziert wird. Bild 12-5 zeigt, dass die
Komplexität eines Problems mit zunehmender Problemgröße überproportional wächst. Wenn man daher
ein Problem der Größe n und der Komplexität c(n)
in zwei Teilprobleme der Größe n/2 zerlegt, so reduziert man dadurch deren Komplexität auf weniger als
die Hälfte (c(n/2) < c(n)/2). Mit anderen Worten: Die
Teilprobleme sind in Summe wesentlich einfacher zu
lösen als das Gesamtproblem.
Strukturiertes Programmieren. Viele ältere Programmiersprachen enthalten sog. Goto-Anweisungen
(Sprünge zu andern Programmstellen), die früher
auch ausgiebig benutzt wurden. Es hat sich allerdings
gezeigt, dass der uneingeschränkte Gebrauch von
Goto-Anweisungen verworrene Programmstrukturen
entstehen lässt, die schwer zu verstehen, schwer
zu prüfen und schwer zu ändern sind (Dijkstra:
„Die Qualität eines Programmierers ist umgekehrt
proportional zu der Häufigkeit von Gotos in seinen
Programmen“ [4]). Um eine einfache Programmstruktur zu erreichen, soll man sich auf Bausteine
mit einem Eingang und einem Ausgang beschrän-
12 Softwaretechnik
ken, wie sie die fünf nach Dijkstra benannten
D-Diagramm-Konstruktionen
action;
(einfache Aktion),
action; action;
(Sequenz),
if (...) {...}
(bedingte Aktion),
if (...) {...} else {...}
(binäre Auswahl),
while (...) {...}
(Abweisschleife)
und die erweiterten D-Diagramm-Konstruktionen
switch (...) {...}
(Fallunterscheidung),
do {...} while (...)
(Durchlaufschleife),
for (...) {...}
(Zählschleife)
ergeben. Wenn man kleine Effizienzeinbußen in Kauf
nimmt, lassen sich alle denkbaren Programmstrukturen allein mit ihnen, ohne Goto-Anweisungen, konstruieren. In einigen Programmiersprachen gibt es als
Konsequenz davon keine Goto-Anweisung.
12.3.3 Mensch-Maschine-Kommunikation
Programme, die von Menschen bedient werden, bezeichnet man als interaktiv. Sie weisen eine Benutzerschnittstelle (User Interface; UI) auf, die besondere Anforderungen an Entwurf, Implementierung und
Test von Softwaresystemen stellt.
Bereits bei der Problemanalyse sollte im Rahmen
eines benutzerzentrierten Entwurfs darauf geachtet
werden, dass die Fähigkeiten und Ziele der künftigen
Anwender berücksichtigt werden. Das ist Aufgabe
der Software-Ergonomie, die als interdisziplinäres
Fachgebiet Elemente der Informatik, Psychologie
und Medizin verbindet [14].
Bei der Gestaltung der Benutzerschnittstellen werden
Elemente aus den verfügbaren UI-Bibliotheken einem Style Guide folgend eingesetzt, um die Anwender bei der Lösung ihrer Aufgaben zu unterstützen.
Die Elemente entsprechen gewohnten Dingen aus der
Arbeitswelt wie Dokumenten, Texten, Tabellen, Tasten und Schiebereglern. Durch die Ähnlichkeit mit einem Schreibtisch (Desktop) soll die Bedienung intuitiv und leicht erlernbar werden.
In den Anfängen der interaktiven Computerbenutzung (etwa vor 1980) wurde die Benutzerinteraktion
von den Programmen gesteuert, indem sie die Anwender zu Eingaben aufforderten. In moderneren
Benutzerschnittstellen bestimmt der Anwender, wann
welche Eingaben erfolgen sollen. Das Programm
befindet sich die meiste Zeit in einer Warteposition;
Tastatureingaben, Klicks und Gesten werden als
Ereignisse interpretiert und bewirken die Ausführung von Aktionen. In der Softwareentwicklung
führt das zum Prinzip der ereignisorientierten
Programmierung; für die Implementierung interaktiver Programmsysteme sind objektorientierte
Programmiersprachen besonders geeignet.
Zu den wichtigsten Entwurfsprinzipien gehören
Vermeidung von Zuständen (die Anwender sollen in
jeder Situation größtmögliche Freiheit bei der Wahl
ihrer Aktionen haben) und Sichtbarkeit (mögliche
Aktionen sollen aus dem Bildschirminhalt ablesbar
sein). Bei mobilen Geräten sind diese Prinzipien wegen der kleinen Anzeigeflächen schwer einzuhalten.
Beim Entwurf müssen daher die Programmfunktionen in ihrer Vielfalt reduziert und auf Zustände mit
jeweils wenigen Wahlmöglichkeiten verteilt werden.
Umgekehrt können zusätzliche Sensoren (z. B. für
Berührung, Lage, Position, Beschleunigung und
Umgebungslicht) zur Auslösung situationsbedingter
Ereignisse genutzt werden.
Um auch Menschen mit Behinderungen die Benutzung interaktiver Programme zu ermöglichen,
sollte deren Oberfläche barrierefrei sein. Dieses Ziel
wird mit alternativen Ein- und Ausgabetechniken
erreicht, beispielsweise durch Tastatureingaben statt
Mausklicks, Anzeige von Texten statt Bildern oder
hörbare Textausgabe mittels Sprachsynthese.
Interaktive Programme erfordern einen intensiven
Test. Die Validierung der technischen Funktion
muss um eine Prüfung der Bedienbarkeit (Usability
Testing) ergänzt werden, weil der Mensch als Systembestandteil eine zusätzliche Fehlerquelle ist. Das ist
umso kritischer, weil es keinen „Standardmenschen“
gibt; ein interaktives Softwaresystem muss daher
darauf ausgelegt sein, dass verschiedene Anwender
auf völlig unterschiedliche Weisen damit arbeiten.
12.4 Testen
Ziel des Testens ist es, zu prüfen, ob ein Programm
seine Anforderungen erfüllt. Da Testen immer nur
die Anwesenheit von Fehlern zeigen kann, aber nie
ihre Abwesenheit (weil dazu i. Allg. unendlich viele Testfälle nötig wären), definiert man Testen etwas
163
164
Technische Informatik / Programmierung
bescheidener als die Tätigkeit, Fehler in einem Programm zu finden und zu eliminieren [9, 15].
Man unterscheidet zwischen statischen Testmethoden, bei denen man das zu testende Programm
untersucht, ohne es auszuführen, und dynamischen
Testmethoden, bei denen man es mit verschiedenen
Eingabedaten (Testfällen) ausführt und die gelieferten
Ergebnisse mit den erwarteten vergleicht. Bei den
dynamischen Testmethoden unterscheidet man nach
der Phase, in der sie eingesetzt werden, zwischen
Komponententest, Integrationstest und Abnahmetest.
Nach der Art der Testfallauswahl unterscheidet man
zwischen Funktionstest und Strukturtest. Maßnahmen, um die allgemeine Qualität eines Programms
zu verbessern, fasst man unter dem Begriff der
Qualitätssicherung zusammen.
12.4.1 Statische Testmethoden
Verifikation. Idealerweise möchte man mit mathematischen Methoden beweisen, dass ein Programm korrekt ist. Das ist jedoch bei heutigem Stand der Technik nur für sehr kleine und einfache Programme möglich [6]. Außerdem beziehen sich solche Beweise nur
auf die Algorithmen eines Programms, nicht auf seine
Codierung in einer bestimmten Programmiersprache
oder gar auf seine Ausführung auf einer bestimmten
Maschine.
Statische Programmanalyse. Es gibt Werkzeuge,
mit denen man den Quelltext eines Programms auf
typische Fehler im Daten- oder Steuerfluss untersuchen kann. Auf diese Weise können uninitialisierte
oder unbenutzte Variablen gefunden werden oder
nicht erreichbare Anweisungen (z. B. solche, die
unmittelbar nach einer Return-Anweisung stehen).
Auch Stilschwächen können auf diese Weise entdeckt
werden.
Schreibtischtest. Noch bevor ein Programm zum ersten Mal läuft, kann man es am Schreibtisch mit Papier und Bleistift für einige Eingabedaten durchsimulieren. Dabei legt man sich eine Tabelle aller Variablen an und notiert ihre Werte, die sie während der
Simulation annehmen. Schreibtischtests sind sehr zu
empfehlen, da sie das Vertrauen in die Korrektheit des
Programms erhöhen und Fehler frühzeitig aufdecken.
Je früher ein Fehler gefunden wird, desto einfacher ist
seine Behebung.
Codeinspektion. Wie beim Schreibtischtest simuliert
man bei der Codeinspektion ein Programm gedanklich durch, ohne es auszuführen. Im Gegensatz zum
Schreibtischtest tut man das aber in einer Gruppe.
Der Programmierer erklärt sein Programm Schritt für
Schritt; die anderen Teilnehmer denken mit und versuchen, Fehler zu finden. Für den Ablauf von Codeinspektionen gibt es Empfehlungen und Checklisten [5].
12.4.2 Dynamische Testmethoden
Komponententest (unit test). Sobald eine Klasse implementiert ist, kann man sie bereits unabhängig vom
Rest des Systems testen. Dazu muss man einen sog.
Testtreiber schreiben, d. h. ein Programm, das die Methoden der Klasse zu Testzwecken aufruft. Wenn die
Klasse andere noch nicht implementierte Klassen benutzt, muss man diese durch sog. Teststümpfe (stubs)
rudimentär implementieren. Es empfiehlt sich daher,
ein Programmsystem beginnend mit den untersten
Klassen zu testen, da man sich so die Implementierung der Stümpfe erspart. Nach der Art, wie die Testfälle ausgewählt werden, unterscheidet man zwischen
Funktionstest und Strukturtest.
Funktionstest (black box test). Beim Funktionstest betrachtet man das Testobjekt (d. h. eine Methode) als
„schwarzen Kasten“ (black box), dessen Inneres man
nicht kennt. Man ermittelt die Testfälle ausschließlich
aufgrund der Spezifikation des Testobjekts: Wenn das
Testobjekt z. B. n Eingabeparameter mit je m möglichen Werten hat, so ergeben sich mn Testfälle. Hinzu
kommen noch Testfälle für ungültige Parameterwerte.
Da sich auf diese Weise rasch eine astronomisch hohe Anzahl von Testfällen ergäbe, beschränkt man sich
bei den gültigen Parameterwerten auf solche, die unterschiedliches Programmverhalten hervorrufen. Aus
ihnen bildet man Testfälle mit allen möglichen Wertekombinationen und wählt auch je einen Testfall für
jeden ungültigen Parameterwert.
Strukturtest (white box test). Beim Strukturtest sieht
man sich den Quelltext des Testobjekts an und versucht ihn durch Testfälle möglichst gut „abzudecken“.
Dabei unterscheidet man zwischen Anweisungsabdeckung (jede Anweisung muss mindestens einmal ausgeführt werden), Bedingungsabdeckung (jede Abfrage im Programm muss mindestens einmal wahr und
12 Softwaretechnik
einmal falsch liefern) und Pfadabdeckung (jeder Pfad
durch das Programm muss mindestens einmal durchlaufen werden). Während es einfach ist, Testfälle für
die Anweisungsabdeckung zu wählen, sind zur Pfadabdeckung potenziell unendlich viele Testfälle nötig
(ein Programm mit einer Schleife kann unendlich viele Pfade haben). Die Anweisungsabdeckung garantiert nicht, dass alle Pfade durchlaufen wurden, sodass
ein Fehler in einem nicht durchlaufenen Pfad unentdeckt bleibt.
Regressionstest und Testwerkzeuge. Eine Klasse soll
nicht nur ein einziges Mal getestet werden, sondern
nach jeder Änderung wieder. Daher sollte man die
Testfälle aufbewahren, damit man die Tests jederzeit
wiederholen kann. Eine Menge von Testfällen nennt
man eine Testsuite, ihre Wiederholung einen Regressionstest. Um Tests einfach wiederholen zu können,
muss man die Testtreiber so schreiben, dass sie für jeden Testfall prüfen, ob das gelieferte Ergebnis dem erwarteten entspricht. In Abhängigkeit davon wird der
Test als fehlerfrei oder fehlerhaft betrachtet. Es gibt
Werkzeuge für den Regressionstest, die eine Testsuite
ausführen und als Ergebnis die Anzahl der fehlerfreien und fehlerhaften Testfälle liefern [2].
Integrationstest. Nachdem alle Klassen einzeln getestet wurden, kann man sie schrittweise zu größeren
Einheiten zusammensetzen und ihr Zusammenspiel
testen. Hier kann i. Allg. nur ein Funktionstest durchgeführt werden. Die subtilsten Fehler treten oft gerade
bei der Interaktion zwischen Klassen auf. Der Integrationstest kann daher Fehler finden, die im Komponententest nicht entdeckt wurden. Als abschließenden
Integrationstest führt man oft einen sog. Leistungstest
oder Stresstest durch, bei dem man das Programm mit
einer großen Menge von Eingabedaten testet, die oft
sogar zufällig ermittelt wurden und daher auch sinnlose Kombinationen enthalten. Auf diese Weise testet man die Robustheit des Programms bei unsinnigen
Eingaben.
Abnahmetest. Beim Abnahmetest wird das Programm unter Betriebsbedingungen mit realen Daten
getestet, und zwar über einen längeren Zeitraum,
damit man auch seine Zuverlässigkeit feststellen
kann. Der Abnahmetest findet meist beim Auftraggeber statt und entscheidet über die Abnahme des
Produkts.
12.4.3 Qualitätssicherung
Hierunter versteht man alle Maßnahmen, die die Verbesserung der Qualität eines Softwareprodukts zum
Ziel haben. Man unterscheidet zwischen konstruktiven und analytischen Maßnahmen.
Konstruktive Maßnahmen. Hierbei handelt es
sich um vorbeugende Maßnahmen wie die Vorgabe
eines bestimmten Programmierstils (z. B. betreffend
Namensgebung, Kommentierung, Zeileneinrückung)
oder die Vorschreibung von Projektstandards (z. B.
betreffend Codeinspektionen, Regressionstests,
Versionsverwaltung, Dokumentation).
Analytische Maßnahmen. Ihr Ziel ist die Messung
der Qualität des fertigen Produkts und die Ermittlung von Planungsdaten für Nachfolgeprodukte. Um
die Qualität eines Programms messbar zu machen, hat
man versucht, Qualitätsmerkmale (sog. Metriken) zu
definieren, was jedoch bisher nur unvollkommen gelungen ist.
Ein häufig gemessenes Merkmal ist die softwaretechnische Komplexität eines Programms, die aussagen
soll, wie schwierig ein Programm zu verstehen und
zu warten ist. Ein einfaches Komplexitätsmaß ist die
Programmlänge in Zeilen (lines of code, LOC); Da
ein Programm jedoch Leerzeilen, Kommentarzeilen
und Zeilen mit mehreren Anweisungen enthalten
kann, ist die Anzahl der Anweisungen eines Programms ein besseres Maß als die Anzahl der Zeilen.
Ein weiteres bekanntes Komplexitätsmaß wurde von
McCabe [8] definiert und lautet:
Softwaretechnische Komplexität eines Programms
= 1 + Anzahl der binären Verzweigungen.
Es beruht auf dem Gedanken, dass ein Programm
umso schwerer zu verstehen und zu testen ist, je mehr
Pfade vom Eingang zum Ausgang verlaufen. Daneben
gibt es noch viele andere Komplexitätsmaße, die jedoch alle mit dem viel einfacher zu ermittelnden Maß
der Anweisungsanzahl korrelieren und daher selten
verwendet werden. Hinzu kommt, dass diese Maße
nur für das Programmieren im Kleinen, also innerhalb
einer Klasse gelten. Beim Programmieren im Großen
sind aber vielmehr die Anzahl und Größe der Klassen und die Kopplungen zwischen ihnen ausschlag-
165
166
Technische Informatik / Programmierung
gebend. Hier hat sich noch kein brauchbares Komplexitätsmaß durchgesetzt.
12.5 Dokumentation
Software ist wie andere technische Produkte auf
Dokumentation angewiesen. Die Qualität der Dokumentation ist deshalb ein wesentliches Merkmal
der Softwarequalität. Alle Phasen eines Softwareprodukts sollen von Dokumentation begleitet sein. Die
Herstellung einer guten Dokumentation ist schwierig;
noch schwieriger ist es aber, die Dokumentation
aktuell und konsistent zu halten. Welche Dokumente
im einzelnen Fall erforderlich sind, hängt von der
Projektgröße und der Projektart ab. Tabelle 12-3 zeigt
typische Zusammenstellungen von Dokumenten für
kleine, mittlere und große Projekte.
Man unterscheidet verschiedene Arten der Dokumentation (Kommentare, Benutzerdokumentation,
Entwicklungsdokumentation, Hilfetexte), die für
unterschiedliche Leserkreise gedacht sind.
Kommentare. Kommentare sind Erläuterungen
im Quelltext eines Programms. Neben dem Quelltext sind sie die detaillierteste und zuverlässigste
Informationsquelle für Entwickler, da sie bei Programmänderungen am ehesten nachgeführt werden.
Gute Kommentare sollten nicht einfach wiederholen, was auch aus dem Programmtext ersichtlich
ist, sondern Zusatzinformationen geben, die den
Programmtext besser verständlich machen. Es ist
empfehlenswert, bei der Deklaration wichtiger
Variablen sowie am Anfang jeder Methode und jeder
Klasse einen Kommentar anzubringen. Ferner ist
es ratsam, ein Programm mit sog. Assertionen zu
kommentieren, d. h. mit Aussagen über den Zustand
des Programms an Schlüsselstellen (z. B. am Anfang
einer Schleife, nach einer Schleife, am Anfang eines
Else-Zweiges).
Benutzerdokumentation. Die Benutzerdokumentation beschreibt, wie ein Programm zu bedienen ist.
Neben der Installationsanleitung und den Systemvoraussetzungen gibt sie Auskunft über den Zweck und
die Hauptfunktionen des Programms. Sie erklärt, wie
man das Programm startet und welche Parameter man
dabei angeben kann. Bei Programmen mit grafischer
Benutzeroberfläche beschreibt sie den Bildschirmaufbau (eventuell gibt es mehrere Bildschirmmasken)
sowie die verschiedenen Eingabeelemente und
Tabelle 12-3. Aufbau der Dokumentation je nach Projekt-
größe
Projekt
klein
mittel
groß
Dokumente
Bedienungsanleitung
Implementierungsbeschreibung
Benutzerdokumentation
Systemübersicht
Benutzerhandbuch
Entwicklungsdokumentation
Anforderungsdefinition
Implementierungsbeschreibung
Architekturbeschreibung
Modulbeschreibungen
Produktbeschreibung
Benutzerdokumentation
Systemübersicht
Benutzerhandbuch
Installationsbeschreibung
Entwicklungsdokumentation
Anforderungsdefinition
Arbeitskonventionen
Implementierungsbeschreibung
Architekturbeschreibung
Datenbankschema und Dateiformate
Modulbeschreibungen
Schnittstellen
Algorithmen und Datenstrukturen
Testprotokolle
Organisationsdokumente
Terminpläne
Verwaltungsakten
Projekttagebuch
Wartungsunterlagen
Menükommandos. Typische Anwendungsszenarien
des Programms sollten in Form von Anleitungen
(tutorials) dokumentiert werden. Auch Fehlermeldungen und die geeignete Reaktion darauf sollte man
dokumentieren. Es hat sich als nützlich erwiesen,
eine Grobversion der Bedienungsanleitung bereits
vor der Implementierung des Programms zu erstellen.
Sie dient dann als Teil der Anforderungsdefinition
und als erster „Papierprototyp“ des Programms.
Entwicklungsdokumentation. Sie richtet sich
an den Wartungsprogrammierer und umfasst alle
Dokumente, die bei der Entwicklung der Software
12 Softwaretechnik
angefallen sind, also Anforderungsdefinition, Entwurfsdokumente, Implementierungsbeschreibung,
Testprotokolle und sonstige Wartungsunterlagen.
Kern der Entwicklungsdokumentation ist die Implementierungsbeschreibung, die die Architektur des
Programms erläutert und das Innere jeder Klasse
beschreibt. Wenn das Programm eine Datenbank
benutzt, dokumentiert sie auch das Datenbankschema, d. h. die Struktur der Datenbanktabellen und
ihre Beziehungen. Ebenso wird das Format von Einund Ausgabedateien sowie von eventuellen Konfigurationsdateien spezifiziert. Das Innere der Klassen
sollte nicht zu detailliert beschrieben werden, weil
sonst die Gefahr besteht, dass die Dokumentation bei
Programmänderungen nicht nachgeführt wird und
veraltet. Zum genauen Verständnis des Programms
muss man sich ohnehin den Quelltext ansehen, der
(mit Kommentaren versehen) ebenfalls Teil der
Dokumentation ist.
Ein wichtiger Teil der Implementierungsbeschreibung
ist die Spezifikation der Klassenschnittstellen, die für
Programmierer gedacht ist, welche einzelne Klassen
des Programms verwenden wollen. Sie listet die exportierten Felder und Methoden jeder Klasse auf und
versieht sie mit Erläuterungen. Für jede Methode wird
ihr Zweck sowie die Bedeutung ihrer Parameter beschrieben, wenn möglich auch die Fehler, die bei ihrer
Ausführung auftreten können. Die Schnittstellendokumentation kann oft mit Werkzeugen aus den Kommentaren des Quelltexts erzeugt werden.
Hilfetexte. Viele Programme (vor allem solche mit
grafischer Benutzeroberfläche) enthalten sog. Hilfetexte. Das sind Kurzfassungen der Benutzerdokumentation, die man unter einem bestimmten Menüpunkt
abrufen kann oder die kontextabhängig bei gewissen
Mausklicks angezeigt werden. Solche integrierte Hilfestellungen sind nützlich, weil sie das Nachschlagen
im Benutzerhandbuch überflüssig machen.
12.6 Werkzeuge der Softwaretechnik
Wie in jeder Ingenieurdisziplin gibt es in der Softwaretechnik neben schöpferischen Tätigkeiten auch
Routinearbeiten, die man durch Werkzeuge zu unterstützen versucht. Es gibt heute zahlreiche solcher
Werkzeuge, die in verschiedenen Phasen der Softwareentwicklung eingesetzt werden, vor allem in der Im-
plementierungsphase, aber auch in der Entwurfs- und
Testphase sowie zur Dokumentation.
Entwicklungsumgebungen. Programme werden
heute meist mit integrierten Entwicklungsumgebungen (integrated development environments, IDEs)
erstellt. Solche Umgebungen enthalten meist einen
komfortablen Editor, der einzelne Programmdateien
zu „Projekten“ zusammenfasst, Schlüsselwörter durch Farben hervorhebt (syntax coloring),
Programmteile faltet (d. h. aus- und einblendet)
sowie Funktionen zum konsistenten Ändern von
Programmen anbietet (refactoring). Daneben enthalten sie einen Compiler zum Übersetzen der
Programme, einen „Debugger“ zur Fehlersuche
sowie Mechanismen, um alle Teile eines Projekts
„zusammenzubinden“ und auszuführen.
Werkzeuge für den Benutzerschnittstellenentwurf. Für die Gestaltung interaktiver Programme
werden Editoren eingesetzt, die es erlauben,
Bildschirm- und Fensterinhalte wie in einem Grafikprogramm zu zeichnen. Elemente wie Tasten
oder Menüs können häufig auch mit Aktionen
verknüpft werden. Das erlaubt einen einfachen Test
der Benutzerschnittstelle, noch bevor eine einzige
Zeile Programmcode geschrieben ist. Dieser Ansatz
ermöglicht ein prototypisches Vorgehen in der Softwareentwicklung und erlaubt künftigen Anwendern
eine Vorschau auf die geplanten Systemfunktionen
und ihre Umsetzung.
Entwurfswerkzeuge. Für den Entwurf von Programmen gibt es nicht so ausgereifte Werkzeuge wie für
die Implementierung. Meist beschränkt sich das Angebot auf Diagrammeditoren, mit denen man Softwarearchitekturen z. B. in UML beschreiben kann.
Manchmal liegt dahinter auch eine Datenbank, in der
die Klassen und ihre Beziehungen festgehalten werden, und aus der man Teile der Implementierung generieren kann.
Versionsverwaltungswerkzeuge. In großen Softwareprojekten, an denen viele Entwickler arbeiten und
in denen Programmversionen für verschiedene Kunden, Maschinen und Betriebssysteme entstehen, kann
es leicht zum Chaos kommen, wenn jeder Entwickler
Programmteile nach Gutdünken ändert und sie teilweise auf seinem eigenen Rechner, teilweise auf gemeinsamen Rechnern speichert. Daher gehört es in
solchen Projekten zum Standard, Werkzeuge zur Ver-
167
168
Technische Informatik / Programmierung
sionsverwaltung zu benutzen, die von jeder Klasse sowohl ihre geschichtlich gewachsenen Revisionen als
auch ihre verschiedenen Varianten (z. B. für unterschiedliche Kunden) verwalten können. Man kann damit jederzeit auf ältere Programmversionen zurückgreifen und „auf Knopfdruck“ ein Softwareprodukt
für einen bestimmten Kunden aus den passenden Varianten zusammensetzen.
Testwerkzeuge. Hier gibt es einerseits Werkzeuge für
den Regressionstest, die es erlauben, eine Menge von
Testfällen automatisiert auszuführen und die gelieferten mit den erwarteten Ergebnissen zu vergleichen.
Andererseits gibt es auch Werkzeuge für den Strukturtest, die prüfen, ob durch eine Menge von Testfällen alle Anweisungen, Bedingungen oder Pfade eines
Programms abgedeckt werden. Zu den Testwerkzeugen im weiteren Sinne gehören auch statische Programmanalysatoren, die Anomalien in Programmtexten aufdecken (z. B. uninitialisierte Variablen) oder
Komplexitätsmaße berechnen.
Werkzeuge zur Ermittlung des Programmprofils.
Nach einer Faustregel verbringt ein Programm 90%
seiner Laufzeit in 10% seines Codes. Es gibt Werkzeuge (sog. Profiler), die bei der Ausführung eines
Programms die Ausführungshäufigkeiten einzelner
Anweisungen oder Methoden messen. Man kann
dann gezielt versuchen, die am häufigsten ausgeführten Programmteile zu verbessern, um dadurch die
Geschwindigkeit des Programms zu steigern.
Dokumentationswerkzeuge. Die Schnittstellenbeschreibungen von Klassen werden häufig aus
Kommentaren generiert, die bei der Deklaration von
Feldern und Methoden im Quelltext stehen. Es gibt
auch Werkzeuge, die zu Dokumentationszwecken
aus dem Quelltext eine grafische Darstellung der
Softwarearchitektur erzeugen (z. B. in Form von
UML-Diagrammen).
13 Ausblick:
Informatik und Kommunikation
Die Informatik ist eine junge Disziplin, in der noch
laufend neue Teilgebiete und Aspekte hinzukommen.
So stößt zum Beispiel die Geschwindigkeit von Prozessoren bereits an physikalische Grenzen, da Signallaufzeiten durch die Lichtgeschwindigkeit beschränkt
sind. Weitere Geschwindigkeitssteigerungen sind nur
noch durch mehrere Prozessoren pro Rechner erreichbar, die unabhängige Teilaufgaben eines Programms
parallel bearbeiten. Während grobgranulare Parallelität durch parallele Prozesse (Threads) erreicht werden kann, die auf unterschiedlichen Prozessoren laufen, steht die Entwicklung von Programmiersprachen
und Programmiermodellen für feingranulare Parallelität erst an ihrem Anfang.
Neue Teilgebiete der Informatik betreffen auch die
Kommunikation zwischen Rechnern, zwischen Rechner und Mensch und zwischen Mensch und Mensch
mit dem Rechner als verbindendem Element. Das ändert an den Mathematischen Modellen nichts, an den
Digitalen Systemen wenig. Bei der Rechnerorganisation kommen Verteilte Systeme hinzu und das Schichtenmodell der Kommunikation in heterogenen Rechnernetzen.
Besonders durch das Internet wurde klar, welche Probleme die weltweite Kommunikation in heterogenen
Netzen aufwirft. Man will alle Arten von Daten übertragen: Texte, Grafiken, Musik, Festbilder, Fernsehaufzeichnungen. Die großen Datenmengen, die dabei
in kurzer Zeit transportiert werden müssen, brachten das algorithmische Problem der Datenkompression mit sich. Zugleich zeigte es sich, dass bei der
Verbindung aller mit allen das einzelne Rechnersystem gegen Eindringlinge nur schwer geschützt werden kann. Da zugleich der elektronische Handel aufblühte, bei dem es auf Vertraulichkeit, fälschungssichere Unterschriften und ähnliches ankommt, spielen
Sicherheitsfragen in der elektronischen Kommunikation eine große Rolle.
Die Fülle der Dokumente im Internet erfordert die
Entwicklung von Programmen zur Dokumentsuche
und Dokumenterschließung. Eine der neuesten Entwicklungen untersucht die Kommunikationsmöglichkeiten, die Prozessoren mit sich bringen, die in großer
Anzahl in Gegenstände des täglichen Lebens, wie Aktentaschen und Wäschestücke oder gar den menschlichen Körper unsichtbar „implantiert“ werden, sich
selbst drahtlos mit vorhandenen Netzen verbinden,
dadurch allgegenwärtig sind und keine Bedienung
durch ihren Besitzer mehr erfordern.
Es ist charakteristisch für diese neuen Teilgebiete der
Informatik, dass Hardware und Software meist eng
verbunden sind, wenn auch die Software das Übergewicht hat.
Literatur
Formelzeichen zur Programmierung
↓
↑
O(n)
{}
{}
[]
|
Eingangsparameter
Ausgangsparameter
Übergangsparameter
O-Notation
Mengenklammern
Wiederholungssymbol
Optionssymbol
Alternativentrennsymbol
Floor
9.1
9.1
9.2.1
9.4
10.8
11.2.1
11.2.1
11.2.1
10.5
Literatur
Handbücher und Nachschlagewerke
Claus, V.; Schwill, A.: Duden Informatik. 3. Aufl.
Bibliographisches Institut und F.A. Brockhaus AG
2006
Rechenberg, P.; Pomberger, G. (Hrsg.): InformatikHandbuch. 4. Aufl. München: Hanser 2006
Siemers, Ch.; Sikora, A. (Hrsg.): Taschenbuch Digitaltechnik. München: Hanser 2003
Allgemeine Literatur zu Kapitel 1 und 2
Hermes, H.: Aufzählbarkeit, Entscheidbarkeit, Berechenbarkeit. 3. Aufl. Berlin: Springer 1978
Hilbert, D.; Ackermann, W.: Grundzüge der theoretischen Logik. 6. Aufl. Berlin: Springer 1972
Hopcroft, J.E.; Motwani, R., Ullman, J.D.: Einführung in die Automatentheorie, Formale Sprachen
und Komplexitätstheorie. 2. Aufl. Pearson Studium
2002
Liebig, H.: Logischer Entwurf digitaler Systeme.
4. Aufl. Berlin: Springer 2005
Allgemeine Literatur zu Kapitel 3 und 4
Liebig, H.: Logischer Entwurf digitaler Systeme.
4. Aufl. Berlin: Springer 2005
Lipp, H.M.: Grundlagen der Digitaltechnik. 4. Aufl.
München: Oldenbourg 2002
Mano, M.M.; Kime, Ch.R.: Logic and Computer Design Fundamentals. 3rd. ed. Pearson Education 2004
Unger, S.H.: The essence of logic circuits. 2nd ed.
New York: IEEE Press 1996
Tietze, U; Schenk, C.: Halbleiter-Spannungstechnik.
12. Aufl. Berlin: Springer 2002
Allgemeine Literatur zu Kapitel 5 und 6
Bode, A.: Rechnerarchitektur und Prozessoren.
In: Rechenberg, P.; Pomberger, G.: InformatikHandbuch. 4. Aufl. München: Hanser 2006
Flik, Th.: Mikroprozessortechnik und Rechnerstrukturen. 7. Aufl. Berlin: Springer 2005
Flik, Th.; Liebig, H.: Mikroprozessortechnik. 5. Aufl.
Berlin: Springer 1998
Flynn, M.J.: Computer architecture. Boston: Jones
and Bartlett 1995
Hennessy, J.L.; Patterson, D.A.: Computer Architecture: A Quantitative Approach. 5th ed. Waltham, MA:
Morgan Kaufmann 2012
Herrmann, P.: Rechnerarchitektur. 3. Aufl. Braunschweig: Vieweg 2002
Liebig, H.: Rechnerorganisation. 3. Aufl. Berlin:
Springer 2003
Menge, M.: Moderne Prozessorarchitekturen. Berlin:
Springer 2005
Patterson, D.A.; Hennessey, J.L.: Rechnerorganisation und -entwurf: Die Hardware/SoftwareSchnittstelle, München: Oldenbourg Wissenschaftsverlag 2011
Stallings, W.: Computer organization and architecture. 7th ed. Englewood Cliffs: Prentice Hall 2006
Allgemeine Literatur zu Kapitel 7
Flik, Th.: Mikroprozessortechnik. 7. Aufl. Berlin:
Springer 2005
Flynn, M.J.: Computer architecture. Boston: Jones
and Bartlett 1995
Hellwagner, H.: Arbeitsspeicher- und Bussysteme.
In: Rechenberg, P.; Pomberger, G.: InformatikHandbuch. 4. Aufl. München: Hanser 2006
Hennessy, J.L.; Patterson, D.A.: Computer Architecture: A Quantitative Approach. 5th ed. Waltham,
MA: Morgan Kaufmann 2012
169
170
Technische Informatik / Programmierung
Proebster, W.; Schwarzstein, D.: Externe Speicher
und Peripheriegeräte. In: Rechenberg, P.; Pomberger, G.: Informatik-Handbuch. 4. Aufl. München:
Hanser 2006
Steinmetz, R.; Mühlhäuser, M.; Welzl, M.: Rechnernetze. In: Rechenberg, P.; Pomberger, G.:
Informatik-Handbuch. 4. Aufl. München: Hanser
2006
Tanenbaum, A.S.: Computernetzwerke. 4. Aufl. München: Pearson/Addison-Wesley 2003
Ungerer, T.: Parallelrechner und parallele Programmierung. Heidelberg: Spektrum 1997
Völz, H.: Wissen, Erkennen, Information. Digitale Bibliothek 2007
Volkert, J.: Parallelrechner. In: Rechenberg, P.; Pomberger, G.: Informatik-Handbuch. 4. Aufl. München:
Hanser 2006
Allgemeine Literatur zu Kapitel 11
Allgemeine Literatur zu Kapitel 8
Spezielle Literatur zu Kapitel 1
Borrmann, L.: Betriebssysteme. In: Rechenberg, P.;
Pomberger, G.: Informatik-Handbuch. 4. Aufl. München: Hanser 2006
Stallings, W: Betriebssysteme. 4. Aufl. München:
Pearson/Prentice Hall 2003
Tanenbaum, A.S.: Moderne Betriebssysteme. 2. Aufl.
München: Pearson/Prentice Hall 2002
Allgemeine Literatur zu Kapitel 9 und 10
Aho, A.V.; et. al.: Data structures and algorithms.
Reading, Mass.: Addison-Wesley 1983
Cormen, T. H.; et al.: Introduction to algorithms. 3rd
ed. MIT Press 2009
Knuth, D.E.: The art of computer programming. vol.
1: Fundamental algorithms. 3rd. ed. Reading, Mass.:
Addison-Wesley 1997
Ottmann, T.; Widmayer, P.: Algorithmen und Datenstrukturen. 5. Aufl. Heidelberg: Spektrum 2012
Sedgewick, R.; Wayne, K.: Algorithms. 4th ed.
Addison-Wesley Professional 2011
Wirth, N.: Algorithmen und Datenstrukturen mit
Modula-2. 5. Aufl. Stuttgart: Teubner 1996
Goos, G.: Programmiersprachen. In: Rechenberg, P.;
Pomberger G.: Informatik-Handbuch. 4. Aufl. München: Hanser 2006
Pratt, T.W.: Programming languages. 4th ed. London:
Prentice-Hall 2001
Allgemeine Literatur zu Kapitel 12
Ludewig, J.; Lichter, H.: Software Engineering,
Grundlagen, Menschen, Prozesse, Techniken.
Heidelberg: dpunkt 2007
Pomberger, G.; Pree, W.: Software Engineering,
Architektur-Design
und
Prozessorientierung.
3. Aufl. München: Hanser 2004
Sommerville, I.: Software engineering. 9th ed. Amsterdam: Addison-Wesley Longman 2010
1. Giloi, W.K.; Liebig, H.: Logischer Entwurf digitaler
Systeme. 2. Aufl. Berlin: Springer 1980
2. Hill, J.H.; Peterson, G.R.: Computer aided logical design with emphasis on VLSI. 4th ed. New York: John
Wiley 1993
3. (Hilbert/Ackermann 1972)
Spezielle Literatur zu Kapitel 2
1. Liebig, H.: Rechnerorganisation. 3. Aufl. Berlin:
Springer 2003
Spezielle Literatur zu Kapitel 3
1. Mead, C.; Convay, L.: Introduction to VLSI systems.
Reading, Mass.: Addison-Wesley 1980
2. (Liebig 2005)
3. Omondi, A.R.: Computer Arithmetic Systems. Englewood Cliffs: Prentice Hall 1994
Spezielle Literatur zu Kapitel 4
1. (Liebig 2005)
2. Mead, C.; Conway, L.: Introduction to VLSI systems.
Reading, Mass.: Addison-Wesley 1980
3. IEEE standards VHDL language reference manual. IEEE Std 1076-1993
Literatur
Spezielle Literatur zu Kapitel 5
1. (Liebig 2003)
2. (Menge 2005)
Spezielle Literatur zu Kapitel 6
1. Bohn, W.F.; Flik, Th.: Zeichen- und Zahlendarstellungen. In: Rechenberg, P.; Pomberger, G.: InformatikHandbuch. 4. Aufl. München: Hanser 2006
2. (Flik 2005)
3. Goldberg, D.: Computer arithmetic. In: Patterson,
D.A.; Hennessey, J.L.: Computer architecture. San
Mateo, Calif.: Kaufmann 1990
4. Goldberg, D.: What every computer scientist should
know about floating point arithmetic. ACM Computing
Surv. 23 (1991) 5–8
5. Hamming, R.W.: Information and Codierung: Fehlererkennung und -korrektur. Weinheim: VCH 1987
6. Hoffmann, R.: Rechnerentwurf. 3. Aufl. München: Oldenbourg 1993
7. (Liebig)
8. Omondi, A.R.: Computer Arithmetic Systems. Englewood Cliffs: Prentice Hall 1994
9. Stallings, W: Computer Organization & Architecture.
7th ed. Prentice Hall: Pearson 2006
10. Tanenbaum, A.S.: Computernetzwerke. 4. Aufl. München: Pearson/Addison-Wesley 2003
11. The Unicode Consortium: The Unicode Standard. Version 4.0. Reading: Addison-Wesley 2003
Spezielle Literatur zu Kapitel 7
1. Budruk, R.; Anderson, D.; Shanley, T: PCI express
system architecture. 4rd ed. Boston: Addison-Wesley
2004
2. (Flik 2005)
3. Flynn, M.J.: Some computer organizations and their
effectiveness. IEEE Trans. Computers C-21 (1972)
948–960
4. (Flynn 1995)
5. HyperTransport i/o link specification, Revision 2.00.
HyperTransport Technology Consortium 2004
6. PCI local bus specification, rev. 2.2. Portland: PCI Special Interest Group 1998
7. PCI-X addendum to the PCI local bus specification.
Hillsboro: PCI Special Interest Group 1999
8. Shanley, T.; Anderson, D.: PCI system architecture.
4rd ed. Reading: Addison-Wesley 1999
9. (Tanenbaum 2003)
10. (Ungerer 1997)
11. VITA 1.5: 2eSST draft standard 1.8 Scottsdale: VITA
1999
12. (Völz 2007)
13. Widmer, A.X.; Franaszek, P.A.: A DC-balanced,
partitioned-block, 8b/10b transmission code. IBM
Journal of Research and Development 27 (1983), H.5,
440
Spezielle Literatur zu Kapitel 8
1. (Borrmann 2006)
2. Bourne, S.R.: Das UNIX-System V. 2. Aufl. Bonn:
Addison-Wesley 1992
3. Dijkstra, E.W.: Co-operating sequential processes. In:
Genuys, F. (Ed).: Programming languages. London:
Academic Pr. 1968
4. (Stallings 2003)
5. (Tanenbaum 2002)
Spezielle Literatur zu Kapitel 9
1. Blaschek, G.: Die Algorithmenbeschreibungssprache
Jana. http://ssw.jku.at/Teaching/Lectures/Algo/Jana.
pdf
2. Buchberger, B.: Editorial. In: Journal of Symbolic
Computation 1 (1985) 1-6
3. Buchberger, B.: Symbolisches Rechnen. In: Rechenberg, P.; Pomberger, G. (Hrsg.): Informatik-Handbuch,
4. Aufl. München: Hanser 2006
4. de Berg, M.; Cheong, O.; van Kreveld, M.; Overmars,
M.: Computational Geometry: Algorithms and Applications. 3rd ed. New York: Springer 2008
5. Foley, J.D.; u.a.: Introduction to computer graphics.
Reading, Mass.: Addison-Wesley 1993
6. Harrison, J.: Handbook of Practical Logic and Automated Reasoning. Cambridge University Press 2009
7. Henrici, P.: Elemente der numerischen Analysis, 2
Bde. Mannheim: Bibliogr. Inst. 1972, 1973
8. Knuth, D.E.: The art of computer programming, vol.
1: Fundamental algorithms. 3rd ed. Reading, Mass.:
Addison-Wesley 1997
9. Knuth, D.E.: The art of computer programming, vol.
2: Seminumerical algorithms. 3rd ed. Reading, Mass.:
Addison-Wesley 1997
10. Manna, Z.: Mathematical theory of computation. Dover Publications 1993
11. Proakis, J. G.; Manolakis, D. K.: Digital Signal Processing. 4th ed. Prentice Hall 2006
171
172
Technische Informatik / Programmierung
12. Rechenberg, P.; Pomberger, G. (Hrsg.): InformatikHandbuch. 4. Aufl. München: Hanser 2006
13. Sedgewick, R.; Wayne, K.: Algorithms. 4th ed.
Addison-Wesley Professional 2011
14. Shirley, P.; Ashikhmin, M.; Marschner, S.: Fundamentals of Computer Graphics. 3rd ed. A K Peters 2009
15. Stetter, H.J.: Numerik für Informatiker. München: Oldenbourg 1990
16. Überhuber, C.: Computer-Numerik (2 Bde.). Berlin:
Springer 1995
Spezielle Literatur zu Kapitel 11
1. ACM Sigplan Notices 27 (1992), 5 (Das ganze Heft ist
der Sprache Haskell gewidmet, mit Sprachdefinition)
2. Ada 95 Reference Manual. Berlin: Springer 1997
3. Applets: http://java.sun.com/applets/
4. Backus, J.W.; u.a.: Revised report on the algorithmic
language ALGOL 60. Numer. Mathematik 4 (1963)
420–453
5. Blaschek, G.; Pomberger, G.; Ritzinger, F.: Einführung
in die Programmierung mit Modula-2. Berlin: Springer
1990
6. Bibliotheken für numerische Mathematik:
http://www.netlib.org
7. Böszörményi, L.; Weich, C.: Programmieren mit
Modula-3. Berlin: Springer 1995
8. Bratko, I.: Prolog programming for artificial intelligence. 3rd ed. Addison-Wesley 2000
9. Burkhart, H.: Parallele Programmierung. In: Rechenberg, P.; Pomberger, G.: Informatik-Handbuch. 4. Aufl.
München: Hanser 2006
10. Clocksin, W.F.; Mellish, C.S.: Programming in Prolog.
5th ed. Berlin: Springer 2003
11. Computeralgebra-System Maple:
http://www.maplesoft.com
12. Computeralgebra-System MATLAB.
http://www.mathworks.com
13. Computeralgebra-System Reduce.
http://www.reduce-algebra.com
14. DIN 66028: Programmiersprache COBOL
15. DIN EN 27185: Informationstechnik; Programmiersprachen; Pascal
16. DIN EN 29899: Programmiersprachen; C
17. DIN ISO/IEC 8652: Informationstechnik; Programmiersprachen; Ada
18. Gibson, R.G.; Bergin, T.J.: History of programming
languages II. Addison-Wesley 1996
19. Goldberg, A.; Robson, D.: Smalltalk-80. Reading,
Mass.: Addison-Wesley 1995
20. Gosling, J.; u.a.: The Java language specification. 3rd
ed. Reading: Addison-Wesley 2005
21. Hejlsberg, A.; Torgersen, M.; Wiltamuth, S.; Golde,
P.: The C# Programming Language. 4th ed. AddisonWesley Professional 2010
22. Herrtwich, R.G.; Hommel, G.: Nebenläufige Programme. 2. Aufl. Berlin: Springer 1994
23. Hinze, R.: Einführung in die funktionale Programmierung mit Miranda. Stuttgart: Teubner 1992
24. HTML-Spezifikation: http://dev.w3.org/html5/spec/
spec.html
25. Huber, T.C.: Silverlight 4: Das umfassende Handbuch.
Galileo Computing 2010
26. IMSL-Bibliothek: http://www.roguewave.com/
products/imsl-numerical-libraries.aspx
27. ISO/IEC 10514-1 Informationstechnik; Programmiersprachen; Modula-2
28. ISO/IEC 13211-1 Informationstechnik; Programmiersprachen; Prolog
29. ISO/IEC 14882 Informationstechnik; Programmiersprachen; C++
30. IISO/IEC 1539-1:2004; Information technology; Programming languages; Fortran
31. Jensen, K.; Wirth, N.: Pascal user manual and report
(revised for the ISO Pascal standard). 4th ed. New
York: Springer 1991
32. Kernighan, B.W.; Ritchie, D.M.: Programmieren in C.
2. Aufl. München: Hanser 1990
33. Koch, S.: JavaScript: Einführung, Programmierung
und Referenz. 6. Aufl. dpunkt.verlag 2011
34. Koepf, W.; u.a.: Mathematik mit DERIVE. Braunschweig: Vieweg 1993
35. Lerdorf R.; Bergmann, S.; Hicking, G.: PHP kurz und
gut. 3. Aufl. O’Reilly 2006
36. Melzer, I.: Service-orientierte Architekturen mit Web
Services. 4. Aufl. Spektrum Akademischer Verlag
2010
37. Metcalf, M.; Reid, J.; Cohen, M.: Fortran 95/2003 explained. Oxford University Press 2004
38. NAG-Bibliothek:
http://www.nag.com
39. Mössenböck, H.: Objektorientierte Programmierung in
Oberon-2. 3. Aufl. Berlin: Springer 1998
40. Mössenböck, H.: Sprechen Sie Java? 4. Aufl. Heidelberg: dpunkt 2011
41. Mössenböck, H.: Kompaktkurs C# 4.0. 3. Aufl. Heidelberg: dpunkt 2009
Literatur
42. O’Sullivan, B.; Goerzen, J.; Stewart, D.: Real World
Haskell. O’Reilly 2008
43. Perrot, R.H.; Zarea-Aliabadi, A.: Supercomputer languages. Computing Surveys 18 (1986) 5–22
44. Reiser, M.; Wirth, N.: Programmieren in Oberon. Korr.
Nachdr. Bonn: Addison-Wesley Longman 1997
45. Schwichtenberg, H.: Microsoft ASP.NET 4.0 mit Visual C# 2010. Microsoft Press 2011
46. Steele, G.L.: Common LISP. 2nd ed. Bedford, Mass.:
Digital Pr. 1990
47. Stoyan, H.; Görz, G.: LISP. 1. korr. Nachdruck, Berlin:
Springer 1986
48. Stroustrup, B.: The C++ programming language. 3rd
ed. Amsterdam: Addison-Wesley Professional 2000
49. Wexelblat, R.L. (Ed.): History of programming languages. New York: Academic Pr. 1981
50. Wirth, N.: Programmieren in Modula-2. 2. Aufl. Berlin: Springer 1991
51. Wißmann, D.: JavaServer Pages: Dynamische Websites mit JSP erstellen. 2. Aufl. W3L GmbH 2009
52. Wolfram, S.: The Mathematica book. 4th ed. Cambridge University Press 1999
Spezielle Literatur zu Kapitel 12
1. Beck, K.: Extreme programming explained. AddisonWesley 2000
2. Beck, K.: JUnit pocket guide. O’Reilly 2004
3. Boehm, B.: A spiral model of software development
and enhancement. IEEE Computer 21 (5) 61-72
4. Dijkstra, E. W.: Go to statement considered harmful.
Commun. ACM 11 (1968) 147-148
5. Gilb, T.; Graham, D.: Software inspection. AddisonWesley 1993
6. Gries, D.: The science of programming. Springer 1989
7. Kappel, G.; Hitz, M.; Retschitzegger, W.; Kapsammer,
E.: UML@work. dpunkt 2005
8. McCabe, T.: A complexity measure. IEEE Trans. Software Engineering SE-2 (1976) 308–320
9. Myers, G.; J., Sandler, C.; Badgett, T.; Thomas, T.M.::
The art of software testing. 2nd ed. Wiley 2004
10. Parnas, D.L.: On the criteria to be used in decomposing systems into modules. Commun. ACM 15 (1972)
1053–1058
11. Pomberger, G.; Bischofberger, W.: Prototypingoriented software development. Berlin: Springer
1992
12. Pomberger, G.; Pree, W.: Software Engineering,
Architektur-Design und Prozessorientierung. 3. Aufl.
München: Hanser 2004
13. Schwaber, K.; Beedle, M.: Agile software development
with Scrum. Prentice Hall 2002
14. Shneiderman, B.; Plaisant, C.; Cohen, M.; Jacobs, S.:
Designing the user interface. 5th ed. Addison-Wesley
2009
15. Spillner, A.; Linz, T.: Basiswissen Softwaretest.
3. Aufl. dpunkt 2005
16. Wirth, N.: Program development by stepwise refinement. Commun. ACM 14 (1971) 221–227
17. Wirth, N.: On the composition of well-structured programs. Computing Surveys 6 (1974) 247–259
173