Mach是個(gè)什么鬼
蘋(píng)果的官方OS X和iOS文檔的分層:
- 用戶體驗(yàn)層
- 應(yīng)用框架層
- 核心框架層
- Darwin
Darwin 是完全開(kāi)源的,是整個(gè)系統(tǒng)的基礎(chǔ),提供了底層API。上層是閉源的。
這邊主要關(guān)注Darwin:

圖里面可以看出來(lái),mach同層次的還有I/OKit、libKern等等,和BSD一起都包含于XNU內(nèi)核。
現(xiàn)在XNU已經(jīng)開(kāi)開(kāi)源了,鏈接如下:
xnu源碼
內(nèi)核XNU是Darwin的核心,也是真?zhèn)€OSX的核心,包括幾個(gè)組件:
- Mach微內(nèi)核
- BSD層
- libKern
- I/OKit
其中Mach的職責(zé)是:
- 進(jìn)程和線程抽象
- 虛擬內(nèi)存管理
- 任務(wù)調(diào)度
- 進(jìn)程間通訊和消息傳遞機(jī)制
打個(gè)比方:
extern mach_port_t mach_host_self(void);//獲取主線程
extern mach_port_t mach_thread_self(void);//獲取當(dāng)前線程
extern mach_msg_return_t mach_msg(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify);//向線程發(fā)送/接受消息
那么基本上mach基本上是個(gè)什么東西可以有個(gè)了解了。
mach異常捕獲
理論依據(jù):
- 帶有一致語(yǔ)義的單一異常處理設(shè)施:Mach只提供了一個(gè)異常處理機(jī)制用于處理所有類(lèi)型的異常——包括用戶定義的異常、平臺(tái)無(wú)關(guān)的異常以及平臺(tái)特定的異常。根據(jù)異常的類(lèi)型對(duì)異常進(jìn)行分組,具體的平臺(tái)可以定義具體的字類(lèi)型。
- 清晰和簡(jiǎn)潔:異常處理的接口依賴于Mach已有的良好定義的消息和端口架構(gòu),因此非常優(yōu)雅。這就允許調(diào)試器和外部處理程序的擴(kuò)展——甚至在理論上還支持?jǐn)U產(chǎn)給予網(wǎng)絡(luò)的異常處理。
- Mach不提供異常處理邏輯:只提供傳遞異常通知的框架
帶有一致予語(yǔ)義的單一異常處理設(shè)施
異常是通過(guò)內(nèi)核中的基礎(chǔ)設(shè)施——消息傳遞機(jī)制異議處理的。
一個(gè)異常基本跟一條消息的復(fù)雜度差不多,所以異常也是由出錯(cuò)線程/任務(wù)(通過(guò)msg_send())拋出,然后由一個(gè)處理線程/端口/程序(通過(guò)msg_recv())捕獲。
// usr/include/mach/message.h
// #define MACH_SEND_MSG 0x00000001
// #define MACH_RCV_MSG 0x00000002
// mach_msg_option_t 詳見(jiàn):message.h:632
extern mach_msg_return_t mach_msg(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify);
處理程序可以處理異常,也可以清楚異常,或者終止線程。
Mach的異常處理模型和其他異常處理模型不同,其他異常處理模型可以運(yùn)行在出錯(cuò)線程的上下文中,但是Mach異常處理程序在不同的上下文中運(yùn)行。
// user/include/mach/task.h
/*
通過(guò)設(shè)置端口監(jiān)聽(tīng)出錯(cuò)信息,
但是并不是在出錯(cuò)線程監(jiān)聽(tīng),
所以沒(méi)法獲取線程的上下文,
要通過(guò)處理程序中提取出錯(cuò)線程的信息,
然后才可以獲取出錯(cuò)線程的上下文。
*/
kern_return_t task_set_exception_ports
(
task_t task,
exception_mask_t exception_mask,
mach_port_t new_port,
exception_behavior_t behavior,
thread_state_flavor_t new_flavor
);
通常情況下任務(wù)和線程的異常端口都是NULL,也就是異常不被處理。但是如果接了其他第三方崩潰收集的SDK的話。有可能不是NUUL。所以需要獲取第三方的端口。
// user/include/mach/task.h
kern_return_t task_get_exception_ports
(
task_inspect_t task,
exception_mask_t exception_mask,
exception_mask_array_t masks,
mach_msg_type_number_t *masksCnt,
exception_handler_array_t old_handlers,
exception_behavior_array_t old_behaviors,
exception_flavor_array_t old_flavors
);
Mach不提供異常處理邏輯###
發(fā)生異常的時(shí)候,首先嘗試將異常拋給線程的異常端口,然后嘗試拋給任務(wù)的異常端口,左后再拋給主機(jī)的異常端口。如果沒(méi)有一個(gè)端口返回 KERN_SUCCESS,整個(gè)任務(wù)被終止。
Mach異常捕捉
常見(jiàn)的Mach異常
usr/include/mach.exception_types.h
#define EXC_BAD_ACCESS 1 /* 內(nèi)存訪問(wèn)異常 */
/* code:描述錯(cuò)誤的Kern_return_t*/
/* subcode:發(fā)生內(nèi)存訪問(wèn)異常的地址 */
#define EXC_BAD_INSTRUCTION 2 /* 指令異常*/
/* 非法或未定義的指令或操作樹(shù)*/
#define EXC_ARITHMETIC 3 /* 算術(shù)異常*/
/* code:準(zhǔn)確的異常來(lái)源*/
#define EXC_EMULATION 4 /* 模擬指令異常*/
/* 遇到了模擬指令*/
/* code 和 subcode:詳細(xì)信息 */
#define EXC_SOFTWARE 5 /* 軟件產(chǎn)生的異常 */
/* code:具體的異常 */
/* Codes 0 - 0xFFFF :硬件 */
/* Codes 0x10000 - 0x1FFFF :操作系統(tǒng)模擬(Unix) */
#define EXC_BREAKPOINT 6 /* 和跟蹤、斷點(diǎn)相關(guān)的異常 */
/* code:詳細(xì)信息 */
#define EXC_SYSCALL 7 /* 系統(tǒng)調(diào)用 */
#define EXC_MACH_SYSCALL 8 /* Mach 系統(tǒng)調(diào)用 */
#define EXC_RPC_ALERT 9 /* RPC 報(bào)警 */
#define EXC_CRASH 10 /* 異常的進(jìn)程推出 */
#define EXC_RESOURCE 11 /* 達(dá)到資源消耗限制 */
/* code:詳細(xì)信息 */
#define EXC_GUARD 12 /* 違反保護(hù)資源保護(hù) */
#define EXC_CORPSE_NOTIFY 13 /* 異常過(guò)程退出尸體狀態(tài)*/
#define EXC_CORPSE_VARIANT_BIT 0x100 /* 變位用。*/
處理異常行為(flavor)###
| 行為 | 用途 |
|---|---|
| EXCEPTION_DEFAULT | 將線程標(biāo)識(shí)符傳遞給異常處理程序 |
| EXCEPTION_STATE | 將線程的寄存器狀態(tài)傳遞給異常處理程序。i386使用的是TREAD_STATE_X86和THREAD_STATE_64,ARM使用的是THREAD_STATE_ARM和THREAD_STATE_ARM_64 |
| EXCEPTION_STATE_IDENTITY | 將線程標(biāo)識(shí)符和狀態(tài)都傳給異常程序 |
捕捉
-
獲取已存在的異常處理端口:
//用于儲(chǔ)存已存在的異常端口 static struct { exception_mask_t masks[EXC_TYPES_COUNT]; exception_handler_t ports[EXC_TYPES_COUNT]; exception_behavior_t behaviors[EXC_TYPES_COUNT]; thread_state_flavor_t flavors[EXC_TYPES_COUNT]; mach_msg_type_number_t count; } dt_previousExceptionPorts; int get_previous_ports() { const task_t thisTask = mach_task_self(); kern_return_t kr; exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION| EXC_MASK_ARITHMETIC| EXC_MASK_CRASH| EXC_MASK_BREAKPOINT;; //獲取當(dāng)前的異常處理端口并儲(chǔ)存 kr = task_get_exception_ports(thisTask, mask, dt_previousExceptionPorts.masks, &dt_previousExceptionPorts.count, dt_previousExceptionPorts.ports, dt_previousExceptionPorts.behaviors, dt_previousExceptionPorts.flavors); if (KERN_SUCCESS != kr) { //獲得當(dāng)前端口失敗 return -1; } return 0; } -
創(chuàng)建一個(gè)新端口:
static mach_port_t exception_port = MACH_PORT_NULL; int create_new_port() { if (MACH_PORT_NULL == exception_port) { const task_t thisTask = mach_task_self(); kern_return_t kr; kr = mach_port_allocate(thisTask, MACH_PORT_RIGHT_RECEIVE, &exception_port); if (KERN_SUCCESS != kr) { //創(chuàng)建端口失敗 return -1 } return 0; } 添加端口權(quán)限:

int insert_right() {
const task_t thisTask = mach_task_self();
kern_return_t kr;
kr = mach_port_insert_right(thisTask,
exception_port,
exception_port,
MACH_MSG_TYPE_MAKE_SEND);
if(KERN_SUCCESS != kr) {
//設(shè)置權(quán)限失敗
return -1;
}
return 0;
}
-
設(shè)置異常監(jiān)聽(tīng)端口:
int set_exception_port() { const task_t thisTask = mach_task_self(); kern_return_t kr; kr = task_set_exception_ports(thisTask, mask, exception_port, EXCEPTION_STATE_IDENTITY, MACHINE_THREAD_STATE); if(KERN_SUCCESS != kr) { //設(shè)置監(jiān)聽(tīng)端口失敗 return -1; } return 0; } -
創(chuàng)建異常處理線程:
int create_exception_thread() { pthread_t thread; if (pthread_create(&thread,NULL,exc_handler,NULL) != 0) { return -1; } return 0; } 實(shí)現(xiàn)異常處理方法:
接受選項(xiàng)

發(fā)送選項(xiàng)

static void* exc_handler(void *arg) {
mach_msg_return_t mr;
__Request__exception_raise_state_identity_t request = {{0}};
while(true) {
//接受exception 消息
kr = mach_msg(&request.Head,
MACH_RCV_MSG,
0,
sizeof(__Request__exception_raise_state_identity_t),
exception_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (KERN_SUCCESS != kr) {return NULL;} else {break;}
}
/*
----------------
可以在這里對(duì)request里面的數(shù)據(jù)進(jìn)行處理
如:
1、堆棧獲取
2、崩潰類(lèi)型
3、code
4、subcode
----------------
*/
//重置端口
const task_t thisTask = mach_task_self();
kern_return_t kr;
for (mach_msg_type_number_t i = 0; i < dt_previousExceptionPorts.count; i ++) {
CuckooInfo(@"Resotring port index %d",i);
kr = task_set_exception_ports(thisTask,
dt_previousExceptionPorts.masks[i],
dt_previousExceptionPorts.ports[i],
dt_previousExceptionPorts.behaviors[i],
dt_previousExceptionPorts.flavors[i]);
if (KERN_SUCCESS != kr) {
return NULL;
}
}
__Reply__exception_raise_t reply = {{0}};
reply.Head = request.Head;
reply.NDR = request.NDR;
reply.RetCode = KERN_FAILURE;
//將消息發(fā)送出去,交給exception_port處理
//但是發(fā)送出去之后可能會(huì)被signal再次捕捉,
//所以需要過(guò)濾一些重復(fù)捕捉的東西。
kern_return_t kr = mach_msg(& reply.Head,
MACH_SEND_MSG,
sizeof(__Reply__exception_raise_t),
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (KERN_SUCCESS != kr) {
return NULL;
}
return NULL;
}
Mach異常解析
奔潰類(lèi)型解析
//usr/include/mach/exc.h
//__Request__exception_raise_state_t
//和__Request__exception_raise_state_identity_t 就不列舉了
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];
} __Request__exception_raise_t __attribute__((unused));
可以獲取request中的exception來(lái)獲取崩潰類(lèi)型。
SIGNAL類(lèi)型解析

通過(guò)結(jié)合exception和code[0]、code[1]來(lái)解析,可以通過(guò)xnu源碼看的到
bsd/dev/arm/unix_signal.c:713:
boolean_t
machine_exception(
int exception,
mach_exception_subcode_t code,
__unused mach_exception_subcode_t subcode,
int *unix_signal,
mach_exception_subcode_t * unix_code
)
{
switch (exception) {
case EXC_BAD_INSTRUCTION:
*unix_signal = SIGILL;
*unix_code = code;
break;
case EXC_ARITHMETIC:
*unix_signal = SIGFPE;
*unix_code = code;
break;
default:
return (FALSE);
}
return (TRUE);
}
bsd/uxkern/ux_exception.c:427:
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;
}
}
堆棧解析###
詳見(jiàn)堆棧解析