Zygote进程的创建过程(Android 8.1)

本篇文章的内容中,我将要给大家介绍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_secondary



on 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_secondary



on 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等文件。详细过程如图:

Zygote进程的创建过程(Android 8.1)_第1张图片

 

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

 

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进程的启动过程就分析完成了。

 

 

你可能感兴趣的:(Android技术实现原理解析)