(連載)Android 8.0 : Android系統(tǒng)啟動流程之zygote進程(一)

前言

在上一篇中我們講到飒焦,init進程會解析.rc文件牺荠,然后得到一些service去啟動休雌,這些service通常不是普通的服務挑辆,文檔里面的稱呼是daemon(守護進程).
所謂守護進程就是這些服務進程會在系統(tǒng)初始化時啟動鱼蝉,并一直運行于后臺魁亦,直到系統(tǒng)關閉時終止. 我們本篇講的zygote進程就是其中之一洁奈,zygote進程主要負責
創(chuàng)建Java虛擬機利术,加載系統(tǒng)資源印叁,啟動SystemServer進程轮蜕,以及在后續(xù)運行過程中啟動普通的應用程序. 由于zygote進程內容比較多,我將分兩個篇章來講率触,
本篇只講zygote的觸發(fā)到創(chuàng)建Java虛擬機的部分.

本文主要講解以下內容

  • zygote觸發(fā)過程
  • zygote參數(shù)解析
  • 創(chuàng)建虛擬機

本文涉及到的文件

platform/system/core/rootdir/init.zygoteXX.rc
platform/system/core/rootdir/init.rc
platform/frameworks/base/cmds/app_process/app_main.cpp
platform/frameworks/base/core/jni/AndroidRuntime.cpp
platform/libnativehelper/JniInvocation.cpp
platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

一、zygote觸發(fā)過程

1.1 init.zygoteXX.rc

定義在platform/system/core/rootdir/init.zygoteXX.rc

我們知道service是定義在.rc文件中的垒玲,那么zygote定義在哪兒呢合愈?在init.rc中有這樣一句

import /init.${ro.zygote}.rc

上節(jié)中講到 ${ro.zygote} 會被替換成 ro.zyogte 的屬性值佛析,這個是由不同的硬件廠商自己定制的寸莫,
有四個值膘茎,zygote32披坏、zygote64棒拂、zygote32_64帚屉、zygote64_32 漾峡,也就是說可能有四種 .rc 文件生逸,分別是:

  • init.zygote32.rc:zygote 進程對應的執(zhí)行程序是 app_process (純 32bit 模式)
  • init.zygote64.rc:zygote 進程對應的執(zhí)行程序是 app_process64 (純 64bit 模式)
  • init.zygote32_64.rc:啟動兩個 zygote 進程 (名為 zygote 和 zygote_secondary),對應的執(zhí)行程序分別是 app_process32 (主模式)辣之、app_process64
  • init.zygote64_32.rc:啟動兩個 zygote 進程 (名為 zygote 和 zygote_secondary)怀估,對應的執(zhí)行程序分別是 app_process64 (主模式)多搀、app_process32

為什么要定義這么多種情況呢康铭?直接定義一個不就好了从藤,這主要是因為Android 5.0以后開始支持64位程序夷野,為了兼容32位和64位才這樣定義.
不同的zygote.rc內容大致相同悯搔,主要區(qū)別體現(xiàn)在啟動的是32位妒貌,還是64位的進程.
init.zygote32_64.rc和init.zygote64_32.rc會啟動兩個進程灌曙,且存在主次之分. 我們以init.zygote64_32.rc為例

// 進程名稱是zygote,運行的二進制文件在/system/bin/app_process64
// 啟動參數(shù)是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system //創(chuàng)建一個socket,名字叫zygote,以tcp形式
    onrestart write /sys/android_power/request_state wake //onrestart 指當進程重啟時執(zhí)行后面的命令
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks //創(chuàng)建子進程時,向/dev/cpuset/foreground/tasks 寫入pid

// 另一個service ,名字 zygote_secondary
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

1.2 start zygote

定義在 platform/system/core/rootdir/init.rc

定義了service,肯定有地方調用 start zygote ,搜索一下就在init.rc中找到了, 只要觸發(fā) zygote-start 就可以

on zygote-start && property:ro.crypto.state=unencrypted
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

zygote-start 是在 on late-init 中觸發(fā)的

on late-init
    ...

    trigger zygote-start

late-init 在哪兒觸發(fā)的呢拧晕?其實上一篇中有講到,在init進程的最后靡馁,會加入 late-init 的trigger

    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }

由此分析臭墨,zygote的觸發(fā)是在init進程最后赔嚎,接下來,我們看看start zygote是如何繼續(xù)執(zhí)行的.

1.3 app_processXX

上一篇中我們知道 start 命令有一個對應的執(zhí)行函數(shù) do_start ,定義在platform/system/core/init/builtins.cpp中

do_start首先是通過FindServiceByName去service數(shù)組中遍歷胧弛,根據(jù)名字匹配出對應的service,然后調用service的Start函數(shù)尤误,
Start函數(shù)我們在上一篇結尾有分析,主要是fork出一個新進程然后執(zhí)行service對應的二進制文件结缚,并將參數(shù)傳遞進去.

static const Map builtin_functions = {
        ...

        {"start",                   {1,     1,    do_start}},

        ...
};

static int do_start(const std::vector<std::string>& args) {
    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]); //找出對應service
    if (!svc) {
        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
        return -1;
    }
    if (!svc->Start())
        return -1;
    return 0;
}

zygote對應的二進制文件是 /system/bin/app_process64 (以此為例)损晤,我們看一下對應的mk文件,
對應的目錄在platform/frameworks/base/cmds/app_process/Android.mk,
其實不管是app_process尤勋、app_process32還是app_process64,對應的源文件都是app_main.cpp.

...

app_process_src_files := \
    app_main.cpp \


LOCAL_SRC_FILES:= $(app_process_src_files)

...

LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64

...

接下來德崭,我們分析app_main.cpp.

二斥黑、zygote參數(shù)解析

platform/frameworks/base/cmds/app_process/app_main.cpp

在app_main.cpp的main函數(shù)中,主要做的事情就是參數(shù)解析. 這個函數(shù)有兩種啟動模式:

  • 一種是zygote模式眉厨,也就是初始化zygote進程锌奴,傳遞的參數(shù)有--start-system-server --socket-name=zygote,前者表示啟動SystemServer憾股,后者指定socket的名稱
  • 一種是application模式鹿蜀,也就是啟動普通應用程序,傳遞的參數(shù)有class名字以及class帶的參數(shù)

兩者最終都是調用AppRuntime對象的start函數(shù)服球,加載ZygoteInit或RuntimeInit兩個Java類茴恰,并將之前整理的參數(shù)傳入進去

由于本篇講的是zygote進程啟動流程,因此接下來我只講解ZygoteInit的加載.

int main(int argc, char* const argv[])
{
    //將參數(shù)argv放到argv_String字符串中斩熊,然后打印出來
    //之前start zygote傳入的參數(shù)是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    if (!LOG_NDEBUG) {
      String8 argv_String;
      for (int i = 0; i < argc; ++i) {
        argv_String.append("\"");
        argv_String.append(argv[i]);
        argv_String.append("\" ");
      }
      ALOGV("app_process main with argv: %s", argv_String.string());
    }

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//構建AppRuntime對象往枣,并將參數(shù)傳入
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to '--' or first non '-' arg goes to the vm.
    //
    // The first argument after the VM args is the "parent dir", which
    // is currently unused.
    //
    // After the parent dir, we expect one or more the following internal
    // arguments :
    //
    // --zygote : Start in zygote mode
    // --start-system-server : Start the system server.
    // --application : Start in application (stand alone, non zygote) mode.
    // --nice-name : The nice name for this process.
    //
    // For non zygote starts, these arguments will be followed by
    // the main class name. All remaining arguments are passed to
    // the main method of this class.
    //
    // For zygote starts, all remaining arguments are passed to the zygote.
    // main function.
    //
    // Note that we must copy argument string values since we will rewrite the
    // entire argument block when we apply the nice name to argv0.
    //
    // As an exception to the above rule, anything in "spaced commands"
    // goes to the vm even though it has a space in it.

    //上面這段英文大概講的是,所有在 "--" 后面的非 "-"開頭的參數(shù)都將傳入vm, 但是有個例外是spaced commands數(shù)組中的參數(shù)

    const char* spaced_commands[] = { "-cp", "-classpath" };//這兩個參數(shù)是Java程序需要依賴的Jar包粉渠,相當于import
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;
    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) { //將spaced_commands中的參數(shù)額外加入VM
          runtime.addOption(strdup(argv[i]));
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {//比較參數(shù)是否是spaced_commands中的參數(shù)
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') { //如果參數(shù)第一個字符是'-'分冈,直接跳出循環(huán),之前傳入的第一個參數(shù)是 -Xzygote,所以執(zhí)行到這兒就跳出了霸株,i=0
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        ALOGV("app_process main add option '%s'", argv[i]);
    }

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    //跳過一個參數(shù)雕沉,之前跳過了-Xzygote,這里繼續(xù)跳過 /system/bin ,也就是所謂的 "parent dir"
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {//表示是zygote啟動模式
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;//這個值根據(jù)平臺可能是zygote64或zygote
        } else if (strcmp(arg, "--start-system-server") == 0) {//需要啟動SystemServer
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {//表示是application啟動模式去件,也就是普通應用程序
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {//進程別名
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {//application啟動的class
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {//className不為空坡椒,說明是application啟動模式
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);//將className和參數(shù)設置給runtime

        if (!LOG_NDEBUG) {//打印class帶的參數(shù)
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else { //zygote啟動模式
        // We're in zygote mode.
        maybeCreateDalvikCache(); //新建Dalvik的緩存目錄

        if (startSystemServer) {//加入start-system-server參數(shù)
            args.add(String8("start-system-server"));
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag); //加入--abi-list=參數(shù)

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {//將剩下的參數(shù)加入args
            args.add(String8(argv[i]));
        }
    }

    if (!niceName.isEmpty()) {//設置進程別名
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) { //如果是zygote啟動模式扰路,則加載ZygoteInit
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {//如果是application啟動模式,則加載RuntimeInit
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

我們看到倔叼,在最后調用的是runtime.start函數(shù)汗唱,這個就是要啟動虛擬機了,接下來我們分析start函數(shù)

三缀雳、創(chuàng)建虛擬機

這部分我將分兩步講解渡嚣,一是虛擬機的創(chuàng)建,二是調用ZygoteInit類的main函數(shù)

3.1 創(chuàng)建虛擬機肥印、注冊JNI函數(shù)

platform/frameworks/base/core/jni/AndroidRuntime.cpp

前半部分主要是初始化JNI识椰,然后創(chuàng)建虛擬機,注冊一些JNI函數(shù)深碱,我將分開一個個單獨講


void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

    ... //打印一些日志腹鹉,獲取ANDROID_ROOT環(huán)境變量

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);//初始化JNI,加載libart.so
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {//創(chuàng)建虛擬機
        return;
    }
    onVmCreated(env);//表示虛擬創(chuàng)建完成,但是里面是空實現(xiàn)

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {注冊JNI函數(shù)
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    ... //JNI方式調用ZygoteInit類的main函數(shù)
}

3.1.1 JniInvocation.Init

定義在platform/libnativehelper/JniInvocation.cpp

Init函數(shù)主要作用是初始化JNI敷硅,具體工作是首先通過dlopen加載libart.so獲得其句柄功咒,然后調用dlsym從libart.so中找到
JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM绞蹦、JNI_GetCreatedJavaVMs三個函數(shù)地址力奋,賦值給對應成員屬性,
這三個函數(shù)會在后續(xù)虛擬機創(chuàng)建中調用.

bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
  char buffer[PROP_VALUE_MAX];
#else
  char* buffer = NULL;
#endif
  library = GetLibrary(library, buffer);//默認返回 libart.so
  // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
  // This is due to the fact that it is possible that some threads might have yet to finish
  // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
  // unloaded.
  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  /*
   * 1.dlopen功能是以指定模式打開指定的動態(tài)鏈接庫文件幽七,并返回一個句柄
   * 2.RTLD_NOW表示需要在dlopen返回前景殷,解析出所有未定義符號,如果解析不出來澡屡,在dlopen會返回NULL
   * 3.RTLD_NODELETE表示在dlclose()期間不卸載庫猿挚,并且在以后使用dlopen()重新加載庫時不初始化庫中的靜態(tài)變量
   */
  handle_ = dlopen(library, kDlopenFlags); // 獲取libart.so的句柄
  if (handle_ == NULL) { //獲取失敗打印錯誤日志并嘗試再次打開libart.so
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, kDlopenFlags);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }

  /*
   * 1.FindSymbol函數(shù)內部實際調用的是dlsym
   * 2.dlsym作用是根據(jù) 動態(tài)鏈接庫 操作句柄(handle)與符號(symbol),返回符號對應的地址
   * 3.這里實際就是從libart.so中將JNI_GetDefaultJavaVMInitArgs等對應的地址存入&JNI_GetDefaultJavaVMInitArgs_中
   */
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

3.1.2 startVm

定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp

這個函數(shù)特別長驶鹉,但是里面做的事情很單一绩蜻,其實就是從各種系統(tǒng)屬性中讀取一些參數(shù),然后通過addOption設置到AndroidRuntime的mOptions數(shù)組中存起來室埋,
另外就是調用之前從libart.so中找到JNI_CreateJavaVM函數(shù)办绝,并將這些參數(shù)傳入,由于本篇主要講zygote啟動流程姚淆,因此關于虛擬機的實現(xiàn)就不深入探究了

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
    JavaVMInitArgs initArgs;
    ...
    addOption("exit", (void*) runtime_exit);各//將參數(shù)放入mOptions數(shù)組中
    ...
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();//將mOptions賦值給initArgs
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {//調用libart.so的JNI_CreateJavaVM函數(shù)
            ALOGE("JNI_CreateJavaVM failed\n");
            return -1;
    }
    return 0;
}

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
}

jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);//調用之前初始化的JNI_CreateJavaVM_
}

3.1.3 startReg

定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp

startReg首先是設置了Android創(chuàng)建線程的處理函數(shù)孕蝉,然后創(chuàng)建了一個200容量的局部引用作用域,用于確保不會出現(xiàn)OutOfMemoryException肉盹,
最后就是調用register_jni_procs進行JNI注冊

int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
    //設置Android創(chuàng)建線程的函數(shù)javaCreateThreadEtc昔驱,這個函數(shù)內部是通過Linux的clone來創(chuàng)建線程的

    ALOGV("--- registering native functions ---\n");

    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);//創(chuàng)建一個200容量的局部引用作用域,這個局部引用其實就是局部變量

    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { //注冊JNI函數(shù)
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);//釋放局部引用作用域

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}

3.1.4 register_jni_procs

定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp

它的處理是交給RegJNIRec的mProc,RegJNIRec是個很簡單的結構體疹尾,mProc是個函數(shù)指針

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) { //調用mProc
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

struct RegJNIRec {
   int (*mProc)(JNIEnv*);
};

我們看看register_jni_procs傳入的RegJNIRec數(shù)組gRegJNI,里面就是一堆的函數(shù)指針

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    REG_JNI(register_android_util_Log),
    REG_JNI(register_android_util_MemoryIntArray)
    ...
}

我們隨便看一個register_com_android_internal_os_ZygoteInit,這實際上是自定義JNI函數(shù)并進行動態(tài)注冊的標準寫法,
內部是調用JNI的RegisterNatives,這樣注冊后上忍,Java類ZygoteInit的native方法nativeZygoteInit就會調用com_android_internal_os_ZygoteInit_nativeZygoteInit函數(shù)

int register_com_android_internal_os_ZygoteInit(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));
}

以上便是第一部分的內容骤肛,主要工作是從libart.so提取出JNI初始函數(shù)JNI_CreateJavaVM,然后讀取一些系統(tǒng)屬性作為參數(shù)調用JNI_CreateJavaVM創(chuàng)建虛擬機窍蓝,
在虛擬機創(chuàng)建完成后腋颠,動態(tài)注冊一些native函數(shù),接下來我們講第二部分吓笙,反射調用ZygoteInit類的main函數(shù)

3.2 反射調用ZygoteInit類的main函數(shù)

虛擬機創(chuàng)建完成后淑玫,我們就可以用JNI反射調用Java了,其實接下來的語法用過JNI的都應該比較熟悉了面睛,直接是CallStaticVoidMethod反射調用ZygoteInit的main函數(shù)

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
     
    //接下來的這些語法大家應該比較熟悉了絮蒿,都是JNI里的語法,主要作用就是調用ZygoteInit類的main函數(shù) 
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);//將字符中的.轉換為/
    jclass startClass = env->FindClass(slashClassName);//找到class
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);//調用main函數(shù)

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)//退出當前線程
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0) //創(chuàng)建一個線程叁鉴,該線程會等待所有子線程結束后關閉虛擬機
        ALOGW("Warning: VM did not shut down cleanly\n");
}

小結

本篇主要講zygote進程的觸發(fā)過程土涝,zygote是如何解析傳進來的參數(shù),然后講了Java虛擬機的創(chuàng)建. 有了虛擬機幌墓,就可以執(zhí)行Java代碼了但壮,
下一篇我將講解JNI有關的知識,因為這是溝通Java層和C++層的橋梁常侣,frameworks層有非常多的native方法蜡饵,如果不了解JNI相關的知識,
代碼是很難讀懂的.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末胳施,一起剝皮案震驚了整個濱河市溯祸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巾乳,老刑警劉巖您没,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胆绊,居然都是意外死亡氨鹏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門压状,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仆抵,“玉大人,你說我怎么就攤上這事种冬×统螅” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵娱两,是天一觀的道長莺匠。 經常有香客問我,道長十兢,這世上最難降的妖魔是什么趣竣? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任摇庙,我火速辦了婚禮,結果婚禮上遥缕,老公的妹妹穿的比我還像新娘卫袒。我一直安慰自己,他們只是感情好单匣,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布夕凝。 她就那樣靜靜地躺著,像睡著了一般户秤。 火紅的嫁衣襯著肌膚如雪码秉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天鸡号,我揣著相機與錄音泡徙,去河邊找鬼。 笑死膜蠢,一個胖子當著我的面吹牛堪藐,可吹牛的內容都是我干的。 我是一名探鬼主播挑围,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼礁竞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了杉辙?” 一聲冷哼從身側響起模捂,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜘矢,沒想到半個月后狂男,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡品腹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年岖食,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舞吭。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡泡垃,死狀恐怖,靈堂內的尸體忽然破棺而出羡鸥,到底是詐尸還是另有隱情蔑穴,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布惧浴,位于F島的核電站存和,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜捐腿,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一祭饭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叙量,春花似錦、人聲如沸九串。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春趴拧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刑顺。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工玻孟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扑馁。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓涯呻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腻要。 傳聞我的和親對象是個殘疾皇子复罐,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容