Axel Bruns

Die Geschichte des Computers


Скачать книгу

      Level-1-Cache (L1-Cache): Dieser Cache läuft mit dem Prozessortakt. Er ist sehr klein (etwa 4 bis 256 Kilobyte), dafür aufgrund seiner Position im Prozessorkern selbst sehr schnell abrufbar.

      Level-2-Cache (L2-Cache): Der L2-Cache befindet sich meist im Prozessor, aber nicht im Kern selbst. Er umfasst zwischen 64 Kilobyte und 12 Megabyte.

      Level-3-Cache (L3-Cache): Bei Mehrkernprozessoren teilen sich die einzelnen Kerne den L3-Cache.

      Er ist der langsamste der drei Caches, aber auch der größte (bis zu 256 Megabyte).Um die Rollen der Untereinheiten konkreter zu veranschaulichen, hier der Ablauf der Verarbeitung eines einzelnen Maschinenbefehls. Die aufgeführten Einzelschritte können teilweise gleichzeitig oder überlappend ablaufen, die Nummerierung hat nichts mit der Anzahl der Taktzyklen zu tun, die der Befehl benötigt. Zusätzliche Feinheiten wie Prozessor-Pipelines oder Sprungvorhersage (Branch Prediction) führen zu weiteren Timing-Finessen, die hier im Sinne der Vereinfachung vorerst weggelassen werden. Aus dem gleichen Grund sind komplexe Berechnungen abhängig von der gewählten Adressierungsart zur Ermittlung einer endgültigen Speicheradresse nicht erwähnt.

      Der Befehlszähler wird auf die nächste Adresse weitergezählt. Der Inhalt wird vom Steuerwerk über das Bus-Interface auf den Adressbus gelegt, ein Leseimpuls wird signalisiert. Nach der Verzögerung durch die endliche Zugriffszeit des RAMs liegt an den Datenleitungen der Inhalt dieser Speicherzelle an. Das Steuerwerk kopiert diese Daten über das Bus-Interface in das Befehlsregister. Der Befehl wird vor-decodiert, ob er komplett geladen ist Wenn es ein Befehl ist, der aus mehreren Bytes besteht, werden sie (falls das durch eine größere Busbreite nicht schon geschehen ist) durch Wiederholung der Schritte 1 bis 3 aus dem Speicher geholt und in die zuständigen Prozessorregister kopiert. Gehört zum Befehl auch das Auslesen einer Speicherzelle des RAMs, wird vom Steuerwerk die Adresse für diese Daten auf die Adressleitungen gelegt, ein Leseimpuls wird signalisiert. Danach muss genügend lange Zeit gewartet werden, dass das RAM diese Informationen sicher bereitstellen konnte. Anschließend wird der Datenbus ausgelesen und in das zuständige Prozessorregister kopiert.

      Der Befehl wird fertig-dekodiert und die zu seiner Abarbeitung benötigten Untereinheiten aktiviert, die internen Datenpfade werden entsprechend geschaltet. Das Rechenwerk erledigt die eigentliche Verarbeitung innerhalb des Prozessors, beispielsweise die Addition zweier Registerinhalte. Das Ergebnis landet wieder in einem der Prozessorregister. Wenn der Befehl ein Sprung- oder Verzweigungsbefehl ist, wird das Ergebnis nicht in einem Datenregister abgelegt, sondern im Befehlszähler. Das Steuerwerk aktualisiert je nach Ergebniswert ggf. das Statusregister mit seinen Zustandsflags. Gehört zum Befehl auch das Rückspeichern eines Ergebnisses/Registerinhalts in das RAM, wird vom Steuerwerk die Adresse für diese Daten auf die Adressleitungen gelegt und der Dateninhalt auf die Datenleitungen, ein Schreibimpuls wird signalisiert. Danach muss genügend lange Zeit gewartet werden, dass das RAM diese Informationen sicher aufnehmen konnte. Der Befehl ist jetzt abgearbeitet, und es kann oben bei Schritt 1 zum nächsten Befehl weitergeschritten werden.Die beiden wesentlichen Grundarchitekturen für CPUs sind die Von-Neumann- und die Harvard-Architektur. Bei der nach dem Mathematiker

      John von Neumann benannten Von-Neumann-Architektur gibt es keine Trennung zwischen dem Speicher für Daten und Programmcode. Dagegen sind bei der Harvard-Architektur Daten und Programm(e) in strikt voneinander getrennten Speicher- und Adressräumen abgelegt, auf die typischerweise durch zwei separierte Bussysteme parallel zugegriffen wird.Beide Architekturen haben ihre spezifischen Vor- und Nachteile. Die Von-Neumann-Architektur ist einfacher, da Programmcode und Daten grundsätzlich identisch behandelt werden. Hierdurch sind einheitliche Betriebssystem-Routinen zum Laden und Speichern verwendbar. Auch kann sich der Programmcode im Gegensatz zur Harvard-Architektur selbst modifizieren, wodurch Maschinencode z. B. per Debugger leicht bearbeitbar und modifizierbar ist. Nachteile/Risiken dieser ‚Selbstmodifikation‘ liegen im Bereich der Softwareergonomie und -Stabilität, zum Beispiel können Laufzeitfehler wie Pufferüberlauf mit höherer Wahrscheinlichkeit auftreten.Durch die Trennung in zwei physikalische Speicher und Busse hat die Harvard-Architektur potenziell eine höhere Leistungsfähigkeit, da Daten- und Programmzugriffe parallel erfolgen können.

      Jedoch müssen Race Conditions bei solchen parallelen Zugriffen, die zu einem nicht-deterministischen Programmverhalten führen können, ausgeschlossen werden. Bei einer Harvard-Architektur sind durch die physische Trennung von Daten und Programm einfach eine Zugriffsrechtetrennung und Speicherschutz realisierbar. Um z. B. zu verhindern, dass bei Softwarefehlern Programmcode überschrieben werden kann, wurde (vor allem historisch) für Programmcode ein im Betrieb nur lesbarer Speicher (z. B. ROM, Lochkarten) verwendet, für die Daten dagegen schreib- und lesbarer Speicher (z. B. RAM, Ringkernspeicher).Praktisch alle modernen CPUs stellen sich aus Programmsicht als Von-Neumann-Architektur dar, ihr interner Aufbau entspricht aber aus Leistungsgründen in vielen Aspekten eher einer parallelen Harvard-Architektur. So ist es nicht unüblich, dass eine CPU intern mehrere unabhängige Datenpfade (insbesondere beim L1-Cache) und Cachehierarchiestufen besitzt, um mit möglichst vielen parallelen Datenpfaden eine hohe Leistung zu erzielen. Die dadurch potenziell möglichen Daten-Inkohärenzen und Zugriffs-Race-Conditions werden intern durch aufwändige Datenprotokolle und -management verhindert.Auch werden heutzutage Arbeitsspeicher-Bereiche, die ausschließlich Daten beinhalten, als nicht ausführbar markiert, sodass Exploits, die ausführbaren Code in Datenbereichen ablegen, diesen nicht ausführen können. Umgekehrt kann das Schreiben in Bereiche mit Programmcode verweigert werden (Pufferüberlauf-Exploits).Der Befehlssatz bezeichnet die Gesamtheit der Maschinenbefehle eines Prozessors.

      Der Umfang des Befehlssatzes variiert je nach Prozessortyp beträchtlich. Ein großer Befehlssatz ist typisch für Prozessoren mit CISC-Architektur (englisch Complex Instruction Set Computing - Rechnen mit komplexem Befehlssatz), ein kleiner Befehlssatz ist typisch für Prozessoren mit RISC-Prozessorarchitektur (englisch Reduced Instruction Set Computing - Rechnen mit reduziertem Befehlssatz).Die traditionelle CISC-Architektur versucht, immer mehr und immer komplexere Funktionen direkt durch Maschinenbefehle auszudrücken. Sie zeichnet sich besonders durch die große Anzahl zur Verfügung stehender Maschinenbefehle aus, die meist 100 (weit) überschreitet. Diese sind außerdem in der Lage, komplexe Operationen direkt auszuführen (etwa Gleitkommazahl-Operationen). Dadurch können komplexe Vorgänge durch wenige, „mächtige“ Befehle implementiert werden. Das Nebeneinander von komplexen (langwierigen) und einfachen (schnell ausführbaren) Befehlen macht ein effizientes Prozessordesign schwierig, besonders das Pipelinedesign.In den 1980er Jahren entstand als Reaktion darauf das RISC-Konzept, mit dem bewussten Verzicht auf das Bereitstellen von komplexer Funktionalität in Instruktionsform. Es werden ausschließlich einfache, untereinander ähnlich komplexe Instruktionen bereitgestellt. Dabei wird versucht, sehr schnell abzuarbeitende Befehle zur Verfügung zu stellen, dafür jedoch nur wenige (weniger als 100), sehr einfache. Hierdurch vereinfachte sich das Prozessordesign deutlich und ermöglichte Optimierungen, die üblicherweise eine höhere Prozessortaktung und wiederum schnellere Ausführungsgeschwindigkeit erlaubten.

      Dies geht unter anderem darauf zurück, dass weniger Taktzyklen benötigt werden und die Dekodierung aufgrund geringerer Komplexität schneller ist. Ein einfacheres Prozessordesign bedeutet jedoch eine Verschiebung des Entwicklungsaufwands hin zur Software als Bereitsteller komplexerer Funktionalität. Der Compiler hat nun die Aufgabe einer effizienten und korrekten Umsetzung mit dem vereinfachten Instruktionsatz.Heute ähnelt die weit verbreitete x86-Architektur – als (früher) typischer Vertreter der CISC-Klasse – intern einer RISC-Architektur: Einfache Befehle sind meist direkt µ-Operationen; komplexe Befehle werden in µ-Ops zerlegt. Diese µ-Ops ähneln den einfachen Maschinenbefehlen von RISC-Systemen, wie auch der interne Prozessoraufbau (z. B. gibt es keinen Microcode mehr für die µ-Operationen, sie werden „direkt verwendet“).Eine weitere Art eines Prozessordesigns ist die Verwendung von VLIW.

      Dort werden mehrere Instruktionen in einem Wort zusammengefasst. Dadurch ist vom Anfang an definiert, auf welcher Einheit welche Instruktion läuft. Out-of-Order-Ausführung, wie sie in modernen Prozessoren zu finden ist, gibt es bei dieser Art von Befehlen nicht.Zusätzlich unterscheidet man auch noch zwischen der Adressanzahl im Maschinenbefehl: 0-Adressbefehle (Stackrechner) 1-Adressbefehle (Akkumulatorrechner) 2-Adressbefehle (typischerweise CISC-Rechner) 3-Adressbefehle