Darwin是Mac OS和IOS的操作系统,而XNU是Darwin操作系统的内核部分。XNU是混合内核,兼具宏内核和微内核的特性,而Mach即为其微内核,如图所示:
所以,mach异常是内核层发生的异常。
1、Mach异常的触发和调用流程
Mach异常是最底层的内核异常,用捕获OC异常的方法无法拿到,Mach异常的触发和调用流程如下图所示:
- 1、硬件产生的信号被Mach层捕获
- 2、Mach异常处理程序
exception_triage()
通过调用exception_deliver()
首先尝试将异常抛给thread端口、然后尝试抛给task端口,最后再抛给host端口(默认端口),exception_deliver
通过调用mach_exception_raise
,触发异常; - 3、异常在内核中以消息机制进行处理,通过
task_set_exception_posrts()
设置自定义的接收Mach异常消息的端口,相当于插入了一个exception
处理程序。 - 4、Mach 异常在Mach层被捕获并抛出后,会在BSD层被
catch_mach_exception_raise
处理,并通过ux_exception()
将异常转换为对应的UNIX信号,并通过threadsignal()
将信号投递到出错线程,iOS中的 POSIX API 就是通过 Mach 之上的 BSD 层实现的。
2、mach异常的捕获
mach的部分api暴露给了用户态,用户态的开发者可以直接通过Mach API设置thread、task和host的异常端口,来捕获Mach异常,抓取Mach Crash,捕获流程如下:
代码如下:
#pragma mark - Primary
-(void)registerCatch{
NSLog(@"haleli >>> switch_mach_crash : %@",@"开始") ;
[[LLDebugTool sharedTool] saveMachCrashSwitch:YES];
/**
Masks for exception definitions
**/
exception_mask_t mask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC |
EXC_MASK_SOFTWARE |
EXC_MASK_BREAKPOINT;
kern_return_t kr ;
const task_t thisTask = mach_task_self() ;
//分配端口
if(exceptionPort == MACH_PORT_NULL){
kr = mach_port_allocate(thisTask,MACH_PORT_RIGHT_RECEIVE,&exceptionPort) ;
if(kr != KERN_SUCCESS){
NSLog(@"haleli >>> failed to allocate exception port : %s",mach_error_string(kr)) ;
return ;
}else{
NSLog(@"haleli >>> allocate exception port : %d",exceptionPort) ;
}
kr = mach_port_insert_right(thisTask, exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND);
if(kr != KERN_SUCCESS){
NSLog(@"haleli >>> failed to insert rights : %s",mach_error_string(kr)) ;
return ;