0、引言

Android的底层内核是基于Linux构建而成,是在Native世界,而Android上层的应用是隶属Java世界。那么在Android系统启动过程中,系统是如何从Native孵化出Java世界的呢?这便是这篇文章的主角Zygote的主要职责。

本文所选Android系统版本是9.0 Pie,文中所有代码片段路径在代码块第一行已经标注。文章的目的是记录自己的学习历程与心得,不做商用或盈利,凡是学习过程中学习或引用过的大佬博文或著作都会尽力标注,在此感谢各位前辈的不吝分享。本文借鉴如下:

  • 《Android系统启动-zygote篇》—— 袁辉辉
  • 《Android系统进程Zygote启动过程的源代码分析》—— 罗升阳
  • 《[深入理解Android卷一全文-第四章]深入理解zygote》 —— 邓平凡
  • 《Android10.0系统启动之Zygote进程-[Android取经之路]》—— IngresGe

1、C/C++中的Zygote

上篇文章《Android9.0(Pie)1号进程init的启动流程学习》中说到,Android系统的1号进程init会通过.rc配置文件启动其他进程,zygote也是此方式被启动的。不过在pie\system\core\rootdir\目录下有好几个zygote的rc文件,根据生产环境的设备的CPU配置不同,有init.zygote32.rc、init.zygote32_64.rc、init.zygote64.rc、init.zygote64_32.rc这几个选择。在init.rc头部可以看到其导入对应配置的rc文件时,是根据属性ro.zygote来控制的,由于我的生产环境该属性值为zygote32,所以 参考《Android系统init进程启动及init.rc全解析》和《Android源码之init.rc文件详解》 来看看init.zygote32.rc吧。

// pie\system\core\rootdir\init.zygote32.rcservice zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass main    //该服务指定类属为main,这样方便操作多个服务同时启动或停止priority -20  //参考linux的进程优先级nice,取值范围【-20,19】,值越小优先级越高user root     //在执行此服务之前先切换用户名为rootgroup root readproc reserved_disk    //类似于user,切换组名socket zygote stream 660 root system    //在目标机/dev/socket/目录下创建一个unix domain类型的socket,该socket文件命名为zygote,端口为660,运行该程序需要root或system权限onrestart write /sys/android_power/request_state wake    //该服务重启时,向指定文件中写入内容onrestart write /sys/power/state ononrestart restart audioserver    //该服务重启时,重启指定服务onrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks    //创建子进程时向该文件中写入进程的pid

1.1、app_process

从第一行可以看出zygote只是对程序的重命名,实际运行的是目标机/system/bin目录下的app_process程序,其后面跟的是运行该程序所带的参数,即这个app_process是一个命令行程序,那么先找到这个程序的main函数,鉴于该函数较长,我们将主要代码分割成几个部分来看。

1.1.1、AndroidRuntime初始化

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{if (!LOG_NDEBUG) {    //debug模式下打印一下传进来的参数,方便调试和定位问题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));    //调用父类AndroidRuntime的构造函数进行初始化argc--;argv++;    // 忽略程序名所占用的第一个参数argv[0]…………
}

这里的AppRuntime是继承的pie\frameworks\base\core\jni\AndroidRuntime.cpp,AppRuntime类的构造函数为空,所以转去调用父类AndroidRuntime的构造函数:

//    pie\frameworks\base\core\jni\AndroidRuntime.cpp
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :mExitWithoutCleanup(false),       //使用命令行传进来的参数初始化该类的成员变量,mArgBlockStart(argBlockStart),    //首个参数起始位置,mArgBlockLength(argBlockLength)   //以及所有参数块所占内存大小
{init_android_graphics();              //初始化android图形功能mOptions.setCapacity(20);             //虚拟机启动时需要一些option作为参数,这里设置option数量assert(gCurRuntime == NULL);          //每个进程都要进行空指针检测gCurRuntime = this;
}

1.1.2、spaced_commands        TODO

这一部分spaced_commands相关的内容有点不知所云,但又不影响往后分析,所以先放着,后面明白了再来补充,有大佬知道的也可以评论里补充下,不胜感激。

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
…………const char* spaced_commands[] = { "-cp", "-classpath" };bool known_command = false;int i;for (i = 0; i < argc; i++) {if (known_command == true) {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) {known_command = true;ALOGV("app_process main found known command '%s'", argv[i]);}}if (argv[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]);}
}

1.1.3、参数解析

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
…………bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // 跳过没有用到的参数"/system/bin"while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {    //解析到参数"--zygote"时,zygote = true;                     //说明为zygote模式niceName = ZYGOTE_NICE_NAME;       //准备app_process进程的别名ZYGOTE_NICE_NAME="zygote"} else if (strcmp(arg, "--start-system-server") == 0) {    //解析到参数"--start-system-server"时startSystemServer = true;                              //需要启动system_server} else if (strcmp(arg, "--application") == 0) {    //如果参数中有"--application",application = true;                            //则为application模式} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);                      //application模式下设置别名} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);                          //application模式下设置类名break;} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) {    //非zygote模式下,传递参数"application"给RuntimeInitargs.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);    //传递类名和剩余参数if (!LOG_NDEBUG) {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模式下maybeCreateDalvikCache();                        //创建/data/dalvik-cache/目录if (startSystemServer) {args.add(String8("start-system-server"));    //传递参数"start-system-server"}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {    //读取abi接口listLOG_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);                 //传递系统支持的CPU架构类型参数"--abi-list=armeabi-v7a,armeabi,……"for (; i < argc; ++i) {args.add(String8(argv[i]));    //传递剩余的参数}}
}

这里主要是针对main函数传递进来的参数,解析zygote模式和application模式下的相关参数,根据参数进行设置类名、进程别名、置位标识、传递参数到AndroidRuntime等动作。

1.1.4、runtime.start()

// pie\frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
…………if (!niceName.isEmpty()) {    runtime.setArgv0(niceName.string(), true);    //设置进程的别名为前面准备好的niceName}if (zygote) {    //zygote模式runtime.start("com.android.internal.os.ZygoteInit", args, zygote);    //调用父类AndroidRuntime的start()函数} else if (className) {    //application模式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.");}
}

这里主要是为app_process进程设置别名,然后调用runtime.start()调用父类AndroidRuntime的start()函数,该函数的功能我们后面再看。到此为止的函数调用情况如下图所示:

1.2、AndroidRuntime::start()

1.2.1、环境变量相关

这个函数依然分割开来看,第一部分比较简单,就做了一些事件打印,和环境变量相关的设置与检测:

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());static const String8 startSystemServer("start-system-server");bool primary_zygote = false;for (size_t i = 0; i < options.size(); ++i) {if (options[i] == startSystemServer) {primary_zygote = true;const int LOG_BOOT_PROGRESS_START = 3000;LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));    //带时间打印一下startSystemServer事件}}const char* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {rootDir = "/system";if (!hasDir("/system")) {LOG_FATAL("No root directory specified, and /system does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);    //设置环境变量}const char* artRootDir = getenv("ANDROID_ART_ROOT");    //检查环境变量if (artRootDir == NULL) {LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");return;}const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");if (i18nRootDir == NULL) {LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");return;}const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");if (tzdataRootDir == NULL) {LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");return;}…………
}

1.2.2、加载虚拟机库

// // pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
…………
JniInvocation jni_invocation;jni_invocation.Init(NULL);…………
}

接下来能看到的就是JniInvocation类,该类相关定义位于pie/libnativehelper/目录下,其主要功能是向外部提供了动态调用虚拟机内部的相关接口。首先来看代码中调用的Init()函数:

// pie\libnativehelper\JniInvocation.cpp
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__char buffer[PROP_VALUE_MAX];
#elsechar* buffer = NULL;
#endiflibrary = GetLibrary(library, buffer);      //获取默认库libart.so//RTLD_NOW:dlopen函数的打开模式为立即解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL//RTLD_NODELETE:在dlclose()期间不卸载库,因为即使在JNI_DeleteJavaVM返回后,一些线程可能还没有完成退出,如果卸载库,这可能会导致段错误  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;handle_ = dlopen(library, kDlopenFlags);    //打开libart.so库,获取到句柄if (handle_ == NULL) {                      //如果打开失败,则再尝试一次,确保正常打开libart.soif (strcmp(library, kLibraryFallback) == 0) {ALOGE("Failed to dlopen %s: %s", library, dlerror());return false;}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;}}//FindSymbol()函数通过调用dlsym()函数,获取JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs这三个函数的地址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;
}

可以看出Init函数主要功能就是寻找并打开默认虚拟机库,并根据打开文件句柄获取到库中几个关键函数的指针。需要说明下的是,这里GetLibrary()去获取的默认库是虚拟机的库,在较早的版本比如4.4 kitkak中使用的是dalvik虚拟机,所以获取的默认库是libdvm.so,在之后的高版本中就有art虚拟机了,所以默认库就是libart.so。两者的主要区别主要是art虚拟机把字节码的翻译优化从运行时提前到安装时, 以空间换时间,从而优化运行加载时间。

1.2.3、启动JavaVM

// pie\libnativehelper\JniInvocation.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{………… JNIEnv* env;if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {return;}onVmCreated(env);    //空接口…………
}

首先出现的JNIEnv是在JNI编程中经常会出现的东西,其相关的定义是在文件pie\libnativehelper\include_jni\jni.h中,所以这里也是为后面注册JNI函数做准备。先来看startVm()函数,其主要功能就是创建JavaVM,该函数中有很多addOption(操作,这里截取一部分进行说明。

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{JavaVMInitArgs initArgs;…………addOption(***);    //此处省略一大堆通过属性值或配置来向Vector<JavaVMOption> mOptions中添加的动作…………//对结构体JavaVMInitArgs的成员赋值initArgs.version = JNI_VERSION_1_4;initArgs.options = mOptions.editArray();initArgs.nOptions = mOptions.size();initArgs.ignoreUnrecognized = JNI_FALSE;//调用前面打开的libart.so库中的JNI_CreateJavaVM函数以创建JavaVM。//JavaVM*本质上是每个进程的,而JNIEnv*是每个线程的。如果这里创建成功,就可以发出JNI调用了。if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {   ALOGE("JNI_CreateJavaVM failed\n");return -1;}return 0;
}

1.2.4、注册JNI函数

// pie\libnativehelper\JniInvocation.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{…………if (startReg(env) < 0) {    //注册JNI函数ALOGE("Unable to register all android natives\n");return;}…………
}// pie\libnativehelper\JniInvocation.cpp
int AndroidRuntime::startReg(JNIEnv* env)
{ATRACE_NAME("RegisterAndroidNatives");//设置Threads.cpp中创建线程的函数为javaCreateThreadEtcandroidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);ALOGV("--- registering native functions ---\n");//每个注册函数都会返回一个或多个native引用的对象,此时虚拟机还没有完全启动起来,//所以这里先用Frame管理局部引用的生命周期,参数200是为了足够的空间来存储。env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {    //JNI函数注册动作env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);return 0;
}

参考《android系统核心机制 基础(02)Thread类解析》,可知这里的androidSetCreateThreadFunc()函数是pie\system\core\libutils\Threads.cpp中的函数,这里的动作是将Threads类中创建线程的函数从androidCreateRawThreadEtc()设置为了AndroidRuntime.cpp中定义的javaCreateThreadEtc()。虽说最终调用的还是androidCreateRawThreadEtc()函数,但线程函数改为了AndroidRuntime.cpp中的javaThreadShell()函数。

// pie\frameworks\base\core\jni\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*));   //javaThreadShell()函数中使用完后要记得freeint result;LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc");args[0] = (void*) entryFunction;args[1] = userData;args[2] = (void*) strdup(threadName);   // javaThreadShell()函数中使用完后要记得free//最终还是调用androidCreateRawThreadEtc(),但将线程函数设置为了javaThreadShell()result = androidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args,  threadName, threadPriority, threadStackSize, threadId);    return result;
}// 线程函数
int AndroidRuntime::javaThreadShell(void* args) {void* start = ((void**)args)[0];void* userData = ((void **)args)[1];char* name = (char*) ((void **)args)[2];free(args);    //释放上面javaCreateThreadEtc函数中malloc的argsJNIEnv* env;int result;if (javaAttachThread(name, &env) != JNI_OK)    //使当前创建的线程对VM可见return -1;result = (*(android_thread_func_t)start)(userData);    //运行创建的该线程javaDetachThread();    //线程退出时将当前线程从虚拟机可见的线程集中分离free(name);return result;
}

androidSetCreateThreadFunc()函数分析完后,我们往下看比较重要的register_jni_procs()函数,其主要功能就是注册JNI函数了:

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{//传递进来的RegJNIRec类型的数组gRegJNI里面存储的全是一些注册函数的函数指针,//所以执行RegJNIRec的成员mProc,相当于执行对应的函数,去完成每个函数的注册动作。for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}// RegJNIRec的定义
#ifdef NDEBUG#define REG_JNI(name)      { name }struct RegJNIRec {int (*mProc)(JNIEnv*);};
#else#define REG_JNI(name)      { name, #name }struct RegJNIRec {int (*mProc)(JNIEnv*);const char* mName;};
#endif//截取部分gRegJNI数组内容
static const RegJNIRec gRegJNI[] = {
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),…………
}

1.2.5、准备Java main函数的形参argv

准备要进入Java世界、调用Java的main函数了,但需要先把将要执行main函数的类名"com.android.internal.os.ZygoteInit",和前面添加的虚拟机设置相关的一堆option传递过去,所以这里需要准备一下,以String[]的形式传递。

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{…………jclass stringClass;jobjectArray strArray;jstring classNameStr;stringClass = env->FindClass("java/lang/String");    //先找到Java的String类assert(stringClass != NULL);strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);    //相当于strArray = new String[options.size() + 1];assert(strArray != NULL);//字符串需要转换为Java世界的UTF格式,相当于classNameStr = new String("com.android.internal.os.ZygoteInit");classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr);    //相当于strArray[0] = classNameStr; 即"com.android.internal.os.ZygoteInit"//把存储在Vector中的所有option都转换为Java世界的String类型,顺序存储到strArray中[1, options.size()]的位置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);}…………
}

1.2.6、启动虚拟机

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{…………//将"com.android.internal.os.ZygoteInit"转化为"com/android/internal/os/ZygoteInit"格式char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);    //根据路径找到ZygoteInit类if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");    //找到ZygoteInit类的static main方法if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);    //执行ZygoteInit.main()函数
#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}…………
}

这里的动作就很好理解了,无非就是找到com/android/internal/os/ZygoteInit.java,然后执行该类的main函数,自此将进入Java世界。需要注意理解的是,根据代码中的注释,这里就是启动虚拟机的过程,当前线程也就成为了虚拟机的主线程,所以这个函数只有在虚拟机退出(比如崩溃)的时候才会return。

1.2.7、退出时释放资源

// pie\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{…………free(slashClassName);                            //释放字符串所占空间ALOGD("Shutting down VM\n");                     //虚拟机崩溃或退出的时候才会执行到这里if (mJavaVM->DetachCurrentThread() != JNI_OK)    //将当前线程从虚拟机可见的线程集中分离ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)               //销毁前面创建的JavaVMALOGW("Warning: VM did not shut down cleanly\n");
}

到此为止zygote 在C/C++部分的流程就告一段落了,这一部分的调用流程总结如下图:

2、Welcome To Java

在上面我们分析了,C/C++世界Zygote相关的启动代码,在结尾处,终于在ZygoteInit.main()函数执行时,进入到了Java世界。所以这里接着main函数往下看,追踪Java世界中Zygote相关的内容,main()函数的代码依旧是分割开来解析 。

2.1、准备工作

// pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java
public static void main(String[] argv) {ZygoteServer zygoteServer = null;//pie\libcore\dalvik\src\main\java\dalvik\system\ZygoteHooks.javaZygoteHooks.startZygoteNoThreadCreation();    //调用native函数,功能是确保此时没有其他线程启动try {Os.setpgid(0, 0);                         //设置pid} catch (ErrnoException ex) {throw new RuntimeException("Failed to setpgid(0,0)", ex);}Runnable caller;try {final long startTime = SystemClock.elapsedRealtime();    //系统启动到现在的时间,包含设备深度休眠的时间final boolean isRuntimeRestarted = "1".equals( SystemProperties.get("sys.boot_completed"));     //该属性值在设备物理重启时为空,reboot重启后为1 String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";    //设置boot时间打印TAG//pie\frameworks\base\core\java\android\util\TimingsTraceLog.javaTimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,Trace.TRACE_TAG_DALVIK); //通过systrace来追踪bootTimingsTraceLog.traceBegin("ZygoteInit");            //追踪开始,每个traceBegin()对应一个traceEnd()//使能DDMS(Dalvik Debug Monitor Server),注册所有已知的Java VM的处理块的监听器。//线程监听、内存监听、native堆内存监听、debug模式监听…RuntimeInit.preForkInit();    boolean startSystemServer = false;String zygoteSocketName = "zygote";String abiList = null;boolean enableLazyPreload = false;for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {         //读取"start-system-server"参数startSystemServer = true;//ro.zygote属性值为zygote64_32或zygote64_32时,会存在另外一个进程zygote_secondary,//zygote_secondary -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload} else if ("--enable-lazy-preload".equals(argv[i])) {enableLazyPreload = true;} else if (argv[i].startsWith(ABI_LIST_ARG)) {       //读取"--abi-list"参数abiList = argv[i].substring(ABI_LIST_ARG.length());} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {    //读取"--socket-name"参数zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}}// PRIMARY_SOCKET_NAME = "zygote"final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);if (!isRuntimeRestarted) {if (isPrimaryZygote) {FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,startTime);    //FrameworkStatsLog.java == statslog-framework-java-gen//SECONDARY_SOCKET_NAME = "zygote_secondary"} else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {    FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,startTime);}}if (abiList == null) {throw new RuntimeException("No ABI list supplied.");}…………

这一部分主要是为后面的任务做准备工作:

  • 禁止启动其他线程;
  • 设置pid;
  • 解析C/C++层传进来的参数argv;
  • 设置相关日志追踪;

2.2、preload()

// pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) {…………if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());preload(bootTimingsTraceLog);    //预加载EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd();}}

这里的预加载是指将Java类、资源文件、图像资源等公共资源在zygote启动的时候就进行加载。这样一来,根据fork的copy-on-write机制,其他由zygote fork出来的进程在使用这些资源的时候就不需要再次加载了,而是直接使用。所以这是一种牺牲系统开机时间,来提高系统应用运行时的运行效率的手段。

//pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.javastatic void preload(TimingsTraceLog bootTimingsTraceLog) {Log.d(TAG, "begin preload");bootTimingsTraceLog.traceBegin("BeginPreload");beginPreload();                          //ZygoteHooks.onBeginPreload();bootTimingsTraceLog.traceEnd();          //BeginPreloadbootTimingsTraceLog.traceBegin("PreloadClasses");preloadClasses();                        //预加载一些类bootTimingsTraceLog.traceEnd();          //PreloadClassesbootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");//加载一些应用程序使用但不能放入引导类路径的jar包库,这些库过去是引导类路径的一部分,但必须删除。//由于向后兼容性的原因,旧的系统应用程序仍然会使用它们,因此它们被缓存在这里以保持性能特征cacheNonBootClasspathClassLoaders();    bootTimingsTraceLog.traceEnd();          //CacheNonBootClasspathClassLoadersbootTimingsTraceLog.traceBegin("PreloadResources");preloadResources();                      //加载常用资源,以便它们可以跨进程共享,比如apk开发常用到的color、drawable等资源bootTimingsTraceLog.traceEnd();          //PreloadResourcesTrace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");nativePreloadAppProcessHALs();           //一些被大多数app进程加载的内容,需要通过HAL来添加(native)Trace.traceEnd(Trace.TRACE_TAG_DALVIK);Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");maybePreloadGraphicsDriver();            //根据属性ro.zygote.disable_gl_preload来判断是否禁止预加载图像驱动相关内容(native)Trace.traceEnd(Trace.TRACE_TAG_DALVIK);preloadSharedLibraries();                //加载几个共享库:libandroid.so、libcompiler_rt.so、libjnigraphics.so                          preloadTextResources();                  //启动字体缓存,设置Typeface              WebViewFactory.prepareWebViewInZygote(); //为了内存共享,WebViewFactory执行所有必须在zygote进程中运行的初始化endPreload();warmUpJcaProviders();                    //注册AndroidKeyStoreProvider并预热已经注册的providerLog.d(TAG, "end preload");sPreloadComplete = true;}

其中的preloadClasses()函数是去加载目标设备目录树中,/system/etc/preloaded-classes这个文件中每行一个全限定名格式的类(#开头的注释行和空白行则自动跳过)。该文件是由文件frameworks\base\tools\preload\WritePreloadedClassFile.java自动生成,其对于哪些类需要预加载有明确的说明:

/*
* pie\frameworks\base\tools\preload\WritePreloadedClassFile.java
* The set of classes to preload. We preload a class if:
* a) it's loaded in the bootclasspath (i.e., is a system class)                1、即系统类
* b) it takes > MIN_LOAD_TIME_MICROS = 1250 us to load, and                    2、加载时长超过1250ms的类
* c) it's loaded by more than one process, or it's loaded by anapplication     3、不止一个进程会去加载的类
*/

2.3、gcAndFinalize()

//pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) {…………bootTimingsTraceLog.traceBegin("PostZygoteInitGC");// 调用ZygoteHooks.gcAndFinalize(),通过runFinalizationSync()可以在没有HeapWorker线程的Zygote中调用finalizers,//以运行几个特殊的gc来尝试清理几代软可及和最终可及的对象,以及任何其他垃圾。gcAndFinalize();bootTimingsTraceLog.traceEnd(); // PostZygoteInitGCbootTimingsTraceLog.traceEnd(); // 对应bootTimingsTraceLog.traceBegin("ZygoteInit")   Zygote.initNativeState(isPrimaryZygote);    //初始化zygote的native状态(native方法)ZygoteHooks.stopZygoteNoThreadCreation();   //可以启动其他线程了,对应前面的ZygoteHooks.startZygoteNoThreadCreation();…………}

gcAndFinalize()方法主要就是在预加载动作之后、后续从zygote fork其他进程的动作之前,进行的一次垃圾回收。这里需要补充看一下上面这个代码段中的Zygote.initNativeState()方法:

// pie\frameworks\base\core\java\com\android\internal\os\Zygote.javastatic void initNativeState(boolean isPrimary) {nativeInitNativeState(isPrimary);}// pie\frameworks\base\core\jni\com_android_internal_os_Zygote.cpp
static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jclass, jboolean is_primary) {gZygoteSocketFD = android_get_control_socket(is_primary ? "zygote" : "zygote_secondary");    //获取socket的句柄fdif (gZygoteSocketFD >= 0) {ALOGV("Zygote:zygoteSocketFD = %d", gZygoteSocketFD);} else {ALOGE("Unable to fetch Zygote socket file descriptor");}gUsapPoolSocketFD = android_get_control_socket(is_primary ? "usap_pool_primary" : "usap_pool_secondary");if (gUsapPoolSocketFD >= 0) {ALOGV("Zygote:usapPoolSocketFD = %d", gUsapPoolSocketFD);} else {ALOGE("Unable to fetch USAP pool socket file descriptor");}//创建套接字,该套接字将被用来发送未经请求的消息到system_server,该套接字将在派生子进程后被关闭initUnsolSocketToSystemServer();    gIsSecurityEnforced = security_getenforce();    //根据selinux策略,普通apk是禁止security_getenforce的,selinux_android_seapp_context_init();           //所以在zygote fork之前初始化并缓存该策略值//Zygote进程在fork每个子进程之前首先卸载根存储空间,因为Zygote进程不使用根存储空间,所以取消对其下面的挂载名称空间的共享。//每个fork的子进程(包括SystemServer)只挂载它们自己的根存储空间,在MountEmulatedStorage方法中不需要卸载存储操作。UnmountStorageOnInit(env);if (!SetTaskProfiles(0, {})) {    //加载必须的performance profile信息zygote::ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");}
}

该函数的功能概括来讲做了这四件事:

  • 从环境变量中获取socket句柄fd;
  • 初始化安全属性;
  • 卸载适当的存储;
  • 加载必要的性能概要信息;

还有需要说明一下的是代码里出现的USAP(Unspecialized App Process),是指在android高版本里提出来的一种zygote fork子进程的机制。通过prefork的方式提前创建好一批进程,当有应用启动时,直接将已经创建好的进程分配给它,从而省去了fork的动作,从而可以提升性能。详情参考《Android Framework | 一种新型的应用启动机制:USAP》。

2.4、 forkSystemServer()

前面的三个小节做好准备工作后,下面就要开始做zygote比较重要的的一个任务了,那就是fork出system_server进程:

//pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) {…………zygoteServer = new ZygoteServer(isPrimaryZygote);    //创建zygote的ServerSocketif (startSystemServer) {Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);if (r != null) {    //{r == null} in the parent process, and {r != null} in the child processr.run();    //通过反射机制执行SystemServer.java的main()函数return;}}…………}

forkSystemServer的具体细节请参考我《Android9.0(Pie) system_server进程学习》一文。我们只需要大概知道,这里zygote首先fork出了它的第一个进程,然后将该进程改名为system_server。在该进程中加载了类加载器,然后通过反射机制找到SystemServer.java的main函数,并封装到Runnable类型的r中。最后回到上面的代码,通过r.run()执行该main方法,以启动SystsmServer的java类。

2.5、runSelectLoop()

zygote除了fork出system_server进程这个任务外,还有一个重要的任务,那就是接收AMS(ActivityManagerService)发来创建java层应用程序的请求,fork出一个个进程,并在新进程中执行该请求中相关应用程序的main方法。上层的一个个应用程序就是这样通过zygote创建而来,因为应用程序都是由zygote孕育而来,所以就不难理解zygote(受精卵)的名称的由来了。我们继续往下看看它是如何完成这一任务的:

// pie\frameworks\base\core\java\com\android\internal\os\ZygoteInit.javapublic static void main(String[] argv) {…………Runnable caller;try {…………Log.i(TAG, "Accepting command socket connections");caller = zygoteServer.runSelectLoop(abiList);} catch (Throwable ex) {Log.e(TAG, "System zygote died with fatal exception", ex);throw ex;} finally {if (zygoteServer != null) {zygoteServer.closeServerSocket();    //zygote挂掉的时候关闭ServerSocket}}if (caller != null) {caller.run();}}

runSelectLoop()方法冗长,有太多USAP相关的操作,我们这里省略相关内容,只看核心操作:

// pie\frameworks\base\core\java\com\android\internal\os\ZygoteServer.java
Runnable runSelectLoop(String abiList) {ArrayList<FileDescriptor> socketFDs = new ArrayList<>();ArrayList<ZygoteConnection> peers = new ArrayList<>();socketFDs.add(mZygoteSocket.getFileDescriptor());    //先将server socket加入到这个socketFDs列表peers.add(null);while (true) {    //间隔时间持续轮询…………int[] usapPipeFDs = null;StructPollfd[] pollFDs;pollFDs = new StructPollfd[socketFDs.size()];    //每轮循环,都重新创建需要监听的pollFDsint pollIndex = 0;for (FileDescriptor socketFD : socketFDs) {pollFDs[pollIndex] = new StructPollfd();pollFDs[pollIndex].fd = socketFD;pollFDs[pollIndex].events = (short) POLLIN;    //关注poll事件到来++pollIndex;}…………int pollTimeoutMs;//设置poll轮询时延间隔 int pollReturnValue;try {pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);…………if (pollReturnValue == 0) {    …………} else {while (--pollIndex >= 0) {if ((pollFDs[pollIndex].revents & POLLIN) == 0) {continue;}if (pollIndex == 0) {    //server socket最先加入fds, 因此这里是server socket收到数据ZygoteConnection newPeer = acceptCommandPeer(abiList);    //收到新的建立通信的请求,建立通信连接peers.add(newPeer);  //加入到peers和fds, 即下一次也开始监听socketFDs.add(newPeer.getFileDescriptor());} else if (pollIndex < usapPoolEventFDIndex) {    //说明接收到AMS通过socket发送过来创建应用程序的请求try {//有socket连接时创建ZygoteConnection对象,并添加到pollFDsZygoteConnection connection = peers.get(pollIndex);boolean multipleForksOK = !isUsapPoolEnabled()&& ZygoteHooks.isIndefiniteThreadSuspensionSafe();final Runnable command =connection.processCommand(this, multipleForksOK);    //完成创建子进程的请求if (mIsForkChild) {if (command == null) {throw new IllegalStateException("command == null");}return command;    //依然是提供Runnable接口,通过反射机制执行目标应用程序的main方法} else {if (command != null) {throw new IllegalStateException("command != null");}//处理完后,关闭socket连接,并从peers和socketFDs列表中移除if (connection.isClosedByPeer()) {    connection.closeSocket();peers.remove(pollIndex);socketFDs.remove(pollIndex);}}} catch (Exception e) {…………}

这里大概意思就是,循环间隔一段时间轮询连接socket消息,看是否有AMS客户端发过来创建应用程序的请求,有的话则通过pie\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.processCommand()方法创建进程并启动目标应用程序。我们只需要直到这里是接收AMS创建应用程序的请求,完成目标应用程序创建与启动的就好。详细细节我后面学习到AMS之后,单独写一篇文章来追踪AMS与zygote之间通过socket通信,创建应用程序的过程吧。

3、结语

总结来说,zygote在开机过程中,其先是启动虚拟机、注册JNI函数、进入java世界,然后fork出system_server进程,之后作为守护进程监听并处理创建普通应用程序的工作。原图请见:Pie_Zygote | ProcessOn免费在线作图,在线流程图,在线思维导图 |

Zygote——Android系统中java世界的受精卵相关推荐

  1. Android系统中的Binder通信机制分析(7)- Java 层的 Binder 机制

    声明 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6.7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾- 文中参考 ...

  2. Android系统中的进程管理:进程的创建

    对于操作系统来说,进程管理是其最重要的职责之一. 考虑到这部分的内容较多,因此会拆分成几篇文章来讲解. 本文是进程管理系统文章的第一篇,会讲解Android系统中的进程创建. 本文适合Android平 ...

  3. Android系统中的进程管理:内存的回收

    本文是Android系统进程管理的第三篇文章.进程管理的前面两篇文章,请参见这里: Android系统中的进程管理:进程的创建 Android系统中的进程管理:进程的优先级 本文适合Android平台 ...

  4. Android系统中的进程管理:进程的优先级

    本文是Android进程管理系列文章的第二篇,会讲解进程管理中的优先级管理. 进程管理的第一篇文章:<进程的创建>请跳转至这里. 本文适合Android平台的应用程序开发者,也适合对于An ...

  5. Android 系统中 Location Service 的实现与架构

    定位服务是移动设备上最常用的功能之一,本文以 Android 源码为基础,详细分析了 Android 系统中定位服务的架构和实现. 在 Android 系统中,所有系统服务的实现都是类似的.只要明白其 ...

  6. Android系统中通过shell命令实现wifi的连接控制

    简介 工作中遇到一个"变态"的需求,在android系统中不通过java层控制wifi的连接(主要是修改ap的essid和password),而是需要通过native层实现对wif ...

  7. android属于数据库管理系统,详细谈谈Android系统中的SQLite数据库的应用

    数据库是按照数据结构来组织.存储和管理数据的仓库,而在信息话的社会,数据库又不单单仅限与数据的相关内容,现在数据库技术是管理信息系统.办公自动化系统.决策支持系统等各类信息系统的核心部分,而SQL是结 ...

  8. 向Android系统中添加模块及产品流程

     添加Android模块  一.基础知识: (1)在Android系统中,编译都是以模块(而不是文件)作为单位的,每个模块都有一个唯一的名称: (2)为了将模块编译到Android系统中,每个模块都需 ...

  9. Android系统中如何添加USB网络共享

    Android系统中如何添加USB网络共享 类别 需求 索引类别 USB网络共享 问题描述 平台是RT1296,在Android系统中已经有支持USB网络共享,但相应的产品系统中还未开启USB网络共享 ...

最新文章

  1. Redis学习笔记~Redis在windows环境下的安装
  2. Promise.race 的原理
  3. 美国纽约的一个摄像头!刷新即现奇迹!
  4. flume案例-网络数据采集-Flume安装
  5. Struts 整合 SpringMVC
  6. java 文件监听器_java7 文件监听器
  7. java同名变量在list中添加两次_快速解决List集合add元素,添加多个对象出现重复的问题...
  8. python print 变量_Python之print()函数与变量
  9. rocketmq学习杂记
  10. 向着DJANGO奔跑!
  11. 梯度消失与梯度爆炸---解决方案(二)--杀手锏
  12. 使用EditPlus 3时,如何重新设置HTML Page的Default模板
  13. Linux 内核 下载 编译 安装 2021 ubuntu
  14. 错题集--大一C语言选择题
  15. 大数据面试题知识点分析(十一)之Flume面试真题及答案
  16. linux 应用程序 dma,Linux之DMA API(上)
  17. c++_设计一个 Studnet(学生)类
  18. linux win95模拟,Windows 95模拟器
  19. 【数据结构】单链表逆序
  20. python爬虫-异步爬虫

热门文章

  1. Ubuntu 思维导图 XMind 安装笔记
  2. c盘明明没装什么东西却爆满怎么办?
  3. Linux 硬链接数的理解和cd . cd ..的实质
  4. 深入浅出Vue.js阅读——整体流程——实例方法与全局API的实现原理
  5. 移动端(ios and android)长按识别二维码(含js与原生互调)
  6. QCC3040-QACT工具使用
  7. 计算机er夏令营拿了优营,就一定能保研上岸吗?
  8. 分布式系统服务单点问题的探讨
  9. C#_Unicode字符串 转中文编码
  10. 深度学习:使用visdom可视化训练过程时,出现蓝屏,不能正常显示的解决办法