Veriyolu hatası nedir?


257

"Veriyolu hatası" mesajı ne anlama geliyor ve bir segfaulttan farkı nedir?


5
Her ikisi için de basit bir açıklama eklemek istiyorum: Segmentasyon hatası, izin verilmeyen belleğe erişmeye çalıştığınız anlamına gelir (örneğin, programınızın bir parçası değildir). Ancak, bir veri yolu hatası, genellikle mevcut olmayan belleğe erişmeye çalıştığınız anlamına gelir (örneğin, 12G'de bir adrese erişmeye çalışırsınız, ancak yalnızca 8G belleğiniz vardır) veya kullanılabilir bellek sınırını aşarsanız.
xdevs23

Bunu hangi platformda gördün? PC? Mac? x86? 32/64?
Peter Mortensen

Yanıtlar:


246

Veri yolu hataları günümüzde x86'da nadirdir ve işlemciniz genellikle istenen bellek erişimini denemediğinde ortaya çıkar:

  • hizalama gereksinimlerini karşılamayan bir adrese sahip bir işlemci talimatı kullanmak.

Segmentasyon hataları, işleminize ait olmayan belleğe erişirken ortaya çıkar, çok yaygındır ve genellikle aşağıdakilerin sonucudur:

  • yeniden konumlandırılmış bir şeye bir işaretçi kullanarak.
  • başlatılmamış dolayısıyla sahte bir işaretçi kullanarak.
  • bir boş gösterici kullanarak.
  • bir arabellek taşması.

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).


106
Bunlar nadir değildir; Ben sadece C Zor Yoldan Nasıl
Öğrenilir

24
Otobüs hatalarının başka bir nedeni (yine de Linux'ta), işletim sisteminin fiziksel belleğe sahip sanal bir sayfayı (örneğin, düşük bellek koşulları veya büyük sayfa belleği kullanırken büyük sayfaların dışında) destekleyememesidir.) Genellikle mmap (ve malloc) sanal adres alanını ayırırsanız ve çekirdek isteğe bağlı olarak fiziksel belleği atar (yumuşak sayfa hataları olarak adlandırılır.) Yeterince büyük bir malloc yapın ve sonra buna yazın ve bir veri yolu hatası alırsınız.
Eloff

1
benim için bölüm /var/cachesadece doluydu askubuntu.com/a/915520/493379
c33s

2
Benim durumumda, bir yöntem bir geri çağrıyı (bir öznitelik nesneyi ve diğeri yönteme işaret eder) depolayan bir nesneye static_castbir 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.
Christopher K.

@bltxd Otobüs hatalarının doğasını biliyor musunuz? yani halka veriyolundaki mesajın, halka üzerindeki bir durağın kendisi tarafından gönderilen bir mesajı da kabul ettiği bir mekanizma var mıdır, ancak halka boyunca tümüyle gittiğini ve kabul edilmediğini önerdiği herhangi bir hedefe. Satır doldurma arabelleğinin bir hata durumu döndürdüğünü tahmin ediyorum ve emekli olduğunda boru hattını temizler ve doğru istisna mikro yordamını çağırır. Bu temelde bellek denetleyicisinin, BAR'lar vb. Değiştiğinde dahili olarak yapması gerektiğini düşündüren aralıktaki tüm adresleri kabul etmesini gerektirir
Lewis Kelsey

84

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.


14

mmap minimal POSIX 7 örneği

Çekirdek SIGBUSbir 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.SAve SCTLR_EL1.SA0alanları tarafından kontrol ediliyor gibi görünüyor, burada ilgili dokümanları biraz daha özetledim .


11

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


1
Kodunuzla yaptığınız kötü hilelere bağlıdır. İşaretçi matematik yapmak ve sonra bir sorun moduna erişmek için tahmin etmek gibi aptalca bir şey yaparsanız bir BUS hatası / Hizalama Tuzağı tetikleyebilirsiniz (yani, bir uint8_t dizisi ayarladınız, dizinin işaretçisine bir, iki veya üç ekleyin ve sonra tahmin edin kısa, int veya uzun bir süre için ve rahatsız edici sonuca erişmeye çalışın.) X86 sistemleri, gerçek bir performans cezası olsa da, bunu yapmanıza izin verecektir. BAZI ARMv7 sistemleri bunu yapmanıza izin verir - ancak çoğu ARM, MIPS, Güç vb.
Svartalf

6

Herhangi bir nedenle bir kod sayfası sayfalanamadığı zaman SIGBUS'u da alabilirsiniz.


7
Bu genellikle işlemi çalıştırırken .so dosyasını güncellediğimde olur
poordeveloper

Bunun bir başka nedenimmap/dev/shm
ilija139

4

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).


1
Durumda verilerim vardı [8]; Bu artık 32 bit mimaride 4'ün katıdır. Yani, hizalanmış. Şimdi hatayı almaya devam edecek miyim? Ayrıca, lütfen açıklayın, işaretçiler için bir veri türü dönüşümü için kötü bir fikir mi? Kırılgan bir mimaride yanlış hizalama hatalarına neden olur mu? Lütfen detaylandırın, bana yardımcı olacak.
dexterous

Heh. İşaretçi matematiğini yaptığınız bir işaretçide tür dönüşümü yaptığınız için çok fazla tür dönüşümü değildir. Yukarıdaki koda dikkatlice bakın . Derleyici, veri işaretçinizi dikkatlice dword ile hizaladı ve daha sonra referansı İKİ ile dengeleyerek ve dword olmayan bir sınır üzerinde dword hizalanmış erişime çok ihtiyaç duyan derleyici ile her şeyi mahvediyorsunuz.
Svartalf

Tüm bunlar için kullanacağım kelime "kırılgan" değil. X86 makineleri ve kodları bir süredir insanlara oldukça aptalca şeyler yapıyor, bu onlardan biri. Bu tür bir sorun yaşıyorsanız kodunuzu yeniden düşünün - X86'da başlamak için çok performans değil.
Svartalf

@Svartalf: x86'da, hizalanmamış işaretçilerdeki kelime erişimi, hizalanmış işaretleyicilere sözcük erişiminden kesinlikle daha yavaştır, ancak en azından tarihsel olarak, koşulsuz olarak baytları bir araya getiren basit koddan daha hızlıdır ve kesinlikle deneyen koddan daha basittir. çeşitli boyutlardaki işlemlerin en uygun kombinasyonunu kullanmak için Keşke C standardı, derleyicinin belirli bir platformda en iyi yaklaşımı kullanmasını sağlamak için daha büyük tamsayı türlerini daha küçük tamsayı / karakter dizisine / dizisinden paketleme / açma araçlarını içermesini isterdim.
supercat

@Supercat: Mesele şu ki - X86'da onunla kaçıyorsun. Bunu ARM, MIPS, Power, vb. Üzerinde denersiniz ve size kötü şeyler olur. Arch V7'den daha düşük ARM'de, kodunuzda bir hizalama hatası olacak ve V7'de, çalışma zamanınız buna göre ayarlanmışsa, bir SEVERE performans isabeti ile başa çıkabilirsiniz. Sadece bunu yapmak istemiyorsun. Kör olmak kötü uygulamalar. : D
Svartalf

3

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 strcatilk 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.


Muhtemelen yığın taşması koruması veriyolu hatasını arttırır.
Joshua

1
"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).
Mark Lakata

2

İş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


2

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.


2
Benim i7 kesinlikle bir MMU var, ama ben hala OS X (başlatılmamış işaretçisini geçen 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?
Calvin Huang

2

Kök dizin% 100 iken bir veri yolu hatası alıyordum.



1

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 SIGBUSde 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.


Anlaşılan, bu benim deneyimimdeki otobüs hatalarının en yaygın nedenidir.
itaych

0

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.


M> = n ise, dış döngü i'nin önceden mevcut değerine bağlı olarak bir kez veya hiç yürütülmez. Eğer m <n ise, dizininizin sınırları tükenene ve büyük olasılıkla bir bus hatasına değil, bir segmentasyon hatasına neden olana kadar j indeksi arttıkça süresiz olarak çalışır. Bu kod derlenirse, 'i' değişkeninin belleğine erişmekte sorun yoktur. Üzgünüm ama bu cevap yanlış.
itaych

0

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.


Bu soruya nasıl cevap veriyor?
Peter Mortensen

-1

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.


1
Heh ... bu durumda, Windows ve diğer makineler için her zaman okuduğunuz istismarları parçalayan yığın yerine BUS hata endişeleriniz olurdu. BUS hataları, adres geçersiz olduğundan makinenin erişemediği "belleğe" erişme girişiminden kaynaklanır. (Dolayısıyla "BUS" hatası terimi.) Bunun nedeni, işlemcinin adresi veri yolu hatlarına yerleştiremediği sürece geçersiz hizalamalar ve benzerleri gibi bir dizi arızadan kaynaklanabilir.
Svartalf
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.