关于 Signal Catcher 线程中对线程的理解
首先简述下 Signal Catcher,Signal Catcher 线程接受到 kernel 系统底层的消息进行 dump 当前虚拟机的信息并且设置每个线程的标志位(check_point)和请求线程状态为挂起,当线程运行过程中进行上下文切换时会检查该标记。等到线程都挂起后,开始遍历 Dump 每个线程的堆栈和线程数据后再唤醒线程。关于 ANR 的更多内容在我的其他博客中进行查阅~~.
==本文重点讲的是在分析 Singal Catcher 时对线程有了更新的了解。==
在 Android 里面只能通过 pthread_create 去创建一个线程,Thread 只是 Android Runtime 里面的一个类,一个 Thread 对象创建之后就会被保存在线程的 TLS 区域,所以一个 Linux 线程都对应了一个 Thread 对象,可以通过 Thread 的 Current()函数来获取当前线程关联的 Thread 对象,通过这个 Thread 对象就可以获取一些重要信息,例如当前线程的 Java 线程状态,Java 栈帧,JNI 函数指针列表等等,之所以说是 Java 线程状态,Java 栈帧,是因为 Android 运行时其实是没有自己单独的线程机制的,Java 线程底层都是一个 Linux 线程,但是 Linux 线程是没有像 Watting,Blocked 等状态的,并且 Linux 线程也是没有 Java 堆栈的,那么这些线程状态和 Java 栈帧必须有一个地方保存,要不然就丢失了,Thread 对象就是一个很理想的“储物柜”。
只有当创建出来的 Thread 对象执行了 attach 函数后,一个 Linux 线程在真正和虚拟机运行时关联起来,才变成了 Java 线程,才有了自己的 java 线程状态和 java 栈帧等数据结构,那些纯粹的 native 是不能执行 java 代码的,所以当系统发生 crash 或者 anr 进行 dump 进程的堆栈的时候,有些线程是没有 java 堆栈的,只有 native 和 kernel 堆栈,就是这个原因。那么这个 attach()函数中做了哪些事情呢:
首先创建了一个 Thread 对象,接着执行了 init()函数,然后在最后修改了线程的状态 kNative(Java 线程的状态是保存在 Thread 对象中的,具体来说是由对象中的 tls32_这个结构体保存的,可以通过修改这个结构体来设置线程当前的状态:
这里主要分析 init()函数,首先要先了解一下 ART 执行代码的方式,ART 虚拟机和 Dalvik 最大的变化就是 ART 虚拟机==不在解释执行字节码,而是直接找到对应的机器码直接执行==。ART 会在安装应用程序的时候执行 dex2oat 进程得到一个 oat 文件完成字节码翻译成本地机器码的工作,这个 oat 文件一般保存在/data/app/应用名称/oat/目录下,这个 oat 文件里面就是编译好的机器码,但是这些机器码不可能单独存在,需要借助于 ART 运行时(执行一个 jni 方法或者在 heap 中操作),这个可以类比于编译 so 库文件的时候引用到了外部函数(其实 oat 和 so 文件都是 ELF 可执行格式文件,只是 oat 文件相比于标准的 ELF 格式文件多出了几个 section)。区别是打开标准的 so 文件的时候,一般用的是 dlopen 这个函数,该函数会把没有加载的 so 库加载进来,然后把这些外部函数重定位好;而 oat 文件为了快速加载,ART 在==线程的 TLS 区域保存了一些函数==,编译好的机器码就是调用这些函数指针来和 AT 运行时建立联系,这些函数就是在 Thread 的 init 过程中初始化好的
版权声明: 本文为 InfoQ 作者【北洋】的原创文章。
原文链接:【http://xie.infoq.cn/article/1d603d71eaeac285d6ee6ee5d】。文章转载请联系作者。
评论