Android Zygote进程启动源码

587 阅读6分钟

一、 写在前面

  Zygote Android中最重要进程之一,Android中SystemServer进程、应用进程均是通过该进程fork而来。了解了Zygote进程的启动流程源码(基于Android源码master分支)对Android整体框架以及Android应用进程启动相关源码可谓是大有裨益。

二、 Zygote启动-native层

1、Zygote进程启动入口

  Zygote进程是init进程通过解析init.zygote64/32.rc文件启动的。其入口位于app_main.cpp的main函数中。部分代码如下所示:

//argc:参数个数
//argv:zygote进程启动参数,来源于xx.rc文件
int main(int argc, char* const argv[])
{
    //省略日志打印
    ......
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    //第一个参数没有使用到,因此直接跳过
    argc--;
    argv++;

    //The first argument after the VM args is the "parent dir", which
    // is currently unused.
    ......

    int i;
    for (i = 0; i < argc; i++) {
        //参数赋值给AppRuntime
        ......
    }

    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        //判断是否是启动zygote进程
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        //判断是否启动SystemServer进程,java层有使用到该参数	
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // We're not in zygote mode, the only argument we need to pass
        ......
    } else {
         //将参数存储到Vector列表中
        // We're in zygote mode.
        ......
    }

    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }
    //判断当前是否是启动zygote进程
    if (zygote) {
        //正式进入到Zygote进程启动流程
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (!className.isEmpty()) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        ......
    }
}

2、Zygote进程初始化

  如下代码主要完成了虚拟机启动(可参考博客:基于Android R之ART虚拟机的创建流程)、动态注册Android框架中native函数与C++层函数的映射以及通过反射调用到ZygoteInit类中main函数。

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    static const String8 startSystemServer("start-system-server");
    bool primary_zygote = false;
    ......

    //getenv(xx)用于获取Android系统环境变量
    //判断Android根目录是否存在
    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);
    }

    //判断art根目录是否存在
    const char* artRootDir = getenv("ANDROID_ART_ROOT");
    if (artRootDir == NULL) {
        LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");
        return;
    }

    //判断系统国际化数据根目录是否存在(/system/usr/share/i18n)
    //i18n数据包括与本地化相关的资源,例如语言翻译、日期/时间格式和货币符号,这些资源被Android应用程序用于提供本地化的用户体验
    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;
    }
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    //启动虚拟机,以及根据虚拟机创建当前线程对应的JNIEnv(jni调用到java层相关对象)对象
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    //动态注册Android框架中native函数与C++层函数的映射
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    ......
    //省略参数构造相关代码

    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            //最终通过反射调用到Android框架类com.android.internal.os.ZygoteInit的main函数中
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
                        ......
              }
    }
    free(slashClassName);
    ......
}

3、startReg

  该代码实现比较简单,就是循环调用结构体数组RegJNIRec中的函数指针,以动态注册Android框架中native函数与C++层函数的映射。大体代码如下:

//部分函数指针定义
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),
        ......
}

#define REG_JNI(name)      { name }
struct RegJNIRec {
        //定义函数指针
    int (*mProc)(JNIEnv*);
};

int AndroidRuntime::startReg(JNIEnv* env)
{
    env->PushLocalFrame(200);
    
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    return 0;
}


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) {
            return -1;
        }
    }
    return 0;
}

  如下以register_android_os_SystemClock为例。最终会调用到android_os_SystemClock类中。代码如下:

static const JNINativeMethod gMethods[] = {
    { "uptimeMillis", "()J", (void*) uptimeMillis },
    { "uptimeNanos", "()J", (void*) uptimeNanos },
    { "elapsedRealtime", "()J", (void*) elapsedRealtime },
    { "elapsedRealtimeNanos", "()J", (void*) elapsedRealtimeNano },

    { "currentThreadTimeMillis", "()J",
            (void*) android_os_SystemClock_currentThreadTimeMillis },
    { "currentThreadTimeMicro", "()J",
            (void*) android_os_SystemClock_currentThreadTimeMicro },
    { "currentTimeMicro", "()J",
            (void*) android_os_SystemClock_currentTimeMicro },
};
int register_android_os_SystemClock(JNIEnv* env)
{
    //最后一个参数表示需要注册函数个数
    return RegisterMethodsOrDie(env, "android/os/SystemClock", gMethods, NELEM(gMethods));
}

三、 Zygote启动-Java层

1、ZygoteInit

  ZygoteInit中main函数整体来说就三件事情:(1)Zygote相关初始化;(2)fork systemServer进程;(3)创建LocalSocketServer用于监听进程创建。

public static void main(String[] argv) {
    ZygoteServer zygoteServer = null;
	
    //用于表示Zygote进程启动,如果在该进程中新建线程则会报错
    //在zygote中只能是单线程,这和后续子进程创建fork有关
    ZygoteHooks.startZygoteNoThreadCreation();

    try {
        //用于将当前进程的进程组ID(PGID)设置为与该进程的pid相同的值
        //第一个参数为0表示将当前进程PGID设置为自身的PID,第二个参数为0表示将该操作用于当前进程
        Os.setpgid(0, 0);
    } catch (ErrnoException ex) {
        throw new RuntimeException("Failed to setpgid(0,0)", ex);
    }

    Runnable caller;
    try {
        final long startTime = SystemClock.elapsedRealtime();
        //判断当前Android系统是否是重启
        final boolean isRuntimeRestarted = "1".equals(SystemProperties.get("sys.boot_completed"));
		
        //判断当前进程是64位还是32位
        String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
        TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                Trace.TRACE_TAG_DALVIK);
        bootTimingsTraceLog.traceBegin("ZygoteInit");
        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])) {
                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)) {
                zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
            } else {
                throw new RuntimeException("Unknown command line argument: " + argv[i]);
            }
        }
		
        //判断当前是否是zygote进程启动
        final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
        //省略trace相关
        .......
		
        //gc相关,尝试清除无用对象
        gcAndFinalize();
		
        //zygote native层相关初始化,比如获取socket fd、selinux初始化以及storage存储目录初始化
        Zygote.initNativeState(isPrimaryZygote);
		
        //表示现在可以创建线程
        ZygoteHooks.stopZygoteNoThreadCreation();
		
        //用于构造LocalSocket
        zygoteServer = new ZygoteServer(isPrimaryZygote);
		
        //fork SystemServer进程
        if (startSystemServer) {
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
            if (r != null) {
                r.run();
                return;
            }
        }

        Log.i(TAG, "Accepting command socket connections");
        //LocalSocketServer等待链接,用于子进程创建
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        throw ex;
    } finally {
        if (zygoteServer != null) {
            zygoteServer.closeServerSocket();
        }
    }

    if (caller != null) {
        caller.run();
    }
}

四、总结

  如上即为Zygote启动的整体流程,其所涉及到的代码并不多,但细节也不少;对于其他没有涉及到的细节部分可移步Android系统启动-zygote篇。除了上述源码之外,如下还有几个有用的知识点可能对当前以及后续进程启动相关源码理解有一定的帮助:

  • JavaVM:每个进程有且只有一个JavaVM实例;
  • JNIEnv:每个线程有且只有一个JNIEnv实例。由Java虚拟机动态创建,并通过JNI_OnLoad函数传递给本地方法。JNIEnv实现是平台相关的,并且由Java虚拟机厂商提供。在Android平台上,JNIEnv的实现由Dalvik虚拟机和ART虚拟机提供,每个线程都有自己的JNIEnv实例,并且在Java代码和本地代码之间交互时自动创建和释放;
  • JNI_OnLoad:当本地so库加载到Java虚拟机中时,JVM会自动调用JNI_OnLoad函数。在JNI_OnLoad函数中,本地库可以获取JNIEnv,并将其存储在本地线程中,以供本地方法使用;
  • fork:在C或C++代码中,使用 fork() 函数创建一个新的子进程,这个函数会返回两次,一次在父进程中返回子进程的 PID,一次在子进程中返回 0。