本篇文章的内容中,我将要给大家介绍Android开发中最长见到的一个底层进程——Zygote进程,它是我们开发的所有APP进程的父进程。可以说,Android中所有的Java进程都是由Zygote进程fork出来的。

那面Zygote进程又是如何启动的呢?它的启动过程从init进程启动开始,到Zygote进程真正运行起来,并且进行相关初始化完成为止。

我们来详细分析下Zygote进程的启动过程。

说明:我们的源码是基于Android 8.1系统进行分析的。

 

init进程的启动

init进程是Android系统启动的第一个进程。我们来看init进程是如何启动的?(源码位置:system/core/init/init.cpp)

Linux内核启动的是从start_kernel函数开始的,start_kernel是所有Linux平台进入系统内核初始化后的入口函数,它主要完成一系列内核初始化相关工作,并且调用第一个用户进程——init进程。init启动之后,也就代表系统已经顺利地启动了Linux内核。

 

我们来看init进程的入口函数——init.cpp的main()方法:

int main(int argc, char** argv) {    if (!strcmp(basename(argv[0]), "ueventd")) {//1、如果文件名是"ueventd",则执行守护进程ueventd的主函数ueventd_main()        return ueventd_main(argc, argv);    }        if (!strcmp(basename(argv[0]), "watchdogd")) {//2、如果文件名是"watchdogd",则执行看门狗守护进程的主函数        return watchdogd_main(argc, argv);    }    ……    add_environment("PATH", _PATH_DEFPATH);//3、设置环境变量    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);    if (is_first_stage) {        // Clear the umask.        umask(0);        //4、创建一些基本的目录,包括/dev、/porc、/sysfc等。同时把一些文件系统,如tmpfs、devpt、proc、sysfs等mount到项目的目录。        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");        mkdir("/dev/pts", 0755);        mkdir("/dev/socket", 0755);        mount("devpts", "/dev/pts", "devpts", 0, NULL);        #define MAKE_STR(x) __STRING(x)        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));        // Don't expose the raw commandline to unprivileged processes.        chmod("/proc/cmdline", 0440);        gid_t groups[] = { AID_READPROC };        setgroups(arraysize(groups), groups);        mount("sysfs", "/sys", "sysfs", 0, NULL);        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));        //5、SELinux初始化逻辑        // Set up SELinux, loading the SELinux policy.        selinux_initialize(true);        // We're in the kernel domain, so re-exec init to transition to the init domain now        // that the SELinux policy has been loaded.        if (selinux_android_restorecon("/init", 0) == -1) {            PLOG(ERROR) << "restorecon failed";            security_failure();        }        ……    }    ……    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//6、在/dev目录下创建一个空文件".booting"表示初始化正在进行。初始化结束后,这个文件会被删除。    // Propagate the kernel variables to internal variables    // used by init as well as the current required properties.    export_kernel_boot_props();//7、主要是调用property_init()函数来初始化Android的属性系统。    ……    epoll_fd = epoll_create1(EPOLL_CLOEXEC);//8、调用epoll_create1创建epoll句柄,如果创建失败,则退出。    if (epoll_fd == -1) {        PLOG(ERROR) << "epoll_create1 failed";        exit(1);    }    signal_handler_init();//9、调用signal_handler_init()函数,主要是装载进程信号处理器。当子进程被kill之后,会在父进程接受一个信号。防止称为僵尸进程的子进程占用程序表的空间。    ……    //10、解析init.rc文件    std::string bootscript = GetProperty("ro.boot.init_rc", "");    if (bootscript.empty()) {        parser.ParseConfig("/init.rc");        parser.set_is_system_etc_init_loaded(            parser.ParseConfig("/system/etc/init"));        parser.set_is_vendor_etc_init_loaded(            parser.ParseConfig("/vendor/etc/init"));        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));    } else {        parser.ParseConfig(bootscript);        parser.set_is_system_etc_init_loaded(true);        parser.set_is_vendor_etc_init_loaded(true);        parser.set_is_odm_etc_init_loaded(true);    }    ……    }

 

执行过程如上,主要步骤我们通过注释可以清楚的了解到。

 

init进程启动过程中,会执行init.rc脚本文件(位置: /system/core/rootdir/init.rc)。它通过解析init.rc脚本来构建出系统的初始形态,解析init.rc会把一条条命令映射到内存中,然后依次启动。init.rc里面的顺序大致顺序如下:on early-init -> init -> late-init -> boot。

其中,init.rc会执行Zygote相关的启动脚本。所以说,Zygote进程是Linux系统的init进程通过解析配置脚本来进行启动的。脚本位置:/system/core/rootdir/init.zygote64.rc (64位)。

 

Zygote进程

通常,子进程被fork出来后,会继续执行系统调用exec,exec将用一个新的可执行文件的内容替换当前进程的代码段、数据段、堆和栈段。Fork加exec 是Linux启动应用的标准做法,init进程也是这样来启动的各种服务的。

Zygote创建应用程序时却只使用了fork,没有调用exec。Zygote初始化时会创建创建虚拟机,同时把需要的系统类库和资源文件加载到内存里面。Zygote fork出子进程后,这个子进程也继承了能正常工作的虚拟机和各类系统资源,接下来子进程只需要装载APK文件的字节码文件就可以运行了。这样应用程序的启动时间就会大大缩短。

init.rc脚本

我们来看init.rc脚本是如何启动Zygote进程的:

import /init.${ro.zygote}.rc //这里通过ro.zygote属性来控制启动不同版本的Zyogte进程。例如,ro.zygote=zygote64_32(zygote32/zygote32_64/zygote64)……on late-init    # Now we can start zygote for devices with file based encryption    trigger zygote-start    ……# It is recommended to put unnecessary data/ initialization from post-fs-data# to start-zygote in device's init.rc to unblock 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_secondaryon 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_secondaryon 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进程是通过init.rc脚本,在init进程中启动的(以service的方式启动),详细过程后面会讲到。

另外,在init.rc中,我们可以看到anr文件的权限设置:

mkdir /data/anr 0775 system system

这里可以看到其实anr文件的目录其实对我们正常应用的APP是开放了rx权限(读、执行权限),但是非系统应用在Android 5.0之后就读取不到anr日志了,这个原因是因为在Android5.0之后,Android默认开启了SELinux权限控制。

ro.zygote变量

我们看到,脚本文件使用了另一个.rc文件,文件名称使用了ro.zygote变量,它的值有四种:

  • zygote32

  • zygote32_64

  • zygote64

  • zygote64_32

分别对应了4个.rc文件(在init.rc同目录下):

  • init.zygote32

  • init.zygote64

  • init.zygote32_64

  • init.zygote64_32

为什么会有4个Zygote脚本文件呢?

这其实代表了Andorid系统支持4种运行模式:

  1. 纯32位模式:属性ro.zygote的值为zygote32

  2. 混32位模式(即32位为主,64位为辅)模式:属性ro.zygote的值为zygote32_64

  3. 纯64位模式:属性ro.zygote的值为zygote64

  4. 混64位模式(即 64位为主,32位为辅)模式:属性ro.zygote值为zygote64_32

Zygote进程的脚本文件

Zygote对应的配置脚本文件,描述了init该如何启动Zygote进程。

我们以64位CPU架构的脚本文件init.zygote64.rc作为示例:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server    class main    priority -20    user root    group root readproc    socket zygote stream 660 root system    onrestart write /sys/android_power/request_state wake    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

它使用的是Android init配置脚本的语法,我们重点关注第一行内容:

  • service zygote:它告诉init进程,我们现在要配置一个名为zygote的服务(这里的服务是init内部的概念)。

  • /system/bin/app_process64:指明zygote服务对应的二进制文件的路径。init进程会fork一个子进程来运行指定的程序。对应zygote而言,这个程序就是/system/bin/app_process。

  • -Xzygote /system/bin --zygote --start-system-server:传递给app_process的启动参数。

当init进程真的启动zygote服务的时候,就会走到service_start()函数(init.cpp)。

下面我们来看Zygote真正的启动代码。

Zygote进程启动过程

Zygote进程的启动,涉及到了init.cpp、service.cpp、app_main.cpp以及脚本文件init.rc、init.zygote64.rc等文件。详细过程如图:

 

启动过程中的相关类及类的功能如下:

 

init.cpp

  • 初始化相关配置。

  • 通过handle_control_message()监听消息,执行Zygote服务。

  • 调用service.cpp的Restart()方法。

 

service.cpp

  • 调用Start()方法。

  • fork Zygote进程。

 

app_main.cpp

  • Zygote进程的入口函数main()

  • 创建AppRuntime对象

  • 调用AppRuntime对象的start方法

 

AndroidRuntime.cpp

  • AppRuntime的start方法实际指向AndroidRuntime的start方法

  • 创建了一个JniInvocation的实例,并且调用它的成员函数init来初始化JNI环境

  • 执行startVm,创建虚拟机及其对应的JNI接口

  • 执行startReg,注册JNI函数

  • 执行env.CallStaticVoidMethod调用到Java层,初始化Zygote进程

 

Zygoteinit.java

  • 执行main方法

  • 调用ZygoteServier的registerServerSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息

  • 调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。

  • 调用forkSystemServer()方法启动SystemServer进程

  • 调动runSelectLoop方法来监听和处理启动应用的请求

app_main.cpp的main函数

我们来看重点来看下app_main.cpp的main函数:

int main(int argc, char* const argv[]){    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));//1、创建AppRuntime对象    // Process command line arguments    // ignore argv[0]    argc--;    argv++;        ……    bool known_command = false;    //****************2、从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server    int i;    for (i = 0; i < argc; i++) {        if (known_command == true) {          runtime.addOption(strdup(argv[i]));          // The static analyzer gets upset that we don't ever free the above          // string. Since the allocation is from main, leaking it doesn't seem          // problematic. NOLINTNEXTLINE          ALOGV("app_process main add known option '%s'", argv[i]);          known_command = false;          continue;        }        for (int j = 0;             j < static_cast(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]));        // The static analyzer gets upset that we don't ever free the above        // string. Since the allocation is from main, leaking it doesn't seem        // problematic. NOLINTNEXTLINE        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;    //****************3、将上面的内容赋给相应的变量    ++i;  // Skip unused "parent dir" argument.    while (i < argc) {        const char* arg = argv[i++];        if (strcmp(arg, "--zygote") == 0) {            zygote = true;            niceName = ZYGOTE_NICE_NAME;//niceName将被设置为app_process的进程名,32位机为“zygote”,64位机为“zygote64”。        } 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;        }    }    //*************4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.    Vector args;    if (!className.isEmpty()) {        // 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);        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 {        // We're in zygote mode.        maybeCreateDalvikCache();        if (startSystemServer) {            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);        // In zygote mode, pass all remaining arguments to the zygote        // main() method.        for (; i < argc; ++i) {            args.add(String8(argv[i]));        }    }    //5、进程的名称修改    if (!niceName.isEmpty()) {        runtime.setArgv0(niceName.string(), true /* setProcName */);    }    //6、启动Java类    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.");    }}

主要过程如下:

1、创建AppRuntime对象。

 

2、处理从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server,传递给AppRuntime对象。

  • -Xzygote是传递给虚拟机的参数

  • /system/bin 是 parent dir(程序运行目录)

  • --zygote表示以zygote模式启动

 

3、将上面的内容赋给相应的变量

以-Xzygote /system/bin --zygote --start-system-server为例,结果如下:

  • 变量 parentDir 等于/system/bin

  • 变量 niceName 等于 zyoget

  • 变量 startSystemServer 等于 true

  • 变量 zygote 等于 true

 

4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.

 

5、如果niceName不为空,则将本进程的名称修改为参数** --nice-name** 指定的字符串。缺省的情况下,niceName 的值为"zygote"或者"zygote64"。其中set_process_name函数用来改变进程的mi9ngcheng。setArgv0函数是用来替换启动参数串中的"app_process"为参数。

 

6、启动Java类,如果启动参数带有 "--zygote"。则执行ZygoteInit。

分三种情况:

  • zygote 模式,即启动ZygoteInit

  • 非zygote 模式,即启动RuntimeInit

  • 以上两种均不是

Java层的ZygoteInit的main()方法

我们再来分析下上述过程中的ZygoteInit的main()方法:

public static void main(String argv[]) {    ZygoteServer zygoteServer = new ZygoteServer();//1、创建ZygoteServer对象    // Mark zygote start. This ensures that thread creation will throw    // an error.    ZygoteHooks.startZygoteNoThreadCreation();    // Zygote goes into its own process group.    try {        Os.setpgid(0, 0);    } catch (ErrnoException ex) {        throw new RuntimeException("Failed to setpgid(0,0)", ex);    }    final Runnable caller;    try {        // Report Zygote start time to tron unless it is a runtime restart        if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {            MetricsLogger.histogram(null, "boot_zygote_init",                    (int) SystemClock.elapsedRealtime());        }        String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";        TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,                Trace.TRACE_TAG_DALVIK);        bootTimingsTraceLog.traceBegin("ZygoteInit");        RuntimeInit.enableDdms();        //***********2、解析参数        boolean startSystemServer = false;        String socketName = "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)) {                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、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息        zygoteServer.registerServerSocket(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());            preload(bootTimingsTraceLog);//4、调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,                SystemClock.uptimeMillis());            bootTimingsTraceLog.traceEnd(); // ZygotePreload        } else {            Zygote.resetNicePriority();        }        // Do an initial gc to clean up after startup        bootTimingsTraceLog.traceBegin("PostZygoteInitGC");        gcAndFinalize();        bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC        bootTimingsTraceLog.traceEnd(); // ZygoteInit        // Disable tracing so that forked processes do not inherit stale tracing tags from        // Zygote.        Trace.setTracingEnabled(false, 0);        // Zygote process unmounts root storage spaces.        Zygote.nativeUnmountStorageOnInit();        // Set seccomp policy        Seccomp.setPolicy();        ZygoteHooks.stopZygoteNoThreadCreation();        if (startSystemServer) {            Runnable r = forkSystemServer(abiList, socketName, zygoteServer);//5、启动SystemServer进程            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the            // child (system_server) process.            if (r != null) {                r.run();                return;            }        }        Log.i(TAG, "Accepting command socket connections");        // The select loop returns early in the child process after a fork and        // loops forever in the zygote.        caller = zygoteServer.runSelectLoop(abiList);//6、调动runSelectLoop方法来监听和处理启动应用的请求    } catch (Throwable ex) {        Log.e(TAG, "System zygote died with exception", ex);        throw ex;    } finally {        zygoteServer.closeServerSocket();    }    // We're in the child process and have exited the select loop. Proceed to execute the    // command.    if (caller != null) {        caller.run();    }}

1、创建ZygoteServer对象。

 

2、解析参数。

 

3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息。

为zygote命令注册一个socket连接的服务端socket。init进程会根据这条选项来创建一个"AF_UNIX"socket,并把它的句柄放到环境变量"ANDROID_SOCKET_zygote"中。同理我们也可以这样得到句柄,得到句柄后,new了一个FileDescriptor对象,并通过调用setInt$()方法来设置其值。最后new了LocalServerSocket对象,来创建本地的服务socket,并将其值保存在全局变量sServerSocket中。

 

4、调用preload()方法预加载资源。

为了加快应用程序的启动,Android把系统公用的Java类和一部分Framework的资源保存在zygote中了,这样就可以保证zygote进程fork子进程的是共享的。

  • preloadClasses():预加载Java类

  • preloadResources():预加资源

  • preloadOpenGL():预加载OpenGL资源

  • preloadSharedLibraries():预计加载共享库

  • preloadTextResources():预加载文本资源

  • WebViewFactory.prepareWebViewInZygote():初始化WebView

 

5、启动SystemServer进程。

  • 为fork准备参数parsedArgs

  • 调用Zygote.forkSystemServer()方法来创建system_server

  • 调用handleSystemServerProcess()方法执行system_server的剩余工作

 

6、调动runSelectLoop方法来监听和处理启动应用的请求。

ZygoteInit类的main()方法调用runSelectLoop()方法来监听和处理启动应用的请求。

执行zygote进程的循环。当来一个新的连接请求时,则建立接受并建立连接,并在连接中读取请求的命令。

 

至此,Init进程以及Zygote进程的启动过程就分析完成了。

 

 

更多相关文章

  1. Unity 与 Android(安卓)互调用
  2. app与Android以及IOS的交互
  3. android的开机启动画面过程分析
  4. ReactNative学习笔记之调用原生模块(进阶)之Callback、Promise使用
  5. Android定制:修改开机启动画面
  6. Android小項目之---時間線程應用(附源碼)
  7. android的DeepLink
  8. 让JNI告诉你 你的应用为什么被卸载
  9. android安全问题(四) 抢先开机启动

随机推荐

  1. Android(安卓)下枚举型使用、及与 int 转
  2. Android菜单设计(2) : options menu使用
  3. Android堆内存也可自己定义大小
  4. Facebook开源项目:我们为什么要用Fresco框
  5. 移动终端高级开发工程师
  6. Android(安卓)网络电台的一种实现方案
  7. 【Android(安卓)Training - 03】使用Frag
  8. ubuntu 10.10 alternate 系统SSH服务安装
  9. Android(安卓)Binder通信机制学习(二)
  10. Android(安卓)Dialog