Framework學習(二)Zygote進程

一、概述

zygote,在英語中是受精卵的意思。而在Android系統中也有這麼一個“受精卵進程” – Zygote進程。

在Android系統中,Zygote進程是所有Android進程的父進程。它通過fork的形式,創建SystemServer進程和應用程序進程。而Zygote進程則是通過linux系統的init進程啓動的。

在Android系統中各種進程的啓動過程:init進程 ––> Zygote進程 ––> SystemServer進程 ––>各種應用進程

其中,應用程序進程即我們編寫的應用的進程,SystemServer進程爲系統服務進程,比如後面還要學到的AMS、WMS都是在該進程中。

需要注意的是:

1、Android中所有的應用進程的創建都是一個應用進程通過Binder請求SystemServer進程,SystemServer進程發送socket消息給Zygote進程,統一由Zygote進程創建出來的。

2、Zygote進程在啓動的時候會創建一個虛擬機實例,因此通過Zygote進程在父進程,創建的子進程都會繼承這個虛擬機實例,App中的JAVA代碼可以得到翻譯執行。

3、進程與進程之間需要跨進程通信,由Zygote進程作爲父進程還可以獲得一個Binder線程池,這樣進程之間就可以使用Binder進行跨進程通信了。

二、啓動過程

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

192int main(int argc, char* const argv[])
193{
       ...
282    // Parse runtime arguments.  Stop at first unrecognized option.
283    bool zygote = false;
284    bool startSystemServer = false;
285    bool application = false;
286    String8 niceName;
287    String8 className;
288
289    ++i;  // Skip unused "parent dir" argument.
       //init.rc中會配置一些參數,這裏進行比較設置一些變量走進不同的分支
290    while (i < argc) {
291        const char* arg = argv[i++];
292        if (strcmp(arg, "--zygote") == 0) {
               //啓動的是Zygote進程
293            zygote = true;
294            niceName = ZYGOTE_NICE_NAME;
295        } else if (strcmp(arg, "--start-system-server") == 0) {
               //啓動的是system-server進程
296            startSystemServer = true;
297        } else if (strcmp(arg, "--application") == 0) {
298            application = true;
299        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
300            niceName.setTo(arg + 12);
301        } else if (strncmp(arg, "--", 2) != 0) {
302            className.setTo(arg);
303            break;
304        } else {
305            --i;
306            break;
307        }
308    }
309
       ...
       //設置一個“好聽的名字” zygote,之前的名稱是app_process
357    if (!niceName.isEmpty()) {
358        runtime.setArgv0(niceName.string(), true /* setProcName */);
359    }
360
361    if (zygote) {
             //通過runtime啓動zygote
364        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
365    } else if (className) {
366        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
367    } else {
368        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
369        app_usage();
370        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
371    }
372}

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

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
1057{
1058    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
1059            className != NULL ? className : "(unknown)", getuid());
1060
1061    static const String8 startSystemServer("start-system-server");
1062
1063    /*
1064     * 'startSystemServer == true' means runtime is obsolete and not run from
1065     * init.rc anymore, so we print out the boot start event here.
1066     */
1067    for (size_t i = 0; i < options.size(); ++i) {
1068        if (options[i] == startSystemServer) {
1069           /* track our progress through the boot sequence */
1070           const int LOG_BOOT_PROGRESS_START = 3000;
1071           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
1072        }
1073    }
1074
1075    const char* rootDir = getenv("ANDROID_ROOT");
1076    if (rootDir == NULL) {
1077        rootDir = "/system";
1078        if (!hasDir("/system")) {
1079            LOG_FATAL("No root directory specified, and /android does not exist.");
1080            return;
1081        }
1082        setenv("ANDROID_ROOT", rootDir, 1);
1083    }
1084
1085    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
1086    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
1087
1088    /* start the virtual machine */
1089    JniInvocation jni_invocation;
1090    jni_invocation.Init(NULL);
1091    JNIEnv* env;
1092    if (startVm(&mJavaVM, &env, zygote) != 0) {
1093        return;
1094    }
1095    onVmCreated(env);
1096
1097    /*
1098     * Register android functions.
1099     */
1100    if (startReg(env) < 0) {
1101        ALOGE("Unable to register all android natives\n");
1102        return;
1103    }
1104
1105    /*
1106     * We want to call main() with a String array with arguments in it.
1107     * At present we have two arguments, the class name and an option string.
1108     * Create an array to hold them.
1109     */
1110    jclass stringClass;
1111    jobjectArray strArray;
1112    jstring classNameStr;
1113
1114    stringClass = env->FindClass("java/lang/String");
1115    assert(stringClass != NULL);
1116    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
1117    assert(strArray != NULL);
1118    classNameStr = env->NewStringUTF(className);
1119    assert(classNameStr != NULL);
1120    env->SetObjectArrayElement(strArray, 0, classNameStr);
1121
1122    for (size_t i = 0; i < options.size(); ++i) {
1123        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
1124        assert(optionsStr != NULL);
1125        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
1126    }
1127
1128    /*
1129     * Start VM.  This thread becomes the main thread of the VM, and will
1130     * not return until the VM exits.
1131     */
1132    char* slashClassName = toSlashClassName(className != NULL ? className : "");
1133    jclass startClass = env->FindClass(slashClassName);
1134    if (startClass == NULL) {
1135        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1136        /* keep going */
1137    } else {
1138        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1139            "([Ljava/lang/String;)V");
1140        if (startMeth == NULL) {
1141            ALOGE("JavaVM unable to find main() in '%s'\n", className);
1142            /* keep going */
1143        } else {
1144            env->CallStaticVoidMethod(startClass, startMeth, strArray);
1145
1146#if 0
1147            if (env->ExceptionCheck())
1148                threadExitUncaughtException(env);
1149#endif
1150        }
1151    }
1152    free(slashClassName);
1153   //這行Log比較常見,因爲其他應用進程也是由zygote 進程fork 出來的,所有其他進程也包含這段代碼,如果其他進程在java 層crash,那麼也會走到這裏
1154    ALOGD("Shutting down VM\n");
1155    if (mJavaVM->DetachCurrentThread() != JNI_OK)
1156        ALOGW("Warning: unable to detach main thread\n");
1157    if (mJavaVM->DestroyJavaVM() != 0)
1158        ALOGW("Warning: VM did not shut down cleanly\n");
1159}

主要做了三件事情,1、調用startVm開啓虛擬機,2、調用startReg註冊JNI方法,3、使用JNI把Zygote進程啓動起來。啓動的類名爲 com.android.internal.os.ZygoteInit,方法名爲main。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
        //1、創建ZygoteServer
        ZygoteServer zygoteServer = new ZygoteServer();
        //此處開啓設置,創建過程禁止多線程,爲什麼?後面會解答
        ZygoteHooks.startZygoteNoThreadCreation();
        ...
        try {
            ...
            //設置DDMS可用
            RuntimeInit.enableDdms();
            
            //初始化啓動參數
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            // 2、解析app_main.cpp傳來的參數
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }
            //3、創建一個Server端的Socket
            zygoteServer.registerServerSocketFromEnv(socketName);
            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
               //4、預加載進程的資源和類
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }
            ...
            //此處關閉設置,允許多線程
            ZygoteHooks.stopZygoteNoThreadCreation();
                
            //5、fork一個SystemServer進程
            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
                if (r != null) {
                    r.run();
                    return;
                }
            }

           //6、啓動一個死循環,監聽socket,啓動新的應用進程  
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            zygoteServer.closeServerSocket();
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }
        if (caller != null) {
            caller.run();
         }
    }

總結一下,在main方法中主要完成了6件事:

  1. 創建ZygoteServer
  2. 解析app_main.cpp傳來的參數,獲取abi列表,獲取scoket連接名稱。(這裏需要注意的是:android系統中進程之間通訊的方式是Binder,但是有一個例外是SystemService進程與Zygote進程之間是通過Socket的方式進行通訊的)
  3. 通過registerZygoteSocket函數,註冊Socket,作爲服務端,接受ActivityManagerService的請求來創建應用程序進程
  4. 預加載類和資源,包括顏色,R文件,drawable、類等
  5. 啓動system_server進程,SystemServer進程會啓動系統的關鍵服務
  6. 調用runSelectLoop函數監聽socket來等待客戶端請求

/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

void registerServerSocketFromEnv(String socketName) {
        if (mServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                //從環境變量中獲取名爲ANDROID_SOCKET_zygote的fd
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
           }

            try {
                //創建一個FD對象
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                //不是使用IP和端口,這裏用這個FD創建LocalServerSocket
                mServerSocket = new LocalServerSocket(fd);
                mCloseSocketFd = true;
           } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
       }
}

注意,這時通過fd創建了一個socket服務端–LocalServerSocket。當Zygote進程將SystemServer進程啓動後,就會在這個服務端的Socket上來等待ActivityManagerService請求Zygote進程來創建新的應用程序進程。

這裏爲什麼是使用FD創建呢?LocalServerSocket具體是怎麼創建的?

具體可以查看 -> https://www.jianshu.com/p/ab9b83a77af6

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

static void preload(TimingsTraceLog bootTimingsTraceLog) {
124        Log.d(TAG, "begin preload");
125        bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
126        beginIcuCachePinning();
127        bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
128        bootTimingsTraceLog.traceBegin("PreloadClasses");
           //預加載類,在preloadClasses方法中會通過Class.forName將類加載到系統中,生成字節碼
129        preloadClasses();
130        bootTimingsTraceLog.traceEnd(); // PreloadClasses
131        bootTimingsTraceLog.traceBegin("PreloadResources");
           //預加載資源文件
132        preloadResources();
133        bootTimingsTraceLog.traceEnd(); // PreloadResources
134        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
135        nativePreloadAppProcessHALs();
136        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
137        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
138        preloadOpenGL();
139        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
           //預加載共享庫
140        preloadSharedLibraries();
           //預加載文字資源
141        preloadTextResources();
142        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
143        // for memory sharing purposes.
144        WebViewFactory.prepareWebViewInZygote();
145        endIcuCachePinning();
146        warmUpJcaProviders();
147        Log.d(TAG, "end preload");
148
149        sPreloadComplete = true;
150    }

從代碼中,我們可以看到哪些資源會被進行預加載:類文件、資源文件、共享庫、文字資源。總的來說,這些都是系統中App共享的資源。通過此時預加載,可以減少資源加載時間,加快了應用啓動速度。

在preloadClasses()函數中,會通過Class.forName將類加載到系統中,生成字節碼。此時預加載的類有哪些,可以查看清單:http://androidxref.com/8.0.0_r4/xref/frameworks/base/preloaded-classes

注意:這裏預加載資源都是在主線程中完成的,爲什麼不開多線程呢?猜測是防止多線程帶來的同步問題。所以纔在ZygoteInit的方法中開啓了禁止多線程的設置,完成後才關閉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章