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.
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.).
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.
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:
Linux verwendet zwei Betriebsmodi, um die Sicherheit und Stabilität des Systems zu gewährleisten:
Kernel-Mode (Supervisor Mode): Im Kernel-Mode hat der Prozess vollen Zugriff auf Hardware und kann alle CPU-Instruktionen ausführen. Dieser Modus wird für Operationen genutzt, die direkten Zugriff auf die Hardware erfordern, wie die Verwaltung des virtuellen Speichers und die Behandlung von Interrupts.
User-Mode: Im User-Mode ist der Zugriff eines Prozesses auf Hardware und kritische Systemressourcen stark eingeschränkt. Prozesse können nicht direkt auf den Hardware-Speicher zugreifen, ihre eigenen Basisadressen ändern oder Interrupt-Service-Routinen manipulieren. Dieser Modus ist für die normale Programmausführung vorgesehen und dient der Sicherheit des Gesamtsystems, indem er verhindert, dass fehlerhafte oder bösartige Programme das System beeinträchtigen.
Dieses Diagramm veranschaulicht
die Unterscheidung und Interaktion zwischen Kernel- und Usermode sowie
deren Verhältnis zu Systemaufrufen und Hardware-Ressourcen:
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.
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.
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.
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.
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.
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
Hexadezimale Werte lassen sich direkt in binäre Werte umwandeln. Die ersten zwei Befehle unseres Programms würden in Binärform wie folgt aussehen:
0xA110 (MOV R1, #10) in Binär:
1010 0001 0001 00000xA220 (MOV R2, #20) in Binär:
1010 0010 0010 0000Die 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.
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.
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.
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.
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.
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.
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.
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.
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:
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:
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.
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.
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.
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.
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.
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.
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.
Externe Interrupts: Reagieren auf Signale von externen Geräten wie Tastaturen, Mäusen, Netzwerkkarten. Sie sind typischerweise asynchron zum aktuellen Programmablauf.
Timer-Interrupts: Werden von der Systemuhr (Timer) erzeugt, um regelmäßige Aktionen zu planen oder Präemptives Multitasking zu ermöglichen.
DMA-Interrupts: Ausgelöst durch den Abschluss eines Direct Memory Access (DMA) Vorgangs, wodurch der Prozessor informiert wird, dass Datenübertragungen abgeschlossen sind.
Systemaufrufe: Spezielle Software-Interrupts, die von Anwendungen genutzt werden, um Kerneldienste wie Dateioperationen oder Netzwerkkommunikation anzufordern.
Signal-Interrupts: Von Prozessen gesendete oder vom Betriebssystem ausgelöste Signale, die Ereignisse wie beispielsweise den Zugriffsversuch auf einen geschützten Speicherbereich anzeigen.
| 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:
G) sendet ein Interrupt-Signal an den
Prozessor (P).ISR) auf.ISR führt die notwendigen Schritte zur Behandlung
des Interrupts durch.ISR ihre Arbeit abgeschlossen hat,
informiert sie den Prozessor darüber, dass die Interrupt-Behandlung
abgeschlossen ist.Dieses Diagramm bietet einen grundlegenden Überblick über den Ablauf von Interrupts und deren Behandlung in einem computergestützten System.
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”
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”
| 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. |
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. |
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.
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. |
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:
ein bestimmtes Alignment im Speicher herzustellen
bestehende Befehle im direkten Maschinencode zu „löschen“ indem sie mit NOP-Befehlen überschrieben werden,
eine – normalerweise allerdings nur grob vorhersagbare – Zeitverzögerung zu bewirken (ggf. in einer Schleife mehrfach aufgerufen), beispielsweise um durch Race Conditions ausgelöste Fehler zu vermeiden oder das Timing eines externen Signals zu definieren
Pipeline-Hazards zu verhindern
einen Branch Delay Slot zu füllen oder
als „Füllmaterial“ bei nicht genutztem Speicher (z. B. BIOS, ROMs oder eingebetteten Systemen)
als „Füllmaterial“ um bei Exploits durch einen Overflow eine bessere Trefferquote zu erzielen