這篇文章主要講解了“iOS Mach異常和signal信號分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“iOS Mach異常和signal信號分析”吧!
成都創(chuàng)新互聯(lián)公司主營羅田網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app軟件開發(fā),羅田h5微信小程序定制開發(fā)搭建,羅田網(wǎng)站營銷推廣歡迎羅田等地區(qū)企業(yè)咨詢
1. iOS Mach異常
1.1 XNU
Darwin是Mac OS和iOS的操作系統(tǒng),而XNU是Darwin操作系統(tǒng)的內(nèi)核部分。XNU是混合內(nèi)核,兼具宏內(nèi)核和微內(nèi)核的特性,而Mach即為其微內(nèi)核。
Darwin操作系統(tǒng)和MacOS、iOS系統(tǒng)版本號的對應(yīng)如上圖所示,Mac可執(zhí)行下述命令查看Darwin版本號。
system_profiler SPSoftwareDataType
1.2 Mach
Mach:[m?k],操作系統(tǒng)微內(nèi)核,是許多新操作系統(tǒng)的設(shè)計(jì)基礎(chǔ)。
Mach微內(nèi)核中有幾個(gè)基礎(chǔ)概念:
Tasks,擁有一組系統(tǒng)資源的對象,允許"thread"在其中執(zhí)行。
Threads,執(zhí)行的基本單位,擁有task的上下文,并共享其資源。
Ports,task之間通訊的一組受保護(hù)的消息隊(duì)列;task可對任何port發(fā)送/接收數(shù)據(jù)。
Message,有類型的數(shù)據(jù)對象集合,只可以發(fā)送到port。
1.3 模擬Mach Message發(fā)送
● Mach提供少量API,蘋果文檔介紹較少。
// 內(nèi)核中創(chuàng)建一個(gè)消息隊(duì)列,獲取對應(yīng)的port
mach_port_allocate();
// 授予task對port的指定權(quán)限
mach_port_insert_right();
// 通過設(shè)定參數(shù):MACH_RSV_MSG/MACH_SEND_MSG用于接收/發(fā)送mach message
mach_msg();
下述代碼模擬向Mach Port發(fā)送Message,接收Message后做處理:
● 首先調(diào)用createPortAndAddListener創(chuàng)建Mach Port;
● 調(diào)用sendMachPortMessage:向已創(chuàng)建的Mach Port發(fā)送消息;
● 執(zhí)行結(jié)果示例:
2018-02-27 09:33:37.797435+0800 xxx[54456:5198921] create a port: 41731
2018-02-27 09:33:37.797697+0800 xxx[54456:5198921] Send a mach message: [100].
2018-02-27 09:33:37.797870+0800 xxx[54456:5199525] Receive a mach message:[100], remote_port: 0, local_port: 41731, exception code: 28672
● 示例代碼:
// 創(chuàng)建Mach Port并監(jiān)聽消息
+ (mach_port_t)createPortAndAddListener {
mach_port_t server_port;
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port);
assert(kr == KERN_SUCCESS);
NSLog(@"create a port: %d", server_port);
kr = mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND);
assert(kr == KERN_SUCCESS);
[self setMachPortListener:server_port];
return server_port;
}
+ (void)setMachPortListener:(mach_port_t)mach_port {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
mach_message mach_message;
mach_message.Head.msgh_size = 1024;
mach_message.Head.msgh_local_port = server_port;
mach_msg_return_t mr;
while (true) {
mr = mach_msg(&mach_message.Head,
MACH_RCV_MSG | MACH_RCV_LARGE,
0,
mach_message.Head.msgh_size,
mach_message.Head.msgh_local_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (mr != MACH_MSG_SUCCESS && mr != MACH_RCV_TOO_LARGE) {
NSLog(@"error!");
}
mach_msg_id_t msg_id = mach_message.Head.msgh_id;
mach_port_t remote_port = mach_message.Head.msgh_remote_port;
mach_port_t local_port = mach_message.Head.msgh_local_port;
NSLog(@"Receive a mach message:[%d], remote_port: %d, local_port: %d, exception code: %d",
msg_id,
remote_port,
local_port,
mach_message.exception);
abort();
}
});
}
// 向指定Mach Port發(fā)送消息
+ (void)sendMachPortMessage:(mach_port_t)mach_port {
kern_return_t kr;
mach_msg_header_t header;
header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
header.msgh_size = sizeof(mach_msg_header_t);
header.msgh_remote_port = mach_port;
header.msgh_local_port = MACH_PORT_NULL;
header.msgh_id = 100;
NSLog(@"Send a mach message: [%d].", header.msgh_id);
kr = mach_msg(&header,
MACH_SEND_MSG,
header.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
}
1.4 捕獲Mach異常
● task_set_exception_ports() 設(shè)置內(nèi)核接收Mach異常消息的Port,替換為自定義的Port后,即可捕獲程序執(zhí)行過程中產(chǎn)生的異常消息。
● 執(zhí)行結(jié)果示例:
2018-02-27 09:52:11.483076+0800 xxx[55018:5253531] create a port: 23299
2018-02-27 09:52:14.484272+0800 xxx[55018:5253531] ********** Make a [BAD MEM ACCESS] now. **********
2018-02-27 09:52:14.484477+0800 xxx[55018:5253611] Receive a mach message:[2405], remote_port: 23555, local_port: 23299, exception code: 1
● 示例代碼:
+ (void)createAndSetExceptionPort {
mach_port_t server_port;
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port);
assert(kr == KERN_SUCCESS);
NSLog(@"create a port: %d", server_port);
kr = mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND);
assert(kr == KERN_SUCCESS);
kr = task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS | EXC_MASK_CRASH, server_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
[self setMachPortListener:server_port];
}
// 構(gòu)造BAD MEM ACCESS Crash
- (void)makeCrash {
NSLog(@"********** Make a [BAD MEM ACCESS] now. **********");
*((int *)(0x1234)) = 122;
}
1.5 Runloop
Mach Port的應(yīng)用不止于內(nèi)核級別,在Cocoa Foundation和Core Foundation層同樣有其應(yīng)用,比如說:Runloop。
Runloop sources分兩類:
1.Input sources
Port-Based sources
Custom Input sources
2.Timer sources
其中Port-Based sources即基于Mach Port,在Runloop中完成消息傳遞。
上述的Mach API為內(nèi)核層透出接口,Cocoa Foundation和Core Foundation層分別封裝了Mach Port的接口供調(diào)用,參考:Apple - Runloop Programming Guard,有詳細(xì)的示例代碼。
2. signal信號
signal是一種軟中斷信號,提供異步事件處理機(jī)制。signal是進(jìn)程間相互傳遞信息的一種粗糙方法,使用場景:
進(jìn)程終止相關(guān);
終端交互;
編程錯誤或硬件錯誤相關(guān),系統(tǒng)遇到不可恢復(fù)的錯誤時(shí)觸發(fā)崩潰機(jī)制讓程序退出,比如:除0、內(nèi)存寫入錯誤等。
這里我們主要考慮系統(tǒng)遇到不可恢復(fù)的錯誤時(shí)即Crash時(shí),信號相關(guān)的應(yīng)用。signal信號處理是UNIX操作系統(tǒng)機(jī)制,所以Android平臺理論上也是使用的,可以基于signal來捕獲Android Native Crash。
2.1 signal注冊和處理
signal()
#import<sys/signal.h>;
注冊signal handler;
調(diào)用成功時(shí),會移除signo信號當(dāng)前的操作,以handler指定的新信號處理程序替代;
信號處理函數(shù)返回void,因?yàn)闆]有地方給該函數(shù)返回。注冊自定義信號處理函數(shù),構(gòu)造Crash后,發(fā)出信號并執(zhí)行自定義信號處理邏輯。
【附】:Xcode Debug運(yùn)行時(shí),添加斷點(diǎn),在Crash觸發(fā)前,執(zhí)行pro hand -p true -s false SIGABRT命令。
(lldb) pro hand -p true -s false SIGABRT
NAME PASS STOP NOTIFY
=========== ===== ===== ======
SIGABRT true false true
2018-02-27 12:57:25.284651+0800 xxx[58061:5651844] ********** Make a 'NSRangeException' now. **********
2018-02-27 12:57:25.294945+0800 xxx[58061:5651844] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
2018-02-27 12:57:25.888332+0800 xxx[58061:5651844] [signal handler] - handle signal: 6
● 示例代碼:
// 設(shè)置自定義信號處理函數(shù)
+ (void)setSignalHandler {
signal(SIGABRT, test_signal_handler);
}
static void test_signal_handler(int signo) {
NSLog(@"[signal handler] - handle signal: %d", signo);
}
// 構(gòu)造NSRangeException異常,觸發(fā)SIGABRT信號發(fā)送
- (void)makeCrash {
NSLog(@"********** Make a 'NSRangeException' now. **********");
NSArray *array = @[ @"aaa" ];
}
2.2 LLDB Debugger
Xcode Debug模式運(yùn)行App時(shí),App進(jìn)程signal被LLDB Debugger調(diào)試器捕獲;需要使用LLDB調(diào)試命令,將指定signal處理拋到用戶層處理,方便調(diào)試。
● 查看全部信號傳遞配置:
// process handle縮寫
pro hand
● 修改指定信號傳遞配置:
// option:
// -P: PASS
// -S: STOP
// -N: NOTIFY
pro hand -option false 信號名
// 例:SIGABRT信號處理在LLDB不停止,可繼續(xù)拋到用戶層
pro hand -s false SIGABRT
2.3 可重入
向內(nèi)核發(fā)送信號時(shí),進(jìn)程可能執(zhí)行到代碼的任意位置,例:進(jìn)程在執(zhí)行重要操作,中斷后可能產(chǎn)生不一致狀態(tài),或進(jìn)程正在處理另一信號。因此要確保信號處理程序只執(zhí)行可重入操作:
● 寫中斷處理程序時(shí),假定中斷進(jìn)程可能處于不可重入函數(shù)中。
● 慎重修改全局?jǐn)?shù)據(jù)。
2.4 高級信號處理
signal()函數(shù)非常基礎(chǔ),只提供了最低限度的信號管理的標(biāo)準(zhǔn)。而sigaction()系統(tǒng)調(diào)用,提供更強(qiáng)大的信號管理能力。當(dāng)信號處理程序運(yùn)行時(shí),可以用來阻塞特定信號的接收,也可以用來獲取信號發(fā)送時(shí)各種操作系統(tǒng)和進(jìn)程狀態(tài)的信息。
● 示例代碼:
// 設(shè)置自定義信號處理函數(shù)
+ (void)setSignalHandlerInAdvance {
struct sigaction act;
// 當(dāng)sa_flags設(shè)為SA_SIGINFO時(shí),設(shè)定sa_sigaction來指定信號處理函數(shù)
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = test_signal_action_handler;
sigaction(SIGABRT, &act, NULL);
}
static void test_signal_action_handler(int signo, siginfo_t *si, void *ucontext) {
NSLog(@"[sigaction handler] - handle signal: %d", signo);
// handle siginfo_t
NSLog(@"siginfo: {\n si_signo: %d,\n si_errno: %d,\n si_code: %d,\n si_pid: %d,\n si_uid: %d,\n si_status: %d,\n si_value: %d\n }",
si->si_signo,
si->si_errno,
si->si_code,
si->si_pid,
si->si_uid,
si->si_status,
si->si_value.sival_int);
}
感謝各位的閱讀,以上就是“iOS Mach異常和signal信號分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對iOS Mach異常和signal信號分析這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
當(dāng)前題目:iOSMach異常和signal信號分析
文章轉(zhuǎn)載:http://muchs.cn/article4/jophoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、服務(wù)器托管、Google、App設(shè)計(jì)、響應(yīng)式網(wǎng)站、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)