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

识别以下二维码,干货
图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/522503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎样清除手机中的html文件夹,怎么彻底清除手机APP卸载后的残留文件夹 能删与不能删文件名...

手机里的文件和手机下载了app之后&#xff0c;即使卸载&#xff0c;也很容易有垃圾残留&#xff0c;而且占用的空间还挺多&#xff0c;但是根本不知道哪些是垃圾&#xff0c;要怎么清除这些垃圾&#xff1f;  一般清理垃圾软件&#xff0c;如360&#xff0c;腾讯管家等可以清…

Kubernetes要成为一个企业友好平台,到底还缺啥?

戳蓝字“CSDN云计算”关注我们哦&#xff01;Kubernetes的崛起令人惊叹。在短短几年时间内&#xff0c;它已经从一个由一群云原生开发者倡导的开源项目转变为由三家主要云服务提供商推广的标准运维平台。由于应用程序工作负载从VM转移到容器&#xff0c;Kubernetes已成为自动化…

局域网设置_局域网设置

把路由连好后,在2台机上分本地网络-(TCP/IP)里,别输入同一IP段就可以了例如:192.168.1.2255.255.255.0192.168.1.3255.255.255.0上面说的只是通过连接好路由后,不考虑上网状况,只是电脑对连可以打游戏和互传东西的方法以下是从设置路由到设置机器从而能上网和互连的过程:打开一…

10分钟上线 - API网关 + 函数计算实现图片处理服务

摘要&#xff1a; 阿里云函数计算服务&#xff08;FunctionCompute&#xff0c;FC&#xff09;是一个事件驱动的全托管计算服务。通过函数计算与云端各个服务的广泛集成&#xff0c;开发者只需要编写函数代码&#xff0c;就能够快速地开发出弹性高可用的后端系统。接下来我们利…

Spring Boot2 整合 MyBatis 多数据源

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0…

如何利用阿里云打造感知零售平台

摘要&#xff1a; 一、背景介绍所谓感知零售是指的根据店铺顾客的情绪变化以及顾客在店铺内对商品货架停留位置来判断顾客对商品感兴趣程度&#xff0c;实时在线调整营销策略。帮助企业第一时间掌握顾客感受&#xff0c;第一时间下发营销策略&#xff0c;第一时间得到营销效果反…

Spark精华问答 | 怎么运行Spark?

戳蓝字“CSDN云计算”关注我们哦&#xff01;为什么要学习Spark&#xff1f;作为一个用来实现快速而通用的集群计算的平台。扩展广泛使用的MapReduce计算模型&#xff0c;而且高效地支持更多的计算模式&#xff0c;包括交互式查询和流处理。Spark的一个重要特点就是能够在内存中…

mac抹掉磁盘重装系统未能与服务器取得联系_【工具】mac笔记本rm -rf 后 如何恢复删除的文件...

本来是要cp -rf 到另一个地方的&#xff0c;然后用git管理起来的。当时不知道怎么了&#xff0c;可能是最近加班多了直接执行了rm -rf 。这种一般是可以恢复的就是要来回折腾&#xff0c;不过也没办法只有折腾了&#xff0c;写了几天的内容可不想再重写一边~这里做一下恢复的记…

基于AliOS Things玩转智能语音

摘要&#xff1a; 随着AI技术的进步&#xff0c;智能语音开始将人机交互从手眼睛的传统模式中解放出来。带给人们更便捷、更风趣、更有人情味的体验&#xff0c;让被操作对象变得不再只是一个死板的工具&#xff0c;而更像是一个有生命的助理。“帮我打开空调”&#xff0c;“明…

SpringBoot2.0整合Mybatis-Plus多数据源

文章目录一、pom依赖二、application.yml多数据源配置三、持久层3.1. UserMapper 接口3.2. 接口映射UserMapper.xml(可以不写)四、逻辑处理层4.1. IUserService 接口4.2. 接口实现类IUserServiceImpl五、通用返回对象封装六、测试UserController七、启动类加扫描mapper注解八、…

百度积极回应阿波龙项目不实报道;半数开发者认为学习新语言很困难;腾讯在长沙建立首个智慧产业总部……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 惠普在其官网上公布了即将发…

精打细算使用MaxCompute搭建数仓

摘要&#xff1a; MaxCompute是一套阿里自主研发的数据仓库解决方案。产品除了功能、性能、简单等优势外&#xff0c;还能在费用上节省下一大笔前。墨迹天气使用MaxCompute&#xff0c;除了性能和稳定性也有提升外&#xff0c;整体存储和计算的费用比之前节省70%。这是如何做到…

计算机知识产权 教学目标,计算机教学设计模板

计算机教学设计模板计算机是现今学生们学习的一个重点&#xff0c;那么关于计算机的教学设计又应该怎么进行呢?下面就随小编一起去阅读计算机教学设计模板&#xff0c;相信能带给大家启发。第一篇&#xff1a;计算机教学设计模板【学习目标】了解计算机网络的发展&#xff0c;…

linux 环境 安装nginx

文章目录一、Nginx简介&#xff1a;1.1 Nginx是什么&#xff1f;1.2 能干什么&#xff1f;1.3 有什么特点&#xff1f;二、Nginx安装依赖环境2.1 安装gcc2.2 安装pcre2.3 安装zlib2.4 安装openssl2.5 综合命令安装(一步到位)三、下载/解压/安装NGINX3.1 在线下载nginx3.2 解压n…

行!这下 CSDN 玩大了!粉丝:太良心

CSDN从建立之初&#xff0c;我们的初心就是为了帮助广大开发者解决技术问题&#xff0c;成为大家最喜爱的技术社区。今年是CSDN的20周年&#xff0c;我们也为大家准备了一份良心礼物&#xff0c;可谓吐血操作。我们与AI博士唐宇迪 / Oracle认证讲师等4位讲师&#xff0c;共同为…

markdown html vue,vue项目引入markdown

npm install showdown接下来是用法&#xff1a;export default {data () {return {content:,converter:null}},watch:{content:contentChanged},mounted(){ this.init(); },methods: {init(){var showdown require(showdown);var converter new showdown.Converter();this.co…

厉害了!阿里安全图灵实验室在ICDAR2017 MLT竞赛刷新世界最好成绩

摘要&#xff1a; 近日&#xff0c;阿里安全图灵实验室&#xff08;Alibaba Turing Lab&#xff09;的ATL Cangjie OCR算法在ICDAR2017的MLT&#xff08;Competition on Multi-lingual scene text detection&#xff09;自然场景多语言文本检测竞赛中刷新了世界最好成绩&#x…

Spring Boot 一个依赖搞定 session 共享,没有比这更简单的方案了!

有的人可能会觉得题目有点夸张&#xff0c;其实不夸张&#xff0c;题目没有使用任何修辞手法&#xff01;认真读完本文&#xff0c;你就知道gblfy说的是对的了&#xff01; 文章目录一、背景二、分析思路三、实战1. 创建工程2. pom.xml3. redis配置4. 使用5. 项目打包6. 启动项…

孩子学计算机最佳年龄,孩子学编程最佳年龄是几岁

孩子学编程最佳年龄是几岁2021-01-18 16:58:11文/董玉莹孩子学习编程的最好是几岁&#xff1f;本文整理了相关内容&#xff0c;欢迎阅读。孩子学习编程的最好是几岁6-18岁&#xff01;从国内外的典型案例来看6-18岁是孩子学习“编程”的最佳时机&#xff0c;这个阶段的孩子左右…

python画两条曲线_python – 在Matplotlib中绘制两个图之间的线

在许多情况下,来自其他答案的解决方案是次优的(因为只有在计算点之后没有对图进行任何更改时它们才有效). 更好的解决方案是使用专门设计的ConnectionPatch&#xff1a; import matplotlib.pyplot as plt from matplotlib.patches import ConnectionPatch import numpy as np f…