| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> 「ANR」Android SIGQUIT(3) 信号拦截与处理 -> 正文阅读 |
|
[移动开发]「ANR」Android SIGQUIT(3) 信号拦截与处理 |
作者:非台 背景Android的ANR频次(Application Not Responding)一直是Android用户体验的重要指标,然而在Android 6.0+的设备上,由于设备anr目录权限的收敛,已经不能通过扫描/data/anr/traces.txt文件来获取ANR文件了,因此今天我们来简单聊聊获取ANR的另一种方式,Android环境下,信号SIGQUIT(3)拦截。 信号量处理关于信号SIGQUIT的拦截,我们需要了解信号量处理的部分相关函数,kill、signal、sigaction、sigwait、pthread_sigmask等系统信号量处理相关函数是阅读本文的必备知识,因此在这一章节简单介绍下,更多系统函数知识,请阅读《UNIX环境高级编程》。 kill [1] 头文件:#include<signal.h> 定义函数:int kill(pid_t?pid,int?signo) 函数说明:kill函数可以对进程发送signal,Android AMS在发生ANR的是其实是通过Process.sendSignal(pid,signal)来通信的,Process.sendSignal方法在JNI层,其实调用的是kill 想详细了解ANR的同学可以看
signal [2] 头文件:#include<signal.h> 定义函数:sig_t signal(int?signum,sig_t?handler); 函数说明:signal()用于确定以后当信号sig出现时的处理方法。如果handler的值是SIG_DFL,那么就采用实现定义的缺省行为;如果handler的值是SIG_IGN,那么就忽略该信号;否则,调用handler所指向的函数(参数为信号类型)。有效的信号包括:
signal()返回信号sig原来的handler;如果出错,则返回SIG_ERR。当随后出现信号sig时,就中断正在执行的操作,转而执行信号处理函数(*handler)(sig)。如果从信号处理程序中返回,则从中断的位置继续执行。 sigaction [3] 头文件:#include<signal.h> 定义函数:int sigaction(int?signum,const struct sigaction *act?,struct sigaction *oldact) 函数说明:sigaction会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。如参数结构sigaction定义如下:
代码1 sigaction结构体 信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。
sigwait [4] 头文件:#include<signal.h> 定义函数:int sigwait(const sigset_t *set, int *sig); 函数说明:sigwait提供了一种等待信号的到来,以串行的方式从信号队列中取出信号进行处理的机制。sigwait只等待函数参数中指定的信号集,即如果新产生的信号不在指定的信号集内,则 sigwait继续等待。对于一个稳定可靠的程序,我们一般会有一些疑问:
注:Android的“Signal Catcher”线程是通过sigwait来等待SIGQUIT信号。 pthread_sigmask [5] 头文件:#include<signal.h> 定义函数:int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset); 函数说明:每个线程均有自己的信号屏蔽集(信号掩码),可以使用pthread_sigmask函数来屏蔽某个线程对某些信号的响应处理,仅留下需要处理该信号的线程来处理指定的信号。实现方式是:利用线程信号屏蔽集的继承关系(在主线程中对sigmask进行设置后,主线程创建出来的线程将继承主线程的掩码)。 signal、sigaction、sigwait 的差异 signal、sigaction、sigwait这三个方法都可以接收信号,并处理,那么他们的差异有哪些,以及处理顺序如何? sigaction 和 signal 的区别:内核里有signal系统调用函数,它注释里也说是为了向后兼容,功能已被sigaction取代了,详见《源码剖析signal和sigaction的区别》[6],也就是可以理解为signal的能力是sigaction的子集,signal和sigaction最终都是调了系统调用rt_sigaction。 sigaction 和 sigwait 的区别:?如果多个线程在sigwait调用时,等待的是同一个信号,当信号递送的时候,只有一个线程可以从sigwait中返回,具体是那个线程则是未定义的(由系统决定)。如果信号被捕获(进程通过使用sigaction建立了一个信号处理程序),而且线程正在sigwait调用中等待同一信号,那么这时将由操作系统实现来决定以何种方式递送信号。在这种情况下,操作系统实现可以让sigwait返回,也可以激活信号处理程序,但不可能出现两者皆可的情况。 我们做了一个实验,如果信号量发送给目标线程,且目标现在存在sigwait,则执行sigwait;如果信号量发送给目标线程,且目标线程设置SIG_BLOCK(屏蔽信息),其他线程在sigwait,则执行sigwait;其他状态执行sigaction的行为—— 这个只在笔者的MAC电脑上实验的结论。 使用sigwait的好处在于它可以简化信号处理,允许把异步产生的信号用同步的方式处理。为了防止信号中断线程,可以把信号加到每个线程的信号屏蔽字中,然后安排专用线程作信号处理。这些专用线程可以进行函数调用,不需要担心在信号处理程序中调用哪些函数是安全的,因为这些函数调用来自正常的线程环境,而非传统的信号处理程序,传统信号处理程序通常会中断线程的正常执行。详见《Libev源码分析06》[7]。 Android系统ANR SIGQUIT(3)的处理有了前面的前置知识点,我们来看,Android ANR信号机制是怎么做的呢?简单的说:目标进程在创建的时候,会启动一个“Signal Catcher”专项线程来处理信号量,AMS在弹对话框的同时,会有一个系统调用,发出SIGQUIT(3)信号量,“Signal Catcher”专门来处理SIGQUIT(3)信号量,从而dump目标进程的线程状态到/data/anr/traces.txt文件。 SignalCatcher 线程创建 当Android运行应用时,如果应用进程还没有创建,ActivityManagerService会请求Zygote fork进程(详见《Android应用进程的创建过程》[8])最终会通过Runtime 创建“SignalCatcher”线程。
代码2 Runtime::Init 方法
代码3 Runtime::BlockSignals 方法 代码2、代码3是Runtime初始化信号量的方法,这里通过pthread_sigmask(SignalSet内部实现,有兴趣的小伙伴可以看signal_set.h)屏蔽了SIGPIPE、SIGQUIT、SIGUSER1信号量,使得当前线程(主线程)不会去处理系统发送的SIGPIPE、SIGQUIT、SIGUSER1信号量、由其他线程去处理。 由于Zogyte在fork子进程时,子进程会继承父进程的信号集,因此子进程创建的主线程,以及主线程创建的子线程都会继承这信号集,导致fork的子进程的主线程以及其子线程都不会处理SIGPIPE、SIGQUIT、SIGUSER1信号,只能通过sigwait来处理。 SignalCatcher 原理
代码4 SignalCatcher::Run 方法 代码4 SignalCatcher::Run方法,正如前面所说,Android系统确实通过了“SignalCatcher”线程通过pthread_sigmask和sigwait来专项处理SIGQUIT、SIGUSR1的信号量。Android系统如此设计,我的理解是为了保障SIGQUIT、SIGUSR1的处理一定由“SignalCatcher”线程来完成,我认为这里由两个优点:
Android自定义SIG_QUIT拦截的实现
代码5 SIG_QUIT拦截自定义SigPad::RunSigQuitMonitor方法 代码5 SigPad::RunSigQuitMonitor 方法,有了Linux信号的前置知识以及对Android “SignalCatcher”初始化的介绍,我们可以通过pthread_sigmask设置SIG_UNBLOCK来解除当前进程主线程对SIGQUIT的屏蔽。再通过sa_sigaction对SIGQUIT信号量处理方法重定向,从而实现自己的ANR监控方法。 小结以上,有了对 Android SIGQUIT 信号处理的了解,我们就可以快速实现 Android 自定义的 SIGQUIT 信号拦截器,也欢迎广大读者朋友留言交流。 引用[1] https://baike.baidu.com/item/kill()/2680256 [2] https://baike.baidu.com/item/signal.h/7316160?fr=aladdin [3] https://baike.baidu.com/item/sigaction [4] https://baike.baidu.com/item/sigwait [5] https://baike.baidu.com/item/pthread_sigmask [6] 源码剖析signal和sigaction的区别:https://blog.csdn.net/wangzuxi/article/details/44814825 [7] Libev源码分析06:https://www.cnblogs.com/gqtcgq/p/7247097.html [8] Android应用进程的创建过程:https://www.jianshu.com/p/b4cb8608d7fb 关注我们,每周 3 篇移动技术实践&干货给你思考! |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 23:13:40- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |