Что такое ошибка шины?

255

Что означает сообщение об ошибке шины и чем оно отличается от сегфоута?

Raldi
источник
5
Я хотел бы добавить простое объяснение для обоих: ошибка сегментации означает, что вы пытаетесь получить доступ к памяти, к которой у вас нет доступа (например, она не является частью вашей программы). Тем не менее, при ошибке шины это обычно означает, что вы пытаетесь получить доступ к памяти, которой не существует (например, вы пытаетесь получить доступ к адресу на 12G, но у вас есть только 8G памяти) или если вы превышаете лимит используемой памяти.
xdevs23
На какой платформе вы это видели? ПК? Mac? x86? 32/64?
Питер Мортенсен

Ответы:

244

В настоящее время ошибки шины встречаются редко на x86 и возникают, когда ваш процессор не может даже попытаться получить доступ к памяти, как правило:

  • использование инструкции процессора с адресом, который не удовлетворяет его требованиям выравнивания.

Ошибки сегментации возникают при доступе к памяти, которая не принадлежит вашему процессу, они очень распространены и обычно являются результатом:

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

PS: если быть более точным, это не манипулирование самим указателем, который вызовет проблемы, это доступ к памяти, на которую он указывает (разыменование).

bltxd
источник
106
Они не редкость; Я только на Упражнении 9 из «Как выучить С трудным путем» и уже столкнулся с одним ...
11684
24
Другой причиной ошибок шины (в любом случае в Linux) является то, что операционная система не может создать резервную копию виртуальной страницы с физической памятью (например, из-за нехватки памяти или из-за огромных страниц при использовании огромной памяти страниц.) Обычно mmap (и malloc) просто зарезервируйте виртуальное адресное пространство, и ядро ​​назначает физическую память по требованию (так называемые ошибки программных страниц). Создайте достаточно большой 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).

От: Здесь

Oli
источник
1
Зависит от неприятных трюков, которые вы делаете со своим кодом. Вы можете вызвать ошибку BUS / Alignment Trap, если вы делаете что-то глупое, как, например, сделать указатель с математикой и затем typecast для доступа к проблемному режиму (т. Е. Вы устанавливаете массив uint8_t, добавляете один, два или три к указателю массива и затем typecast на короткий, int или long и попытайтесь получить доступ к ошибочному результату.) Системы X86 в значительной степени позволят вам сделать это, хотя и с реальным снижением производительности. НЕКОТОРЫЕ системы ARMv7 позволят вам сделать это, но большинство ARM, MIPS, Power и т. Д. Будут ворчать по этому поводу.
Свартальф
6

Вы также можете получить SIGBUS, если по какой-то причине невозможно вставить кодовую страницу.

Джошуа
источник
7
Это часто случается, когда я обновляю .so файл во время работы процесса
бедный разработчик
Другая причина, по которой это происходит, - если вы пытаетесь mmapсоздать файл, размер которого больше/dev/shm
ilija139
4

Один классический случай ошибки шины возникает в некоторых архитектурах, таких как SPARC (по крайней мере, некоторые SPARC, возможно, это было изменено), когда вы делаете неправильный доступ. Например:

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

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

размотать
источник
1
В случае, если у меня были данные [8]; Это теперь кратно 4 в 32-битной архитектуре. Итак, оно выровнено. Я все еще получу ошибку сейчас? Также, пожалуйста, объясните, является ли плохой идеей преобразование типов данных для указателей. Приведет ли это к ошибкам выравнивания на хрупкой архитектуре. Пожалуйста, опишите подробно, это поможет мне.
Ловкий
Хех. Это не столько преобразование типов, сколько преобразование типов для указателя, для которого вы выполняли математическую обработку указателя. Посмотрите внимательно на код выше. Компилятор тщательно выровнял указатель данных по данным с помощью dword, а затем вы все испортили в компиляторе, сместив ссылку на TWO и приведя тип к очень большому количеству необходимого для выравнивания по dword доступа к тому, что будет границей без dword.
Свартальф
«Хрупкий» - это не то слово, которое я бы использовал для всего этого. Машины и код X86 заставляют людей какое-то время делать довольно глупые поступки, и это один из них. Переосмыслите свой код, если у вас возникли проблемы такого рода - он не очень эффективен для X86 с самого начала.
Свартальф
@Svartalf: В x86 доступ к словам по невыровненным указателям, конечно, медленнее, чем доступ к словам по совмещенным указателям, но, по крайней мере, исторически они были быстрее, чем простой код, который безоговорочно собирает вещи из байтов, и они, безусловно, проще, чем код, который пытается использовать оптимальную комбинацию операций различного размера. Я хотел бы, чтобы стандарт C включал средства упаковки / распаковки больших целочисленных типов в / из последовательности меньших целых чисел / символов, чтобы позволить компилятору использовать любой подход, который лучше всего подходит для данной платформы.
суперкат
@Supercat: Дело в том, что на X86 вам это сойдет с рук. Вы попробуете это на ARM, MIPS, Power и т. Д., И с вами произойдут неприятные вещи. На ARM меньше, чем Arch V7, у вашего кода будет ошибка выравнивания, а на V7 вы можете, ЕСЛИ для этого установлена ​​ваша среда выполнения, справиться с ней С НЕСКОЛЬКИМ снижением производительности. Вы просто не хотите этого делать. Это плохая практика, чтобы быть тупым. : D
Svartalf
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"хранится в сегменте памяти, доступном только для чтения, поэтому запись в него невозможна. Это не защита от переполнения стека, а защита от записи в память (это дыра в безопасности, если ваша программа может переписать себя).
Марк Лаката
2

Это зависит от вашей ОС, процессора, компилятора и, возможно, других факторов.

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

-Адам

Адам Дэвис
источник
2

Обычно это означает неприсоединенный доступ.

Попытка получить доступ к памяти, которая физически отсутствует, также приведет к ошибке шины, но вы не увидите этого, если используете процессор с MMU и операционную систему, которая не глючит, потому что у вас не будет никаких -существующая память сопоставлена ​​с адресным пространством вашего процесса.

Марк Бейкер
источник
2
Мой i7, конечно, имеет MMU, но я все еще сталкивался с этой ошибкой, изучая C на OS X (передавая неинициализированный указатель на scanf). Означает ли это, что OS X Mavericks глючит? Каково было бы поведение на не глючной ОС?
Кельвин Хуан
2

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

goCards
источник
1

Причиной ошибки шины в Mac OS X было то, что я попытался выделить около 1 МБ в стеке. Это хорошо работало в одном потоке, но при использовании openMP это приводит к ошибке шины, потому что Mac OS X имеет очень ограниченный размер стека для неосновных потоков .

Alleo
источник
1

Я согласен со всеми ответами выше. Вот мои 2 цента относительно ошибки шины:

Ошибка 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»? Вот что в этом случае вызывает ошибку шины.

stuxnetting
источник
Если m> = n, то внешний цикл будет выполняться один раз или не выполняться вообще, в зависимости от ранее существовавшего значения i. Если m <n, то он будет работать бесконечно с увеличением индекса j, пока вы не выйдете за пределы своего массива и, скорее всего, вызовете ошибку сегментации, а не ошибку шины. Если этот код компилируется, то нет проблем с доступом к памяти самой переменной 'i'. Извините, но этот ответ неверен.
итайч
0

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

Я использую кросс-компилятор GCC ARM gnueabihf из Ubuntu 64 бит.

oromoiluig
источник
Как это отвечает на вопрос?
Питер Мортенсен
-1

Типичное переполнение буфера, которое приводит к ошибке шины,

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

Здесь, если размер строки в двойных кавычках ("") больше размера буфера, это дает ошибку шины.

Виная Сагар
источник
1
Хех ... если бы это было так, у вас были бы проблемы с ошибкой BUS вместо того, чтобы разбивать стек, о котором вы читали все время для Windows и других машин. Ошибки шины вызваны попыткой доступа к «памяти», к которой машина просто не может получить доступ, потому что адрес неверен. (Отсюда и термин «ошибка BUS».) Это может быть связано с множеством сбоев, в том числе с неправильным выравниванием и т. П., Если процессор не может разместить адрес на линиях шины.
Свартальф