framework | Zygote进程流程分析

·  阅读 1795
framework | Zygote进程流程分析

前言

前面一篇文章 # framework | init进程流程分析 中,我们介绍了Android用户态的第一个进程init进程的执行过程,主要是用来启动、监听属性服务、监听子进程终止和处理僵尸进程以及启动Zygote、servicemanager等关键进程。

在启动Zygote进程的分析中,我们知道其实是通过系统调用fork()函数来创建一个进程,然后执行Zygote.rc中的执行文件,从而开始Zygote进程业务。

本篇文章我们就来分析大名鼎鼎的Zygote进程的流程,前面init进程分析中都是C++代码,本篇文章将逐渐见到我们熟悉的虚拟机以及Java代码。

正文

在Android系统中,Zygote进程非常重要,我们称它为孵化器或者受精卵,因为DVM和ART、应用程序进程以及运行Java Framework系统服务的SystemServer进程都是由Zygote进程来创建的。

通过fork的形式来创建应用进程和SystemServer进程,而且由于Zygote进程在启动时会创建DVM或者ART,因此通过fork而创建的应用程序进程和SystemServer进程可以在内部获取一个DVM或者ART的实例副本

我们从上篇init进程分析中的启动Zygote来说起,在其中说到解析rc文件时,会先通过fork创建一个进程,然后最终会调用预置脚本的main()函数来开启Zygote进程的业务。

Native层流程分析

先简单看一下Native层流程图:

sequenceDiagram
init进程 ->> app_main.cpp:fork Zygote进程,执行脚本
Note right of app_main.cpp:Zygote进程业务入口函数
app_main.cpp ->> app_main.cpp:main()
app_main.cpp ->> AndroidRuntime.cpp:start()
AndroidRuntime.cpp ->> AndroidRuntime.cpp:startVm()创建Java虚拟机
AndroidRuntime.cpp ->> AndroidRuntime.cpp:startReg注册JNI函数
AndroidRuntime.cpp ->> AndroidRuntime.cpp:通过反射获取ZygoteInit.java类
AndroidRuntime.cpp ->> ZygoteInit.java:通过反射调用其main方法
Note right of ZygoteInit.java:进入Java世界

既然Zygote进程已经创建,而且已经开始运行,所以我们直接来看一下其业务开始的入口函数:

/frameworks/base/cmds/app_process/app_main.cpp
    int main(int argc, char* const argv[])
188 {
189    ...
278    while (i < argc) {
279        const char* arg = argv[i++];
280        if (strcmp(arg, "--zygote") == 0) { 
               //如果当前运行在Zygote进程中,则将zygote设置为ture
281            zygote = true;
282            niceName = ZYGOTE_NICE_NAME;
283        } else if (strcmp(arg, "--start-system-server") == 0) {
                //如果当前运行在SystemServer进程中,则将startSystemServer设置为true
284            startSystemServer = true;
285        ...
296    }
297
298    ...
348
349    if (zygote) {
            //启动Zygote进程
350        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
351    } else if (className) {
352        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
353    } else {
354        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
355        app_usage();
356        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
357    }
358}
复制代码

Zygote进程是通过fork自己来创建子进程的,而fork我们在上一篇文章里说过,它是一个系统调用,其实就是复制自己,这样做可以减少创建进程时进行诸多配置,所以Zygote进程以及它的子进程都可以进入该函数,所以在main函数中要区分当前运行在哪个进程

由代码中逻辑我们可知会调用runtime的start方法,runtime是AppRuntime类的对象,它是一个C++类

/frameworks/base/cmds/app_process/app_main.cpp
class AppRuntime : public AndroidRuntime
复制代码

但是发现该类并没有叫做start的方法,所以其实该方法定义是在其父类里面,我们来看一下:

/frameworks/base/core/jni/AndroidRuntime.cpp
    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
1035  {
1038    ...
        //启动Java虚拟机
1067    JniInvocation jni_invocation;
1068    jni_invocation.Init(NULL);
1069    JNIEnv* env;
1070    if (startVm(&mJavaVM, &env, zygote) != 0) {
1071        return;
1072    }
1073    onVmCreated(env);
1074    //注册JNI方法
1078    if (startReg(env) < 0) {
1079        ALOGE("Unable to register all android natives\n");
1080        return;
1081    }
1082
1083    //有了JNI,我们这里就可以调用Java方法了
1088    jclass stringClass;
1089    jobjectArray strArray;
1090    jstring classNameStr;
1091    //由传进来的参数可以知道,Java世界的Zygote初始化类是
        //“com.android.internal.os.ZygoteInit”
1092    stringClass = env->FindClass("java/lang/String");
1093    assert(stringClass != NULL);
1094    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
1095    assert(strArray != NULL);
1096    classNameStr = env->NewStringUTF(className);
1097    assert(classNameStr != NULL);
1098    env->SetObjectArrayElement(strArray, 0, classNameStr);
1099
1100    for (size_t i = 0; i < options.size(); ++i) {
1101        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
1102        assert(optionsStr != NULL);
1103        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
1104    }
1105
1106    //将className的"."替换为"/"
1110    char* slashClassName = toSlashClassName(className != NULL ? className : "");
1111    jclass startClass = env->FindClass(slashClassName);
1112    if (startClass == NULL) {
1113        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
1114        /* keep going */
1115    } else { 
           //找到了ZygoteInit的main方法
1116        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1117            "([Ljava/lang/String;)V");
1118        if (startMeth == NULL) {
1119            ALOGE("JavaVM unable to find main() in '%s'\n", className);
1120            /* keep going */
1121        } else {
                //调用main方法,开启愉快的Java之旅
1122            env->CallStaticVoidMethod(startClass, startMeth, strArray);
1123
1124#if 0
1125            if (env->ExceptionCheck())
1126                threadExitUncaughtException(env);
1127#endif
1128        }
1129    }
1130    free(slashClassName);
1137}
复制代码

这个类就是我们之前在说架构时的AndroidRuntime部分代码,它其实是C++代码,在文中都注释了非常明白了,主要做了如下事:

  1. 启动Java虚拟机,想运行Java代码,必须要创建Java虚拟机程序,在这里会创建和启动java虚拟机。关于虚拟机的知识,后面文章会单独分析。
  2. 注册JNI方法,关于JNI的原理后面会细说,说白了就是Java可以调用C/C++程序,而C/C++程序可以调用Java代码,也就是通过JNI打通了Java世界和Native世界的屏障。这里注册JNI方法,其实就是为了后面使用JNI而做准备。
  3. 根据传入的Java类名,通过JNI找到Java类,然后把"."换成"/",这是因为在JNI中是不允许以"."作为变量名的,后面细说;然后再通过JNI方法,调用该类的main函数。

由于ZygoteInit的main方法由Java编写,这样Zygote就从Native层进入了Java框架层

Java层流程分析

还是先来看一下大致调用流程图:

sequenceDiagram
ZygoteInit.java -) ZygoteServer.java:registerServerSocket():开启Socket服务端
ZygoteInit.java ->> ZygoteInit.java:preload():预加载资源
activate ZygoteInit.java
Note right of ZygoteInit.java:forkSystemServer():创建SystemServer进程
ZygoteInit.java ->> ZygoteConnection.java:parseArgs = new ZygoteConnection.Argument()
ZygoteInit.java ->> Zygote.java:pid = Zygote.forkSystemServer()
Zygote.java ->> Zygote.java:nativeForkSystemServer:Linux内核fork进程
Note right of Zygote.java:这时SystemServer进程已经创建
opt pid == 0
ZygoteInit.java ->> ZygoteInit.java:关闭socket、初始化systemServer进程
end
ZygoteInit.java ->> ZygoteServer.java:runSelectLoop()开启循环
loop 循环等待
ZygoteServer.java -> ZygoteServer.java:处理AMS的创建进程请求
end

话不多说,我们来看看Java世界的该方法:

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    public static void main(String argv[]) {
            //新建ZygoteServer实例
699        ZygoteServer zygoteServer = new ZygoteServer();
700
701        ...
747            //创建一个Server端的Socket,socketName就是"zygote"
748            zygoteServer.registerServerSocket(socketName);
749          
751            if (!enableLazyPreload) {
752                bootTimingsTraceLog.traceBegin("ZygotePreload");
753                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
754                    SystemClock.uptimeMillis());
                    //预加载资源
755                preload(bootTimingsTraceLog);
756                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
757                    SystemClock.uptimeMillis());
758                bootTimingsTraceLog.traceEnd(); // ZygotePreload
759            } else {
760                Zygote.resetNicePriority();
761            }
762
763           ...
780
781            if (startSystemServer) {
                    //创建和启动SystemServer进程
782                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
786                if (r != null) {
787                    r.run();
788                    return;
789                }
790            }
791
792            //等待AMS请求
796            caller = zygoteServer.runSelectLoop(abiList);
797        } catch (Throwable ex) {
798            Log.e(TAG, "System zygote died with exception", ex);
799            throw ex;
800        } finally {
                //一直循环,这个只有Zygote进程死了才会执行
801            zygoteServer.closeServerSocket();
802        }
806        if (caller != null) {
807            caller.run();
808        }
809    }
复制代码

这里简化了很多逻辑,我们只分析一些主要的流程,从上面注释我们可以知道,ZygoteInit的main函数主要做了如下几件事:

  1. 创建了一个Server端的Socket,该Socket的主要作用就一个,就是等待ActivityManagerService(AMS)的请求,来创建新的进程。关于AMS我们肯定听过,它负责Android四大组件的运行,而其中就有一个场景就是启动新的APP进程,这时就需要创建新进程,而创建新进程的工作就是这里。
  2. 预加载类和资源
  3. 孵化和启动SystemServer进程,该进程比较重要,我们熟悉的AMS、WMS等系统服务都是运行在该进程中,后面分析SystemServer进程时,我们再仔细分析。

上面就是Zygote进程在Java层所做的主要工作,我们来简单梳理一下。

创建本地Server Socket

主要是通过调用ZygoteServer中的registerServerSocket方法:

/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
     void registerServerSocket(String socketName) {
67        if (mServerSocket == null) {
68            int fileDesc;
                //这里名字就是"ANDROID_SOCKET_zygote"
69            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
70            try {
71                String env = System.getenv(fullSocketName);
72                fileDesc = Integer.parseInt(env);
73            } catch (RuntimeException ex) {
74                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
75            }
76
77            try {
78                FileDescriptor fd = new FileDescriptor();
79                fd.setInt$(fileDesc);
                    //创建Socket的本地服务端
80                mServerSocket = new LocalServerSocket(fd);
81            } catch (IOException ex) {
82                throw new RuntimeException(
83                        "Error binding to local socket '" + fileDesc + "'", ex);
84            }
85        }
86    }
复制代码

这里注意创建本地Socket的代码是在ZygoteServer.java中,这个mServerSocket在后面会被使用,等会再说。

预加载资源

在Zygote进程的初始化过程中,会预加载一些必要的资源,方法如下:

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
 static void preload(TimingsTraceLog bootTimingsTraceLog) {
123        //预加载类,这里使用类加载器加载/system/ect/preload-class下定义的类
            //该文件是一个文本文件,以全包名的方式写了几百个类
128        preloadClasses();
129        //预加载资源,主要是加载一些图片、颜色等资源到内存中
131        preloadResources();
132        //预加载OpenGL
137        preloadOpenGL();
138        //通过System.loadLibrary方式加载3个共享库
            //分别是android、compiler_rt、jnigraphics
139        preloadSharedLibraries();
            //加载文本等资源
140        preloadTextResources();
141        ...
149    }
复制代码

这里加载资源非常关键,由前面我们知道到目前Zygote进程已经有虚拟机实例、预加载了部分资源和Java类,以及预加载了共享库,这时创建子进程时,是通过fork形式的,即复制自己,新的进程也会有这些内容

关于这部分加载类的细节,涉及类加载器、Class类等知识,后面文章会详细分析。

启动SystemServer进程

Zygote进程的另一个重要工作就是启动SystemServer进程,我们来看看Java世界中是如何启动进程的:

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    private static Runnable forkSystemServer(String abiList, String socketName,
621            ZygoteServer zygoteServer) {
622        long capabilities = posixCapabilitiesAsBits(
623            OsConstants.CAP_IPC_LOCK,
624            OsConstants.CAP_KILL,
625            OsConstants.CAP_NET_ADMIN,
626            OsConstants.CAP_NET_BIND_SERVICE,
627            OsConstants.CAP_NET_BROADCAST,
628            OsConstants.CAP_NET_RAW,
629            OsConstants.CAP_SYS_MODULE,
630            OsConstants.CAP_SYS_NICE,
631            OsConstants.CAP_SYS_PTRACE,
632            OsConstants.CAP_SYS_TIME,
633            OsConstants.CAP_SYS_TTY_CONFIG,
634            OsConstants.CAP_WAKE_ALARM
635        );

637        if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
638            capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
639        }
640        //设置参数
641        String args[] = {
642            "--setuid=1000",
643            "--setgid=1000",
644            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
645            "--capabilities=" + capabilities + "," + capabilities,
646            "--nice-name=system_server",
647            "--runtime-args",
648            "com.android.server.SystemServer",
649        };
650        ZygoteConnection.Arguments parsedArgs = null;
651
652        int pid;
653
654        try {
                //这里是为了把硬编码的参数转换成特定类对象
655            parsedArgs = new ZygoteConnection.Arguments(args);
656            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
657            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
658
659            //fork新进程
660            pid = Zygote.forkSystemServer(
661                    parsedArgs.uid, parsedArgs.gid,
662                    parsedArgs.gids,
663                    parsedArgs.debugFlags,
664                    null,
665                    parsedArgs.permittedCapabilities,
666                    parsedArgs.effectiveCapabilities);
667        } catch (IllegalArgumentException ex) {
668            throw new RuntimeException(ex);
669        }
670
671        //pid为0,说明当前是子进程
672        if (pid == 0) {
673            if (hasSecondZygote(abiList)) {
674                waitForSecondaryZygote(socketName);
675            }
676            //子进程即SystemServer进程不需要监听socket
677            zygoteServer.closeServerSocket();
                //SystemServer进程处理它的事务
678            return handleSystemServerProcess(parsedArgs);
679        }
680
681        return null;
682    }
复制代码

该部分是创建、启动SystemServer进程,首先是用代码来创建args数组,也就是启动参数,从参数我们可以看出SystemServer进程的用户id和用户组id被设置为1000,并且拥有1001-1010、1018、1021、1032、3001-3010用户组的权限;进程名为system_server,启动的类名为com.android.server.SystemServer

然后通过Zygote类的forkSystemServer函数创建了一个子进程,该方法底层调用了native方法,原理也就是Linux的fork系统调用。然后有个非常有意思的地方,就是pid为0表示是子进程,这里或许有点疑惑,这里是为什么?

这时就要从多进程和fork的含义来理解了,fork的意思是复制,即调用完fork后,新的systemServer进程会有和Zygote进程一样的信息,比如虚拟机、预加载的资源等,那这时就相当于2个进程都运行到这里了,那如何分道扬镳呢 就看这个pid的值,如果是0就说明是运行在子进程中。

这个还是很有意思的,关于Linux的fork系统调用,后面关于Linux知识时,再细说一下。

runSelectLoop

这个的设计也非常巧妙,runSelectLoop的作用就是从前面创建的Socket服务端中循环取出其他进程发来的消息,当消息结果caller不为空时,执行这个消息

这里就要想个问题,因为是多进程,执行该方法也可能是其他进程,比如前面刚刚创建的systemServer进程,但是我们还记得创建的systemServer进程已经关闭socket了,所以这里的选择循环会一直运行在Zygote进程中

该方法代码如下:

/frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
     Runnable runSelectLoop(String abiList) {
144        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
145        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
146        //这个mServerSocket就是前面创建的服务端Socket
147        fds.add(mServerSocket.getFileDescriptor());
148        peers.add(null);
149        //无限循环等待AMS请求Zygote进程创建新的应用进程。
150        while (true) {
151            StructPollfd[] pollFds = new StructPollfd[fds.size()];
152            for (int i = 0; i < pollFds.length; ++i) {
153                pollFds[i] = new StructPollfd();
154                pollFds[i].fd = fds.get(i);
155                pollFds[i].events = (short) POLLIN;
156            }
157            try {
158                Os.poll(pollFds, -1);
159            } catch (ErrnoException ex) {
160                throw new RuntimeException("poll failed", ex);
161            }
162            for (int i = pollFds.length - 1; i >= 0; --i) {
163                if ((pollFds[i].revents & POLLIN) == 0) {
164                    continue;
165                }
166                //i等于0,说明服务端Socket和客户端连接上了,即Zygote进程和AMS建立了链接
167                if (i == 0) {
168                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
169                    peers.add(newPeer);
170                    fds.add(newPeer.getFileDesciptor());
171                } else {
172                    try {
173                        ZygoteConnection connection = peers.get(i); 
                            //会创建子进程
174                        final Runnable command = connection.processOneCommand(this);
176                        if (mIsForkChild) {
179                            if (command == null) {
180                                throw new IllegalStateException("command == null");
181                            }
183                            return command;
184                        } else {
186                            if (command != null) {
187                                throw new IllegalStateException("command != null");
188                            }
193                            if (connection.isClosedByPeer()) {
194                                connection.closeSocket();
195                                peers.remove(i);
196                                fds.remove(i);
197                            }
198                        }
199                    } catch (Exception e) {
200                        ...
222                    }
223                }
224            }
225        }
226    }
复制代码

这里说明了一种进程通信方式,即Zygote进程和AMS所在的进程(systemServer进程)的通信方式是Socket,关于这部分细节内容,等后面创建应用进程时再细说。

在Java层的Zygote进程流程分析中,涉及了如下几个Java类,这里简单总结一下:

类名位置作用
ZygoteInit.java/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java启动Zygote进程的开始类,被用来给Native层做跳板,即JNI调用。主要功能函数有preload预加载资源、preloadSharedLib预加载共享库、preloadClass预加载类库、新建和处理systemServer进程等。
ZygoteServer.java/Zygote进程的Socket服务端,用来循环监听AMS进程发过来的请求,用于新建用户进程。
Zygote.java/该类主要作用就是创建子进程,没有共有构造函数,只提供了几个主要的静态方法,用于创建APP进程和systemServer进程。
ZygoteConnection.java/该类可以看成是一个Socket链接帮助类,用来分析AMS发过来的请求,以及解析和处理创建进程的参数等。

总结

Zygote进程作为开启Java世界大门的重要进程,简单总结一下其启动流程和作用:

  • 在Native层:
  1. 创建Java虚拟机,这也是Android系统能运行Java代码的底层原因。
  2. 注册JNI函数,赋能JNI功能,JNI可以让Java和C++代码互相调用。
  3. 通过JNI功能,调用Java世界的ZygoteInit的main函数,开启Java的业务。
  • 在Java层:
  1. 创建了本地Socket服务,并且循环监听,该服务的作用是和AMS所在的SystemServer进程通信,用来创建用户应用进程。这个而从另一方面也就说明了,Zygote进程和SystemServer进程的通信是Socket,在后面我们会介绍Android中大名鼎鼎的Binder通信。
  2. 预加载资源,包括类、图片资源等,这样的好处就是fork的新进程,就会持有这些信息。
  3. 创建和启动SystemServer进程,该进程管理和运行着我们熟悉的Java Framework中的各种服务,下篇文章细说。

本篇文章涉及较多linux和C++知识,笔者也在努力加油学习,文中有问题,欢迎指正。最后记录一下Flag。# 一个Android开发的学习Flag记录贴

收藏成功!
已添加到「」, 点击更改