Android 进阶——系统启动之Android进程造物者Zygote 进程启动详解(六)

引言

前面系列文章介绍了Android系统的第一个用户进程——init进程由解析init.rc脚本启动,完成属性系统的初始化等工作后紧接着启动Android系统上的造物者——Zygote进程,这篇文章接着分析。

写时拷贝(Copy On Write):Linux 进程基本上都是通过fork 创建的,fork出来的子进程除了内核中一些核心数据结构和父进程不同之外,其余大部分的内存空间都是和父进程共享的,当且仅当子进程需要去改写这些共享内存时,内核才会为子进程分配一个新的页面,并将老页面的数据复制一份到新页面中。

以后更多精选系列文章以高度完整的系列形式发布在公众号,真正的形成一套完整的技术栈,欢迎关注,目前第一个系列是关于Android系统启动系列的文章,大概有二十几篇干货:
在这里插入图片描述

一、Zygote进程概述

Zygote 中文翻译受精卵,是由Android系统第一个用户进程init进程启动起来的,在Android 系统中,所有的应用程序进程包括用于运行系统关键核心服务的SystemServer进程都是由Zygote 进程fork 而来的,就像受精卵一样具有孵化的功能,因此又被它称之为进程孵化器。即Zygote 通过 fork自身(复制自身)方式创建System进程和应用程序进程的。而且Zygote 进程在启动时会自主在内部创建一个VM实例,而通过fork 而得到的所有子进程自然继承Zygote的空间,换言之,System进程和应用进程内部也持有一个VM实例的拷贝,这样进程才可以通过这个VM实例运行Java 语言开发的程序组件

fork 函数执行成功后,会有两次返回,其中一次是在当前进程中返回,另一次是在新创建出来的子进程中返回(当返回值等于0时)。

二、Zygote进程的启动

1、Init进程解析init.rc脚本触发Zygote进程的启动

1.1、init.rc 脚本触发zygote 以service 形式启动

Zygote进程是通过 aosp/system/core/rootdir/init.rc脚本启动的,如下所示部分内容

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc
....

在第五行init.${ro.zygote}.rc可以看到有一个全局系统属性名为ro.zygote(我设备得到的值zygote64_32)

adb shell getprop | grep ro.zygote

其实这是一个init.rc 的动态引入的语法,则代表引入的.rc文件名为 init.zygote64_32.rc,内容如下:

service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    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
    writepid /dev/cpuset/foreground/tasks

service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

由上得知,定义了两个Zygote服务:zygote和zygote_secondary,分别对应不同的可执行文件,前者是app_process32,后者是app_process64。但它们进程的可执行文件都是app_process,对应的源码在/frameworks/base/cmds/app_process/app_main.cpp。

1.2、解析 init.zygote64_32.rc 中的service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote 指令

当解析到第一行的命令时,遇到service 指令时就会相应地调用system/core/init/service.cpp # Service::start函数来执行启动工作,对应的含义依次为zygote进程所对应的应用程序文件为/system/bin/app_process32,就紧接着跟着进程启动需要传递的参数,--start-system-server 表示Zygote进程启动完毕后需要马上将System进程启动。对应的app_process 启动命令格式:

app_process [虚拟机参数]  运行目录  参数 [Java类]
名称说明
虚拟机参数以“-”开头,用于指定指定Android虚拟机时的参数
运行目录程序的运行目录,一般是/system/bin
参数以“–”开头,比如–zygote 表示要启动的zygote进程,–application则表示以普通进程的方式执行Java代码
Java类要执行的Java类,即执行这个类的静态方法main,需要注意传递参数“–zygote” 时不会执行这个类而是执行ZygoteInit这个类的main方法

1.3、调用system/core/init/service.cpp # Service::start 函数创建zygote进程和/system/core/init/util.cpp # create_socket函数创建名为zygote 的socket

bool Service::Start() {
   ...
    pid_t pid = fork();
    if (pid == 0) {
        umask(077);

        for (const auto& ei : envvars_) {
            add_environment(ei.name.c_str(), ei.value.c_str());
        }
		//遍历为Zygote进程配置的socket列表,并创建对应的socket并把返回的fd发布到系统中
        for (const auto& si : sockets_) {
            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
                                (si.type == "dgram" ? SOCK_DGRAM :
                                 SOCK_SEQPACKET)));
            const char* socketcon =
                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();

            int s = create_socket(si.name.c_str(), socket_type, si.perm,
                                  si.uid, si.gid, socketcon);
            if (s >= 0) {
                PublishSocket(si.name, s);
            }
        }
		...
        std::vector<std::string> expanded_args;
        std::vector<char*> strs;
        expanded_args.resize(args_.size());
        strs.push_back(const_cast<char*>(args_[0].c_str()));
        for (std::size_t i = 1; i < args_.size(); ++i) {
            if (!expand_props(args_[i], &expanded_args[i])) {
                ERROR("%s: cannot expand '%s'/n", args_[0].c_str(), args_[i].c_str());
                _exit(127);
            }
            strs.push_back(const_cast<char*>(expanded_args[i].c_str()));
        }
        strs.push_back(nullptr);
		//str[0] 值等于/system/bin/app_process 通过shell 加载该文件
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
            ERROR("cannot execve('%s'): %s/n", strs[0], strerror(errno));
        }

        _exit(127);
    }

    if (pid < 0) {
        ERROR("failed to start '%s'/n", name_.c_str());
        pid_ = 0;
        return false;
    }
    ...
    return true;
}

第二行就表示在Zygote启动过程中需要再内部创建一个名为“zygote”的socket,用于执行与System 进程进行通信的。

/*
 * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
 * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
 * daemon. We communicate the file descriptor's value via the environment
 * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
 */
int create_socket(const char *name, int type, mode_t perm, uid_t uid,
                  gid_t gid, const char *socketcon)
{
    struct sockaddr_un addr;
    int fd, ret, savederrno;
    char *filecon;

    if (socketcon) {
        if (setsockcreatecon(socketcon) == -1) {
            ERROR("setsockcreatecon(/"%s/") failed: %s/n", socketcon, strerror(errno));
            return -1;
        }
    }
	//调用内核的socket函数创建并返回对应的fd,PF_UNIX表示创建用于本地进程间通信的类型socket,比如说AMS 在请求与Zygote进程通信时创建新的应用进程前,需要先打开下面绑定的设备文件来创建一个连接到Zygote 进程的客户端socket
    fd = socket(PF_UNIX, type, 0);
    if (fd < 0) {
        ERROR("Failed to open socket '%s': %s/n", name, strerror(errno));
        return -1;
    }

    if (socketcon)
        setsockcreatecon(NULL);

    memset(&addr, 0 , sizeof(addr));
    addr.sun_family = AF_UNIX;
    //ANDROID_SOCKET_DIR 是一个宏 值为/dev/socket
    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
             name);

    ret = unlink(addr.sun_path);
    if (ret != 0 && errno != ENOENT) {
        ERROR("Failed to unlink old socket '%s': %s/n", name, strerror(errno));
        goto out_close;
    }

    filecon = NULL;
    if (sehandle) {
        ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
        if (ret == 0)
            setfscreatecon(filecon);
    }
	//bind 函数的作用就是将上面创建socket的返回的fd 与socket地址addr 绑定起来得到一个设备文件——dev/socket/zygote
    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
    savederrno = errno;

    setfscreatecon(NULL);
    freecon(filecon);

    if (ret) {
        ERROR("Failed to bind socket '%s': %s/n", name, strerror(savederrno));
        goto out_unlink;
    }
	//配置权限
    ret = lchown(addr.sun_path, uid, gid);
    if (ret) {
        ERROR("Failed to lchown socket '%s': %s/n", addr.sun_path, strerror(errno));
        goto out_unlink;
    }
    ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
    if (ret) {
        ERROR("Failed to fchmodat socket '%s': %s/n", addr.sun_path, strerror(errno));
        goto out_unlink;
    }

    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'/n",
         addr.sun_path, perm, uid, gid);

    return fd;

out_unlink:
    unlink(addr.sun_path);
out_close:
    close(fd);
    return -1;
}

socket 创建完毕之后还需要通过/system/core/init/service.cpp#PublishSocket 函数发布到系统中

void Service::PublishSocket(const std::string& name, int fd) const {
    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
    std::string val = StringPrintf("%d", fd);
    add_environment(key.c_str(), val.c_str());

    /* make sure we don't close-on-exec */
    fcntl(fd, F_SETFD, 0);
}

2、Zygote进程加载system/bin/app_process文件完成启动

从上面我们可以知道Zygote进程在启动过程中会加载system/bin/app_process程序文件,app_process 除了能启动Zygote进程,还可以使用它来执行Java类中的某个main方法。

2.1、执行/frameworks/base/cmds/app_process/app_main.cpp # main 函数

app_process/app_main.cpp的main函数主要作用就是解析传入的启动参数,完成以下工作。

2.1.1、创建AppRuntime 对象

在app_main.cpp文件里定义了AppRuntime 类(AppRuntime继承自AndroidRuntime类),主要用于创建和初始化虚拟机以及启动Java线程,AppRuntime 在一个进程中有且只有一个实例对象并保存在全局变量gCurRuntime指针中

\frameworks\base\core\jni\AndroidRuntime.cpp

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),
        mArgBlockStart(argBlockStart),
        mArgBlockLength(argBlockLength)
{
    SkGraphics::Init();//初始化skia 图形引擎
    // Pre-allocate enough space to hold a fair number of options. 预分配空间来存放传入的虚拟机参数
    mOptions.setCapacity(20);

    assert(gCurRuntime == NULL);        // one per process 确保只能被初始化一次
    gCurRuntime = this;
}
2.1.2、给AppRuntime 配置参数和设置进程名称(即将当前进程名称设置为zygote)
2.1.3、解析启动参数

解析参数后niceName的值为zygote,startSystemServer的值为true,zygote为true。

2.1.4、AppRuntime #start函数执行ZygoteInit或者RuntimeInit类的main方法

如果启动的不是zygote进程则执行RuntimeInit类

int main(int argc, char* const argv[])
{
    ...
     //创建runtime局部对象,Zygote 进程就是需要依靠它来继续完成启动的,如果启动的是Zygote进程那么ZygoteInit类中会进入等到Socket事件的循环,不会被销毁;而执行的是一个java类则会在执行完毕后被销毁。
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    argc--;
    argv++;
    // Parse runtime arguments.  Stop at first unrecognized option.
    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++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } 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);
        }
        ...
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();
        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }
        String8 abiFlag("--abi-list=");
        args.add(abiFlag);
        // In zygote mode, pass all remaining arguments to the zygote main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string());
        set_process_name(niceName.string());//设置进程名称
    }
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    }
    ...
}

2.2、frameworks/base/core/jni/AndroidRuntime.cpp # start

Zygote进程在运行Java代码前还需要初始化整个Java运行环境。

2.2.1、设置环境变量ANDROID_ROOT的值为/system
2.2.2、进行JNI 接口初始化并启动虚拟机并触发onVmCreated 函数回调

实际上是触发AppRuntime#onVmCreated 回调。

2.2.3、startReg注册系统JNI函数

通过调用register_jni_procs函数将全局数组gRegJNI的本地JNI函数注册到虚拟机中。

2.2.4、JNI调用 com.android.internal.os.ZygoteInit的main函数
/*
 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    //标志着Android系统启动,因为以后的应用进程都是由zygotefork出来的,所以不会再执行start函数了,如果系统反复出现这个日志且id都是zygote则说明Zygote进程在不断地被重启。
     ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());
    //定义变量直接初始化,其字面值是"start-system-server"的副本(不包括最后的那个空字符)
    static const String8 startSystemServer("start-system-server");
    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
        }
    }
    //环境变量ANDROID_ROOT的设置,即环境变量ANDROID_ROOT的值被设置成了/system
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    /* start the virtual machine */
    //通过jni_invocation.Init(NULL)完成jni接口的初始化,并启动虚拟机的代码,后续再分析细节
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.注册系统JNI函数
     */
    if (startReg(env) < 0) {
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    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);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(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 {
            //jni调用传入的className 即 com.android.internal.os.ZygoteInit的main函数
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

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

    ALOGD("Shutting down VM/n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread/n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly/n");
}

2.3、JNI调用/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java #main 方法

    public static void main(String argv[]) {
    	 ...
        boolean isFirstBooting = false;
        ZygoteHooks.startZygoteNoThreadCreation();
        try {
            boolean startSystemServer = false;
            String socketName = "zygote";
            	...
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = 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]);
                }
            }
            registerZygoteSocket(socketName);
            if(!isFirstBooting && !isBox){
                if (startSystemServer) {
                    startSystemServer(abiList, socketName);
                }
            }
             preload();
            ...
            runSelectLoop(abiList);
            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }
2.3.1、registerZygoteSocket(socketName) 创建一个Server端socket用于等待AMS 请求Zygote进程创建新的应用进程
    private static void registerZygoteSocket(String socketName) {
        if (sServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                //通过系统环境变量得到fd的值并转换为fd
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
            }

            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                sServerSocket = new LocalServerSocket(fd);
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }
2.3.2、调用preload方法预加载系统资源

包括系统预加载类、Framework资源和OpenGL资源,当应用被fork创建出来后,应用进程内自然就包含了这些资源,无需应用再次自己加载。

2.3.3、startSystemServer(abiList, socketName) fork 创建System进程(SystemServer进程)
    /**
     * Prepare the arguments and fork for the system server process.
     */
    private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {

        /* Hardcoded command line to start the system server */
        //System进程启动的参数,可以看到System进程的用户ID和用户组ID都是1000,同时还有其他用户组的权限
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;
        int pid;
        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

            /* Request to fork the system server process */
            //fork 创建System进程
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                if(isBox){
                    waitForSecondaryZygote(socketName);
                }
                Log.d(TAG,"--------call waitForSecondaryZygote,skip this---,abiList= "+abiList);
            }
            // 处理System进程启动事宜
            handleSystemServerProcess(parsedArgs);
        }

        return true;
    }
2.3.4、执行runSelectLoop(abiList) 方法等待处理AMS 请求Zygote 创建新应用进程
    /**
     * Runs the zygote process's select loop. Accepts new connections as
     * they happen, and reads commands from connections one spawn-request's
     * worth at a time.
     *
     * @throws MethodAndArgsCaller in a child process when a main() should
     * be executed.
     */
    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        while (true) {
            StructPollfd[] pollFds = new StructPollfd[fds.size()];
            for (int i = 0; i < pollFds.length; ++i) {
                pollFds[i] = new StructPollfd();
                pollFds[i].fd = fds.get(i);
                pollFds[i].events = (short) POLLIN;
            }
            try {
                Os.poll(pollFds, -1);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce();
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

简而言之,/frameworks/base/cmds/app_process/app_main.cpp # main 解析init.rc 脚本传入的参数,触发AppRuntime #start函数进而执行ZygoteInit#main 方法。

2.4、fork 创建SystemServer进程

Zygote进程在fork 创建SystemServer进程后返回到SystemServer进程继续进行相关System进程启动工作,至此Zygote 进程启动完毕,Zygote进程在初始化时就会创建Android虚拟机、注册JNI函数、预加载系统资源文件和Java 类。所有应用进程都共享和继承这些资源,Zygote进程启动工作完毕后,也会变成守护进程,负责处理启动App 应用程序的请求,预知后事如何请参见下文。

三、Zygote启动应用进程

预知后事如何请参见下文,不过得先启动AMS服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CrazyMo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值