kyle shanks
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 'Android Runtime、Thread' disqus: kyleAlien --- Android Runtime === ## OverView of Content [TOC] ## Runtime 是什麼 :::success * [**Runtime 是啥**](https://stackoverflow.com/questions/3900549/what-is-runtime) ? **++簡單來說就是支持程式運行的基礎 Library,Runtime 與語言綁定++** ```shell= 1. C Runtime: 綁定 C standard library 2. Java Runtime: 綁定 Java Virtual Machine (包括 `.jar` 包) 3. Android Runtime: 綁定 Dalvik... 等等必要工具 ``` ::: ### Android Runtime 概述 * 啟動 Android Runtime,**綁定如下** 1. **Dalvik VM**: Android Java VM,**其功能是解析 Dex 檔案,每個進程皆有一個虛擬機** :::info * **虛擬機功能、JIN** ? 1. 虛擬機主要是在解釋 Dex 格式的 ByteCode,並將這些 ByteCode 翻譯成裝置能懂得二進制程式 2. JIN,全名是 `Just In Time` 它的功能是把程式預先轉為二進制,從而大大提高了效能 (一般來說傳統 C、C++ 這種靜態語言才跑得比較快,但 JIN 讓 Java 跑得更快,甚至可能超過 C、C++ 程式) ::: 2. **Android Java Library**: 大部份來自於 Apache Hamony,開源的 Java API,Eg. `java.lang`、`java.util`、`java.net` 3. **JNI**: C、C++ 與 Java 相互調用的接口 4. **Libc**: Android 使用的 libc 是 `bionic` ## Runtime 啟動 * **Runtime 啟動時機**: Android runtime 是透過 init 進程 -> 觸發 Zygote ([**app_main**](https://android.googlesource.com/platform/frameworks/base/+/master/cmds/app_process/app_main.cpp)#main)進程啟動 -> 在 Zygote 創建的過程中啟動 ```cpp= // /cmds/app_process/app_main.cpp int main(int argc, char* const argv[]) { ... // argv[0] 是啟動名稱,eg. zygote AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); int i; ...依照分析的參數啟動 Android Runtime if (zygote) { // runtime 是 AndroidRuntime 對象 // 啟動 ZygoteInit 類 runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) { runtime.start("com.android.internal.os.RuntimeInit", args, zygote); } else { ... } } ``` > ![](https://i.imgur.com/LwCicNC.png) ### 啟動虛擬機 - AndroidRuntime#start * Zygote 進程對 AndroidRuntime **傳入 class 路徑** 後,ZygoteInit 就會啟動在 Java 虛擬機上(透過 [**AndroidRuntime**](https://android.googlesource.com/platform/frameworks/base/+/master/core/jni/AndroidRuntime.cpp) 的 start 函數),其中較為重要的函數如下 | 函數名 | 功能 | | -------- | -------- | | startVm | 啟動 Java 虛擬機 | | startReg | 完成 Java 虛擬機的 JNI 註冊 | ```cpp= // /core/jni/AndroidRuntime.cpp void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... JNIEnv* env; // 啟動虛擬機 @startVm 繼續往下分析 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } // 啟動後的回調 onVmCreated(env); /* * Register android functions. */ // @ startReg if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } ... 省略部分 } int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) { ... 省略虛擬機參數設定 /* * Initialize the VM. * * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ // 對 JNI_CreateJavaVM 繼續往下分析 if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\n"); return -1; } return 0; } ``` > ![](https://i.imgur.com/4dhSm4Y.png) ## 啟動 Java 虛擬機 - startVm 現在來分析 startVm 函數 ```cpp= // /core/jni/AndroidRuntime.cpp void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... JNIEnv* env; // 啟動虛擬機 @startVm 繼續往下分析 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } ... 省略部分 } int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) { ... 省略虛擬機參數設定 /* * Initialize the VM. * * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ // 對 JNI_CreateJavaVM 繼續往下分析 if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\n"); return -1; } return 0; } ``` ### JNI 創建虛擬機 - [JNI_CreateJavaVM](https://android.googlesource.com/platform/art/+/refs/heads/master/runtime/jni/java_vm_ext.cc) * Native 層透過 JNI 調用 Java 函數來創建虛擬機實例 ```cpp= // /runtime/java_vm_ext.cc extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { ScopedTrace trace(__FUNCTION__); const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args); if (JavaVMExt::IsBadJniVersion(args->version)) { LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version; return JNI_EVERSION; } // 設定 runtime 參數 RuntimeOptions options; for (int i = 0; i < args->nOptions; ++i) { JavaVMOption* option = &args->options[i]; options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo)); } bool ignore_unrecognized = args->ignoreUnrecognized; // 透過 Runtime 靜態方法 Create 創建 !!! if (!Runtime::Create(options, ignore_unrecognized)) { return JNI_ERR; } ... 省略部分 android::InitializeNativeLoader(); // 取得當前 runtime Runtime* runtime = Runtime::Current(); // 啟動 runtime bool started = runtime->Start(); if (!started) { delete Thread::Current()->GetJniEnv(); delete runtime->GetJavaVM(); LOG(WARNING) << "CreateJavaVM failed"; return JNI_ERR; } *p_env = Thread::Current()->GetJniEnv(); *p_vm = runtime->GetJavaVM(); return JNI_OK; } ``` > ![](https://i.imgur.com/T5M09mF.png) ### 創建 [Runtime](https://android.googlesource.com/platform/art/+/master/runtime/runtime.cc) 實例 * 靜態單例創建 [**Runtime**](https://android.googlesource.com/platform/art/+/master/runtime/runtime.cc) 實例,而 Runtime 單例是 ++進程單例++ ```cpp= // /runtime/runtime.cc Runtime* Runtime::instance_ = nullptr; bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) { RuntimeArgumentMap runtime_options; // @ 分析 Create return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) && Create(std::move(runtime_options)); } bool Runtime::Create(RuntimeArgumentMap&& runtime_options) { // TODO: acquire a static mutex on Runtime to avoid racing. if (Runtime::instance_ != nullptr) { return false; } // 創建 Runtime 實例 instance_ = new Runtime; Locks::SetClientCallback(IsSafeToCallAbort); // @ 分析 Init if (!instance_->Init(std::move(runtime_options))) { instance_ = nullptr; return false; } return true; } ``` > ![](https://i.imgur.com/5KrgJgc.png) ### 初始化 [Runtime#Init](https://android.googlesource.com/platform/art/+/master/runtime/runtime.cc#1344) - 創建 JVM、堆棧、類連結 * Runtime 會初始化創建 `堆棧`、創建 JVM 實例 1. **gc::Heap** 創建 JVM 使用的 **堆棧空間** ```cpp= // /runtime/runtime.cc bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... 省略部分 // 創建堆管理對象 (也就是 Heap) heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), runtime_options.GetOrDefault(Opt::HeapGrowthLimit), runtime_options.GetOrDefault(Opt::HeapMinFree), runtime_options.GetOrDefault(Opt::HeapMaxFree), runtime_options.GetOrDefault(Opt::HeapTargetUtilization), foreground_heap_growth_multiplier, runtime_options.GetOrDefault(Opt::StopForNativeAllocs), runtime_options.GetOrDefault(Opt::MemoryMaximumSize), runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity), GetBootClassPath(), GetBootClassPathLocations(), GetBootClassPathFds(), GetBootClassPathImageFds(), GetBootClassPathVdexFds(), GetBootClassPathOatFds(), image_locations_, instruction_set_, // Override the collector type to CC if the read barrier config. kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_, kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground) : runtime_options.GetOrDefault(Opt::BackgroundGc), runtime_options.GetOrDefault(Opt::LargeObjectSpace), runtime_options.GetOrDefault(Opt::LargeObjectThreshold), runtime_options.GetOrDefault(Opt::ParallelGCThreads), runtime_options.GetOrDefault(Opt::ConcGCThreads), runtime_options.Exists(Opt::LowMemoryMode), runtime_options.GetOrDefault(Opt::LongPauseLogThreshold), runtime_options.GetOrDefault(Opt::LongGCLogThreshold), runtime_options.Exists(Opt::IgnoreMaxFootprint), runtime_options.GetOrDefault(Opt::AlwaysLogExplicitGcs), runtime_options.GetOrDefault(Opt::UseTLAB), xgc_option.verify_pre_gc_heap_, xgc_option.verify_pre_sweeping_heap_, xgc_option.verify_post_gc_heap_, xgc_option.verify_pre_gc_rosalloc_, xgc_option.verify_pre_sweeping_rosalloc_, xgc_option.verify_post_gc_rosalloc_, xgc_option.gcstress_, xgc_option.measure_, runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM), use_generational_cc, runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs), runtime_options.Exists(Opt::DumpRegionInfoBeforeGC), runtime_options.Exists(Opt::DumpRegionInfoAfterGC)); ... 省略部分 } ``` 2. **JavaVMExt::Create** 創建 JVM 實例 ```cpp= // /runtime/runtime.cc bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... 省略部分 std::string error_msg; // 創建 JVM 虛擬機 java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg); if (java_vm_.get() == nullptr) { LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg; return false; } // Add the JniEnv handler. // TODO Refactor this stuff. java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler); ...省略部分 } ``` 3. **Thread::Attach** 創建 main Thread ```cpp= // /runtime/runtime.cc #include "thread.h" bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... 省略部分 // 開始線程 Thread::Startup(); // 創建主線程,並把該線程取為 main Thread* self = Thread::Attach("main", false, nullptr, false); CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId); CHECK(self != nullptr); self->SetIsRuntimeThread(IsAotCompiler()); // Set us to runnable so tools using a runtime can allocate and GC by default self->TransitionFromSuspendedToRunnable(); // Now we're attached, we can take the heap locks and validate the heap. GetHeap()->EnableObjectValidation(); CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); ...省略部分 } ``` 4. **ClassLinker** 創建類連結器,並將它初始化 ```cpp= // /runtime/runtime.cc bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... 省略部分 // 創建類連結器 if (UNLIKELY(IsAotCompiler())) { class_linker_ = new AotClassLinker(intern_table_); } else { class_linker_ = new ClassLinker( intern_table_, runtime_options.GetOrDefault(Opt::FastClassNotFoundException)); } if (GetHeap()->HasBootImageSpace()) { // 初始化類連結器 bool result = class_linker_->InitFromBootImage(&error_msg); if (!result) { LOG(ERROR) << "Could not initialize from image: " << error_msg; return false; } if (kIsDebugBuild) { for (auto image_space : GetHeap()->GetBootImageSpaces()) { image_space->VerifyImageAllocations(); } } ...省略部分 VLOG(startup) << "Runtime::Init exiting"; return true; } ``` > ![](https://i.imgur.com/mp8YORJ.png) ### 創建 main Thread * main Thread 會在虛擬機創建時 (Init 時) 被創建出來 ```cpp= // /runtime/runtime.cc #include "thread.h" bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ... 省略部分 // 開始線程 Thread::Startup(); // 創建主線程,並把該線程取為 main Thread* self = Thread::Attach("main", false, nullptr, false); CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId); CHECK(self != nullptr); self->SetIsRuntimeThread(IsAotCompiler()); // Set us to runnable so tools using a runtime can allocate and GC by default self->TransitionFromSuspendedToRunnable(); // Now we're attached, we can take the heap locks and validate the heap. GetHeap()->EnableObjectValidation(); CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); ...省略部分 } ``` * Thread 是透過 C++ `new` 創建的空間 ```cpp= template <typename PeerAction> Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) { // 判斷 Runtime 實例 Runtime* runtime = Runtime::Current(); if (runtime == nullptr) { ... 省略錯誤 log return nullptr; } Thread* self; { MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); if (runtime->IsShuttingDownLocked()) { ... 省略錯誤 log return nullptr; } else { Runtime::Current()->StartThreadBirth(); // ++ 創建新 Thread++ self = new Thread(as_daemon); // 初始化 Thread bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM()); Runtime::Current()->EndThreadBirth(); // 若初始化失敗則 delete 記憶體空間 if (!init_success) { delete self; return nullptr; } } } self->InitStringEntryPoints(); CHECK_NE(self->GetState(), kRunnable); self->SetState(kNative); // Run the action that is acting on the peer. if (!peer_action(self)) { runtime->GetThreadList()->Unregister(self); // Unregister deletes self, no need to do this here. return nullptr; } ... 省略部份 { ScopedObjectAccess soa(self); runtime->GetRuntimeCallbacks()->ThreadStart(self); } return self; } ``` ### 虛擬機啟動時序圖 * 虛擬機啟動時序圖 - 調用到 Runtime 前 > ![](https://i.imgur.com/aRjrNOQ.png) * Java 虛擬機啟動時序圖 - 調用到 Runtime 後 > ![](https://i.imgur.com/pHTVzrT.png) ## onVmCreated - 創建全域連結 ```cpp= // /core/jni/AndroidRuntime.cpp void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... JNIEnv* env; // 啟動虛擬機 @startVm 繼續往下分析 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } // 啟動後的回調,創建全域連結 onVmCreated(env); ... 省略部分 } ``` ### 創建 class 的全域連結 * 創建 class 的全域連結 (**NewGlobalRef**),如果 Zygote 則不會做其他事情 ```cpp= // /core/jni/AndroidRuntime.cpp class AppRuntime : public AndroidRuntime { public: AppRuntime(char* argBlockStart, const size_t argBlockLength) : AndroidRuntime(argBlockStart, argBlockLength) , mClass(NULL) { } void setClassNameAndArgs(const String8& className, int argc, char * const *argv) { mClassName = className; for (int i = 0; i < argc; ++i) { mArgs.add(String8(argv[i])); } } virtual void onVmCreated(JNIEnv* env) { // Zygote 進程會傳空進來 if (mClassName.isEmpty()) { return; // Zygote. Nothing to do here. } char* slashClassName = toSlashClassName(mClassName.string()); mClass = env->FindClass(slashClassName); if (mClass == NULL) { ALOGE("ERROR: could not find class '%s'\n", mClassName.string()); } free(slashClassName); // 創建全域連結 mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass)); } ... 省略部分函數 String8 mClassName; Vector<String8> mArgs; jclass mClass; }; ``` ## AndroidRuntime - startReg 啟動 Runtime & JVM 後,**透過 `startReg()` 註冊 ++JVM 全部的 Native 方法++** ```cpp= // /core/jni/AndroidRuntime.cpp void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ... /* * Register android functions. */ if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } ... 省略部分 } int AndroidRuntime::startReg(JNIEnv* env) { ... androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc); ... env->PushLocalFrame(200); // @ 追蹤 register_jni_procs 函數 if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { env->PopLocalFrame(NULL); return -1; } env->PopLocalFrame(NULL); //createJavaThread("fubar", quickTest, (void*) "hello"); return 0; } ``` ### JVM 的 JNI 註冊 - register_jni_procs * `register_jni_procs` 函數會循環調用 **gRegJNI 數組** 的成員所對應的方法 1. register_jni_procs 函數 ```cpp= // AndroidRuntime.cpp static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) { for (size_t i = 0; i < count; i++) { // 執行陣列中所有的函數指針 if (array[i].mProc(env) < 0) { // 判斷是否成功 ... 錯誤函數指針 log return -1; } } return 0; } ``` 2. gRegJNI 數組:內全部都是要註冊的函數指針,回傳小於 0 則代表註冊失敗 ```cpp= // AndroidRuntime.cpp #ifdef NDEBUG #define REG_JNI(name) { name } struct RegJNIRec { // 指針函數 int (*mProc)(JNIEnv*); } // REG_JNI 是一個宏 static const RegJNIRec gRegJNI[] = { // 其成員全部都是函數指針 // 拿 register_com_android_internal_os_RuntimeInit 舉個例子 REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_CharsetUtils), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_MemoryIntArray), REG_JNI(register_android_app_admin_SecurityLog), REG_JNI(register_android_content_AssetManager), ... 省略部份 } // 註冊範例 int register_com_android_internal_os_RuntimeInit(JNIEnv* env) { const JNINativeMethod methods[] = { {"nativeFinishInit", "()V", (void*)com_android_internal_os_RuntimeInit_nativeFinishInit}, {"nativeSetExitWithoutCleanup", "(Z)V", (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup}, }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods)); } int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env) { const JNINativeMethod methods[] = { { "nativeZygoteInit", "()V", (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit }, }; return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit", methods, NELEM(methods)); } ``` ## Java Thread 類 線程是操作系統 CPU 分配資源的基礎單位,**屬於抽象範疇** 線程仍是可執行的程式,就這一點來說 Thread 與其他的類並無不同,不過 Thread 中的屬性 & 方法僅用於完成 **==線程管理== 這像任務**(開始、停止、中斷.... 這些操作) :::info * 有關 Thread 的其他操做,請看另一篇 [**Thread 使用文章**](https://hackmd.io/7Ru0TE45Tnm1LEUqx4qe-A?view) ::: ### [Thread](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/lang/Thread.java) 內部原理 - [Runnable](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/lang/Runnable.java) * Thread 內部實現了 Runnable 界面 ```java= // Runnable.java public interface Runnable { public abstract void run(); } // ------------------------------------------------------- // Thread.java public class Thread implements Runnable { ... 省略 } ``` * 使用方法有兩種,最後會間接調用到 run 方法 1. 繼承 Thread,並重寫 Runnable 的 run() 方法 ```java= class ThreadImpl extends Thread { @Override public void run() { // TODO: } } ``` 2. 匿名直接實現 Runnable 方法,並設定給 Thread ```java= class ThreadImpl_2 { void threadTest() { Thread t = new Thread(new Runnable() { @Override public void run() { // TODO } }); } } ``` * 分析 Thead#start 方法:start 方法會呼叫 Native nativeCreate 創建 thread ```java= // Thread.java boolean started = false; public synchronized void start() { // 已啟動,再次呼叫 start 則會拋出 IllegalThreadStateException 異常 if (started) throw new IllegalThreadStateException(); group.add(this); started = false; try { // @ 追蹤Native 方法 nativeCreate(this, stackSize, daemon); started = true; } /* 省略 finally */ } private native static void nativeCreate(Thread t, long stackSize, boolean daemon); ``` :::success * Thread 有以下幾種狀態 ```java= // /java/lang/Thread.java public enum State { /** * The thread has been created, but has never been started. */ NEW, /** * The thread may be run. */ RUNNABLE, /** * The thread is blocked and waiting for a lock. */ BLOCKED, /** * The thread is waiting. */ WAITING, /** * The thread is waiting for a specified amount of time. */ TIMED_WAITING, /** * The thread has been terminated. */ TERMINATED } ``` ::: ### Android 應用 - 使用多執行序 の 方法 * 這種 **併發** 編程技術,在 Android 中主要有三種方式可以執行,詳細的使用、分析可以點進連結去了解(寫在另外一篇文章) 1. 繼承 Thread or 實做 Runnable 方法 2. 使用 Android 提供的特殊類 * 使用 [**AsyncTask**](https://hackmd.io/ouTTernZT7uH5RBJHCCq5A) 這個類(適合 ++短期操作++) * 使用 [**Executor、ThreadPoolExecutor**](https://hackmd.io/ouTTernZT7uH5RBJHCCq5A) 這個類(適合 ++長期操作++) 3. 使用 [**IntentServer**](https://hackmd.io/qwwq4oLMTmaw2UksbpPOUg#IntentService) 這個類,它是 Server 的子類,**運作在子線程** ## Native Thread 相關知識 :::info Native Thread 就是對 pThread 做一個簡單的封裝 ::: ### [java_lang_thread](https://cs.android.com/android/platform/superproject/+/master:art/runtime/native/java_lang_Thread.cc) - nativeCreate * 在上面我們看到 Java Thread#start 方法,會呼叫 Natvie#nativeCreate 方法(JNI 類),它會對應到 [**java_lang_Thread.cc**](https://cs.android.com/android/platform/superproject/+/master:art/runtime/native/java_lang_Thread.cc)#Thread_nativeCreate 方法 | Java 方法 | JNI 方法 | | -------- | -------- | | nativeCreate | **Thread_nativeCreate** | ```java= // java_lang_Thread.cc static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size, jboolean daemon) { ... 省略部分 Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE); } ``` * [**Thread**](https://cs.android.com/android/platform/superproject/+/master:art/runtime/thread.cc)#CreateNativeThread:創建 [**Natvie thread 對象**](https://cs.android.com/android/platform/superproject/+/master:art/runtime/thread.cc),並 **將 Java thread 對象存入 Native**,再呼叫 `pthread_create` 方法 (另個小節分析) ```cpp= // thread.cc void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { ... 省略部分 // 創建 c++ 層的 thread 對象 Thread* child_thread = new Thread(is_daemon); // 將 Java thread 對象存入 Native child_thread->tlsPtr_.jpeer = env->NewGlobalRef(java_peer); ... 省略部分 int pthread_create_result = 0; if (child_jni_env_ext.get() != nullptr) { pthread_t new_pthread; ... 省略部分 // @ 之後分析 pthread_create 方法 pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread); CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread"); if (pthread_create_result == 0) { child_jni_env_ext.release(); // NOLINT pthreads API. return; } } ``` ### [Threads](https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/Threads.cpp) run - ThreadPool 線程池 * 有另一種情況,在 Zygote 啟動時,我們在 App 啟動時 AndroidRuntime 會透過 [**ProcessState**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/ProcessState.cpp) 創建 ThreadPool :::success * 詳細請參考 [**啟動 Binder 線程池**](https://hackmd.io/Yw1s2x32QoqawvOsW039ZA?view#Binder-%E7%B7%9A%E7%A8%8B%E6%B1%A0) 分析 ::: ```cpp= // ProcessState.cpp void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } } ``` * [**Thread**](https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/Threads.cpp)#run 方法對於線程的創建有分為兩種 | 創建來源 | 實做方法 | | -------- | -------- | | Java | createThreadEtc | | Native | androidCreateRawThreadEtc | ```cpp= // /libutils/Threads.cpp status_t Thread::run(const char* name, int32_t priority, size_t stack) { ... 省略部份 mStatus = OK; mExitPending = false; mThread = thread_id_t(-1); // hold a strong reference on ourself mHoldSelf = this; mRunning = true; bool res; // 區分兩種創建方式 if (mCanCallJava) { // Java 創建 // @ 追蹤 createThreadEtc res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } else { // Native 創建 // @ 追蹤 androidCreateRawThreadEtc res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } // 創建失敗處理 if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! mRunning = false; mThread = thread_id_t(-1); mHoldSelf.clear(); // "this" may have gone away after this. return UNKNOWN_ERROR; } return OK; } ``` 1. **Native Thread 創建 pThread**:**Native 層創建的 Thread 其實就是創建一個 ++pThread 對象++** ```cpp= // 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) { // 創建 pthead 使用的屬性參數 pthread_attr_t attr; pthread_attr_init(&attr); .. 省略部分 errno = 0; // 創建 pthread 對象 pthread_t thread; // @ 分析 pthread_create 方法 int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData); pthread_attr_destroy(&attr); if (result != 0) { // 創建失敗 log... return 0; } // 設定 thread id if (threadId != nullptr) { *threadId = (android_thread_id_t)thread; // XXX: this is not portable } // 成功返回 1 return 1; } ``` 2. **Java Thread 創建 pThread**:從 **`Threads.cpp`** 中可以看到 Thread 的創建有分為兩種,Java 的創建是呼叫 `createThreadEtc` 函數 (inline 函數) ```cpp= // AndroidThreads.h extern int androidCreateThreadEtc(android_thread_func_t entryFunction, void *userData, const char* threadName, int32_t threadPriority, size_t threadStackSize, android_thread_id_t *threadId); // Create thread with lots of parameters 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 = nullptr) { return androidCreateThreadEtc(entryFunction, userData, threadName, threadPriority, threadStackSize, threadId) ? true : false; } ``` * createThreadEtc 函數:最終仍會呼叫到 `androidCreateRawThreadEtc`,也就是 **創建一個 pThread 對象** ```cpp= // /libutils/Threads.cpp // 函數指針 static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; // 實做 int androidCreateThreadEtc(android_thread_func_t entryFunction, void *userData, const char* threadName, int32_t threadPriority, size_t threadStackSize, android_thread_id_t *threadId) { // 查看 gCreateThreadFn return gCreateThreadFn(entryFunction, userData, threadName, threadPriority, threadStackSize, threadId); } // 創建 pThread 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) { // 創建 pthead 使用的屬性參數 pthread_attr_t attr; pthread_attr_init(&attr); .. 省略部分 errno = 0; // 創建 pthread 對象 pthread_t thread; // @ 追蹤 pthread_create 方法 int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData); pthread_attr_destroy(&attr); if (result != 0) { // 創建失敗 Log ... return 0; } if (threadId != nullptr) { *threadId = (android_thread_id_t)thread; // XXX: this is not portable } // 成功返回 1 return 1; } ``` ### 創建 pthread - mmap * 從上面我們知道不管是在 Java 層創建 thread、Native 層創建 thread、啟動時的 BinderThread **最終都會呼叫到 `pthread_create` 方法** ```cpp= // pthread_create.cpp int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr, void* (*start_routine)(void*), void* arg) { ... 省略部分 bionic_tcb* tcb = nullptr; void* child_stack = nullptr; // 為 thread 分配空間 // @ 追蹤 __allocate_thread 方法 int result = __allocate_thread(&thread_attr, &tcb, &child_stack); if (result != 0) { return result; } ... 省略部分 return 0; } ``` * pthread_create#`__allocate_thread` 方法:重點是呼叫 `__allocate_thread_mapping` 方法 ```java= // pthread_create.cpp static int __allocate_thread(pthread_attr_t* attr, bionic_tcb** tcbp, void** child_stack) { ThreadMapping mapping; char* stack_top; bool stack_clean = false; if (attr->stack_base == nullptr) { ... // @ 追蹤 __allocate_thread_mapping 方法 mapping = __allocate_thread_mapping(attr->stack_size, attr->guard_size); // 創建失敗則返回 if (mapping.mmap_base == nullptr) return EAGAIN; ... } else { // @ 追蹤 __allocate_thread_mapping 方法 mapping = __allocate_thread_mapping(0, PTHREAD_GUARD_SIZE); // 創建失敗則返回 if (mapping.mmap_base == nullptr) return EAGAIN; stack_top = static_cast<char*>(attr->stack_base) + attr->stack_size; } // 對齊 stack_top = align_down(stack_top - sizeof(pthread_internal_t), 16); pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top); if (!stack_clean) { // 如果 thread 不由 mmap 分配,那它可能尚未清空,所以這裡會手動清空 thread 空間 memset(thread, 0, sizeof(pthread_internal_t)); } ... 省略部分 return 0; } ``` * 可以看到 `__allocate_thread_mapping` 方法中 **會呼叫 mmap 隨機指定一個空間,為該 thread 動態分配空間** :::info * 如果要查看 mmap 函數的使用,可以用 man 指令 ```shell= man mmap ``` ::: ```java= ThreadMapping __allocate_thread_mapping(size_t stack_size, size_t stack_guard_size) { ... 省略部分 const int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE; // 呼叫 mmap 動態拓展空間 char* const space = static_cast<char*>( mmap(nullptr, mmap_size, PROT_NONE, // 分頁不可被訪問 flags, -1, // -1 代表不指定文件偏移,由系統分配 0) ); if (space == MAP_FAILED) { ... 分配失敗訊息 return {}; } ThreadMapping result = {}; result.mmap_base = space; result.mmap_size = mmap_size; result.mmap_base_unguarded = space + stack_guard_size; result.mmap_size_unguarded = mmap_size - stack_guard_size - PTHREAD_GUARD_SIZE; result.static_tls = space + mmap_size - PTHREAD_GUARD_SIZE - layout.size(); result.stack_base = space; result.stack_top = result.static_tls; return result; } ``` ## Appendix & FAQ :::info ::: ###### tags: `Android 系統`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully