Co oznacza komunikat „błąd magistrali” i czym się różni od segfault?
Co oznacza komunikat „błąd magistrali” i czym się różni od segfault?
Odpowiedzi:
Błędy magistrali są obecnie rzadkie na x86 i występują, gdy procesor nie może nawet próbować uzyskać dostępu do pamięci, zwykle:
Błędy segmentacji występują podczas uzyskiwania dostępu do pamięci, która nie należy do twojego procesu, są bardzo częste i zwykle są wynikiem:
PS: Mówiąc ściślej, nie jest to manipulowanie samym wskaźnikiem, który spowoduje problemy, lecz dostęp do pamięci, na którą wskazuje (dereferencje).
/var/cache
była po prostu pełna askubuntu.com/a/915520/493379
static_cast
zmodyfikowała void *
parametr do obiektu, który przechowuje wywołanie zwrotne (jeden atrybut wskazuje na obiekt, a drugi na metodę). Następnie wywoływane jest oddzwonienie. Jednak to, co zostało przekazane, void *
było czymś zupełnie innym, a zatem wywołanie metody spowodowało błąd magistrali.
Segfault ma dostęp do pamięci, do której nie masz dostępu. Jest tylko do odczytu, nie masz uprawnień itp.
Błąd magistrali próbuje uzyskać dostęp do pamięci, której prawdopodobnie nie ma. Użyłeś adresu, który nie ma znaczenia dla systemu, lub niewłaściwego rodzaju adresu dla tej operacji.
mmap
przykład minimalnej wersji POSIX 7
„Błąd magistrali” występuje, gdy jądro wysyła SIGBUS
do procesu.
Minimalny przykład, który go wytwarza, ponieważ ftruncate
został zapomniany:
#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */
int main() {
int fd;
int *map;
int size = sizeof(int);
char *name = "/a";
shm_unlink(name);
fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
/* THIS is the cause of the problem. */
/*ftruncate(fd, size);*/
map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
/* This is what generates the SIGBUS. */
*map = 0;
}
Biegnij z:
gcc -std=c99 main.c -lrt
./a.out
Testowane w Ubuntu 14.04.
POSIX opisuje SIGBUS
jako:
Dostęp do niezdefiniowanej części obiektu pamięci.
Specyfikacja mmap mówi, że:
Odniesienia w zakresie adresów rozpoczynającym się od pa i kontynuującym bajty len do całych stron po końcu obiektu powinny skutkować dostarczeniem sygnału SIGBUS.
I shm_open
mówi, że generuje obiekty o rozmiarze 0:
Obiekt pamięci współdzielonej ma rozmiar zero.
Tak więc *map = 0
dotykamy końca przydzielonego obiektu.
Niezrównany dostęp do pamięci stosu w ARMv8 aarch64
Zostało to wspomniane w: Co to jest błąd magistrali? dla SPARC, ale tutaj podam bardziej powtarzalny przykład.
Wszystko czego potrzebujesz to wolnostojący program aarch64:
.global _start
_start:
asm_main_after_prologue:
/* misalign the stack out of 16-bit boundary */
add sp, sp, #-4
/* access the stack */
ldr w0, [sp]
/* exit syscall in case SIGBUS does not happen */
mov x0, 0
mov x8, 93
svc 0
Ten program następnie podnosi SIGBUS na Ubuntu 18.04 aarch64, jądro Linuksa 4.15.0 na serwerze ThunderX2 .
Niestety nie mogę go odtworzyć w trybie użytkownika QEMU v4.0.0, nie jestem pewien dlaczego.
Błąd wydaje się być opcjonalny i kontrolowany przez pola SCTLR_ELx.SA
i SCTLR_EL1.SA0
, streściłem pokrewne dokumenty nieco tutaj .
Wierzę, że jądro podnosi SIGBUS, gdy aplikacja wykazuje przesunięcie danych na szynie danych. Myślę, że skoro większość [?] Nowoczesnych kompilatorów dla większości procesorów pada / wyrównuje dane dla programistów, problemy z wyrównaniem już (przynajmniej) złagodzone, a zatem obecnie nie widać zbyt często SIGBUSA (AFAIK).
Od: tutaj
Możesz także uzyskać SIGBUS, gdy z jakiegoś powodu strona kodowa nie może być stronicowana.
mmap
utworzenia pliku większego niż rozmiar/dev/shm
Konkretny przykład błędu magistrali, który właśnie napotkałem podczas programowania C w OS X:
#include <string.h>
#include <stdio.h>
int main(void)
{
char buffer[120];
fgets(buffer, sizeof buffer, stdin);
strcat("foo", buffer);
return 0;
}
W przypadku, gdy nie pamiętasz, dokumenty strcat
dołączają drugi argument do pierwszego, zmieniając pierwszy argument (odwróć argumenty i działa dobrze). W Linuksie daje to błąd segmentacji (zgodnie z oczekiwaniami), ale w OS X daje błąd magistrali. Czemu? Naprawdę nie wiem.
"foo"
jest przechowywany w segmencie pamięci tylko do odczytu, więc nie można do niego pisać. Nie byłaby to ochrona przed przepełnieniem stosu, tylko ochrona przed zapisem pamięci (jest to dziura w zabezpieczeniach, jeśli Twój program może sam przepisać).
Klasyczne wystąpienie błędu magistrali występuje w niektórych architekturach, takich jak SPARC (przynajmniej niektóre SPARC, być może to zostało zmienione), kiedy ma się źle ustawiony dostęp. Na przykład:
unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;
Ten fragment próbuje zapisać 32-bitową wartość całkowitą 0xdeadf00d
na adres, który (najprawdopodobniej) nie jest właściwie wyrównany, i wygeneruje błąd magistrali na architekturach, które są „wybredne” pod tym względem. Intel x86, nawiasem mówiąc, nie jest taką architekturą, pozwoliłby na dostęp (choć należy go wykonać wolniej).
Zależy to od systemu operacyjnego, procesora, kompilatora i ewentualnie innych czynników.
Ogólnie oznacza to, że magistrala procesora nie może wykonać polecenia lub wystąpił konflikt, ale może to oznaczać cały szereg rzeczy w zależności od środowiska i uruchomionego kodu.
-Adam
Zwykle oznacza to niezrównany dostęp.
Próba dostępu do pamięci, która nie jest fizycznie obecna, również dałaby błąd magistrali, ale nie zobaczysz tego, jeśli używasz procesora z MMU i systemu operacyjnego, który nie jest wadliwy, ponieważ nie będziesz mieć żadnego -istniejąca pamięć odwzorowana na przestrzeń adresową procesu.
scanf
). Czy to oznacza, że OS X Mavericks jest wadliwy? Jak wyglądałoby zachowanie systemu operacyjnego bez wad?
Przyczyną błędu magistrali w systemie Mac OS X było to, że próbowałem przydzielić około 1 Mb na stosie. Działa to dobrze w jednym wątku, ale podczas korzystania z openMP powoduje to błąd magistrali, ponieważ Mac OS X ma bardzo ograniczony rozmiar stosu dla wątków innych niż główne .
Zgadzam się ze wszystkimi powyższymi odpowiedziami. Oto moje 2 centy za błąd magistrali:
Błąd magistrali nie musi wynikać z instrukcji zawartych w kodzie programu. Może się to zdarzyć, gdy uruchomisz plik binarny, a podczas wykonywania plik binarny zostanie zmodyfikowany (nadpisany przez kompilację lub usunięty itp.).
Sprawdzanie, czy tak jest:
Prostym sposobem sprawdzenia, czy to jest przyczyna, jest uruchomienie uruchomionych instancji tego samego pliku binarnego i uruchomienie kompilacji. Obie działające instancje uległyby awarii z SIGBUS
błędem wkrótce po zakończeniu kompilacji i zastąpiły plik binarny (ten, który obecnie działają obie instancje)
Zasadniczy powód: Dzieje się tak, ponieważ system operacyjny zamienia strony pamięci, aw niektórych przypadkach plik binarny może nie zostać całkowicie załadowany do pamięci, a awarie te występują, gdy system operacyjny próbuje pobrać następną stronę z tego samego pliku binarnego, ale plik binarny zmienił się od czasu ostatniego Przeczytaj to.
Aby dodać do odpowiedzi blxtd powyżej, błędy magistrali występują również wtedy, gdy proces nie może uzyskać dostępu do pamięci określonej „zmiennej” .
for (j = 0; i < n; j++) {
for (i =0; i < m; i++) {
a[n+1][j] += a[i][j];
}
}
Czy zauważyłeś „ niezamierzone ” użycie zmiennej „i” w pierwszym „for loop”? To właśnie powoduje błąd magistrali w tym przypadku.
Właśnie dowiedziałem się na własnej skórze, że na procesorze ARMv7 możesz napisać kod, który daje błąd segmentacji, gdy nie jest zoptymalizowany, ale daje błąd magistrali po skompilowaniu z -O2 (zoptymalizuj więcej).
Korzystam z kompilatora krzyżowego GCC ARM gnueabihf z Ubuntu 64-bit.
Typowe przepełnienie bufora, które powoduje błąd magistrali,
{
char buf[255];
sprintf(buf,"%s:%s\n", ifname, message);
}
Jeśli wielkość ciągu w podwójnych cudzysłowach („”) jest większa niż rozmiar buf, oznacza to błąd magistrali.