Що таке помилка шини?


254

Що означає повідомлення про "помилку шини" та як воно відрізняється від сегмента?


5
Я хотів би додати просте пояснення для обох: Помилка сегментації означає, що ви намагаєтеся отримати доступ до пам’яті, до якої вам не дозволено (наприклад, це не є частиною вашої програми). Однак, помилка шини зазвичай означає, що ви намагаєтеся отримати доступ до пам'яті, яка не існує (наприклад, ви намагаєтеся отримати доступ до адреси в 12G, але у вас є лише 8G пам'яті) або якщо ви перевищуєте обмеження корисної пам'яті.
xdevs23

На якій платформі ви це бачили? ПК? Мак? x86? 32/64?
Пітер Мортенсен

Відповіді:


243

Помилки шини в наш час рідкісні для x86 і трапляються, коли ваш процесор навіть не може спробувати запит на доступ до пам'яті, як правило:

  • використовуючи інструкцію процесора з адресою, яка не відповідає його вимогам вирівнювання.

Помилки сегментації виникають під час доступу до пам'яті, яка не належить до вашого процесу, вони є дуже поширеними і, як правило, є результатом:

  • використовуючи вказівник на щось, що було розміщено.
  • використовуючи неініціалізований, отже, фіктивний покажчик.
  • за допомогою нульового вказівника.
  • переповнення буфера.

PS: Якщо бути точнішим, це не маніпулювання самим покажчиком, що спричинить проблеми, це доступ до пам’яті, на яку він вказує (перенаправлення).


105
Вони не рідкісні; Я щойно на вправі 9 із "Як навчитися C важким шляхом" і вже зустрічався з одним ...
11684,

24
Ще одна причина помилок шини (в будь-якому випадку в Linux) полягає в тому, що операційна система не може створити віртуальну сторінку з фізичною пам'яттю (наприклад, з низькою пам'яттю або з величезних сторінок при використанні величезної сторінки пам'яті.) Зазвичай mmap (і malloc) просто зарезервуйте віртуальний адресний простір, і ядро ​​призначає фізичну пам'ять на вимогу (так звані помилки м'якої сторінки). Зробіть достатньо великий маллок, а потім запишіть його достатньо, і ви отримаєте помилку шини.
Елофф

1
для мене розділ, що містить, /var/cacheбув просто повний askubuntu.com/a/915520/493379
c33s

2
У моєму випадку метод static_castредагує void *параметр об'єкту, який зберігає зворотний виклик (один атрибут вказує на об'єкт, а інший на метод). Тоді викликається зворотний дзвінок. Однак те, що було передано як void *щось зовсім інше, і, таким чином, виклик методу викликав помилку в шині.
Крістофер К.

@bltxd Чи знаєте ви характер помилок шини. тобто чи має повідомлення на шині кільця якийсь механізм, коли зупинка на кільці також приймає повідомлення, яке воно надіслало, але в будь-який бік призначення, оскільки це говорить про те, що він пройшов увесь шлях навколо кільця і ​​його не прийнято. Я здогадуюсь, що буфер заливки рядків повертає статус помилки, а коли він виходить, він промиває конвеєр і викликає правильну мікропрограму винятку. Це, по суті, вимагає, щоб контролер пам'яті прийняв всю адресу в своєму діапазоні, що дозволило б припустити, що при
Льюїс Келсі

84

Segfault - це доступ до пам'яті, до якої ви не маєте доступу. Це лише для читання, у вас немає дозволу тощо.

Помилка шини намагається отримати доступ до пам'яті, яка, можливо, там не може бути. Ви використовували безглузду для системи адресу або неправильний тип адреси для цієї операції.


14

mmap мінімальний приклад POSIX 7

"Помилка шини" трапляється при надсиланні ядра SIGBUS процес.

Мінімальний приклад, який виробляє, тому що 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;
}

Виконати з:

gcc -std=c99 main.c -lrt
./a.out

Тестовано в Ubuntu 14.04.

POSIX описується SIGBUS як:

Доступ до невизначеної частини об’єкта пам'яті.

Специфікація mmap говорить, що:

Посилання в межах діапазону адрес, починаючи з pa і продовжуючи надавати байти на цілі сторінки після кінця об'єкта, призводять до подачі сигналу SIGBUS.

І shm_open каже, що він генерує об'єкти розміром 0:

Об'єкт спільної пам'яті має розмір нуля.

Тож *map = 0ми торкаємося кінця виділеного об'єкта.

Неузгоджений доступ до пам'яті стека в ARMv8 aarch64

Про це йшлося у: Що таке помилка шини? для SPARC, але тут я надам більш відтворюваний приклад.

Все, що вам потрібно, це автономна програма 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

Потім програма піднімає SIGBUS на Ubuntu 18.04 aarch64, ядро ​​Linux 4.15.0 на серверній машині ThunderX2 .

На жаль, я не можу відтворити його в користувальницькому режимі QEMU v4.0.0, не знаю чому.

Несправність , як представляється, по бажанню і контролюються SCTLR_ELx.SAі SCTLR_EL1.SA0полями, я узагальнив пов'язані документи трохи далі тут .


11

Я вважаю, що ядро ​​підвищує SIGBUS, коли програма демонструє невідповідність даних на шині даних. Я думаю, що оскільки більшість [?] Сучасних компіляторів для більшості процесорів вкладають / вирівнюють дані для програмістів, проблеми з вирівнюванням ще (принаймні) пом'якшені, і, отже, в ці дні SIGBUS не бачить занадто часто (AFAIK).

З: Ось


1
Залежить від бридких хитрощів, які ви робите зі своїм кодом. Ви можете запустити помилку BUS / вирівнювання вирівнювання, якщо ви зробите щось нерозумне, як зробити математику вказівника, а потім набрати для доступу до проблемного режиму (тобто ви встановите масив uint8_t, додайте один, два чи три до вказівника масиву, а потім наберіть на короткий, інт або довгий і спробуйте отримати доступ до образотворчого результату.) Системи X86 в значній мірі дозволять вам це зробити, хоч і за реальне покарання. ДЕЯКІ системи ARMv7 дозволять вам це робити, але більшість ARM, MIPS, Power та ін.
Svartalf

6

Ви також можете отримати SIGBUS, коли кодова сторінка з якихось причин не може бути запущена.


7
Це часто трапляється, коли я
оновлюю

Ще одна причина - якщо ви спробуєте mmapфайл, більший за розмір/dev/shm
ilija139

3

Конкретний приклад помилки шини, з якою я щойно стикався під час програмування C на OS X:

#include <string.h>
#include <stdio.h>

int main(void)
{
    char buffer[120];
    fgets(buffer, sizeof buffer, stdin);
    strcat("foo", buffer);
    return 0;
}

Якщо ви не пам’ятаєте, документи strcatдодають другий аргумент до першого, змінюючи перший аргумент (переверніть аргументи, і він працює добре). У Linux це призводить до помилки сегментації (як очікувалося), але в OS X це помилка шини. Чому? Я справді не знаю.


Ймовірно, захист від переповнення стека викликає помилку шини.
Джошуа

1
"foo"зберігається в сегменті пам'яті, доступному лише для читання, тому писати до нього неможливо. Це не буде захистом від переповнення стека, а лише захистом від пам'яті (це отвір для безпеки, якщо програма може переписати себе).
Марк Лаката

3

Класичний екземпляр помилки шини - це для певних архітектур, таких як SPARC (принаймні деякі SPARC, можливо, це було змінено) - це коли ви робите неправильно узгоджений доступ. Наприклад:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

Цей фрагмент намагається записати 32-бітне ціле значення 0xdeadf00dна адресу, яка (швидше за все) не є належним чином вирівняна, і створить помилку шини в архітектурах, які є "вибагливими" в цьому плані. Intel x86, до речі, не така архітектура, вона дозволила б отримати доступ (хоч і виконувати його повільніше).


1
У випадку я мав дані [8]; Зараз це кратне число 4 в 32-бітній архітектурі. Отже, вона вирівняна. Чи все-таки я отримаю помилку зараз? Також поясніть, будь ласка, чи погана ідея перетворення типу даних для покажчиків. Чи це призведе до помилок вирівнювання на крихкій архітектурі. Будь ласка, докладно, це допоможе мені.
спритний

Хе. Це не стільки перетворення типів, скільки ти перетворення типів за вказівником, на якому ти робив математику вказівника. Подивіться уважно на код вище. Компілятор ретельно вирівняв ваш указівник на дані, а потім ви все на компіляторі викручуєте, зміщуючи посилання ДВОМ і набираючи клавішу, дуже потребуючи доступу з урахуванням слов у тому, що буде межею без слова.
Швартальф

"Крихке" - це не те слово, яке я б використовував для всього цього. Машини та код X86 вже давно люди роблять досить дурні речі, це одна з них. Перегляньте свій код, якщо у вас виникли такі проблеми - для початку це не дуже ефективно на X86.
Швартальф

@Svartalf: На x86, доступ до слів у нестандартних покажчиках, безумовно, повільніше, ніж доступ до слова до вирівнюваних покажчиків, але принаймні історично вони були швидшими за простий код, який безумовно збирає речі з байтів, і вони, звичайно, простіші, ніж код, який намагається використовувати оптимальне поєднання операцій різного розміру. Я хотів би, щоб стандарт C включав засоби упаковки / розпакування великих цілих типів до / з послідовності менших цілих чисел / символів, щоб дозволити компілятору використовувати будь-який підхід, який найкращий на даній платформі.
supercat

@Supercat: Справа в цьому - ти з ним підеш на X86. Ви спробуйте це на ARM, MIPS, Power тощо, і вам станеться неприємне. На ARM менше, ніж Arch V7, у вашого коду буде помилка вирівнювання, а на V7 ви можете, якщо для вас встановлено час виконання, обробити це хітом виконання SEVERE. Ви просто не хочете цього робити. Це погані практики, щоб бути тупими. : D
Швартальф

2

Це залежить від вашої ОС, процесора, компілятора та, можливо, інших факторів.

Взагалі це означає, що шина процесора не могла виконати команду або зазнала конфлікту, але це може означати цілу низку речей залежно від середовища та коду, який виконується.

-Адам


2

Зазвичай це означає неузгоджений доступ.

Спроба отримати доступ до пам'яті, яка фізично не присутня, також призведе до помилки шини, але ви цього не побачите, якщо ви використовуєте процесор з MMU та ОС, яка не є помилкою, оскільки у вас не буде жодної -існуюча пам'ять, відображена в адресному просторі вашого процесу.


2
У мого i7 звичайно є MMU, але я все-таки натрапив на цю помилку під час вивчення C на OS X (передача неініціалізованого покажчика на scanf). Чи означає це, що OS X Mavericks баггі? Якою була б поведінка в ОС, що не бажає на помилки?
Кальвін Хуанг

2

Я отримував помилку шини, коли кореневий каталог був на 100%.


1

Моя причина помилки в шині на Mac OS X полягала в тому, що я намагався виділити близько 1 Мб на стеці. Це добре працювало в одному потоці, але при використанні openMP цей диск приводить до помилки шини, оскільки Mac OS X має дуже обмежений розмір стека для не головних потоків .


1

Я згоден з усіма відповідями вище. Ось мої 2 центи щодо помилки BUS:

Помилка BUS не повинна виникати з інструкцій в коді програми. Це може статися, коли ви виконуєте бінарний файл і під час виконання двійковий файл модифікується (перезаписується збіркою або видаляється тощо).

Перевірка, чи це так: Простий спосіб перевірити, чи не є причиною цього, запустивши запущені екземпляри одного і того ж бінарного файлу та запустивши збірку. Обидва запущені екземпляри збилися б із SIGBUSпомилкою незабаром після закінчення збірки та замінили двійковий (той, який зараз запущені обидва екземпляри)

Основна причина: Це тому, що ОС обмінюється сторінками пам'яті, а в деяких випадках двійковий файл може бути не повністю завантажений в пам'ять, і ці збої відбудуться, коли ОС намагається отримати наступну сторінку з того ж бінарного файлу, але бінарний файл змінився з моменту останнього читати.


Погоджено, це найчастіша причина помилок шини в моєму досвіді.
ітаїч

0

Щоб додати те, на що відповів blxtd, вище, помилки шини трапляються також тоді, коли ваш процес не може намагатися отримати доступ до пам'яті певної змінної .

for (j = 0; i < n; j++) {
    for (i =0; i < m; i++) {
        a[n+1][j] += a[i][j];
    }
}

Помічаєте " ненавмисне " використання змінної "i" у першому "for loop"? Ось що викликає помилку в шині в цьому випадку.


Якщо m> = n, то зовнішній цикл виконується один раз або взагалі не залежить, залежно від попереднього значення i. Якщо m <n, то він буде працювати безстроково зі збільшенням індексу j, поки у вас не вичерпаються межі вашого масиву і, швидше за все, виклик сегментації, а не помилки шини. Якщо цей код компілюється, то немає жодної проблеми з доступом до пам'яті самої змінної 'i'. Вибачте, але ця відповідь неправильна.
itaych

0

Я щойно з'ясував важкий шлях, що на процесорі ARMv7 ви можете написати якийсь код, який дає помилку сегментації, коли неоптимізований, але він дає вам помилку шини при компіляції з -O2 (оптимізуйте більше).

Я використовую перехресний компілятор GCC ARM gnueabihf з Ubuntu 64 біт.


Як це відповідає на питання?
Пітер Мортенсен

-1

Типовий перелив буфера, що призводить до помилки шини,

{
    char buf[255];
    sprintf(buf,"%s:%s\n", ifname, message);
}

Тут, якщо розмір рядка в подвійних лапки ("") перевищує розмір buf, це дає помилку шини.


1
Хе ... якби це було так, у вас виникли б проблеми помилок BUS замість вибухів, які ви читали про весь час для Windows та інших машин. Помилки BUS викликані спробою отримати доступ до "пам'яті", до якої машина просто не може отримати доступ, оскільки адреса недійсна. (Звідси помилка терміна "BUS".) Це може бути пов’язано з безліччю збоїв, включаючи недійсні вирівнювання та подібне, доки процесор не може розмістити адресу на шинах.
Швартальф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.