"EXC_BAD_ACCESS" и "Ошибка сегментации". Оба они практически одинаковы?

В моих первых маленьких фиктивных приложениях (для практики во время обучения) я столкнулся с большим количеством EXC_BAD_ACCESS, что каким-то образом научило меня Bad-Access: вы касаетесь/Доступ к объекту, которого вы не должны, потому что либо он еще не выделено или освобождено или просто у вас нет доступа к нему.

Посмотрите на этот пример кода, который имеет проблему с плохим доступом, потому что я пытаюсь изменить const:

-(void)myStartMethod{
    NSString *str = @"testing";
    const char *charStr = [str UTF8String];
    charStr[4] = '\0'; // bad access on this line.
    NSLog(@"%s",charStr);
} 

В то время как ошибка сегментации говорит: Ошибка сегментации - это определенная ошибка, вызванная доступом к памяти, которая "не принадлежит вам". Это вспомогательный механизм, который мешает вам разлагать память и вводить жесткие ошибки памяти. Всякий раз, когда вы получаете segfault, вы знаете, что делаете что-то неправильно с памятью (подробнее здесь.

Я хочу знать две вещи. Один, я прав о objective-C EXC_BAD_ACCESS? Правильно ли я понимаю?

Второй, Есть EXC_BAD_ACCESS and Segmentation fault то же самое, и Apple просто импровизирует его имя?

Ответ 1

Нет, EXC_BAD_ACCESS не совпадает с SIGSEGV.

EXC_BAD_ACCESS - исключение Mach (комбинация Mach и xnu составляют ядро ​​Mac OS X), а SIGSEGV - сигнал POSIX. При возникновении сбоев с причиной, заданной как EXC_BAD_ACCESS, часто сигнал сообщается в скобках сразу после: например, EXC_BAD_ACCESS(SIGSEGV). Однако есть еще один сигнал POSIX, который можно увидеть в сочетании с EXC_BAD_ACCESS: это SIGBUS, как EXC_BAD_ACCESS(SIGBUS).

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

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

Чтобы пировать глаза, вот код, в исходном коде ядра xnu-1504.15.3 (Mac OS X 10.6.8 build 10K459), файл bsd/uxkern/ux_exception.c, начинающийся с строки 429, который переводит EXC_BAD_ACCESS на SIGSEGV или SIGBUS.

/*
 *  ux_exception translates a mach exception, code and subcode to
 *  a signal and u.u_code.  Calls machine_exception (machine dependent)
 *  to attempt translation first.
 */

static
void ux_exception(
        int         exception,
        mach_exception_code_t   code,
        mach_exception_subcode_t subcode,
        int         *ux_signal,
        mach_exception_code_t   *ux_code)
{
    /*
     *  Try machine-dependent translation first.
     */
    if (machine_exception(exception, code, subcode, ux_signal, ux_code))
    return;

    switch(exception) {

    case EXC_BAD_ACCESS:
        if (code == KERN_INVALID_ADDRESS)
            *ux_signal = SIGSEGV;
        else
            *ux_signal = SIGBUS;
        break;

    case EXC_BAD_INSTRUCTION:
        *ux_signal = SIGILL;
        break;
    ...

Изменить по отношению к другим вопросам

Обратите внимание, что здесь исключение не относится к исключению на уровне языка, типа, который может быть захвачен синтаксическим сахаром, например try{} catch{}. Исключение здесь относится к действиям центрального процессора при столкновении с определенными типами ошибок в вашей программе (они могут быть или не быть фатальными), как разыменование нулевого указателя, которые требуют внешнего вмешательства.

Когда это происходит, процессор, как говорят, повышает то, что обычно называют исключением или прерыванием. Это означает, что CPU сохраняет то, что он делает (контекст), и имеет дело с исключительной ситуацией.

Чтобы справиться с такой исключительной ситуацией, CPU не запускает какой-либо код обработки исключений (catch -блоков или подобных) в вашем приложении. Сначала он дает управление ОС, начав выполнять предоставленный ядром код, называемый подпрограммой прерывания. Это фрагмент кода, который определяет, что случилось с тем процессом и что с ним делать. Таким образом, ОС имеет возможность судить о ситуации и принимать необходимые меры.

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

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

Таким образом, подобные исключения полностью отличаются от сортировки throw в try{} -блоках и catch{} es. Эти исключения обрабатываются исключительно внутри приложения, не затрагивая ОС вообще. Здесь происходит то, что оператор throw представляет собой просто прославленный переход к самому внутреннему блоку catch, который обрабатывает это исключение. По мере того как исключение пузырится через стек, он разматывает стек за ним, запускает деструкторы и т.п., Как это необходимо.

Ответ 2

В принципе да, действительно EXC_BAD_ACCESS обычно сопряжен с SIGSEGV, который является сигналом, предупреждающим о сбое сегментации.

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

Не думайте о ошибке сегментации в терминах "доступа к объекту", вы получаете доступ к ячейке памяти, поэтому адрес. Этот адрес должен быть согласован системой защиты ОС.

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