Ошибка шины против ошибки сегментации

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

7 ответов

Решение

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

  • SEGV вызывается, когда вы обращаетесь к памяти, к которой вы не предназначены (например, за пределами вашего адресного пространства).
  • SIGBUS вызывается из-за проблем с выравниванием с процессором (например, при попытке прочитать длинный из адреса, который не кратен 4).

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

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

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

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

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

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

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

Да, даже для одной и той же ошибки: Вот серьезный, но упрощенный пример из 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), %rsirsi становится (относительно ПК) адресом начального адреса массива:

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

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

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

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

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

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

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


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


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

#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, программа, которая может выйти с ошибкой сегментации при одном запуске и выйти с ошибкой шины при другом запуске.

Другие вопросы по тегам