前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >关于Signal Catcher线程中对线程的理解

关于Signal Catcher线程中对线程的理解

作者头像
北洋
发布于 2021-12-08 02:20:05
发布于 2021-12-08 02:20:05
65100
代码可运行
举报
文章被收录于专栏:北洋csdn北洋csdn
运行总次数:0
代码可运行

首先简述下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()函数中做了哪些事情呢:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,bool create_peer) {
    Runtime* runtime = Runtime::Current();
    ......
    Thread* self;
    {
        MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
        if (runtime->IsShuttingDownLocked()) {
        ......
        } else {
                Runtime::Current()->StartThreadBirth();
                self = new Thread(as_daemon); //新建一个Thread对象
                bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM()); //调用init函数
                Runtime::Current()->EndThreadBirth();
                if (!init_success) {
                    delete self;
                    return nullptr;
                }
         }
      }
    ......
    self->InitStringEntryPoints();  
    CHECK_NE(self->GetState(), kRunnable);
    self->SetState(kNative);
    ......
    return self;
}

首先创建了一个Thread对象,接着执行了init()函数,然后在最后修改了线程的状态kNative(Java线程的状态是保存在Thread对象中的,具体来说是由对象中的tls32_这个结构体保存的,可以通过修改这个结构体来设置线程当前的状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inline ThreadState Thread::SetState(ThreadState new_state) {
  // Cannot use this code to change into Runnable as changing to Runnable should fail if
  // old_state_and_flags.suspend_request is true.
  DCHECK_NE(new_state, kRunnable);
  if (kIsDebugBuild && this != Thread::Current()) {
    std::string name;
    GetThreadName(name);
    LOG(FATAL) << "Thread \"" << name << "\"(" << this << " != Thread::Current()="
               << Thread::Current() << ") changing state to " << new_state;
  }
  union StateAndFlags old_state_and_flags;
  old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
  tls32_.state_and_flags.as_struct.state = new_state;
  return static_cast<ThreadState>(old_state_and_flags.as_struct.state);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
enum ThreadState {
  //                                   Thread.State   JDWP state
  kTerminated = 66,                 // TERMINATED     TS_ZOMBIE    Thread.run has returned, but Thread* still around
  kRunnable,                        // RUNNABLE       TS_RUNNING   runnable
  kTimedWaiting,                    // TIMED_WAITING  TS_WAIT      in Object.wait() with a timeout
  kSleeping,                        // TIMED_WAITING  TS_SLEEPING  in Thread.sleep()
  kBlocked,                         // BLOCKED        TS_MONITOR   blocked on a monitor
  kWaiting,                         // WAITING        TS_WAIT      in Object.wait()
  kWaitingForGcToComplete,          // WAITING        TS_WAIT      blocked waiting for GC
  kWaitingForCheckPointsToRun,      // WAITING        TS_WAIT      GC waiting for checkpoints to run
  kWaitingPerformingGc,             // WAITING        TS_WAIT      performing GC
  kWaitingForDebuggerSend,          // WAITING        TS_WAIT      blocked waiting for events to be sent
  kWaitingForDebuggerToAttach,      // WAITING        TS_WAIT      blocked waiting for debugger to attach
  kWaitingInMainDebuggerLoop,       // WAITING        TS_WAIT      blocking/reading/processing debugger events
  kWaitingForDebuggerSuspension,    // WAITING        TS_WAIT      waiting for debugger suspend all
  kWaitingForJniOnLoad,             // WAITING        TS_WAIT      waiting for execution of dlopen and JNI on load code
  kWaitingForSignalCatcherOutput,   // WAITING        TS_WAIT      waiting for signal catcher IO to complete
  kWaitingInMainSignalCatcherLoop,  // WAITING        TS_WAIT      blocking/reading/processing signals
  kWaitingForDeoptimization,        // WAITING        TS_WAIT      waiting for deoptimization suspend all
  kWaitingForMethodTracingStart,    // WAITING        TS_WAIT      waiting for method tracing to start
  kWaitingForVisitObjects,          // WAITING        TS_WAIT      waiting for visiting objects
  kWaitingForGetObjectsAllocated,   // WAITING        TS_WAIT      waiting for getting the number of allocated objects
  kStarting,                        // NEW            TS_WAIT      native thread started, not yet ready to run managed code
  kNative,                          // RUNNABLE       TS_RUNNING   running in a JNI native method
  kSuspended,                       // RUNNABLE       TS_RUNNING   suspended by GC or debugger
};

这里主要分析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过程中初始化好的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/07/13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java并发-5.线程状态
Java线程在运行的声明周期有6中不同的状态,给任一时刻,线程只能处于其中一种状态:
悠扬前奏
2019/05/28
6600
Java线程生命周期与状态切换
最近有点懒散,没什么比较有深度的产出。刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。
Throwable
2020/06/23
8870
Android Handler机制1之Thread
每一个进程都有自己的独立的一块内存空间、一组资源系统。其内部数据和状态都是完全独立的。进程的优点是提高CPU的运行效率,在同一个时间内执行多个程序,即并发执行。但是从严格上将,也不是绝对的同一时刻执行多个程序,只不过CPU在执行时通过时间片等调度算法不同进程告诉切换。
隔壁老李头
2018/08/30
8240
Android Handler机制1之Thread
LockSupport中的park与unpark原理
LockSupport是用来创建locks的基本线程阻塞基元,比如AQS中实现线程挂起的方法,就是park,对应唤醒就是unpark。JDK中有使用的如下
爬蜥
2019/07/09
2.3K0
如何阅读JVM 源码
JDK中JVM(安装在本地C:\Program Files\Java\jdk1.8.0_121\jre\bin\server下jvm.dll)本身并不开源,只能找来openJDK来看(说是和JDK相似度很高)
王小明_HIT
2020/06/16
2.1K0
Java线程的那些状态
Java线程(或者说计算机线程),是有一些状态来表示当前线程运行信息的,可以通过jsatck命令来查看Java进程中线程函数栈信息,其中就包括了Java线程状态。
luoxn28
2021/04/08
3800
Java线程的状态分析
本文将从源码角度分析Java线程的各种状态以及进入该状态所对应的操作。OpenJDK版本
KINGYT
2023/03/15
6450
Java线程的状态分析
Java线程状态详解
最近在深入研究Java并发编程,看到网上有很多关于线程状态的总结,有的不全面,有的根本就是错的。因此,在这里我结合最权威的Java源代码,尝试对Java线程状态进行一个详细的解读。
张申傲
2020/09/03
9380
Java线程状态详解
Java线程Thread的状态解析以及状态转换分析 多线程中篇(七)
在Thread类中有内部类 枚举State,用于抽象描述Java线程的状态,共有6种不同的状态
noteless
2019/03/04
9100
Java线程Thread的状态解析以及状态转换分析 多线程中篇(七)
并发多线程学习(五)Java线程的状态及主要转化方法
处于NEW状态的线程此时尚未启动。这里的尚未启动指的是还没调用Thread实例的start()方法。
用户10168815
2023/02/28
3080
并发多线程学习(五)Java线程的状态及主要转化方法
Java线程状态(生命周期)以及线程状态转换详解
线程状态转换进入等待/超时等待进入等待状态进入超时等待LockSupport类简介过期的suspend和resume方法
用户7886150
2021/04/20
7190
高并发之——线程与多线程
在操作系统中,线程是比进程更小的能够独立运行的基本单位。同时,它也是CPU调度的基本单位。线程本身基本上不拥有系统资源,只是拥有一些在运行时需要用到的系统资源,例如程序计数器,寄存器和栈等。一个进程中的所有线程可以共享进程中的所有资源。
冰河
2020/10/29
4270
高并发之——线程与多线程
Java线程的几种状态
java.lang.Thread.State中定义的集中Java线程的状态: 1 /** 2 * A thread state. A thread can be in one of the following states: 3 * <ul> 4 * <li>{@link #NEW}<br> 5 * A thread that has not yet started is in this state. 6 * </li> 7 * <li>{@link
欠扁的小篮子
2018/04/10
1.5K0
Java线程的几种状态
java线程状态及线程状态机切换流程,如何查看JVM线程状态
线程的6大状态:NEW-新建、RUNNABLE-线程执行、BLOCKED-被阻塞、
崔认知
2023/06/19
8040
java线程状态及线程状态机切换流程,如何查看JVM线程状态
Thread类源码(2)
jvm.cpp中调用了Thread::interrupt,回到thread.hpp源码的源码:
黑洞代码
2021/01/14
8390
Thread类源码(2)
本文深入探讨虚拟机运行时的java线程启动、停止、睡眠与中断
上节描述了虚拟机中各式各样的线程及其创建过程,其中尤为重要的是JavaThread,它是Java线程java.lang.Thread在JVM层的表示,包含很多重要数据。
愿天堂没有BUG
2022/10/31
5760
本文深入探讨虚拟机运行时的java线程启动、停止、睡眠与中断
【Android开发高级系列】Dalvik虚拟机专题
三,JavaVM创建之后,我们就有了JNINativeInterface,里面包含了所有的Java接口,比如FindClass,NewObject,CallObjectMethod等
江中散人_Jun
2023/10/16
6970
【Android开发高级系列】Dalvik虚拟机专题
深入理解 Java 线程池的实现原理
如上述代码所示,其来自于java.lang.Thread类,State为Thread类的内部公共枚举类,表示线程的 6 种状态。
CG国斌
2021/12/07
2950
深入理解 Java 线程池的实现原理
从 Hotspot 虚拟机角度来分析 Java 线程启动
Java 线程其实是映射到操作系统的内核线程上的,所以 Java 线程基本上也就是操作系统在进行管理。在 Linux系统中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的多个线程之间是共享资源的。
没有故事的陈师傅
2023/05/01
3050
从 Hotspot 虚拟机角度来分析 Java 线程启动
线程的实现与分析
线程是操作系统调度的最小单位,实现线程有三种方式,而 Java Thread 采用的是 内核线程实现
itliusir
2020/01/15
3510
相关推荐
Java并发-5.线程状态
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验