"Veriyolu hatası" mesajı ne anlama geliyor ve bir segfaulttan farkı nedir?
"Veriyolu hatası" mesajı ne anlama geliyor ve bir segfaulttan farkı nedir?
Yanıtlar:
Veri yolu hataları günümüzde x86'da nadirdir ve işlemciniz genellikle istenen bellek erişimini denemediğinde ortaya çıkar:
Segmentasyon hataları, işleminize ait olmayan belleğe erişirken ortaya çıkar, çok yaygındır ve genellikle aşağıdakilerin sonucudur:
Not: Daha kesin olmak gerekirse, bu işaretçiye sorunlara neden olacak şekilde hareket etmiyor, işaret ettiği belleğe erişiyor (kayıt silme).
/var/cache
sadece doluydu askubuntu.com/a/915520/493379
static_cast
bir void *
parametre düzenledi. Sonra geri arama yapılır. Ancak, geçirilen void *
şey tamamen farklı bir şeydi ve bu nedenle yöntem çağrısı otobüs hatasına neden oldu.
Segfault erişmenize izin verilmeyen belleğe erişiyor. Salt okunur, izniniz yok, vb ...
Veri yolu hatası, orada bulunamayan belleğe erişmeye çalışıyor. Sistem için anlamsız bir adres veya bu işlem için yanlış türde bir adres kullandınız.
mmap
minimal POSIX 7 örneği
Çekirdek SIGBUS
bir işleme gönderdiğinde "veri yolu hatası" oluşur .
Unutulduğu için üreten minimal bir örnek ftruncate
:
#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;
}
Şununla çalıştır:
gcc -std=c99 main.c -lrt
./a.out
Ubuntu 14.04'te test edildi.
POSIX şöyle tanımlar SIGBUS
:
Bellek nesnesinin tanımsız bir bölümüne erişim.
Mmap Spec söylüyor:
Pa'dan başlayan ve bir nesnenin bitiminden sonra tüm sayfalara len bayt devam eden adres aralığı içindeki referanslar, bir SIGBUS sinyalinin verilmesiyle sonuçlanacaktır.
Ve 0 boyutlu nesneler oluşturduğunu shm_open
söylüyor :
Paylaşılan bellek nesnesinin boyutu sıfırdır.
Böylece *map = 0
, tahsis edilen nesnenin sonundan geçiyoruz.
ARMv8 aarch64'te hizalanmamış yığın bellek erişimi
Şuradan bahsedildi: Veri yolu hatası nedir? ancak burada daha tekrarlanabilir bir örnek sunacağım.
Tek ihtiyacınız olan bağımsız bir aarch64 programı:
.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
Bu program daha sonra bir ThunderX2 sunucu makinesinde Ubuntu 18.04 aarch64, Linux çekirdeği 4.15.0'da SIGBUS'ı yükseltir .
Ne yazık ki, QEMU v4.0.0 kullanıcı modunda çoğaltılamıyorum, neden olduğundan emin değilim.
Arıza isteğe bağlı ve SCTLR_ELx.SA
ve SCTLR_EL1.SA0
alanları tarafından kontrol ediliyor gibi görünüyor, burada ilgili dokümanları biraz daha özetledim .
Bir uygulama veri yolunda veri yanlış hizalaması gösterdiğinde çekirdeğin SIGBUS'u yükselttiğine inanıyorum. Çoğu işlemci için modern derleyicilerin çoğu programcılar için verileri doldurduğundan / hizaladığından, yore (en azından) hizalama sorunlarının hafifletildiğini ve dolayısıyla bu günlerde SIGBUS'u çok sık görmediğini düşünüyorum (AFAIK).
Gönderen: İşte
Herhangi bir nedenle bir kod sayfası sayfalanamadığı zaman SIGBUS'u da alabilirsiniz.
mmap
/dev/shm
Bir otobüs hatasının klasik bir örneği, SPARC (en azından bazı SPARC'ler, belki de bu değiştirilmiştir) gibi belirli mimarilerde, yanlış hizalanmış bir erişim yaptığınız zamandır. Örneğin:
unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;
Bu snippet, 32 bit tam sayı değerini 0xdeadf00d
(büyük olasılıkla) düzgün hizalanmamış bir adrese yazmaya çalışır ve bu bağlamda "seçici" olan mimarilerde bir veri yolu hatası oluşturur. Intel x86 bu arada böyle bir mimari değil , erişime izin verecek (daha yavaş da olsa).
OS X'te C programlanırken karşılaştığım bir otobüs hatasının belirli bir örneği:
#include <string.h>
#include <stdio.h>
int main(void)
{
char buffer[120];
fgets(buffer, sizeof buffer, stdin);
strcat("foo", buffer);
return 0;
}
Hatırlamıyorsanız, dokümanlar strcat
ilk argümanı değiştirerek ilk argümanı ilkine ekler (argümanları ters çevirin ve iyi çalışır). Linux'ta bu bir segmentasyon hatası verir (beklendiği gibi), ancak OS X'te bir bus hatası verir. Neden? Gerçekten bilmiyorum.
"foo"
belleğin salt okunur bir bölümünde saklanır, bu yüzden ona yazmak imkansızdır. Yığın taşması koruması olmaz, sadece bellek yazma koruması olur (programınız kendini yeniden yazabilirse bu bir güvenlik deliğidir).
İşletim sisteminize, CPU'nuza, Derleyicinize ve muhtemelen diğer faktörlere bağlıdır.
Genel olarak CPU veri yolunun bir komutu tamamlayamadığı veya bir çakışma yaşadığı, ancak bu, çalışılan ortama ve koda bağlı olarak bir dizi şey anlamına gelebileceği anlamına gelir.
-Adam
Normalde hizalanmamış erişim anlamına gelir.
Fiziksel olarak mevcut olmayan belleğe erişme girişimi de bir veri yolu hatası verir, ancak MMU ve buggy olmayan bir işletim sistemi olan bir işlemci kullanıyorsanız bunu göremezsiniz, çünkü herhangi bir olmayan -İşleminizin adres alanına eşlenen mevcut bellek.
scanf
) C öğrenirken bu hatayla karşılaştım . Bu OS X Mavericks'in arabası olduğu anlamına mı geliyor? Buggy olmayan bir işletim sistemindeki davranış ne olurdu?
Mac OS X'te otobüs hatası nedenim, yığın üzerinde yaklaşık 1Mb ayırmaya çalışmamdı. Bu bir iş parçacığında iyi çalıştı, ancak Mac OS X ana olmayan iş parçacıkları için çok sınırlı yığın boyutuna sahip olduğundan, openMP kullanılırken bu veriyolu hatasına gider .
Yukarıdaki tüm cevaplara katılıyorum. BUS hatasıyla ilgili 2 sentim:
Bir BUS hatasının programın kodu içindeki talimatlardan kaynaklanmasına gerek yoktur. Bu, bir ikili dosya çalıştırdığınızda ve yürütme sırasında, ikili dosya değiştirilir (bir derleme üzerine yazılır veya silinir vb.).
Durumun bu olup olmadığını doğrulama:
Bunun nedeninin olup olmadığını kontrol etmenin basit bir yolu, aynı ikili dosyanın çalışan örneklerini başlatmak ve bir yapı çalıştırmaktır. Her iki çalışan örnek SIGBUS
de derleme bittikten ve ikili dosyayı değiştirdikten kısa bir süre sonra bir hatayla kilitleniyordu (her iki örneğin de şu anda çalışmakta olduğu)
Temel Sebep: Bunun nedeni, işletim sisteminin bellek sayfalarını değiştirmesidir ve bazı durumlarda ikili dosya tamamen belleğe yüklenmeyebilir ve işletim sistemi bir sonraki sayfayı aynı ikili dosyadan almaya çalıştığında, ancak ikili dosya sonuncudan bu yana değiştiğinde oku onu.
Blxtd'in yukarıda cevapladığı şeye eklemek için, işleminiz belirli bir 'değişkenin' belleğine erişemediğinde de otobüs hataları oluşur .
for (j = 0; i < n; j++) {
for (i =0; i < m; i++) {
a[n+1][j] += a[i][j];
}
}
'Dikkat yanlışlıkla ait' kullanımını 'i' değişkeni içinde 'döngüsü için' ilk? Bu durumda otobüs hatasına neden olan şey budur.
Bir ARMv7 işlemcide, optimize edilmediğinde size bir segmentasyon hatası veren bazı kodlar yazabilmenin zor yolunu öğrendim, ancak -O2 ile derlendiğinde size bir otobüs hatası veriyor (daha fazla optimizasyon).
Ubuntu 64 bit GCC ARM gnueabihf çapraz derleyici kullanıyorum.
Veriyolu hatasına neden olan tipik bir arabellek taşması,
{
char buf[255];
sprintf(buf,"%s:%s\n", ifname, message);
}
Burada çift tırnak ("") içindeki dizenin boyutu buf boyutundan büyükse, veri yolu hatası verir.