Błąd szyny

Błąd szyny – błąd wywoływany przez sprzęt komputerowy podczas gdy proces usiłuje uzyskać dostęp do pamięci, której procesor nie jest w stanie fizycznie zaadresować. W większości współczesnych architektur błędy te są znacznie rzadsze niż błędy segmentacji, które występowały głównie ze względu na nieuprawnione próby dostępu do pamięci[1].

Na platformach kompatybilnych z POSIX-em, rezultatem błędów szyny było wysyłanie sygnałów SIGBUS do procesu, który spowodował błąd. SIGBUS może również być spowodowany przez dowolny błąd urządzenia, wykryty przez komputer, pomimo iż błąd szyny rzadko wskazuje na fizyczne uszkodzenie sprzętu – przeważnie spowodowany jest przez błąd w kodzie źródłowym programu[2].

Przyczyny

[edytuj | edytuj kod]

Trzy główne przyczyny występowania błędów szyny to m.in.:

  • Nieistniejące adresy
  • Nieuprawniona próba dostępu
  • Błędy stronicowania

Nieistniejące adresy

[edytuj | edytuj kod]

Oprogramowanie nakazuje procesorowi odczytać lub zapisać dane na konkretnym fizycznym adresie pamięci. Procesor ustawia ten adres fizyczny na swojej szynie adresowej i wysyła komunikat do wszystkich innych urządzeń (podłączonych do CPU) aby informowały o rezultatach w przypadku wysyłania odpowiedzi na ten konkretny adres. Jeśli żadne urządzenie nie odpowiada, CPU zwraca wyjątek stwierdzając, że żądany adres fizyczny jest nierozpoznany przez cały system komputerowy. Dotyczy to tylko fizycznych adresów pamięci. Próba uzyskania dostępu do niezdefiniowanego adresu pamięci wirtualnej jest powszechnie uważana za błąd segmentacji a nie magistrali. Pomimo iż MMU jest oddzielna, procesor nie jest w stanie tego odróżnić.

Nieuprawniona próba dostępu

[edytuj | edytuj kod]

W większości procesorów każdy unikatowy adres pamięci jest pojedynczym bajtem. Wiele procesorów ma dostęp do bajtów z każdego adresu pamięci, lecz nie mają go w przypadku większych jednostek (16 bitów, 32, 64 etc.) jeśli te jednostki nie są „wyrównane” do specyficznej granicy (wyjątek stanowi platforma x86).

Przykładowo, jeżeli dostęp musi być wyrównany z dokładnością do 16 bitów, adresy (podane w bajtach) na 0, 2, 4, 6, bajcie etc. będą uznane za „prawidłowo wyrównane” i tym samym – jako dostępne. Z kolei adresy na 1, 3, 5 bajcie etc. zostaną uznane za „niewyrównane” i nie będą one dostępne. Analogicznie, przy „wyrównaniu” 32-bitowym, dostępne będą adresy na bajcie 0, 4, 8, 12, etc., a wszystkie adresy zawarte w bajtach pomiędzy bajtami „dozwolonymi” będą uznane za niewyrównane.

W zależności od architektury, niektóre systemy mogą posiadać inną konfigurację; Przykładowo, dla sprzętu na bazie IBM System/360 (wraz z IBM System z, Fujitsu B8000, RCA Spectra, oraz UNIVAC Serii 90), instrukcje miały ograniczenie 16-bitowe. Oznaczało to, że adresy musiały zaczynać się na bajcie parzystym. Próby odwołania do adresów nieparzystych kończyły się wyjątkiem specyfikacji[3]. Niemniej jednak, dane mogą być odzyskane z dowolnego adresu w pamięci, a w zależności od instrukcji mogą mieć one długość jednego bajta lub większą.

Przykłady

[edytuj | edytuj kod]

Następujący kod źródłowy przedstawia „nierówny” dostęp do pamięci (napisany w języku C za pomocą składni AT&T).

#include <stdlib.h>

int main(int argc, char **argv)
{
    int *iptr;
    char *cptr;
    
#if defined(__GNUC__)
# if defined(__i386__)
    /* Aktywowanie walidacji wyrównania na x86 */
    __asm__("pushf\norl $0x40000,(%esp)\npopf");
# elif defined(__x86_64__)
     /* Aktywowanie walidacji wyrównania na x86_64 */
    __asm__("pushf\norl $0x40000,(%rsp)\npopf");
# endif
#endif

    /* malloc() zawsze przydziela "wyrównaną" pamięć */
    cptr = malloc(sizeof(int) + 1);
    
    /* Inkrementacja wskaźnika o jeden, przez co stanie się on niewyrównany */
    iptr = (int *) ++cptr;

    /* Usunięcie referencji jako wskaźnika typu 'int', które spowoduje niewyrównany dostęp */
    *iptr = 42;

    /*
       Poniższe operacje spowodują wywołanie błędu sigbus.
       short *sptr;
       int    i;

       sptr = (short *)&i;
       // Dla każdej inkrementacji nieparzystych wartości nastąpi "sigbus".
       sptr = (short *)(((char *)sptr) + 1);
       *sptr = 100;
    
    */

    return 0;
}

Kompilacja i uruchomienie przykładowego kodu na systemie zgodnym z POSIX działającym na architekturze X86 spowoduje przedstawiony błąd:

$ 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

Przypisy

[edytuj | edytuj kod]
  1. Bus Error (Solaris Common Messages and Troubleshooting Guide) [online], docs.oracle.com [dostęp 2017-11-17].
  2. c - What is a bus error? - Stack Overflow [online], stackoverflow.com [dostęp 2017-11-17].
  3. z/Architecture Principles of Operation, SA22-7832-04, Page 6-6, Fifth Edition (September, 2005) IBM Corporation, Poukeepsie, NY, Retrievable from http://publibfp.dhe.ibm.com/epubs/pdf/a2278324.pdf (Retrieved December 31, 2015)