Ошибка шины против ошибки сегментации - PullRequest
37 голосов
/ 08 мая 2009

Разница между ошибкой шины и ошибкой сегментации? Может ли случиться так, что программа выдает ошибку сегмента и останавливается в первый раз, а во второй раз может выдать ошибку шины и выйти?

Ответы [ 7 ]

45 голосов
/ 08 мая 2009

На большинстве архитектур, которые я использовал, различие в том, что:

  • SEGV вызывается при доступе к памяти, к которой вы не предназначены (например, вне вашего адресного пространства).
  • SIGBUS вызывается из-за проблем с выравниванием с ЦП (например, при попытке чтения длинного с адреса, который не кратен 4).
20 голосов
/ 08 мая 2009

SIGBUS также будет вызываться, если вы mmap() файла и попытаетесь получить доступ к части сопоставленного буфера, который выходит за конец файла, а также для условий ошибки, таких как out пространства. Если вы зарегистрируете обработчик сигнала, используя sigaction(), и вы установили SA_SIGINFO, возможно, ваша программа сможет проверить ошибочный адрес памяти и обработать только ошибки файла с отображенной памятью.

6 голосов
/ 08 мая 2009

Например, ошибка шины может быть вызвана тем, что ваша программа пытается сделать что-то, что аппаратная шина не поддерживает. Например, в SPARCs при попытке прочитать многобайтовое значение (например, int, 32-битное) из нечетного адреса возникла ошибка шины.

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

4 голосов
/ 08 мая 2009

Полагаю, вы говорите о сигналах SIGSEGV и SIGBUS, определенных Posix.

SIGSEGV происходит, когда программа ссылается на неверный адрес. SIGBUS является аппаратной ошибкой, определяемой реализацией. Действие по умолчанию для этих двух сигналов - завершить программу.

Программа может ловить эти сигналы и даже игнорировать их.

2 голосов
/ 08 мая 2009

Интерпретация вашего вопроса (возможно, неверно) как означающего «я периодически получаю SIGSEGV или SIGBUS, почему это не согласовано?», Стоит отметить, что выполнение хитрых вещей с указателями не гарантируется стандартами C или C ++ привести к segfault; это просто «неопределенное поведение», которое, как однажды сказал мне профессор, означает, что вместо этого оно может заставить крокодилов выпасть из половиц и съесть вас.

Таким образом, ваша ситуация может заключаться в том, что у вас есть две ошибки, причем первая возникает иногда вызывает SIGSEGV, а вторая (если segfault не произошла и программа все еще работает) вызывает SIGBUS .

Я рекомендую вам пройтись с отладчиком и искать крокодилов.

1 голос
/ 18 июня 2018

Может ли случиться так, что программа выдает ошибку сегмента и останавливается в первый раз, а во второй раз может выдать ошибку шины и выйти?

Да, даже для одной и той же ошибки: Вот серьезный, но упрощенный пример из macOS, который может вызвать как ошибку сегментации (SIGSEGV), так и ошибку шины (SIGBUS) по индексам вне границ массива в детерминированный путь. Упомянутый выше не выровненный доступ не является проблемой для macOS. (Этот пример не вызовет никакого SIGBUS, если он работает внутри отладчика, lldb в моем случае!)

bus_segv.c:

#include <stdlib.h>

char array[10];

int main(int argc, char *argv[]) {
    return array[atol(argv[1])];
}

Пример берет целое число из командной строки, которое служит индексом для массива. Это некоторые значения индекса (даже вне массива), которые не будут вызывать никакого сигнала. (Все приведенные значения зависят от стандартных размеров сегментов / секций. Я использовал clang-902.0.39.1 для создания двоичного файла на Mac OS X High Sierra 10.13.5, i5-4288U CPU @ 2.60GHz.)

Индекс выше 77791 и ниже -4128 вызовет ошибку сегментации (SIGSEGV). 24544 вызовет ошибку шины (SIGBUS). Вот полная карта:

$ ./bus_segv -4129
Segmentation fault: 11
$ ./bus_segv -4128
...
$ ./bus_segv 24543
$ ./bus_segv 24544
Bus error: 10
...
$ ./bus_segv 28639
Bus error: 10
$ ./bus_segv 28640
...
$ ./bus_segv 45023
$ ./bus_segv 45024
Bus error: 10
...
$ ./bus_segv 53215
Bus error: 10
$ ./bus_segv 53216
...
$ ./bus_segv 69599
$ ./bus_segv 69600
Bus error: 10
...
$ ./bus_segv 73695
Bus error: 10
$ ./bus_segv 73696
...
$ ./bus_segv 77791
$ ./bus_segv 77792
Segmentation fault: 11

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

$ otool -tv bus_segv

bus_segv:
(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp, %rbp
0000000100000f64    subq    $0x10, %rsp
0000000100000f68    movl    $0x0, -0x4(%rbp)
0000000100000f6f    movl    %edi, -0x8(%rbp)
0000000100000f72    movq    %rsi, -0x10(%rbp)
0000000100000f76    movq    -0x10(%rbp), %rsi
0000000100000f7a    movq    0x8(%rsi), %rdi
0000000100000f7e    callq   0x100000f94 ## symbol stub for: _atol
0000000100000f83    leaq    0x96(%rip), %rsi
0000000100000f8a    movsbl  (%rsi,%rax), %eax
0000000100000f8e    addq    $0x10, %rsp
0000000100000f92    popq    %rbp    
0000000100000f93    retq    

К leaq 0x96(%rip), %rsi rsi становится (ПК относительно определен) адрес начального адреса массива:

rsi = 0x100000f8a + 0x96 = 0x100001020
rsi - 4128 = 0x100000000 (below segmentation fault)
rsi + 24544 = 0x100007000 (here and above bus error)
rsi + 28640 = 0x100008000 (below bus error)
rsi + 45024 = 0x10000c000 (here and above bus error)
rsi + 53216 = 0x10000e000 (below bus error)
rsi + 69600 = 0x100012000 (here and above bus error)
rsi + 73696 = 0x100013000 (below bus error)
rsi + 77792 = 0x100014000 (here and above segmentation fault)

lldb, вероятно, настраивает процесс с различными ограничениями страницы. Я не смог воспроизвести ошибки шины в сеансе отладки. Таким образом, отладчик мог бы быть обходным путем для двоичных файлов с ошибками шины.

Andreas

1 голос
/ 08 мая 2009

Это будет дублирование Что такое ошибка шины? , если бы не

Может ли случиться так, что программа выдает ошибку сегмента и останавливается в первый раз, а во второй раз может вызвать ошибку шины и выйти?

часть вопроса. Вы должны быть в состоянии ответить на этот вопрос самостоятельно с помощью информации, найденной здесь.


Безумие: делать одно и то же снова и снова и ожидать разных результатов.
- Альберт Эйнштейн


Конечно, принимая вопрос буквально ...

#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main() {
    srand(time(NULL));
    if (rand() % 2)
        kill(getpid(), SIGBUS);
    else
        kill(getpid(), SIGSEGV);
    return 0;
}

Tada, программа, которая может выйти с ошибкой сегментации при одном запуске и выйти с ошибкой шины при другом запуске.

...