Busfehler - Bus error

Beim Rechnen ist ein Busfehler ein Fehler , der von der Hardware ausgelöst wird und ein Betriebssystem (OS) darüber informiert, dass ein Prozess versucht, auf Speicher zuzugreifen , den die CPU nicht physisch adressieren kann: eine ungültige Adresse für den Adressbus , daher der Name. In der modernen Verwendung auf den meisten Architekturen sind diese viel seltener als Segmentierungsfehler , die hauptsächlich aufgrund von Verstößen gegen den Speicherzugriff auftreten: Probleme mit der logischen Adresse oder Berechtigungen.

Auf POSIX- kompatiblen Plattformen führen Busfehler normalerweise dazu, dass das SIGBUS-Signal an den Prozess gesendet wird, der den Fehler verursacht hat. SIGBUS kann auch durch jeden allgemeinen Gerätefehler verursacht werden , dass der Computer erkennt, wenn auch selten ein Bus - Fehler bedeutet , dass die Computer - Hardware ist physikalisch aufgebrochene es normalerweise von einem verursacht Fehler in Software . Busfehler können auch für bestimmte andere Paging-Fehler auftreten. siehe unten.

Ursachen

Es gibt mindestens drei Hauptursachen für Busfehler:

Nicht vorhandene Adresse

Die Software weist die CPU an, eine bestimmte physikalische Speicheradresse zu lesen oder zu schreiben . Dementsprechend setzt die CPU diese physikalische Adresse auf ihren Adressbus und fordert alle anderen an die CPU angeschlossenen Hardware auf, mit den Ergebnissen zu antworten, wenn sie auf diese spezifische Adresse antworten. Wenn keine andere Hardware antwortet, löst die CPU eine Ausnahme aus , die besagt, dass die angeforderte physische Adresse vom gesamten Computersystem nicht erkannt wird. Beachten Sie, dass dies nur physische Speicheradressen abdeckt . Der Versuch, auf eine undefinierte virtuelle Speicheradresse zuzugreifen, wird im Allgemeinen eher als Segmentierungsfehler als als Busfehler betrachtet. Wenn die MMU jedoch getrennt ist, kann der Prozessor den Unterschied nicht erkennen.

Nicht ausgerichteter Zugriff

Die meisten CPUs sind byteadressierbar , wobei sich jede eindeutige Speicheradresse auf ein 8-Bit- Byte bezieht . Die meisten CPUs können von jeder Speicheradresse auf einzelne Bytes zugreifen, aber sie können im Allgemeinen nicht auf größere Einheiten (16 Bit, 32 Bit, 64 Bit usw.) zugreifen, ohne dass diese Einheiten an einer bestimmten Grenze " ausgerichtet " sind (die x86-Plattform ist eine bemerkenswerte Ausnahme ).

Wenn beispielsweise Mehrbyte-Zugriffe 16-Bit-ausgerichtet sein müssen, werden Adressen (in Bytes angegeben) bei 0, 2, 4, 6 usw. als ausgerichtet und daher zugänglich betrachtet, während die Adressen 1, 3, 5 und so weiter würde als nicht ausgerichtet betrachtet werden. Wenn Mehrbyte-Zugriffe 32-Bit-ausgerichtet sein müssen, werden die Adressen 0, 4, 8, 12 usw. als ausgerichtet und daher zugänglich betrachtet, und alle dazwischen liegenden Adressen werden als nicht ausgerichtet betrachtet. Der Versuch, an einer nicht ausgerichteten Adresse auf eine Einheit zuzugreifen, die größer als ein Byte ist, kann einen Busfehler verursachen.

Einige Systeme können abhängig von der verwendeten Architektur einen Hybrid aus diesen haben. Für Hardware, die auf dem IBM System / 360- Mainframe basiert , einschließlich IBM System z , Fujitsu B8000, RCA Spectra und UNIVAC Series 90 , müssen sich die Anweisungen an einer 16-Bit-Grenze befinden, dh Ausführungsadressen müssen an einer beginnen sogar Byte. Versuche, zu einer ungeraden Adresse zu verzweigen, führen zu einer Spezifikationsausnahme. Daten können jedoch von jeder Adresse im Speicher abgerufen werden und können abhängig von der Anweisung ein Byte oder länger sein.

CPUs greifen im Allgemeinen jederzeit über die gesamte Breite ihres Datenbusses auf Daten zu. Um Bytes zu adressieren, greifen sie mit der vollen Breite ihres Datenbusses auf den Speicher zu, maskieren und verschieben dann, um das einzelne Byte zu adressieren. Systeme tolerieren diesen ineffizienten Algorithmus, da er für die meisten Softwareprodukte, insbesondere für die Verarbeitung von Zeichenfolgen, ein wesentliches Merkmal ist. Im Gegensatz zu Bytes können größere Einheiten zwei ausgerichtete Adressen überspannen und würden daher mehr als einen Abruf auf dem Datenbus erfordern. Es ist für CPUs möglich, dies zu unterstützen, aber diese Funktionalität wird selten direkt auf der Ebene des Maschinencodes benötigt. Daher vermeiden CPU-Entwickler normalerweise die Implementierung und geben stattdessen Busfehler für den nicht ausgerichteten Speicherzugriff aus.

Paging-Fehler

FreeBSD , Linux und Solaris können einen Busfehler signalisieren, wenn virtuelle Speicherseiten nicht ausgelagert werden können , z. B. weil sie verschwunden sind (z. B. Zugriff auf eine Speicherzuordnungsdatei oder Ausführen eines Binärabbilds, das während der Ausführung des Programms abgeschnitten wurde) oder weil Eine soeben erstellte Speicherzuordnungsdatei kann nicht physisch zugewiesen werden, da die Festplatte voll ist.

Nicht vorhandenes Segment (x86)

Auf x86 gibt es einen älteren Speicherverwaltungsmechanismus, der als Segmentierung bezeichnet wird . Wenn die Anwendung das Segmentregister mit dem Selektor des nicht vorhandenen Segments lädt (was unter POSIX-kompatiblen Betriebssystemen nur mit einer Assemblersprache möglich ist ), wird die Ausnahme generiert. Einige Betriebssysteme verwendeten dies zum Austauschen, aber unter Linux generiert dies SIGBUS.

Beispiel

Dies ist ein Beispiel für einen nicht ausgerichteten Speicherzugriff, der in der Programmiersprache C mit AT & T-Assemblysyntax geschrieben ist .

#include <stdlib.h>

int main(int argc, char **argv) 
{
    int *iptr;
    char *cptr;
    
#if defined(__GNUC__)
# if defined(__i386__)
    /* Enable Alignment Checking on x86 */
    __asm__("pushf\norl $0x40000,(%esp)\npopf");
# elif defined(__x86_64__) 
     /* Enable Alignment Checking on x86_64 */
    __asm__("pushf\norl $0x40000,(%rsp)\npopf");
# endif
#endif

    /* malloc() always provides memory which is aligned for all fundamental types */
    cptr = malloc(sizeof(int) + 1);
    
    /* Increment the pointer by one, making it misaligned */
    iptr = (int *) ++cptr;

    /* Dereference it as an int pointer, causing an unaligned access */
    *iptr = 42;

    /*
       Following accesses will also result in sigbus error.
       short *sptr;
       int    i;

       sptr = (short *)&i;
       // For all odd value increments, it will result in sigbus.
       sptr = (short *)(((char *)sptr) + 1);
       *sptr = 100;
    
    */

    return 0;
}

Das Kompilieren und Ausführen des Beispiels unter einem POSIX- kompatiblen Betriebssystem unter x86 zeigt den Fehler:

$ gcc -ansi sigbus.c -o sigbus
$ ./sigbus 
Bus error
$ gdb ./sigbus
(gdb) r
Program received signal SIGBUS, Bus error.
0x080483ba in main ()
(gdb) x/i $pc
0x80483ba <main+54>:    mov    DWORD PTR [eax],0x2a
(gdb) p/x $eax
$1 = 0x804a009
(gdb) p/t $eax & (sizeof(int) - 1)
$2 = 1

Die GDB Debugger zeigt , dass die unmittelbare Wert 0x2A an der Stelle in dem gespeicherten gespeichert EAX - Register , unter Verwendung von X86 Assemblersprache . Dies ist ein Beispiel für die indirekte Adressierung von Registern .

Das Drucken der niederwertigen Bits der Adresse zeigt, dass sie nicht an einer Wortgrenze ausgerichtet ist ("dword" unter Verwendung der x86-Terminologie).

Verweise