1. 程式人生 > >理解Android線程創建流程(轉)

理解Android線程創建流程(轉)

ttr cal 創建失敗 指向 ear long readn nbsp bar

/android/libcore/libart/src/main/java/java/lang/Thread.java
/art/runtime/native/java_lang_Thread.cc
/art/runtime/native/java_lang_Object.cc
/art/runtime/thread.cc

/system/core/libutils/Threads.cpp
/system/core/include/utils/AndroidThreads.h
/frameworks/base/core/jni/AndroidRuntime.cpp

一.概述

Android線程,一般地就是指Android虛擬機線程,而虛擬機線程由是通過系統調用而創建的Linux線程。純粹的Linux線程與虛擬機線程的區別在於虛擬機線程具有運行Java代碼的runtime. 除了虛擬機線程,還有Native線程,對於Native線程有分為是否具有訪問Java代碼的兩類線程。接下來,本文分析介紹這3類線程的創建過程。

二. Java線程

2.1 Thread.start

[-> Thread.java]

public synchronized void start() {
     checkNotStarted(); //保證線程只有啟動一次
     hasBeenStarted = true;
     //[見流程2.2]
     nativeCreate(this, stackSize, daemon);
}

nativeCreate()這是一個native方法,那麽其所對應的JNI方法在哪呢?在java_lang_Thread.cc中通過gMethods是一個JNINativeMethod數組,其中一項為:

NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),

這裏的NATIVE_METHOD定義在java_lang_Object.cc文件,如下:

#define NATIVE_METHOD(className, functionName, signature)     { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }

將宏定義展開並代入,可得所對應的方法名為Thread_nativeCreate

,那麽接下來進入該方法。

2.2 Thread_nativeCreate

[-> java_lang_Thread.cc]

static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, 
                                  jlong stack_size, jboolean daemon) {
  //【見小節2.3】
  Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}

2.3 CreateNativeThread

[-> thread.cc]

void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
  Thread* self = static_cast<JNIEnvExt*>(env)->self;
  Runtime* runtime = Runtime::Current();

  ...
  Thread* child_thread = new Thread(is_daemon);
  child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer);
  stack_size = FixStackSize(stack_size);

  env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
                    reinterpret_cast<jlong>(child_thread));

  std::unique_ptr<JNIEnvExt> child_jni_env_ext(
      JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM()));

  int pthread_create_result = 0;
  if (child_jni_env_ext.get() != nullptr) {
    pthread_t new_pthread;
    pthread_attr_t attr;
    child_thread->tlsPtr_.tmp_jni_env = child_jni_env_ext.get();
    //創建線程【見小節2.4】
    pthread_create_result = pthread_create(&new_pthread,
                         &attr, Thread::CreateCallback, child_thread);

    if (pthread_create_result == 0) {
      child_jni_env_ext.release();
      return;
    }
  }
  
  ...
}

2.4 pthread_create

pthread_create是pthread庫中的函數,通過syscall再調用到clone來請求內核創建線程。

  • 原型:int pthread_create((pthread_t thread, pthread_attr_t *attr, void *(start_routine)(void *), void *arg)
  • 頭文件:#include
  • 輸入參數:thread:線程標識符; attr:線程屬性設置; start_routine:線程函數的起始地址; arg:傳遞給start_routine的參數;
  • 返回值:成功則返回0;出錯則返回-1。
  • 功能:創建線程,並調用線程起始地址所指向的函數start_routine。

關於pthread_create的分析,在後續Linux系列文章會再進一步深入分析。

三. Native線程(C/C++)

3.1 Thread.run

[-> Threads.cpp]

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    Mutex::Autolock _l(mLock);
    //保證只會啟動一次
    if (mRunning) {
        return INVALID_OPERATION;
    }
    ...
    mRunning = true;

    bool res;
    
    if (mCanCallJava) {
        //還能調用Java代碼的Native線程【見小節4.1】
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        //只能調用C/C++代碼的Native線程【見小節3.2】
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }

    if (res == false) {
        ...//清理
        return UNKNOWN_ERROR;
    }
    return NO_ERROR;
}

mCanCallJava在Thread對象創建時,在構造函數中默認設置mCanCallJava=true.

  • 當mCanCallJava=true,則代表創建的是不僅能調用C/C++代碼,還能能調用Java代碼的Native線程
  • 當mCanCallJava=false,則代表創建的是只能調用C/C++代碼的Native線程。

3.2 androidCreateRawThreadEtc

[-> Threads.cpp]

int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                               void *userData,
                               const char* threadName __android_unused,
                               int32_t threadPriority,
                               size_t threadStackSize,
                               android_thread_id_t *threadId)
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
        thread_data_t* t = new thread_data_t;
        t->priority = threadPriority;
        t->threadName = threadName ? strdup(threadName) : NULL;
        t->entryFunction = entryFunction;
        t->userData = userData;
        entryFunction = (android_thread_func_t)&thread_data_t::trampoline;
        userData = t;
    }

    if (threadStackSize) {
        pthread_attr_setstacksize(&attr, threadStackSize);
    }

    errno = 0;
    pthread_t thread;
    //通過pthread_create創建線程
    int result = pthread_create(&thread, &attr,
                    (android_pthread_entry)entryFunction, userData);
    pthread_attr_destroy(&attr);
    if (result != 0) {
        ... //創建失敗,則返回
        return 0;
    }

    if (threadId != NULL) {
        *threadId = (android_thread_id_t)thread; 
    }
    return 1;
}

此處entryFunction所指向的是由[小節3.1]傳遞進來的,其值為_threadLoop。

3.3 _threadLoop

[-> Threads.cpp]

int Thread::_threadLoop(void* user)
{
    //user是指Thread對象
    Thread* const self = static_cast<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> weak(strong);
    self->mHoldSelf.clear();

    //該參數對於gdb調試很有作用
    self->mTid = gettid();

    bool first = true;
    do {
        bool result;
        if (first) {
            first = false;
            //首次運行時會調用readyToRun()做一些初始化準備工作
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);
            if (result && !self->exitPending()) {
                //
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }

        {
          Mutex::Autolock _l(self->mLock);
          if (result == false || self->mExitPending) {
              self->mExitPending = true;
              self->mRunning = false;
              self->mThread = thread_id_t(-1);
              self->mThreadExitedCondition.broadcast();
              break;
          }
        }

        strong.clear(); //釋放強引用
        strong = weak.promote(); //重新請求強引用,用於下一次的循環
    } while(strong != 0);

    return 0;
}

不斷循環地調用成員方法threadLoop()。當滿足以下任一條件,則該線程將退出循環:

  1. 當前線程狀態存在錯誤,即mStatus != NO_ERROR;
  2. 當前線程即將退出, 即mExitPending = true; 調用Thread::requestExit()可觸發該過程。
  3. 當前線程的強引用釋放後,無法將弱引用提升成強引用的情況。

對於Native線程的實現方法,往往是通過繼承Thread對象,通過覆寫父類的readyToRun()和threadLoop()完成自定義線程的功能。

四. Native線程(Java)

4.1 createThreadEtc

[-> AndroidThreads.h]

inline bool createThreadEtc(thread_func_t entryFunction,
                            void *userData,
                            const char* threadName = "android:unnamed_thread",
                            int32_t threadPriority = PRIORITY_DEFAULT,
                            size_t threadStackSize = 0,
                            thread_id_t *threadId = 0)
{
    //【見小節4.2】
    return androidCreateThreadEtc(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId) ? true : false;
}

4.2 androidCreateThreadEtc

[-> Threads.cpp]

int androidCreateThreadEtc(android_thread_func_t entryFunction,
                            void *userData,
                            const char* threadName,
                            int32_t threadPriority,
                            size_t threadStackSize,
                            android_thread_id_t *threadId)
{
    //【見小節4.3】
    return gCreateThreadFn(entryFunction, userData, threadName,
        threadPriority, threadStackSize, threadId);
}

此處gCreateThreadFn默認指向androidCreateRawThreadEtc函數。 文章Android系統啟動-zygote篇的小節[3.3.1]已介紹 通過androidSetCreateThreadFunc()方法,gCreateThreadFn指向javaCreateThreadEtc函數。

4.3 javaCreateThreadEtc

[-> AndroidRuntime.cpp]

int AndroidRuntime::javaCreateThreadEtc(
                               android_thread_func_t entryFunction,
                               void* userData,
                               const char* threadName,
                               int32_t threadPriority,
                               size_t threadStackSize,
                               android_thread_id_t* threadId)
{
   void** args = (void**) malloc(3 * sizeof(void*)); 
   int result;

   if (!threadName)
       threadName = "unnamed thread";

   args[0] = (void*) entryFunction;
   args[1] = userData;
   args[2] = (void*) strdup(threadName); 
   
   //【見小節4.4】
   result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,
       threadName, threadPriority, threadStackSize, threadId);
   return result;
}

4.4 androidCreateRawThreadEtc

[-> Threads.cpp]

int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                               void *userData,
                               const char* threadName __android_unused,
                               int32_t threadPriority,
                               size_t threadStackSize,
                               android_thread_id_t *threadId)
{
    ...
    if (threadStackSize) {
        pthread_attr_setstacksize(&attr, threadStackSize);
    }
    
    ...
    //通過pthread_create創建線程
    int result = pthread_create(&thread, &attr,
                    (android_pthread_entry)entryFunction, userData);
    pthread_attr_destroy(&attr);
    
    ...
    return 1;
}

此處entryFunction所指向的是由[小節4.3]傳遞進來的AndroidRuntime::javaThreadShell,接下來,進入該方法。

4.5 javaThreadShell

[-> AndroidRuntime.cpp]

int AndroidRuntime::javaThreadShell(void* args) {
    void* start = ((void**)args)[0]; //指向_threadLoop
    void* userData = ((void **)args)[1]; //線程對象
    char* name = (char*) ((void **)args)[2]; //線程名    
    free(args);
    JNIEnv* env;
    int result;

    //hook虛擬機【見小節4.5.1】
    if (javaAttachThread(name, &env) != JNI_OK)
        return -1;

    // 調用_threadLoop()方法見小節4.5.2】
    result = (*(android_thread_func_t)start)(userData);

    //unhook虛擬機見小節4.5.3】
    javaDetachThread();
    free(name);

    return result;
}

該方法主要功能:

  1. 調用javaAttachThread():將當前線程hook到當前進程所在的虛擬機,從而既能執行C/C++代碼,也能執行Java代碼。
  2. 調用_threadLoop():執行當前線程的核心邏輯代碼;
  3. 調用javaDetachThread():到此說明線程_threadLoop方法執行完成,則從當前進程的虛擬機中移除該線程。

4.5.1 javaAttachThread

[-> AndroidRuntime.cpp]

static int javaAttachThread(const char* threadName, JNIEnv** pEnv)
{
    JavaVMAttachArgs args;
    JavaVM* vm;
    jint result;

    vm = AndroidRuntime::getJavaVM();

    args.version = JNI_VERSION_1_4;
    args.name = (char*) threadName;
    args.group = NULL;
    // 將當前線程hook到當前進程所在的虛擬機
    result = vm->AttachCurrentThread(pEnv, (void*) &args);

    return result;
}

4.5.2 _threadLoop

[-> Threads.cpp]

int Thread::_threadLoop(void* user)
{
    ...
    do {
        if (first) {
            ...
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);
            if (result && !self->exitPending()) {
                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }
        
        Mutex::Autolock _l(self->mLock);
        //當result=false則退出該線程
        if (result == false || self->mExitPending) {
            self->mExitPending = true;
            self->mRunning = false;
            self->mThread = thread_id_t(-1);
            self->mThreadExitedCondition.broadcast();
            break;
        }
        }

        //釋放強引用,讓線程有機會退出
        strong.clear();
        //再次獲取強引用,用於下一輪循環
        strong = weak.promote();
    } while(strong != 0);
    return 0;
}

該過程與【小節3.3】完全一致,見上文。

4.5.3 javaDetachThread

[-> AndroidRuntime.cpp]

static int javaDetachThread(void)
{
    JavaVM* vm;
    jint result;

    vm = AndroidRuntime::getJavaVM();
    //當前進程的虛擬機中移除該線程
    result = vm->DetachCurrentThread();
    return result;
}

在創建Native進程的整個過程,涉及到JavaVM的AttachCurrentThread和DetachCurrentThread方法,都已深入虛擬機內部原理,本文就先講到這裏,不再深入,後續有精力再深入研究虛擬機,準備寫一系列相關文章。

五. 總結

本文介紹了3類線程的創建過程,它們都有一個共同的特點,那就是真正的線程創建過程都是通過調用pthread_create方法(見小節[2.3],[3.2],[4.4]),該方法經過層層調用,最終都會進入clone系統調用,這是linux創建線程或進程的通用接口。

Native線程中是否可以執行Java代碼的區別,在於通過javaThreadShell()方法從而實現在_threadLoop()執行前後增加分別將當前線程增加hook到虛擬機和從虛擬機移除的功能。調用過程:

技術分享

  1. Native線程(Java版):該過程相對比較復雜,見如上流程圖:
  2. Native線程: 相對簡單,只有上圖中的紫色部分:thread.run -> androidCreateRawThreadEtc -> _threadLoop
  3. Java線程: Thread.start -> Thread_nativeCreate -> CreateNativeThread

轉自:http://gityuan.com/2016/09/24/android-thread/

理解Android線程創建流程(轉)