Ответ 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
, который обрабатывает это исключение. По мере того как исключение пузырится через стек, он разматывает стек за ним, запускает деструкторы и т.п., Как это необходимо.