日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

iOS Mach异常和signal信号

發布時間:2024/8/23 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS Mach异常和signal信号 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

摘要: 本著探究下iOS Crash捕獲的目的,學習了下Crash捕獲相關的Mach異常和signal信號處理,記錄下相關內容,并提供對應的測試示例代碼。Mach為XNU的微內核,Mach異常為最底層的內核級異常,在iOS系統中,底層Crash先觸發Mach異常,然后再轉換為對應的signal信號。

作者:阿里云-移動云-大前端團隊

原文鏈接:http://click.aliyun.com/m/43672/

本著探究下iOS Crash捕獲的目的,學習了下Crash捕獲相關的Mach異常和signal信號處理,記錄下相關內容,并提供對應的測試示例代碼。Mach為XNU的微內核,Mach異常為最底層的內核級異常,在iOS系統中,底層Crash先觸發Mach異常,然后再轉換為對應的signal信號。

1. iOS Mach異常

1.1 XNU

Darwin是Mac OS和iOS的操作系統,而XNU是Darwin操作系統的內核部分。XNU是混合內核,兼具宏內核和微內核的特性,而Mach即為其微內核。

Darwin操作系統和MacOS、iOS系統版本號的對應如上圖所示,Mac可執行下述命令查看Darwin版本號。

system_profiler SPSoftwareDataType

1.2 Mach

Mach:[m?k],操作系統微內核,是許多新操作系統的設計基礎。

Mach微內核中有幾個基礎概念:

Tasks,擁有一組系統資源的對象,允許”thread”在其中執行。
Threads,執行的基本單位,擁有task的上下文,并共享其資源。
Ports,task之間通訊的一組受保護的消息隊列;task可對任何port發送/接收數據。
Message,有類型的數據對象集合,只可以發送到port。

1.3 模擬Mach Message發送

Mach提供少量API,蘋果文檔介紹較少。

// 內核中創建一個消息隊列,獲取對應的port mach_port_allocate(); // 授予task對port的指定權限 mach_port_insert_right(); // 通過設定參數:MACH_RSV_MSG/MACH_SEND_MSG用于接收/發送mach message mach_msg();

下述代碼模擬向Mach Port發送Message,接收Message后做處理:

首先調用createPortAndAddListener創建Mach Port;
調用sendMachPortMessage:向已創建的Mach Port發送消息;

執行結果示例:

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

示例代碼:

// 創建Mach Port并監聽消息 + (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發送消息 + (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(),設置內核接收Mach異常消息的Port,替換為自定義的Port后,即可捕獲程序執行過程中產生的異常消息。
執行結果示例:

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]; }// 構造BAD MEM ACCESS Crash - (void)makeCrash {NSLog(@"********** Make a [BAD MEM ACCESS] now. **********");*((int *)(0x1234)) = 122; }

1.5 Runloop

Mach Port的應用不止于內核級別,在Cocoa Foundation和Core Foundation層同樣有其應用,比如說:Runloop。

Runloop sources分兩類:

Input sources

Port-Based sources
Custom Input sources
Timer sources
其中Port-Based sources即基于Mach Port,在Runloop中完成消息傳遞。

上述的Mach API為內核層透出接口,Cocoa Foundation和Core Foundation層分別封裝了Mach Port的接口供調用,參考:Apple - Runloop Programming Guard,有詳細的示例代碼。

2. signal信號

signal是一種軟中斷信號,提供異步事件處理機制。signal是進程間相互傳遞信息的一種粗糙方法,使用場景:

進程終止相關;
終端交互;
編程錯誤或硬件錯誤相關,系統遇到不可恢復的錯誤時觸發崩潰機制讓程序退出,比如:除0、內存寫入錯誤等。
這里我們主要考慮系統遇到不可恢復的錯誤時即Crash時,信號相關的應用。signal信號處理是UNIX操作系統機制,所以Android平臺理論上也是使用的,可以基于signal來捕獲Android Native Crash。

2.1 signal注冊和處理

signal()#import <sys/signal.h>;

注冊signal handler;
調用成功時,會移除signo信號當前的操作,以handler指定的新信號處理程序替代;
信號處理函數返回void,因為沒有地方給該函數返回。
注冊自定義信號處理函數,構造Crash后,發出信號并執行自定義信號處理邏輯。

【附】:Xcode Debug運行時,添加斷點,在Crash觸發前,執行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

示例代碼:

// 設置自定義信號處理函數 + (void)setSignalHandler {signal(SIGABRT, test_signal_handler); }static void test_signal_handler(int signo) {NSLog(@"[signal handler] - handle signal: %d", signo); }// 構造NSRangeException異常,觸發SIGABRT信號發送 - (void)makeCrash {NSLog(@"********** Make a 'NSRangeException' now. **********");NSArray *array = @[ @"aaa" ]; }

2.2 LLDB Debugger

Xcode Debug模式運行App時,App進程signal被LLDB Debugger調試器捕獲;需要使用LLDB調試命令,將指定signal處理拋到用戶層處理,方便調試。

查看全部信號傳遞配置:

// process handle縮寫 pro hand

修改指定信號傳遞配置:

// option: // -P: PASS // -S: STOP // -N: NOTIFY pro hand -option false 信號名// 例:SIGABRT信號處理在LLDB不停止,可繼續拋到用戶層 pro hand -s false SIGABRT

2.3 可重入

向內核發送信號時,進程可能執行到代碼的任意位置,例:進程在執行重要操作,中斷后可能產生不一致狀態,或進程正在處理另一信號。因此要確保信號處理程序只執行可重入操作:

寫中斷處理程序時,假定中斷進程可能處于不可重入函數中。
慎重修改全局數據。

2.4 高級信號處理

signal()函數非常基礎,只提供了最低限度的信號管理的標準。而sigaction()系統調用,提供更強大的信號管理能力。當信號處理程序運行時,可以用來阻塞特定信號的接收,也可以用來獲取信號發送時各種操作系統和進程狀態的信息。

示例代碼:

// 設置自定義信號處理函數 + (void)setSignalHandlerInAdvance {struct sigaction act;// 當sa_flags設為SA_SIGINFO時,設定sa_sigaction來指定信號處理函數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_tNSLog(@"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); }

3. 參考

Apple - Understanding and Analyzing Application Crash Reports
Apple - Runloop Programming Guard
Apple - Mach Overview
漫談iOS Crash收集框架
The LLDB Debugger

識別以下二維碼,干貨

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的iOS Mach异常和signal信号的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。