Android启动系列之一:init进程和Zygote进程

前言

先来看看Android系统启动的流程:

  1. 启动电源及系统启动

    当电源按下时引导芯片代码从预定义的地方(固化在ROM)开始执行。加载引导程序Bootloader到RAM中,然后执行。

  2. 引导程序BootLoader

    引导程序BootLoader是在Android操作系统开始运行前的一个小程序,它的主要作用是把系统OS拉起来并运行。

  3. Linux内核启动

    当内核启动时,设置缓存、加载驱动等。在内核完成系统设置后,首先在系统文件中寻找init.rc文件,并启动init进程。

  4. init进程启动

    init进程主要用来初始化和启动属性服务,也用来启动Zygote进程。

可以看到当我们按下启动电源时,系统启动后会加载引导程序,引导程序有启动Linux内核,当Linux内核加载完成后,第一件事就是启动init进程。

init进程

当Linux内核加载完成后,会首先在系统文件中寻找init.rc文件,并启动init进程,这样就执行了init进程的入口函数,部分代码如下:

c++ 复制代码
int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }   

    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }  
    ...

init进程启动做了很多工作,总的来说只要是以下三件事:

  1. 创建和挂载启动所需的文件目录
  2. 初始化和启动属性服务
  3. 解析init.rc配置文件并启动Zygote进程

这里我们重点关注一下Zygote进程的启动代码,如下:

ini 复制代码
bool Service::Start() { 
    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    if (flags_ & SVC_RUNNING) {
        return false;
    }
    ......
    struct stat sb;

    if (stat(args_[0].c_str(), &sb) == -1) {
        PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
        flags_ |= SVC_DISABLED;
        return false;
    }
    ......
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {

        pid = fork();
    }
 
    if (pid == 0) {
        umask(077);
        ......

        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
        }
        _exit(127);
    }
    ......
    return true;
}

这部分代码在system/core/init/service.cpp中。首先判断Service是否已启动,如果已启动则不再启动。如果没有启动则调用fork函数创建子进程并返回pid值。如果pid值为0说么当前当前代码逻辑在子进程中运行,调用execve函数Service子进程就会启动,并进入该Service的main函数中。如果该Service是Zygote(执行程序的路径为/system/bin/app_processXX),对应的文件为app_main.cpp,这样就会进入它的main函数中。

Zygote进程

在Android中,DVM和ART、应用程序进程以及运行系统的关键服务的SyetemService进程都是由Zygote进程来创建的,所以称之为孵化器。它通过fork复制进程的形式来创建应用程序进程和SystemService进程,由于Zygote进程在启动时会创建DVM或ART,所以fork的应用程序进程和SystemService进程可以在内部获取一个DVM或ART的实例副本。

起初Zygote进程名称并不是"zygote",而是"app_process",这个名称在Android.mk中定义的。Zygote进程启动后,Linux系统下的pctrl系统会调用app_process,将其名称换成"zygote"

启动脚本

在init.rc文件中采用import来引入Zygote启动脚本,如下:

bash 复制代码
import /init.${ro.zygote}.rc

可以看到不会引入一个固定文件,而是根据属性ro.zygote来引入不同的文件,主要取值有以下4种:

  • init.zygote32.rc:32位模式
  • init.zygote32_64.rc:32位为主,64位为辅
  • init.zygote64.rc:64位模式
  • init.zygote64_32.rc:64位为主,32位为辅

这些启动脚本都放在system/core/rootdir目录中。如果是主辅(32_64或64_32)模式的话,会启动两个Zygote进程,以32_64为例:一个进程为zygote,是主进程,执行程序是app_process32;另外一个进程为zygote_secondary,是辅进程,执行程序是app_process64。

Zygote启动过程

init启动Zygote时主要调用app_main.cpp的mian函数中的AppRuntime的start方法来启动Zygote进程,这个过程如下:

我们先从app_main.cpp的mian函数来分析,代码如下:

c 复制代码
// /frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
   ...
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) { 
            zygote = true; 
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) { 
            startSystemServer = true; 
        } 
        ...
    }
    ...
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }
    if (zygote) { 
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        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.");
    }
}

Zygote进程都是通过fork自身来创建子进程的,这样Zygote进程以及它的子进程都会进入这个main函数,所以在上面代码中的while循环中会先进行区分。可以看到首先判断参数arg是否包含"--zygote",如果包含了则说明是Zygote进程,并且将zygote设置为true。然后判断arg中是否包含"--start-system-server",如果包含则说明是SystemServer进程,则将startSystemServer设置为true。

继续往下看,如果zygote是true的话,即是Zygote进程,则调用AppRuntime的start函数(实际上是调用AppRuntime的父类AndroidRuntime),这个函数代码如下:

c++ 复制代码
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
   ...
       
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;

    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);
    

    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    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);
    }

    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    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);

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

首先在第一个if代码处执行了startVm函数来创建java虚拟机;然后在第二个if代码处执行了startReg函数为java虚拟机注册JNI方法。

然后往下看,在for循环后可以看到使用JNI通过参数className找到对应的java类,这个className通过app_main.cpp的mian函数代码可知是"com.android.internal.os.ZygoteInit",这样得到的就是ZygoteInit这个java类。然后在接下来的代码中找到ZygoteInit的main函数并执行。这样Zygote就从Native层进入了java框架层。

在这之前是没有任何代码进入java框架层的,所以说是Zygote开始了java框架层。ZygoteInit的main函数如下:

java 复制代码
public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    ...
    try {
        ...
        zygoteServer.registerServerSocket(socketName);

        if (!enableLazyPreload) {
            ...
            preload(bootTimingsTraceLog);
            ...
        } else {
            ...
        }
        
        ...
        
        if (startSystemServer) {
            Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
            if (r != null) {
                r.run();
                return;
            }
        }

        ...
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        zygoteServer.closeServerSocket();
    }
    ...
}

可以看到首先创建了一个zygoteServer,然后通过registerServerSocket方法创建了一个Server端的Socket,这个name是"zygote"的Socket则等待ActivityManagerService请求Zygote来创建新的应用程序进程。

然后在第一个if代码中预加载类和资源。在第二个if代码中创建并启动SystemServer进程,这样系统服务也会有SystemServer进程启动起来。

最后调用ZygoteServer的runSelectLoop函数来等待AMS请求创建新的应用进程。

所以ZygoteInit的main函数主要做了以下工作:

  1. 创建一个Server端的socket
  2. 预加载类和资源
  3. 启动SystemServer进程
  4. 等待AMS请求创建新的应用进程

总结

总结一下,init进程主要做了三件事:创建和挂载启动所需的文件目录、初始化和启动属性服务和解析init.rc并启动Zygote进程。

Zygote进程启动则做了以下几件事:

  • 创建AppRuntime并调用其start方法,启动Zygote进程
  • 创建java虚拟机并为虚拟机注册JNI方法
  • 通过JNI调用ZygoteInit的main函数进入java框架层
  • 通过registerServerSocket创建服务端socket,并通过runSelectLoop函数来等待AMS请求创建新的应用进程
  • 启动SystemServer进程。
相关推荐
雨白42 分钟前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹2 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空4 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭4 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日5 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安5 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑6 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟10 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡11 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0011 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体