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

简介: 关于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* 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_这个结构体保存的,可以通过修改这个结构体来设置线程当前的状态:

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);
}
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过程中初始化好的


目录
相关文章
|
4月前
|
安全 Java
使用notifyAll唤醒所有等待线程
使用notifyAll唤醒所有等待线程
|
5月前
|
NoSQL Java 应用服务中间件
线程不够用怎么办?
### 并发编程挑战与解决方案概览 - 多线程导致线程爆炸,浪费CPU及可能导致JVM崩溃。线程池缓解问题,但仍有阻塞IO的效率低下。 - 非阻塞IO(如servlet3.1/Tomcat)和事件驱动(Reactive/Future)减少线程使用,但学习曲线陡峭。 - 轻量级线程如Netty、Spring Flux和虚拟线程(Java Loom)提升性能,但普及尚需时日。Java21引入虚拟线程,有望成未来性能关键。
216 10
|
5月前
|
Java
使用notifyAll唤醒所有等待线程的方法与比较
使用notifyAll唤醒所有等待线程的方法与比较
|
Java C语言 Python
线程那些事
线程那些事
49 0
|
Java Linux 程序员
04.关于线程你必须知道的8个问题(下)
大家好,我是王有志。今天是Java面试中线程问题的最后一部分内容,包括我们来聊同步与互斥,线程的本质,调度,死锁以及线程的优缺点等问题。
114 1
04.关于线程你必须知道的8个问题(下)
|
算法 安全 程序员
线程小练习
线程小练习
线程发生阻塞,怎么唤醒线程?
线程发生阻塞,怎么唤醒线程?
226 0
|
算法 Java 调度
线程的挂起和唤醒
线程的挂起和唤醒
|
Java
线程理解
个人学习理解
79 0
|
Java 编译器 Linux
初识 线程
初识 线程
104 0
初识 线程