千家信息网

iOS Mach异常和signal信号分析

发表于:2024-11-12 作者:千家信息网编辑
千家信息网最后更新 2024年11月12日,这篇文章主要讲解了"iOS Mach异常和signal信号分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"iOS Mach异常和signal信号分析
千家信息网最后更新 2024年11月12日iOS Mach异常和signal信号分析

这篇文章主要讲解了"iOS Mach异常和signal信号分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"iOS 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分两类:
1.Input sources
Port-Based sources
Custom Input sources

2.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;

注册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_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信号分析"的内容了,经过本文的学习后,相信大家对iOS Mach异常和signal信号分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

信号 处理 内核 系统 信号处理 函数 消息 进程 操作系统 代码 示例 程序 错误 分析 义信 自定 命令 基础 数据 机制 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 车贷贷款软件开发 实现数据库安全性的主要方法 可以存储图片的数据库 投资医疗数据库 中关村互联网 科技集团简介 多对多关系 数据库表设计 浙江信息化软件开发价格服务标准 电子科大网络技术专业 网络技术多路复用内积 中职计算机网络技术面试 企业如何落实网络安全主体责任 二级数据库程序设计有什么用 网络安全龙头公司股票有哪些 视易点歌机服务器怎么连接 金碟专业版连接不上数据库 印度软件开发外包的好处 疫情网络安全论文 服务器管理器添加角色 网络安全投入5% 青浦区会计软件开发产品介绍 软件开发公司请假制度 安橙网络技术有限公司 sql serve连接数据库 税务申报数据库 下载网络服务器 金碟专业版连接不上数据库 衣酷网络技术有限公司 成都软件开发私活店 管理系统数据库设计优点 大话西游2君子一诺服务器活跃吗
0