前言
大家都可能都在自己的應用中集成Crash收集服務哄陶,通常使用NSSetUncaughtExceptionHandler()
+ signal() / sigaction()
的方式炎码。它可以幫助我們收集到大部分Crash谤饭,直到后來發(fā)現(xiàn)stack overflow并不能被以上方法撲捉到附鸽,而且其它一些SDK也未能收集。那這篇文章簡單介紹下Mach異常與signal的聯(lián)系郭厌。
OS X 诫硕、iOS系統(tǒng)架構
這張圖片來之蘋果Mac Technology Overview,除了用戶體驗層鳄橘,OS X與iOS架構大體上是一直的声离。它們內(nèi)核核心都是XNU(包含Mach、BSD)瘫怜。Mach是微內(nèi)核术徊,負責操作系統(tǒng)中基本職責:進程和線程抽象、虛擬內(nèi)存管理鲸湃、任務調(diào)度赠涮、進程間通信和消息傳遞機制子寓。BSD層簡歷在Mach上,提供一套可靠且更現(xiàn)代的API笋除,提供了POSIX兼容性斜友。
本文用到的XNU的版本號為3248.60.10的源碼,下載地址垃它。
你也可以在http://opensource.apple.com 中下載歷史版本鲜屏。
Mach exceptions 與 POSIX signals
Exception Type項通常會包含兩個元素: Mach異常 和 Unix信號。
Mach exceptions: 允許在進程里或進程外處理国拇,處理程序通過Mach RPC調(diào)用洛史。
POSIX signals: 只在進程中處理,處理程序總是在發(fā)生錯誤的線程上調(diào)用酱吝。
Mach
異常首先是由處理器陷阱引發(fā)的也殖。 通用的Mach異常處理程序exception_triage()
,負責將異常轉(zhuǎn)換成Mach 消息掉瞳。exception_triage()
通過調(diào)用exception_deliver()
嘗試把異常投遞到thread毕源、task最后是host。首先嘗試將異常拋給thread端口陕习,然后嘗試拋給task端口霎褐,最后再拋給host端口(默認端口),如果沒有一個端口返回KERN_SUCCESS该镣,那么任務就會被終止冻璃。
// 位于 osfmk/kern/exception.c
/*
* Routine: exception_triage
* Purpose:
* The current thread caught an exception.
* We make an up-call to the thread's exception server.
* Conditions:
* Nothing locked and no resources held.
* Called from an exception context, so
* thread_exception_return and thread_kdb_return
* are possible.
* Returns:
* KERN_SUCCESS if exception is handled by any of the handlers.
*/
kern_return_t
exception_triage(
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt)
{
thread_t thread;
task_t task;
host_priv_t host_priv;
lck_mtx_t *mutex;
kern_return_t kr = KERN_FAILURE;
assert(exception != EXC_RPC_ALERT);
/*
* If this behavior has been requested by the the kernel
* (due to the boot environment), we should panic if we
* enter this function. This is intended as a debugging
* aid; it should allow us to debug why we caught an
* exception in environments where debugging is especially
* difficult.
*/
if (panic_on_exception_triage) {
panic("called exception_triage when it was forbidden by the boot environment");
}
thread = current_thread();
// 分別嘗試把異常投遞到thread、task最后是host损合。
/*
* Try to raise the exception at the activation level.
*/
mutex = &thread->mutex;
if (KERN_SUCCESS == check_exc_receiver_dependency(exception, thread->exc_actions, mutex))
{
kr = exception_deliver(thread, exception, code, codeCnt, thread->exc_actions, mutex);
if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
goto out;
}
/*
* Maybe the task level will handle it.
*/
task = current_task();
mutex = &task->lock;
if (KERN_SUCCESS == check_exc_receiver_dependency(exception, task->exc_actions, mutex))
{
kr = exception_deliver(thread, exception, code, codeCnt, task->exc_actions, mutex);
if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
goto out;
}
/*
* How about at the host level?
*/
host_priv = host_priv_self();
mutex = &host_priv->lock;
if (KERN_SUCCESS == check_exc_receiver_dependency(exception, host_priv->exc_actions, mutex))
{
kr = exception_deliver(thread, exception, code, codeCnt, host_priv->exc_actions, mutex);
if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
goto out;
}
out:
if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) &&
(exception != EXC_GUARD) && (exception != EXC_CORPSE_NOTIFY))
thread_exception_return();
return kr;
}
異常行為
/*
* Machine-independent exception behaviors
*/
# define EXCEPTION_DEFAULT 1 // Send a catch_exception_raise message including the identity.
# define EXCEPTION_STATE 2 // Send a catch_exception_raise_state message including the thread state.
# define EXCEPTION_STATE_IDENTITY 3 // Send a catch_exception_raise_state_identity message including the thread identity and state.
// 位于 osfmk/kern/exception.c
/*
* Routine: exception_deliver
* Purpose:
* Make an upcall to the exception server provided.
* Conditions:
* Nothing locked and no resources held.
* Called from an exception context, so
* thread_exception_return and thread_kdb_return
* are possible.
* Returns:
* KERN_SUCCESS if the exception was handled
*/
kern_return_t
exception_deliver(
thread_t thread,
exception_type_t exception,
mach_exception_data_t code,
mach_msg_type_number_t codeCnt,
struct exception_action *excp,
lck_mtx_t *mutex)
{
... // 省略部分代碼
switch (behavior) {
case EXCEPTION_STATE: {
mach_msg_type_number_t state_cnt;
thread_state_data_t state;
c_thr_exc_raise_state++;
state_cnt = _MachineStateCount[flavor];
kr = thread_getstatus(thread, flavor,
(thread_state_t)state,
&state_cnt);
if (kr == KERN_SUCCESS) {
if (code64) {
kr = mach_exception_raise_state(exc_port,
exception,
code,
codeCnt,
&flavor,
state, state_cnt,
state, &state_cnt);
} else {
kr = exception_raise_state(exc_port, exception,
small_code,
codeCnt,
&flavor,
state, state_cnt,
state, &state_cnt);
}
if (kr == MACH_MSG_SUCCESS && exception != EXC_CORPSE_NOTIFY)
kr = thread_setstatus(thread, flavor,
(thread_state_t)state,
state_cnt);
}
return kr;
}
case EXCEPTION_DEFAULT:
c_thr_exc_raise++;
if (code64) {
kr = mach_exception_raise(exc_port,
retrieve_thread_self_fast(thread),
retrieve_task_self_fast(thread->task),
exception,
code,
codeCnt);
} else {
kr = exception_raise(exc_port,
retrieve_thread_self_fast(thread),
retrieve_task_self_fast(thread->task),
exception,
small_code,
codeCnt);
}
return kr;
case EXCEPTION_STATE_IDENTITY: {
mach_msg_type_number_t state_cnt;
thread_state_data_t state;
c_thr_exc_raise_state_id++;
state_cnt = _MachineStateCount[flavor];
kr = thread_getstatus(thread, flavor,
(thread_state_t)state,
&state_cnt);
if (kr == KERN_SUCCESS) {
if (code64) {
kr = mach_exception_raise_state_identity(exc_port,
retrieve_thread_self_fast(thread),
retrieve_task_self_fast(thread->task),
exception,
code,
codeCnt,
&flavor,
state, state_cnt,
state, &state_cnt);
} else {
kr = exception_raise_state_identity(exc_port,
retrieve_thread_self_fast(thread),
retrieve_task_self_fast(thread->task),
exception,
small_code,
codeCnt,
&flavor,
state, state_cnt,
state, &state_cnt);
}
if (kr == MACH_MSG_SUCCESS && exception != EXC_CORPSE_NOTIFY)
kr = thread_setstatus(thread, flavor,
(thread_state_t)state,
state_cnt);
}
return kr;
}
default:
panic ("bad exception behavior!");
return KERN_FAILURE;
}/* switch */
}
BSD
當?shù)谝粋€BSD進程調(diào)用bsdinit_task()
函數(shù)啟動時省艳,這函數(shù)還調(diào)用了ux_handler_init()
函數(shù)設置了一個Mach內(nèi)核線程跑ux_handler()
的。
// 位于bsd/kern/bsd_init.c
/* Called with kernel funnel held */
void
bsdinit_task(void)
{
proc_t p = current_proc();
struct uthread *ut;
thread_t thread;
process_name("init", p);
ux_handler_init(); // 初始化handler
// 設置port
thread = current_thread();
(void) host_set_exception_ports(host_priv_self(),
EXC_MASK_ALL & ~(EXC_MASK_RPC_ALERT),//pilotfish (shark) needs this port
(mach_port_t) ux_exception_port,
EXCEPTION_DEFAULT| MACH_EXCEPTION_CODES,
0);
ut = (uthread_t)get_bsdthread_info(thread);
bsd_init_task = get_threadtask(thread);
init_task_failure_data[0] = 0;
#if CONFIG_MACF
mac_cred_label_associate_user(p->p_ucred);
mac_task_label_update_cred (p->p_ucred, (struct task *) p->task);
#endif
load_init_program(p);
lock_trace = 1;
}
// 位于bsd/uxkern/ux_exception.c
void
ux_handler_init(void)
{
thread = THREAD_NULL;
ux_exception_port = MACH_PORT_NULL;
(void) kernel_thread_start((thread_continue_t)ux_handler, NULL, &thread);
thread_deallocate(thread);
proc_list_lock();
if (ux_exception_port == MACH_PORT_NULL) {
(void)msleep(&ux_exception_port, proc_list_mlock, 0, "ux_handler_wait", 0);
}
proc_list_unlock();
}
static void
ux_handler(void)
{
task_t self = current_task();
mach_port_name_t exc_port_name;
mach_port_name_t exc_set_name;
/* self->kernel_vm_space = TRUE; */
ux_handler_self = self;
... // 省略部分
/* Message handling loop. */
// 消息處理循環(huán)
for (;;) {
struct rep_msg {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} rep_msg;
struct exc_msg {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
mach_exception_data_t code;
/* some times RCV_TO_LARGE probs */
char pad[512];
} exc_msg;
mach_port_name_t reply_port;
kern_return_t result;
exc_msg.Head.msgh_local_port = CAST_MACH_NAME_TO_PORT(exc_set_name);
exc_msg.Head.msgh_size = sizeof (exc_msg);
#if 0
result = mach_msg_receive(&exc_msg.Head);
#else
result = mach_msg_receive(&exc_msg.Head, MACH_RCV_MSG,
sizeof (exc_msg), exc_set_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
0);
#endif
if (result == MACH_MSG_SUCCESS) {
reply_port = CAST_MACH_PORT_TO_NAME(exc_msg.Head.msgh_remote_port);
// 消息處理
if (mach_exc_server(&exc_msg.Head, &rep_msg.Head)) {
result = mach_msg_send(&rep_msg.Head, MACH_SEND_MSG,
sizeof (rep_msg),MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL);
if (reply_port != 0 && result != MACH_MSG_SUCCESS)
mach_port_deallocate(get_task_ipcspace(ux_handler_self), reply_port);
}
}
else if (result == MACH_RCV_TOO_LARGE)
/* ignore oversized messages */;
else
panic("exception_handler");
}
}
每一個thread嫁审、task及host自身都有一個異常端口數(shù)組跋炕,通過調(diào)用xxx_set_exception_ports()
(xxx為thread、task或host)可以設置這些異常端口律适。 xxx_set_exception_ports()
第四個參數(shù)為exception_behavior_t behavior
辐烂,這將會使用到與行為相匹配的實現(xiàn)(exc.defs 或 mach_exc.defs)。
各種行為都在host層被catch_[mach]_exception_xxx處理捂贿,64位的對應的是有mach
函數(shù)(可在/bsd/uxkern/ux_exception.c查看)纠修。
這些函數(shù)通過調(diào)用ux_exception()
將異常轉(zhuǎn)換為對應的UNIX信號,并通過threadsignal()
將信號投遞到出錯線程厂僧。
// EXCEPTION_DEFAULT行為 64位處理函數(shù)
kern_return_t
catch_mach_exception_raise(
__unused mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
__unused mach_msg_type_number_t codeCnt
)
{
task_t self = current_task();
thread_t th_act;
ipc_port_t thread_port;
struct proc *p;
kern_return_t result = MACH_MSG_SUCCESS;
int ux_signal = 0;
mach_exception_code_t ucode = 0;
struct uthread *ut;
mach_port_name_t thread_name = CAST_MACH_PORT_TO_NAME(thread);
mach_port_name_t task_name = CAST_MACH_PORT_TO_NAME(task);
/*
* Convert local thread name to global port.
*/
if (MACH_PORT_VALID(thread_name) &&
(ipc_object_copyin(get_task_ipcspace(self), thread_name,
MACH_MSG_TYPE_PORT_SEND,
(void *) &thread_port) == MACH_MSG_SUCCESS)) {
if (IPC_PORT_VALID(thread_port)) {
th_act = convert_port_to_thread(thread_port);
ipc_port_release_send(thread_port);
} else {
th_act = THREAD_NULL;
}
/*
* Catch bogus ports
*/
if (th_act != THREAD_NULL) {
/*
* Convert exception to unix signal and code.
* 調(diào)用 ux_exception將exception轉(zhuǎn)成UNIX信號
*/
ux_exception(exception, code[0], code[1], &ux_signal, &ucode);
ut = get_bsdthread_info(th_act);
p = proc_findthread(th_act);
/* Can't deliver a signal without a bsd process reference */
// 如果未找到進程扣草,那么這個信號就不會投遞了
if (p == NULL) {
ux_signal = 0;
result = KERN_FAILURE;
}
/*
* Stack overflow should result in a SIGSEGV signal
* on the alternate stack.
* but we have one or more guard pages after the
* stack top, so we would get a KERN_PROTECTION_FAILURE
* exception instead of KERN_INVALID_ADDRESS, resulting in
* a SIGBUS signal.
* Detect that situation and select the correct signal.
*/
if (code[0] == KERN_PROTECTION_FAILURE &&
ux_signal == SIGBUS) {
user_addr_t sp, stack_min, stack_max;
int mask;
struct sigacts *ps;
sp = code[1];
stack_max = p->user_stack;
stack_min = p->user_stack - MAXSSIZ;
if (sp >= stack_min &&
sp < stack_max) {
/*
* This is indeed a stack overflow. Deliver a
* SIGSEGV signal.
* 因為棧溢出需返回的是SIGSEGV,這里把SIGBUS替換成SIGSEGV
*/
ux_signal = SIGSEGV;
/*
* If the thread/process is not ready to handle
* SIGSEGV on an alternate stack, force-deliver
* SIGSEGV with a SIG_DFL handler.
*/
mask = sigmask(ux_signal);
ps = p->p_sigacts;
if ((p->p_sigignore & mask) ||
(ut->uu_sigwait & mask) ||
(ut->uu_sigmask & mask) ||
(ps->ps_sigact[SIGSEGV] == SIG_IGN) ||
(! (ps->ps_sigonstack & mask))) {
p->p_sigignore &= ~mask;
p->p_sigcatch &= ~mask;
ps->ps_sigact[SIGSEGV] = SIG_DFL;
ut->uu_sigwait &= ~mask;
ut->uu_sigmask &= ~mask;
}
}
}
/*
* Send signal. 發(fā)送信號
*/
if (ux_signal != 0) {
ut->uu_exception = exception;
//ut->uu_code = code[0]; // filled in by threadsignal
ut->uu_subcode = code[1];
threadsignal(th_act, ux_signal, code[0]);
}
if (p != NULL)
proc_rele(p);
thread_deallocate(th_act);
}
else
result = KERN_INVALID_ARGUMENT;
}
else
result = KERN_INVALID_ARGUMENT;
/*
* Delete our send rights to the task port.
*/
(void)mach_port_deallocate(get_task_ipcspace(ux_handler_self), task_name);
return (result);
}
所以,如果異常是棧溢出辰妙,那么signal是SIGSEGV而不是SIGBUS鹰祸;如果進程退出了或者線程/進程未準備好處理signal,所注冊的signal()
是無法接收信號的上岗。
// 位于bsd/uxkern/ux_exception.c
/*
* 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;
case EXC_ARITHMETIC:
*ux_signal = SIGFPE;
break;
case EXC_EMULATION:
*ux_signal = SIGEMT;
break;
case EXC_SOFTWARE:
switch (code) {
case EXC_UNIX_BAD_SYSCALL:
*ux_signal = SIGSYS;
break;
case EXC_UNIX_BAD_PIPE:
*ux_signal = SIGPIPE;
break;
case EXC_UNIX_ABORT:
*ux_signal = SIGABRT;
break;
case EXC_SOFT_SIGNAL:
*ux_signal = SIGKILL;
break;
}
break;
case EXC_BREAKPOINT:
*ux_signal = SIGTRAP;
break;
}
}
把Mach exception 和 UNIX signal 的轉(zhuǎn)換制表后福荸,如下
用下圖來展示而Mach exception轉(zhuǎn)換成signal的流程(圖截自 Mac OS X and iOS Internals)
實踐
在Mach中蕴坪,異常是通過內(nèi)核中的主要設施-消息傳遞機制-進行處理的肴掷。一個異常與一條消息并無差別,由出錯的線程或任務(通過 msg_send()
)發(fā)送背传,并通過一個處理程(通過 msg_recv()
)接收呆瞻。
由于Mach的異常以消息機制處理而不是通過函數(shù)調(diào)用,exception messages可以被轉(zhuǎn)發(fā)到先前注冊的Mach exception處理程序径玖。這意味著你可以插入一個exception處理程序痴脾,而不干擾現(xiàn)有的無論是調(diào)試器或Apple's crash reporter∈嵝牵可以使用mach_msg() // flag MACH_SEND_MSG
發(fā)送原始消息到以前注冊的處理程序的Mach端口赞赖,將消息轉(zhuǎn)發(fā)到一個現(xiàn)有的處理程序。
知道以上這些冤灾,那我們來嘗試撲捉一下Mach異常
void catchMACHExceptions() {
kern_return_t rc = 0;
exception_mask_t excMask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC |
EXC_MASK_SOFTWARE |
EXC_MASK_BREAKPOINT;
rc = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &myExceptionPort);
if (rc != KERN_SUCCESS) {
fprintf(stderr, "------->Fail to allocate exception port\\\\\\\\n");
return;
}
rc = mach_port_insert_right(mach_task_self(), myExceptionPort, myExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
if (rc != KERN_SUCCESS) {
fprintf(stderr, "-------->Fail to insert right");
return;
}
rc = thread_set_exception_ports(mach_thread_self(), excMask, myExceptionPort, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
if (rc != KERN_SUCCESS) {
fprintf(stderr, "-------->Fail to set exception\\\\\\\\n");
return;
}
// at the end of catchMachExceptions, spawn the exception handling thread
pthread_t thread;
pthread_create(&thread, NULL, exc_handler, NULL);
} // end catchMACHExceptions
static void *exc_handler(void *ignored) {
// Exception handler – runs a message loop. Refactored into a standalone function
// so as to allow easy insertion into a thread (can be in same program or different)
mach_msg_return_t rc;
fprintf(stderr, "Exc handler listening\\\\\\\\n");
// The exception message, straight from mach/exc.defs (following MIG processing) // copied here for ease of reference.
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} Request;
Request exc;
struct rep_msg {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} rep_msg;
for(;;) {
// Message Loop: Block indefinitely until we get a message, which has to be
// 這里會阻塞前域,直到接收到exception message,或者線程被中斷韵吨。
// an exception message (nothing else arrives on an exception port)
rc = mach_msg( &exc.Head,
MACH_RCV_MSG|MACH_RCV_LARGE,
0,
sizeof(Request),
myExceptionPort, // Remember this was global – that's why.
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if(rc != MACH_MSG_SUCCESS) {
/*... */
break ;
};
// Normally we would call exc_server or other. In this example, however, we wish
// to demonstrate the message contents:
printf("Got message %d. Exception : %d Flavor: %d. Code %lld/%lld. State count is %d\\\\\\\\n" ,
exc.Head.msgh_id, exc.exception, exc.flavor,
exc.code[0], exc.code[1], // can also print as 64-bit quantity
exc.old_stateCnt);
rep_msg.Head = exc.Head;
rep_msg.NDR = exc.NDR;
rep_msg.RetCode = KERN_FAILURE;
kern_return_t result;
if (rc == MACH_MSG_SUCCESS) {
result = mach_msg(&rep_msg.Head,
MACH_SEND_MSG,
sizeof (rep_msg),
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
}
}
return NULL;
} // end exc_handler
接下來匿垄,我們測試一下。
- (void)test
{
[self test];
}
調(diào)用這個方法归粉,結(jié)果如下
Exc handler listening
Got message 2401. Exception : 1 Flavor: 0. Code 2/1486065656. State count is 8
(lldb)
我們可以查看mach/exception_types.h
對exception type的定義
Exception: 1 椿疗,即為EXC_BAD_ACCESS
code: 2,即KERN_PROTECTION_FAILURE
而ux_exception()
函數(shù)告訴我們Code與signal是怎么轉(zhuǎn)換的。
也就是 Exception = EXC_BAD_ACCESS
糠悼, code = 2 對應的是SIGBUS
信號届榄,又因為為是stack overflow,信號應該是SIGSEGV倔喂。
那么結(jié)這個exception就是:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_PROTECTION_FAILURE
再試試其它的:
int *pi = (int*)0x00001111;
*pi = 17;
Exc handler listening
Got message 2401. Exception : 1 Flavor: 0. Code 1/4369. State count is 8
(lldb)
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS
最后
除了上面硬件產(chǎn)生的信號铝条,另外還有軟件產(chǎn)生的信號。軟件產(chǎn)生的信號來自kill()
滴劲、pthread_kill()
兩個函數(shù)的調(diào)用攻晒,大概過程是這樣的:kill()
/pthread_kill()
--> ...
--> psignal_internal()
--> act_set_astbsd()
。最終也會調(diào)用act_set_astbsd()
發(fā)送信號到目標線程班挖。這意味著Mach exception流程是不會走的鲁捏。
另外,在abort()源碼注釋著:<rdar://problem/7397932> abort() should call pthread_kill to deliver a signal to the aborting thread
, 它是這樣調(diào)用的(void)pthread_kill(pthread_self(), SIGABRT);
。Apple’s Crash Reporter 把SIGABRT的Mach exception type記為EXC_CRASH给梅,不同與上面轉(zhuǎn)變表假丧。
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
所以盡管Mach exception handle 比 UNIX signal handle 更有優(yōu)勢,但我們還是須要注冊signal handle用于處理EXC_SOFTWARE/EXC_CRASH动羽。
由于篇幅有限包帚,有些細節(jié)可能并沒有展現(xiàn)出來,如果你想有深入的了解运吓,可以閱讀下面參考文獻渴邦,或者一些開源框架。最后拘哨,希望這篇文章可以幫助你了解Mach exception谋梭。
參考文獻:
Apple Kernel Programming Guide
漫談iOS Crash收集框架
Mac OS X and iOS Internals:To the Apple’s Core(中文譯名:深入解析 MAC OS X & IOS 操作系統(tǒng))
Mach Exception Handlers
xnu-3248.60.10.tar.gz
Understanding Crash Reports on iPhone OS(wwdc)
Understanding and Analyzing iOS Application Crash Reports