14 Systemprogrammierung

14.1 Programmablauf und Prozessmanagement unter Linux

14.1.1 Prozessinitialisierung

Unter einem Linux-Betriebssystem beginnt die Ausführung eines Programms, wenn eine Anforderung zum Starten eines Prozesses vom Benutzer oder einem anderen Prozess erfolgt. Das Betriebssystem initiiert diesen Prozess durch das Laden des Programmcodes vom Massenspeicher in den Arbeitsspeicher. Dieser Vorgang wird oft als “Forking” eines Prozesses bezeichnet, gefolgt von einem “Exec”-Aufruf, um das Programm im neuen Prozess auszuführen. Der “Exec”-Aufruf ersetzt den bisherigen Programmcode des Prozesses durch den neuen Programmcode, der ausgeführt werden soll.

14.1.2 Prozessraum

Der Prozessraum, auch Prozessadressraum genannt, ist ein virtueller Speicherbereich, der einem Prozess vom Betriebssystem zugewiesen wird. Dieser Bereich ist isoliert von anderen Prozessen, um die Integrität und Sicherheit der Prozessausführung zu gewährleisten. Er enthält den ausführbaren Code, die Daten (sowohl initialisierte als auch nicht initialisierte), den Heap (für dynamisch allokierte Daten) und den Stack (für die Speicherung von Funktionsaufrufen, lokalen Variablen usw.).

14.1.3 Task-Wechsel und CPU-Zuweisung

Ein Task-Wechsel, oder Kontextwechsel, tritt auf, wenn das Betriebssystem entscheidet, dass ein anderer Prozess CPU-Zeit erhalten soll. Dieser Wechsel wird durch einen Timer-Interrupt ausgelöst, der sicherstellt, dass ein Prozess nicht die CPU monopolisiert. Wenn der Timer-Interrupt auftritt, unterbricht das Betriebssystem den aktuellen Prozess, speichert dessen Zustand (einschließlich des Instruction Pointers und anderer Register) und lädt den Zustand des nächsten auszuführenden Prozesses. Der Instruction Pointer (IP) ist dabei zentral, da er die Adresse der nächsten auszuführenden Anweisung im Programmspeicher angibt.

14.1.4 Instruction Pointer und Befehlsausführung

Der Instruction Pointer wird mit jedem Taktzyklus der CPU inkrementiert, um durch den Programmcodes zu navigieren, es sei denn, eine Sprunganweisung (bedingt oder unbedingt) ändert seinen Wert. Sprünge sind essentiell, um Schleifen, Verzweigungen und Funktionsaufrufe zu realisieren. Ein unbedingter Sprung ändert den Instruction Pointer ohne Bedingungen, während ein bedingter Sprung dies abhängig vom Ergebnis einer vorherigen Operation tut. Dies ermöglicht eine nicht-lineare Ausführung des Codes.

Dieses Sequenzdiagramm skizziert den Weg, den ein Befehl in einem Computersystem nimmt, von der Ausführung bis zur Manipulation von Speicherzellen:

14.1.5 Kernel- und User-Mode

Linux verwendet zwei Betriebsmodi, um die Sicherheit und Stabilität des Systems zu gewährleisten:

Dieses Diagramm veranschaulicht die Unterscheidung und Interaktion zwischen Kernel- und Usermode sowie deren Verhältnis zu Systemaufrufen und Hardware-Ressourcen:

14.1.6 Preemptives Multitasking

Preemptives Multitasking wird durch die Trennung von Prozessen in User- und Kernel-Mode ermöglicht. Ein Timer unterbricht regelmäßig die Prozessausführung, um dem Betriebssystem die Kontrolle zurückzugeben und so den Wechsel zwischen verschiedenen Prozessen zu ermöglichen. Dies stellt sicher, dass kein einzelner Prozess die Systemressourcen monopolisieren kann und dass das System responsiv bleibt. Prozesse im User-Mode können den Timer für den Task-Wechsel nicht beeinflussen, was eine faire Verteilung der CPU-Zeit zwischen allen Prozessen gewährleistet.

14.1.7 Befehlssätze von CPUs

CPUs wie Intel (x86, x86_64 Architekturen) oder ARM basieren auf einem Satz von Instruktionen, bekannt als Befehlssätze. Diese Befehlssätze definieren, welche Operationen eine CPU ausführen kann. Sie umfassen eine Vielzahl von Befehlen, von einfachen Datenbewegungen bis hin zu komplexen mathematischen Operationen. Im Folgenden eine Übersicht über die grundlegenden Kategorien von Befehlen und Beispiele für jede Kategorie:

Kategorie Beschreibung Beispiele
Datenbewegung Übertragen von Daten zwischen CPU-Registern, Speicher und I/O-Geräten. MOV (Move), PUSH, POP
Arithmetische Operationen Ausführung grundlegender mathematischer Operationen. ADD (Addition), SUB (Subtraktion)
Logische Operationen Ausführung bitweiser logischer Operationen. AND, OR, XOR, NOT
Steuerungsfluss Steuerung der Ausführungsreihenfolge von Instruktionen. JMP (Jump), CALL, RET (Return)
Vergleiche und Sprünge Vergleich von Werten und Ausführung von Sprüngen basierend auf diesen Werten. CMP (Compare), JE (Jump if Equal)
Gleitkommaoperationen Ausführung von Operationen mit Gleitkommazahlen. FADD (Floating-Add), FMUL (Floating-Multiply)
String-Operationen Spezielle Befehle für die Verarbeitung von Zeichenketten. MOVS (Move String), CMPS (Compare String)
Bitmanipulation Operationen zur Manipulation einzelner Bits. BT (Bit Test), BTS (Bit Test and Set)
Systemaufrufe Ausführung von systemnahen Operationen, wie Speicherverwaltung. SYSCALL (System Call in x86_64)

Diese Befehle ermöglichen es der CPU, eine breite Palette von Aufgaben auszuführen, von einfachen Berechnungen bis hin zur Interaktion mit Hardwarekomponenten. Die spezifische Implementierung und Verfügbarkeit dieser Befehle kann je nach CPU-Architektur und Generation variieren. Intel- und ARM-basierte Architekturen haben ihre eigenen spezifischen Befehlssätze, die optimiert sind für Leistung und Energieeffizienz entsprechend ihrer Einsatzgebiete, wie Desktop-Computer, Server oder mobile Geräte.

14.1.8 Registertypen in einer CPU

CPUs enthalten verschiedene Typen von Registern, die für spezifische Aufgaben innerhalb der Prozessorarchitektur verwendet werden. Jedes Register dient einem einzigartigen Zweck, von der Speicherung der nächsten auszuführenden Anweisungsadresse bis hin zur Aufnahme von Rechenergebnissen. Im Folgenden finden Sie eine Tabelle, die die verschiedenen Registertypen, ihre Funktionen und Beispiele umfasst:

Registertyp Funktion Beispiele
Allgemeine Register Vielseitig verwendbar für arithmetische Operationen, Datentransfers und Zwischenspeicherung. AX, BX, CX, DX (x86-Architektur)
Indexregister Unterstützung bei der Adressierung von Speichersegmenten und Arrays. SI (Source Index), DI (Destination Index)
Basisregister Bereitstellung von Basisadressen für Speichersegmente. BP (Base Pointer)
Stack-Register Verwaltung des Aufrufstacks; beinhaltet Stack Pointer und Frame Pointer. SP (Stack Pointer), BP (Base Pointer)
Befehlszähler (PC) Hält die Adresse des nächsten auszuführenden Befehls. IP (Instruction Pointer), PC (Program Counter)
Status- und Steuerregister Speichern von Flags und Steuerinformationen zur Prozesssteuerung und Ausführungszuständen. EFLAGS (x86-Architektur)
Segmentregister Ermöglichen den Zugriff auf verschiedene Speichersegmente. CS (Code Segment), DS (Data Segment)
Floating-Point-Register Spezialisiert auf die Speicherung von Gleitkommazahlen und deren Berechnungen. ST(0) bis ST(7) im x87 FPU Stack
SIMD-Register Verwendung für Single Instruction, Multiple Data (SIMD) Operationen zur parallelen Berechnung. XMM, YMM, ZMM (Intel-Architekturen)
Steuerregister Steuerung der CPU-Operationen, wie Speichermanagement und Zugriffsrechte. CR0, CR3 (Control Registers in x86)
Basis-Adressregister Verwendung in Mikrocontrollern für die Adressierung von Spezialfunktionseinheiten. Abhängig von Mikrocontroller-Typ

Diese Register spielen eine zentrale Rolle in der Funktionsweise einer CPU, indem sie die notwendigen Mechanismen für die Ausführung von Programmen, Speicherverwaltung, und die Interaktion mit anderen Systemkomponenten bereitstellen. Die genaue Anzahl und Art der Register kann je nach CPU-Architektur variieren.

14.2 Maschinensprache

Hier ist ein einfaches Pseudo-Assembler-Programm, das verschiedene Arten von Befehlen illustriert. Dieses Beispiel soll ein grundlegendes Verständnis der Funktionsweise und der Einsatzgebiete verschiedener Befehlstypen in Assembler bieten. Es zeigt Datenbewegungen, arithmetische und logische Operationen, Steuerungsfluss und einfache Vergleichsoperationen. Beachte, dass die Syntax und die spezifischen Befehle je nach Assembler und Prozessorarchitektur variieren können.

; Pseudo-Assembler-Programm, das verschiedene Befehlstypen demonstriert

; Initialisierung von Daten
MOV R1, 10      ; Datenbewegung: Lädt den Wert 10 ins Register R1
MOV R2, 20      ; Datenbewegung: Lädt den Wert 20 ins Register R2

; Arithmetische Operationen
ADD R3, R1, R2  ; Addition: Addiert die Inhalte von R1 und R2, speichert das Ergebnis in R3
SUB R4, R2, R1  ; Subtraktion: Subtrahiert R1 von R2, speichert das Ergebnis in R4

; Logische Operationen
AND R5, R1, R2  ; Logisches UND: Führt ein bitweises UND zwischen R1 und R2 durch, speichert das Ergebnis in R5
OR  R6, R1, R2  ; Logisches ODER: Führt ein bitweises ODER zwischen R1 und R2 durch, speichert das Ergebnis in R6

; Vergleiche und Sprünge
CMP R1, R2      ; Vergleich: Vergleicht die Inhalte von R1 und R2
JE  EQUAL       ; Bedingter Sprung: Springt zur Marke EQUAL, wenn R1 gleich R2 ist

; Steuerungsfluss
JMP END         ; Unbedingter Sprung: Springt zur Marke END

EQUAL:
MOV R7, 1       ; Wenn R1 gleich R2 ist, setzen wir R7 auf 1

END:
NOP            ; No Operation: Führt keine Operation durch, Endpunkt des Programms

; Das Programm demonstriert grundlegende Befehle für Datenbewegung,
; arithmetische und logische Operationen, sowie Steuerungsfluss.

In diesem Pseudo-Programm wird gezeigt, wie Daten in Register geladen, einfache arithmetische und logische Berechnungen durchgeführt, Bedingungen geprüft und Steuerungsflüsse auf Basis dieser Bedingungen gesteuert werden. Es dient lediglich als Beispiel und kann in einer echten CPU-Architektur in dieser Form nicht direkt ausgeführt werden.

14.3 ELF File Format

Die Umwandlung eines Assembler-Programms in Maschinencode und dessen Darstellung im ELF (Executable and Linkable Format) Format ist ein komplexer Prozess, der von vielen Faktoren wie der CPU-Architektur und dem verwendeten Assembler oder Compiler abhängt. Hier ist eine stark vereinfachte Darstellung, wie das vorherige Beispielprogramm in Hexadezimalform aussehen könnte, wenn es in ein ELF-Format kompiliert und dann in einer hexadezimalen sowie einer binären Darstellung gezeigt wird. Beachten Sie, dass dies eine hypothetische Darstellung ist und die tatsächlichen Opcode-Werte und Formate je nach Prozessorarchitektur und Compiler variieren.

14.3.1 Hexadezimale Darstellung (kanonisch)

Angenommen, die Opcodes für die Befehle in unserem Beispielprogramm sehen in einer fiktiven CPU-Architektur wie folgt aus (stark vereinfacht und hypothetisch):

MOV R1, #10  -> 0xA110
MOV R2, #20  -> 0xA220
ADD R3, R1, R2 -> 0xB312
SUB R4, R2, R1 -> 0xC421
AND R5, R1, R2 -> 0xD512
OR  R6, R1, R2 -> 0xE612
CMP R1, R2      -> 0xF102
JE  EQUAL       -> 0x9000 (angenommen, EQUAL ist bei 0x00)
JMP END         -> 0x8000 (angenommen, END ist bei 0x00)
EQUAL: MOV R7, 1 -> 0xA701
NOP             -> 0x0000

Diese Opcodes würden dann, nachdem sie durch einen Compiler in das ELF-Format übersetzt wurden, in einer hexadezimalen Darstellung im Dateisystem gespeichert. Ein Hex-Editor könnte die Datei wie folgt anzeigen (stark vereinfacht):

A1 10 A2 20 B3 12 C4 21 D5 12 E6 12 F1 02 90 00 A7 01 00 00

14.3.2 Binäre Darstellung

Hexadezimale Werte lassen sich direkt in binäre Werte umwandeln. Die ersten zwei Befehle unseres Programms würden in Binärform wie folgt aussehen:

14.3.3 Erläuterung

Die CPU interpretiert den Maschinencode, den sie aus dem Arbeitsspeicher liest, direkt. Die hexadezimale Form ist für Menschen leichter zu lesen und zu verstehen als binäre Zahlenfolgen, aber die CPU arbeitet mit diesen binären Daten. Wenn das Programm zur Ausführung kommt, werden die binären Befehle von der CPU gelesen und entsprechend der Architekturspezifikationen verarbeitet.

Es ist wichtig zu verstehen, dass die oben genannten Hex- und Binärwerte hypothetisch sind. In der Realität beinhaltet die Übersetzung von Assembler-Code in Maschinencode die Berücksichtigung von Speicheradressen, Relokationen, Symbolauflösungen und vielen anderen Aspekten, die durch das ELF-Format und den Linker verwaltet werden.

Die Darstellung in Hexadezimal- und Binärform bietet einen Einblick, wie ein Programm in niedriger Ebene repräsentiert wird, bevor es von der CPU ausgeführt wird. Dieser Prozess der Übersetzung und Darstellung ist essentiell für das Verständnis der Computerarchitektur und Programmierung auf niedriger Ebene.

14.4 Code und Daten

Neben dem ausführbaren Code eines Programms, der aus einer Reihe von Maschinenbefehlen besteht, belegt ein Programm auch Speicherbereiche für Daten. Diese Daten können Variablen, Konstanten, Arbeitspuffer und andere für die Ausführung des Programms notwendige Informationen umfassen. Die korrekte Trennung und Verwaltung von Code und Daten im Speicher ist von entscheidender Bedeutung für die sichere und fehlerfreie Ausführung eines Programms.

14.4.1 Wichtigkeit der Trennung von Code und Daten

Es ist essentiell, dass Programmierer beim Schreiben von Software keine Fehler machen, die dazu führen, dass der Programmablauf versehentlich in den Datenteil des Speichers springt und dort gespeicherte Werte als Befehle interpretiert. Solche Fehler können aus einer Reihe von Ursachen entstehen, einschließlich, aber nicht beschränkt auf:

Wenn der Programmablauf auf Daten trifft, die als Befehle interpretiert werden, resultiert das meist in Befehlen, die für die CPU keinen Sinn ergeben. Dies kann zufällig zu sinnvollen Operationen führen, aber aufgrund stochastischer Gründe ist es viel wahrscheinlicher, dass solche “Befehle” zu unerwartetem Verhalten oder Fehlern führen.

14.4.2 Fehler und deren Folgen

Ein häufiges Resultat solcher Fehler ist der sogenannte “Segmentfehler” (Segmentation Fault) unter Unix-basierten Betriebssystemen oder ein “Zugriffsverletzungsfehler” (Access Violation) unter Windows. Diese Fehler treten auf, wenn ein Programm versucht, auf Speicher zuzugreifen, der nicht zu ihm gehört oder der nicht auf die erwartete Weise genutzt werden kann (z.B. Schreibzugriff auf einen nur lesbaren Bereich oder Ausführung von Daten als Code).

Ein Segmentfehler führt in der Regel dazu, dass das Betriebssystem das fehlerhafte Programm sofort beendet, um den restlichen Systembetrieb zu schützen. Das Betriebssystem nutzt dazu Mechanismen wie die Speicherschutzsegmentierung und die Seitenfehlerbehandlung, um ungültige Speicherzugriffe zu erkennen und entsprechend zu reagieren.

14.4.3 Sicherheitsimplikationen

Neben der Gefahr von Abstürzen und unvorhersehbarem Verhalten öffnen Fehler, die eine Ausführung von Daten als Code ermöglichen, auch potenzielle Sicherheitslücken. Angreifer könnten gezielt Pufferüberläufe oder andere Schwachstellen ausnutzen, um schädlichen Code in den Speicher eines Programms einzuschleusen und diesen dann zur Ausführung bringen.

Um solche Fehler zu vermeiden und die Sicherheit von Software zu erhöhen, wenden Entwickler verschiedene Techniken an, darunter:

Diese Maßnahmen helfen, die korrekte Trennung von Code und Daten zu gewährleisten und die Ausführung von Programmen sicherer zu machen.

14.4.4 Paging

Paging ist eine Speicherverwaltungstechnik, die in modernen Betriebssystemen verwendet wird, um die Nutzung des physikalischen Speichers effizienter zu gestalten. Dabei wird der virtuelle Speicher eines Computers in gleich große Blöcke, sogenannte Seiten (pages), unterteilt, während der physikalische Speicher in Rahmen (frames) derselben Größe aufgeteilt wird. Eine Seite kann an jeden beliebigen Rahmen im physikalischen Speicher zugewiesen werden, und diese Zuweisung wird in der Seitentabelle (page table) des Prozesses vermerkt.

Das Betriebssystem verwaltet die Zuordnung zwischen virtuellen Adressen und physischen Adressen. Wenn ein Programm auf eine Speicheradresse zugreift, übersetzt das Betriebssystem die virtuelle Adresse in eine physische Adresse mithilfe der Seitentabelle. Wenn eine Seite im physischen Speicher nicht vorhanden ist (ein sogenannter Page Fault), lädt das Betriebssystem die benötigte Seite vom sekundären Speicher (in der Regel eine Festplatte oder SSD) in den physischen Speicher.

14.4.5 Swapping

Swapping, auch Auslagerung genannt, ist ein Prozess, bei dem ein Betriebssystem Teile des Arbeitsspeichers (RAM) auf einen sekundären Speicher auslagert, um mehr RAM für laufende Prozesse freizumachen. Dies ist besonders nützlich in Situationen, in denen der physische Arbeitsspeicher vollständig belegt ist. Durch das Auslagern von Seiten, die momentan nicht benötigt werden (z.B. Teile eines Prozesses, der momentan wartet oder im Hintergrund läuft), kann das Betriebssystem den begrenzten physikalischen Speicher effektiver nutzen und mehr Prozesse gleichzeitig unterstützen.

14.4.6 Bedeutung von Paging und Swapping

Die Kombination aus Paging und Swapping ermöglicht es Betriebssystemen, den verfügbaren Arbeitsspeicher effizienter zu nutzen und gibt ihnen die Möglichkeit, mehr Programme gleichzeitig auszuführen, als physischer Speicher vorhanden ist. Dies wird als virtuelle Speicherverwaltung bezeichnet. Sie macht die physikalische Größe des Arbeitsspeichers weniger kritisch für den Systembetrieb, da durch das Swapping effektiv ein größerer virtueller Adressraum geschaffen wird, der über die Größe des tatsächlichen physischen Speichers hinausgeht.

14.4.7 Herausforderungen und Auswirkungen

Obwohl Paging und Swapping die Speichernutzung optimieren und den Betrieb mit begrenztem RAM ermöglichen, können sie auch die Systemleistung beeinträchtigen, insbesondere wenn das Swapping intensiv genutzt wird. Der Zugriff auf den sekundären Speicher ist wesentlich langsamer als der Zugriff auf den physischen Arbeitsspeicher, was zu merklichen Leistungseinbußen führen kann, wenn häufig auf ausgelagerte Seiten zugegriffen wird. Moderne Betriebssysteme implementieren daher verschiedene Strategien zur Speicherverwaltung, um die Notwendigkeit des Swappings zu minimieren und die Auswirkungen auf die Systemleistung zu begrenzen.

Dieses Diagramm stellt die Beziehung zwischen virtuellem Speicher, Paging und Swapping sowie deren Interaktion mit dem physischen Speicher und der CPU dar:

14.5 Interrupt Service Routinen auf Assembler-Ebene

Interrupt Service Routinen (ISR) sind essentielle Komponenten in der Systemprogrammierung, insbesondere bei der Entwicklung von Betriebssystemen oder hardwarenaher Software. Auf Assembler-Ebene ermöglichen ISRs die Handhabung von Hardware-Interrupts oder softwareausgelösten Unterbrechungen. Der Prozess involviert mehrere Schlüsselschritte und Konzepte:

  1. Interrupt Request (IRQ): Ein Hardware- oder Software-Event löst einen Interrupt Request aus. Bei Hardware-Interrupts kann dies durch externe Geräte wie Tastaturen, Netzwerkkarten oder Timer erfolgen. Software-Interrupts werden durch spezielle Interrupt-Befehle im Code ausgelöst.

  2. Interrupt-Vektor-Tabelle: Diese im Speicher residente Tabelle enthält Adressen (Zeiger) der ISRs. Jeder Eintrag in der Tabelle korrespondiert mit einem spezifischen Interrupt. Wenn ein Interrupt ausgelöst wird, verwendet der Prozessor diese Tabelle, um die Adresse der zugehörigen ISR zu finden.

  3. Speichern des Kontexts: Bevor die ISR ausgeführt wird, speichert der Prozessor den aktuellen Zustand des Programms, einschließlich des Instruction Pointers und anderer wichtiger Register. Dieser Schritt ist entscheidend, um nach Abschluss der ISR die Ausführung des unterbrochenen Programms fortsetzen zu können.

  4. Ausführung der ISR: Der Prozessor springt zur Adresse der ISR, wie in der Interrupt-Vektor-Tabelle definiert. Die ISR führt dann die notwendigen Aktionen aus, um den Interrupt zu bedienen. Dies kann das Lesen eines Datenwerts von einem Gerät, die Aktualisierung von Systemstrukturen oder die Signalisierung an andere Teile des Systems umfassen.

  5. Wiederherstellen des Kontexts: Nachdem die ISR ihre Aufgabe erfüllt hat, stellt der Prozessor den ursprünglichen Kontext des unterbrochenen Programms wieder her, indem er die zuvor gespeicherten Registerwerte lädt.

  6. Rückkehr zum unterbrochenen Programm: Mit einem speziellen Befehl wie IRET (Interrupt Return) im x86 Assembler, kehrt der Prozessor zur Ausführung des ursprünglichen Programms zurück, als wäre es nie unterbrochen worden.

Auf Assembler-Ebene erfordert die Implementierung und das Management von ISRs ein tiefes Verständnis der Hardware, der Prozessorarchitektur und der Konventionen des verwendeten Betriebssystems. Die Entwicklung von ISR beinhaltet das direkte Manipulieren von CPU-Registern und das sorgfältige Management von Ressourcen, um die Integrität des Systems zu wahren und unerwartetes Verhalten zu vermeiden.

14.5.1 Wichtige Typen von Interrupt Service Routinen (ISRs)

Interrupt Service Routinen (ISRs) sind essenzielle Komponenten in der System- und Anwendungsprogrammierung, die auf Ereignisse oder Signale von der Hardware oder Software reagieren. Sie können in verschiedene Kategorien eingeteilt werden, je nach Ursprung des Interrupts und der Art der Behandlung.

14.5.1.1 Hardware-Interrupts

14.5.1.2 Software-Interrupts

14.5.1.3 Ausnahmebehandlungen

14.5.2 Übersicht

Kategorie Typ Erläuterung
Hardware-Interrupts Externe Interrupts Reaktion auf Signale von externen Geräten.
Hardware-Interrupts Timer-Interrupts Von der Systemuhr erzeugt für regelmäßige Aktionen oder Multitasking.
Hardware-Interrupts DMA-Interrupts Informiert über den Abschluss von DMA-Vorgängen.
Software-Interrupts Systemaufrufe Anforderung von Kerneldiensten durch Anwendungen.
Software-Interrupts Signal-Interrupts Signale von Prozessen oder dem Betriebssystem, die spezielle Ereignisse anzeigen.
Ausnahmebehandlungen Fehler-Interrupts Ausgelöst durch Fehler oder Ausnahmesituationen im Code (z.B. Division durch Null).

Diese Übersicht zeigt, dass ISRs eine breite Palette von Funktionen und Reaktionen innerhalb eines Systems ermöglichen, von der einfachen Kommunikation mit Hardware bis hin zur Behandlung komplexer Fehlerzustände.

Das Sequenzdiagramm beschreibt den Prozess des Auslösens und der Behandlung von Interrupts. Dieses Beispiel fokussiert auf einen generischen Ablauf, bei dem ein Gerät einen Interrupt auslöst, der Prozessor diesen annimmt und eine Interrupt Service Routine (ISR) aufruft, um den Interrupt zu behandeln.

In diesem Diagramm:

Dieses Diagramm bietet einen grundlegenden Überblick über den Ablauf von Interrupts und deren Behandlung in einem computergestützten System.

14.6 Datenbewegung

Von Register zu Register: Manchmal müssen Daten innerhalb der CPU verschoben werden, um Berechnungen durchzuführen oder den Programmfluss zu steuern. Dies kann bedeuten, dass Daten von einem Register in ein anderes verschoben werden, um Platz zu schaffen für neue Daten oder um verschiedene Teile eines Befehls zu kombinieren.

Von Register zum Speicher: Nachdem die CPU Daten verarbeitet hat, müssen die Ergebnisse möglicherweise wieder zurück in den Speicher geschrieben werden, damit sie für zukünftige Verwendungen gespeichert werden können. Dies könnte beispielsweise das Speichern eines berechneten Ergebnisses oder das Aktualisieren eines Werts in einem bestimmten Speicherbereich umfassen.

Zu und von I/O-Geräten: Die CPU kommuniziert auch mit Ein- und Ausgabegeräten wie Tastaturen, Mäusen und Festplatten. Wenn beispielsweise ein Benutzer eine Taste drückt, muss die CPU die Daten von der Tastatur abrufen und möglicherweise in den Speicher laden, um darauf zu reagieren. Umgekehrt kann die CPU Daten an Ausgabegeräte senden, um beispielsweise Informationen auf dem Bildschirm anzuzeigen oder auf die Festplatte zu schreiben.

“load” “store” “move” “push & pop” “exchange”

14.7 Arithmetische Operationen

Diese arithmetischen Befehle ermöglichen es der CPU, mathematische Berechnungen direkt auszuführen, ohne dass externe Programme oder Unterprogramme benötigt werden. Dies führt zu einer effizienteren Verarbeitung von Daten und Programmen.

“Addition” “Subtraktion” “Multiplikation” “Division”

14.8 Logische Operationen

Operation Beschreibung
AND (UND) Führt eine bitweise logische UND-Operation aus. Das Ergebnis ist 1, wenn beide Bits in den Operanden 1 sind, andernfalls ist das Ergebnis 0.
OR (ODER) Führt eine bitweise logische ODER-Operation aus. Das Ergebnis ist 1, wenn mindestens eines der Bits in den Operanden 1 ist, andernfalls ist das Ergebnis 0.
XOR (Exklusives ODER) Führt eine bitweise exklusive ODER-Operation aus. Das Ergebnis ist 1, wenn genau eines der Bits in den Operanden 1 ist, andernfalls ist das Ergebnis 0.
NOT (NICHT) Führt eine bitweise logische NICHT-Operation auf einem Operanden aus. Jede 1 wird zu einer 0 und jede 0 wird zu einer 1 umgewandelt.
Shift (Verschieben) Ermöglicht das Verschieben von Bits in einem Operanden nach links oder rechts. Beim Linksverschieben werden Nullen in die leeren Stellen eingefügt, beim Rechtsverschieben werden die leeren Stellen mit Nullen oder dem Wert des höchstwertigen Bits gefüllt.

14.9 Steuerungsfluss

Diese Befehle steuern, wie und in welcher Reihenfolge Befehle ausgeführt werden und ermöglichen es der CPU, bedingte Anweisungen und Schleifen zu verarbeiten.

Steuerungsfluss-Befehle Beschreibung
Bedingte Sprungbefehle Diese Befehle ermöglichen es der CPU, je nach Bedingung den Programmfluss zu steuern. Zum Beispiel kann ein bedingter Sprungbefehl wie “Springe, wenn Bedingung erfüllt ist” die Ausführung an eine andere Stelle im Programm lenken, wenn eine bestimmte Bedingung wahr ist. Andernfalls wird die nächste Anweisung in der sequentiellen Reihenfolge ausgeführt.
Unbedingte Sprungbefehle Diese Befehle erzwingen einen Sprung zu einer bestimmten Speicheradresse, unabhängig von irgendwelchen Bedingungen. Das bedeutet, dass die CPU den Programmzähler direkt auf die angegebene Adresse setzen wird, um die Ausführung von dort fortzusetzen.
Schleifenbefehle Schleifen ermöglichen es, einen bestimmten Teil des Codes wiederholt auszuführen. Die CPU verwendet spezielle Schleifenbefehle wie “Schleife starten” und “Schleife beenden”, um die Wiederholung zu steuern. Diese Befehle ermöglichen es der CPU, den Programmfluss zurückzusetzen und den Code innerhalb der Schleife erneut auszuführen, bis eine bestimmte Bedingung erfüllt ist.
Unterbrechungen Unterbrechungen sind Ereignisse, die den normalen Ablauf eines Programms unterbrechen, um einen speziellen Code (Interrupt-Service-Routine) auszuführen. Die CPU kann Befehle zum Verwalten von Unterbrechungen enthalten, die es ermöglichen, den aktuellen Zustand des Prozesses zu speichern, zur Interrupt-Service-Routine zu springen und nach Abschluss der Routine zum normalen Programmfluss zurückzukehren.

14.10 Vergleiche und Sprünge

Vergleichsoperationen: Die CPU kann Befehle ausführen, um den Inhalt von Registern oder Speicheradressen zu vergleichen. Typischerweise gibt es Vergleichsbefehle für verschiedene Arten von Vergleichen, wie z.B. Gleichheit, Größer-als, Kleiner-als usw. Diese Befehle vergleichen die Werte und setzen Statusbits oder Flags entsprechend, um das Ergebnis des Vergleichs anzuzeigen.

Bedingte Sprungbefehle: Basierend auf den Ergebnissen von Vergleichen kann die CPU bedingte Sprungbefehle ausführen. Diese Befehle bewirken einen Sprung zu einer anderen Speicheradresse, wenn eine bestimmte Bedingung erfüllt ist. Zum Beispiel könnte ein bedingter Sprungbefehl wie “Springe, wenn das Statusbit für Gleichheit gesetzt ist” die Ausführung an eine bestimmte Adresse im Programm lenken, wenn der vorherige Vergleich ergab, dass die verglichenen Werte gleich sind.

Unbedingte Sprungbefehle: Neben bedingten Sprungbefehlen gibt es auch unbedingte Sprungbefehle, die einen Sprung zu einer bestimmten Speicheradresse erzwingen, unabhängig von irgendwelchen Bedingungen. Diese Befehle können verwendet werden, um den Programmfluss zu lenken, ohne auf das Ergebnis eines Vergleichs zu warten.

14.11 Bitmanipulation:

Bitmanipulationen sind besonders nützlich für die Implementierung von Algorithmen zur Datenkompression, Kryptographie, Signalverarbeitung und anderen Anwendungen, die auf der feinen Steuerung von Binärdaten basieren.

Operation Beschreibung
AND-Befehle Führt eine bitweise logische UND-Operation zwischen zwei Operanden aus.
OR-Befehle Führt eine bitweise logische ODER-Operation zwischen zwei Operanden aus.
XOR-Befehle Führt eine bitweise exklusive ODER-Operation zwischen zwei Operanden aus.
Shift-Befehle Ermöglichen das Verschieben von Bits in einem Datenwort nach links oder rechts.
NOT-Befehle Führen eine bitweise logische NICHT-Operation auf einem Operanden aus.

14.12 No Operation (NOP)

NOP steht für No Operation und ist ein Prozessorbefehl. Auf einer CPU bewirkt das Ausführen einer NOP-Instruktion nichts, außer dem Inkrementieren des Befehlszählers und dem Zeitverbrauch für das Holen dieses Befehls aus dem Arbeitsspeicher und seiner Interpretation.

Der Befehl wird verwendet, um: