深入理解zygote–2

1、welcome to Java World

这个Java世界的从入口在哪里?根据前面的分析,CallStaticVoidMethod最终将调用com.android.internal.os.ZygoteInit的main函数,下面就来就看看这个入口函数,代码如下所示:
Zygoteinit.java :

public static void main(String argv[]) {
        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();
            //注册zygote用的socket
            registerZygoteSocket();
        //预加载类和资源
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
            preloadClasses();
            //cacheRegisterMaps();
            preloadResources();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            if (SamplingProfilerIntegration.isEnabled()) {
                SamplingProfiler sp = SamplingProfiler.getInstance();
                sp.pause();
                SamplingProfilerIntegration.writeZygoteSnapshot();
                sp.shutDown();
            }

            // Do an initial gc to clean up after startup
        //强制一次性垃圾收集
            gc();

            // If requested, start system server directly from Zygote
             //我们传入的参数满足if分支
            if (argv.length != 2) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            if (argv[1].equals("true")) {
                startSystemServer();//启动system_server进程
            } else if (!argv[1].equals("false")) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            Log.i(TAG, "Accepting command socket connections");
            //ZYGOTE_FORK_MODE被定义为false,所以满足else的条件
            if (ZYGOTE_FORK_MODE) {
                runForkMode();
            } else {
                runSelectLoopMode();//zygote调用这个函数
            }

            closeServerSocket();//关闭socket
        } catch (MethodAndArgsCaller caller) {
            caller.run();//很重要的caller run函数,以后分析
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }
在ZygoteInit的main函数中,我们列举除了5大关键点:registerZygoteSocket(); preloadClasses(); startSystemServer(); runSelectLoopMode(); caller.run(),下面我们对其一一进行分析。
1.1、建立IPC通信服务端——-registerZygoteSocket
zygote 及系统中其他程序的通信没有使用binder,而是采用基于AF_UNIX类型的Socket。registerZygoteSocket的函数的使命正是建立这个socket。代码如下所示:

ZygoteInit.java :

private  static  void registerZygoteSocket(){}
  private static void registerZygoteSocket() {
        if (sServerSocket == null) {
            int fileDesc;
            try {
            //从环境中获取socket的fd,这个环境变量由execv传入
                String env = System.getenv(ANDROID_SOCKET_ENV);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(
                        ANDROID_SOCKET_ENV + " unset or invalid", ex);
            }

            try {
        //创建服务端socket,这个socket将listen并入accept Client。
                sServerSocket = new LocalServerSocket(
                        createFileDescriptor(fileDesc));
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }

registerZygoteSocket 很简单,就是创建一个服务端的socket。不过我们应该想到下面两个问题:
谁是客户端。
服务端是怎么处理客户端的消息。

1.2、预加载类和资源

现在我们要分析的就是preloadClass和preloadResources函数,先来看看preloadClasses.
ZygoteInit.java:

private static void preloadClasses() {
        final VMRuntime runtime = VMRuntime.getRuntime();
       //预加载类的信息存储在PRELOADED_CLASSES变量中,他的值为“preloaded-classes”
        InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
                PRELOADED_CLASSES);
        if (is == null) {
            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
        } else {
           //做一些统计和准备工作
            Log.i(TAG, "Preloading classes...");
            long startTime = SystemClock.uptimeMillis();

            // Drop root perms while running static initializers.
            setEffectiveGroup(UNPRIVILEGED_GID);
            setEffectiveUser(UNPRIVILEGED_UID);

            // Alter the target heap utilization.  With explicit GCs this
            // is not likely to have any effect.
            float defaultUtilization = runtime.getTargetHeapUtilization();
            runtime.setTargetHeapUtilization(0.8f);

            // Start with a clean slate.
            runtime.gcSoftReferences();
            runtime.runFinalizationSync();
            Debug.startAllocCounting();

            try {
                BufferedReader br
                    = new BufferedReader(new InputStreamReader(is), 256);
        //读取文件的第一行,忽略#开头的注释行
                int count = 0;
                String line;
                String missingClasses = null;
                while ((line = br.readLine()) != null) {
                    // Skip comments and blank lines.
                    line = line.trim();
                    if (line.startsWith("#") || line.equals("")) {
                        continue;
                    }

                    try {
            //通过java反射来加载类,line中的存储是预加载的类名。
                        if (Config.LOGV) {
                            Log.v(TAG, "Preloading " + line + "...");
                        }
                        Class.forName(line);
                        if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
                            if (Config.LOGV) {
                             此处代码省略``````````
    }
}

preloadClass看起来是如此简单,但是你知道他有多少个类需要预先加载吗?
用coolfind工具程序在framework中搜索名为“”“preloaded-classes的文件,最后会在framework/base目录下找到,他是一个文本文件,内容如下:
preloaded-classes :

# Classes which are preloaded by com.android.internal.os.ZygoteInit.
# Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java.
# MIN_LOAD_TIME_MICROS=1250   //超时控制。
android.R$styleable
android.accounts.AccountManager
android.accounts.AccountManager$4
android.accounts.AccountManager$6
android.accounts.AccountManager$AmsTask
android.accounts.AccountManager$BaseFutureTask
android.accounts.AccountManager$Future2Task
此处代码省略``````

这个preloaded-class 一共有1267行,试想,加载这么多的类得花多长时间:
说明 preload_class 文件由framework/base/tool/preload 工具生成,他需要判断每一个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。这方面的内容读者可以参考有关preload工具中的内容说明。
preload_class 函数执行的时间比较长,这是导致Android系统启动慢的原因之一,可对比进行一些优化,但是是基于对系统有比较深得了解之后才能够实现的。
preloadResources和preloadClass类似,他主要是加载framework-res.apk中的资源,这里就不再介绍它了。

1.3、启动system_server

我们要分析的是第三个关键点:startSystemServer。这个函数会创建java世界中系统Service所驻留的进程system_service,该进程是framework的核心,如果他死了,就会导致zygote自杀,先来看看这个核心进程是如何启动的。
zygoteInit.java :

private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {
        /*
         * First, set the capabilities if necessary
         */

        if (parsedArgs.uid != 0) {
            try {
                setCapabilities(parsedArgs.permittedCapabilities,
                                parsedArgs.effectiveCapabilities);
            } catch (IOException ex) {
                Log.e(TAG, "Error setting capabilities", ex);
            }
        }

        closeServerSocket();

        /*
         * Pass the remaining arguments to SystemServer.
         * "--nice-name=system_server com.android.server.SystemServer"
         */
        RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
        /* should never reach here */
    }

    /**
     * Prepare the arguments and fork for the system server process.
     */
    private static boolean startSystemServer()
            throws MethodAndArgsCaller, RuntimeException {
        /* Hardcoded command line to start the system server */
    //设置参数
        String args[] = {
            "--setuid=1000",  //uid,gid等设置
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
            "--capabilities=130104352,130104352",
            "--runtime-init",
            "--nice-name=system_server",//进程名,叫system_server
            "com.android.server.SystemServer",//启动的类名
        };
        ZygoteConnection.Arguments parsedArgs = null;

        int pid;

        try {
            //把上面的字符串数组转换成Arguments对象。
            parsedArgs = new ZygoteConnection.Arguments(args);

            /*
             * Enable debugging of the system process if *either* the command line flags
             * indicate it should be debuggable or the ro.debuggable system property
             * is set to "1"
             */
            int debugFlags = parsedArgs.debugFlags;
            if ("1".equals(SystemProperties.get("ro.debuggable")))
                debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;

            /* Request to fork the system server process */
        //fork一个子进程,看来这个进程就是system_server进程
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids, debugFlags, null);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
    //下面的代码。如果pid为零,则表示处于子进程中,也就是处于system_server进程中
        if (pid == 0) {
     //system_server进程的工作
            handleSystemServerProcess(parsedArgs);
        }

        return true;
    }

这里出现了一个分水岭,即zygotye进行了一次无性繁殖,分裂出一个system_server
进程,(即代码中Zygote.forkSystemServer这句话),关于他的故事,我们会在后文中专门分析,这里先说zygote。

1.4、有求比应之等待请求 —–runSelectLoopMode

待zygote从startSystemServer 返回后,将进入第四个关键的函数;runSelectLoopMode.
前面的地一个关键点registerZygoteSocket中注册了一个用于IPC的socket,不过那是还没有地方用到他,他的用途将在这个runselectLoopMode中体现出来,请看下面的代码:

ZygoteInit.java :

private static void runSelectLoopMode() throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList();
        ArrayList<ZygoteConnection> peers = new ArrayList();
        FileDescriptor[] fdArray = new FileDescriptor[4];
    //sSserversocket是我们先前在registerZygoteSocket中建立起来的Socket。
        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        int loopCount = GC_LOOP_COUNT;
        while (true) {
            int index;

            /*
             * Call gc() before we block in select().
             * It's work that has to be done anyway, and it's better
             * to avoid making every child do it.  It will also
             * madvise() any free memory as a side-effect.
             *
             * Don't call it every time, because walking the entire
             * heap is a lot of overhead to free a few hundred bytes.
             */
            if (loopCount <= 0) {
                gc();
                loopCount = GC_LOOP_COUNT;
            } else {
                loopCount--;
            }


            try {
                fdArray = fds.toArray(fdArray);
        /*
        selectReadable 内部调用select,使用多路复用I/O模型,当有客户端链接或有数据时,则selectReadable就会返回。     
        */
                index = selectReadable(fdArray);
            } catch (IOException ex) {
                throw new RuntimeException("Error in select()", ex);
            }

            if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {
            //有一个客户端链接上,请注意,客户端在Zygote的代表的是ZygoteConnection
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done;
        //客户端发送了一个请求,peer.get 返回的是ZygoteConnection。
        //后续处理将交给ZygoteConnection的runOnce函数完成。
                done = peers.get(index).runOnce();

                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }

runSelectLoopMode 比较简单,就是:
处理客户连接和客户请求,其中客户在zygote中用zygoteConnection对象来表示。
客户的请求由zygoteconnection的runOnce来处理。

2、关于zygote的总结

zygote是在Android系统中创建Java世界的盘古,他创建了第一个java虚拟机,同时他又是女娲成功的繁殖了framework的核心system_server进程。作为java语言的受益者,我们理应回顾一下zygote创建java世界的步骤:
第一天:创建AppRuntime对象,并调用他的start,此后的活动Appruntime 来控制。
第二天:调用startvm创建java虚拟机,然后用startReg来注册JNI函数。
第三天: 通过JNI来调用com.android.internal.os.ZygoteInit类的main函数,从此进入了java世界,然而这只是刚刚开始什么都没有。
第四天:调用registerZygoteSocket,通过这个函数,他可以响应子孙后代的请求,同时zygote调用preloadClass和preloadResources,为Java世界填砖加瓦。
第五天:zygote觉得自己工作挺多,便通过调用startSystemServer分裂一个进程,system_server来为java世界服务。
第六天:zygote完成了java世界的处创工作,下一步该做的就是调用runselectloopmode后,便沉沉的睡去了。
以后zygote随时守候在我们的周围i,当接受到子孙后代的请求时,他会随时醒来,为他们工作。

文献参考:

整理抄录自 — 《深入理解Android卷1》,邓凡平著。