码迷,mamicode.com
首页 > 其他好文 > 详细

Zygote的源码解析

时间:2016-07-17 00:55:17      阅读:290      评论:0      收藏:0      [点我收藏+]

标签:

1.什么是Zygote?

在Android中所有的应用程序进程,以及用来运行系统关键服务的System进程都是有Zygote进程负责创建的,因此我们就叫他受精卵,毕竟它真的和受精卵很类似。

我们知道,Android的应用程序是由Java编写的,它们不能直接以本地进程的形态运行在Linux上,只能运行在Dalvik/ART虚拟机中。每个应用程序都运行在各自的虚拟机中,应用程序每次运行都要重新初始化并启动虚拟机,这个过程会耗费相当长时间,是导致应用程序启动慢的原因之一。

为了很好地解决这个问题,在Android中,应用程序运行前,Zygote进程通过共享已运行的虚拟机的代码与内存信息,缩短应用程序运行所耗费的时间。并且,它会预先将应用程序要使用的Android Framework中的类与资源加载到内存中,并组织形成所用资源的链接信息。因此,新运行的应用程序在使用所需资源时不必每次重新形成资源的链接信息,从而提高运行速度。

下面我们来先分析一下Zygote的原理和源码分析

2.Zygote的源码入口

Zygote是由Android中第一个进程init启动完所需要的一系列后台程序后才启动起来的,利用adb命令可以看到手机上运行的进程间的派生关系,如下图所示:

技术分享
从图片的最顶部可以看到Zygote的PPID为1,即init进程,而zygote进程ID为263,同时还可以发现launcher,PushService,systemui,location等各种进程的父进程都是263,这进一步验证了Zygote为受精卵的事实,并且Zygote是通过CopyOnWrite方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效地降低了内存的使用量。
由于Zygote是由Java直接编写的,不能直接由init进程启动运行。若想运行Zygote则需要先生成Dalvik/ART虚拟机,执行这一任务就是在app_process里面。
代码如下

int main(int argc, const char* const argv[])
{
      ...
    AppRuntime runtime;
      ...
    // Next arg is startup classname or "--zygote"
    if (i < argc) {
        arg = argv[i++];
        if (0 == strcmp("--zygote", arg)) {
            bool startSystemServer = (i < argc) ? 
                    strcmp(argv[i], "--start-system-server") == 0 : false;
            setArgv0(argv0, "zygote");
            set_process_name("zygote");
            runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer);
        } else {
            set_process_name(argv0);

            runtime.mClassName = arg;

            // Remainder of args get passed to startup class main()
            runtime.mArgC = argc-i;
            runtime.mArgV = argv+i;

            LOGV("App process is starting with pid=%d, class=%s.\n",
                 getpid(), runtime.getClassName());
            runtime.start();
        }
    } 
      ...
}

这里面先创建了一个AppRuntime对象,其中AppRuntime类继承自AndroidRuntime类,而AndroidRuntime类的作用是初始化并运行Dalvik虚拟机,为运行Android应用程序做好准备。
虚拟机是怎么创建起来的本文就不关心了
接着来看AndroidRuntime.start()的主要代码

 /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    jclass startClass;
    jmethodID startMeth;

    slashClassName = strdup(className);
    for (cp = slashClassName; *cp != ‘\0‘; cp++)
        if (*cp == ‘.‘)
            *cp = ‘/‘;

    startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        LOGE("JavaVM unable to locate class ‘%s‘\n", slashClassName);
        /* keep going */
    } else {
        startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            LOGE("JavaVM unable to find main() in ‘%s‘\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

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

这里一开始先调用成员函数starVM创建一个虚拟机实例,接着就获得main的ID并来调用main方法它。继续看main()

    public static void main(String argv[]) {
    try {
        // Start profiling the zygote initialization.
        SamplingProfilerIntegration.start();

        //绑定socket,接收新Android应用程序运行请求
        registerZygoteSocket();
        EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
            SystemClock.uptimeMillis());
        //加载Android Application Framework使用的类与资源
        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 (argv.length != 2) {
            throw new RuntimeException(argv[0] + USAGE_STRING);
        }

        //运行SystemServer
        if (argv[1].equals("true")) {
            startSystemServer();
        } else if (!argv[1].equals("false")) {
            throw new RuntimeException(argv[0] + USAGE_STRING);
        }

        Log.i(TAG, "Accepting command socket connections");

        if (ZYGOTE_FORK_MODE) {
            runForkMode();
        } else {
            //处理新Android应用程序请求
            runSelectLoopMode();
        }

        closeServerSocket();
    } catch (MethodAndArgsCaller caller) {
        caller.run();
    } catch (RuntimeException ex) {
        Log.e(TAG, "Zygote died with exception", ex);
        closeServerSocket();
        throw ex;
    }
}

概括起来,主要完成了以下4个工作:
1)registerZygoteSocket(); 语句用来绑定socket,以便接收新的Android应用程序的运行请求。为了从ActivityManager接收新的Android应用程序的运行请求,Zygote使用UDS(Unix Domain Socket),init进程在运行app_process时,使用init.rc文件中以”/dev/zygote”形式注册的socket;
2)该行用于将应用程序框架中的类、平台资源(图像、XML信息、字符串等)预先加载到内存中。新进程直接使用这些类与资源,而不需要重新加载它们,这大大加快了程序的执行速度;
3)通过app_process运行zygote时,参数”–start-system-server”会调用startSystemServer()方法启动系统服务器,系统服务器用来运行Android平台需要的一些主要的本地服务(主要是SurfaceFlinger,AudioFlinger,MediaPlayerService,CameraService等);
4)runSelectLoopMode();的作用是监视UDS,若收到新的Android应用程序生成请求,则进入处理循环.
技术分享
我们来一一分析这四项工作是如何完成的。

3.详细解析

3.1绑定socket

registerZygoteSocket()方法的代码如下:

/**
 * Registers a server socket for zygote command connections
 *
 * @throws RuntimeException when open fails
 */
private static void registerZygoteSocket() {
    if (sServerSocket == null) {
        int fileDesc;
        try {
            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 {
            sServerSocket = new LocalServerSocket(
                    createFileDescriptor(fileDesc));
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket ‘" + fileDesc + "‘", ex);
        }
    }
}

主要就是先获取socket的文件描述符,然后据此创建一个LocalServerSocket对象并将其赋值给sServerSocket静态变量。在System.getenv(ANDROID_SOCKET_ENV); 中的套接字由init进程记录在ANDROID_SOCKET_ENV环境变量中,在init.rc中有生成该socket的相关内容。

3.2预加载应用程序Framework中的类与平台资源

这个过程主要是由proloadClasses()与preloadResources()两个方法来完成,分别将Framework中的类和资源加载到内存中,并对装载的类与资源生成链接信息。新生成的Android应用程序在使用这些已经装载的类或资源时,直接使用即可,不需要重新生成链接信息。

此外,还加载许多其他的类,比如图形相关的类andriod.graphics,以及通信相关的类android.net等。

preloadClasses()方法的主要代码如下:


private static final String PRELOADED_CLASSES="preloaded-classes";

...

private static void preloadClasses(){

    ...
    //code_1
    InputStream is=ZygoteInit.class.getClassLoader().getResourceAsStream(PRELOADED_CLASSES);

    ...
    BufferedReader br=new BufferedReader(new InputStreamReader(is),256);

    int count=0;
    String line;
    while((line=br.readLine())!=null){
        line=line.trim();
        if(line.startsWith("#")||line.equals("")){
            continue;
        }

        Class.forName(line);
        ...
    }
}

在code_1处获取一个输入流,以便读取”preloaded-classes”文件(frameworks/base/preloaded-classes)中记录的类,总共有1265个类会被预加载,如果每启动一个应用程序,就加载一遍这些类,那么将会耗费巨大的时间。

与预加载类相似,Android应用程序也会预先加载使用的资源,这样在使用这些资源时的效率会大大提高。

preloadResources()方法的代码如下所示:

/**
 * Load in commonly used resources, so they can be shared across
 * processes.
 *
 * These tend to be a few Kbytes, but are frequently in the 20-40K
 * range, and occasionally even larger.
 */
private static void preloadResources() {
    final VMRuntime runtime = VMRuntime.getRuntime();

    Debug.startAllocCounting();
    try {
        runtime.gcSoftReferences();
        runtime.runFinalizationSync();
        mResources = Resources.getSystem();
        mResources.startPreloading();
        if (PRELOAD_RESOURCES) {
            Log.i(TAG, "Preloading resources...");

            long startTime = SystemClock.uptimeMillis();
            TypedArray ar = mResources.obtainTypedArray(
                    com.android.internal.R.array.preloaded_drawables);
            //code_1
            int N = preloadDrawables(runtime, ar);
            Log.i(TAG, "...preloaded " + N + " resources in "
                    + (SystemClock.uptimeMillis()-startTime) + "ms.");

            startTime = SystemClock.uptimeMillis();
            ar = mResources.obtainTypedArray(
                    com.android.internal.R.array.preloaded_color_state_lists);
            //code_2
            N = preloadColorStateLists(runtime, ar);
            Log.i(TAG, "...preloaded " + N + " resources in "
                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
        }
        mResources.finishPreloading();
    } catch (RuntimeException e) {
        Log.w(TAG, "Failure preloading resources", e);
    } finally {
        Debug.stopAllocCounting();
    }
}

这里定义了一个成员变量,用来记录资源加载的状态为了避免因重复调用而造成资源重复加载的情形。若指定资源已经被加载,就会抛出IllegalStateException,并终止方法的执行。

3.3启动SystemServer

startSystemServer()的代码如下所示:

private static boolean startSystemServer()
        throws MethodAndArgsCaller, RuntimeException {
    String args[] = {
        "--setuid=1000",
        "--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",
        "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        int debugFlags = parsedArgs.debugFlags;
        if ("1".equals(SystemProperties.get("ro.debuggable")))
            debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;

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

    /* For child process */
    if (pid == 0) {
        //code_3
        handleSystemServerProcess(parsedArgs);
    }

    return true;
}

1)args[]数组中保存了SystemServer的启动参数,参数的含义也很好理解,无非是为SystemServier设置名称、uid和gid,以及进程分组等。在字符串数组中,最后一个参数com.android.server.SystemServer用于指定SystemServer类;

2)与运行其他应用程序不同,startSystemServer()方法会调用forkSystemServer()方法来创建新进程,并运行SystemServer.系统在运行普通的Android应用程序时,只负责创建应用程序进程,至于进程是否创建成功则并不检查。但是SystemServer则是必须运行的,因此在forkSystemServer()方法中必须检查生成SystemServer进程工作是否正常;

3)handleSystemServerProcess()是用来启动System进程,这个等后面再来分析

3.4 runSelectLoopMode来等待ActivityManagerService请求Zygote创建新的应用程序进程

public class ZygoteInit {  
    ......  

    private static void runSelectLoopMode() throws MethodAndArgsCaller {  
        ArrayList<FileDescriptor> fds = new ArrayList();  
        ArrayList<ZygoteConnection> peers = new ArrayList();  
        FileDescriptor[] fdArray = new FileDescriptor[4];  

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

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

            ......  


            try {  
                fdArray = fds.toArray(fdArray);  
                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) {  
                ZygoteConnection newPeer = acceptCommandPeer();  
                peers.add(newPeer);  
                fds.add(newPeer.getFileDesciptor());  
            } else {  
                boolean done;  
                done = peers.get(index).runOnce();  

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

    ......  
}       

这就是在等待ActivityManagerService来连接这个Socket,然后调用ZygoteConnection.runOnce函数来创建新的应用程

这样,Zygote进程就启动完成了

到这里,我们可以归纳出Android Framework的启动过程:
技术分享

Zygote的源码解析

标签:

原文地址:http://blog.csdn.net/zhoulei0623/article/details/51926643

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!