Android Zygote

        Zygote 是android系统应用中一个相当重要的进程,其主要功能是执行Android应用程序。在android系统中运行新的应用,需要跟Zygote进程(拥有应用程序运行时所需要的各种元素和条件)结合后才能执行。
        Zygote进程运行时,会初始化Dalvik虚拟机,并启动它。android的应用程序是由java编写的,不能直接以本地进程的形态运行在linux上,只能运行在Dalvik虚拟机中。并且每个应用程序都运行在各自的虚拟机中,应用程序每次运行都要重新初始化并启动虚拟机,这就相当耗时。在android中,应用程序运行前,Zygote进程通过共享已运行的虚拟机的代码与内存信息,缩短应用程序运行所耗费的时间。并且,它会事先将应用程序要使用的android Fromework中的类和资源加载到内存中,并组织形成所用资源的链接信息。新运行的android应用程序在使用所需要的资源时不必每次重新形成资源的链接信息,这样提高程序运行速度。
        在android中,使用Zygote进程的目的?对于手机,为了是应用程序在有限的资源型有更快的运行响应速度,提高资源利用率和设备使用时间。android使用 Zygote来有效的减少系统负担,提高运行速度。
   下面讲解zygote进程是如何运行,初始化,提高应用的运行速度的。
  (1)     由 Zygote孵化进程
       init进程是在系统启动后运行在用户空间中的首个进程。init进程启动完系统运行所需的各种 Daemon(守护线程)后,启动  Zygote,如下表所示;
               Android Zygote_第1张图片
   Zygote进程启动后,android的服务与应用程序都由Zygote进程启动运行。
连接手机,使用adb工具,ps查看系统运行的进程。 根据父进程的PID,android设备中运行的进程大致有 Daemon 进程以及 Dalvik虚拟机中运行的android应用程序两大类。下图中,PID 为1 的进程。还有 PPID(父进程)为 1的进程,就是init 进程启动的 Daemon进程。Zygote进程就是其中之一,其进程的PID 为 30,下图可以看到,而父进程为30的进程,都是Zygote进程的子进程,由其创建并启动,大都是android应用程序。
               Android Zygote_第2张图片
注:linxu系统创建并运行一个进程,与android系统中通过Zygote来创建并运行一个进程的区别
                       Android Zygote_第3张图片
        上图为linux创建并运行一个进程的过程,父进程A调用fork()函数创建新的子进程A`。新创建的进程A` 共享父进程的内存结构信息和库连接信息。而后子进程A`调用 exec('B'),将新进程B的代码加载到内存中。此时父进程A的内存信息被清除,并重新分配内存,以便运行被装载的B进程,接着形成新的库连接信息,以供进程B使用。若进程B使用的共享库已被装载至内存,则只需更新连接信息。不然,还要添加一个步骤,即使存储器中的相关库装入内存中。每当运行新进程时,就会重复以上过程。
        那么,android怎么创建并运行一个新进程呢?
        Zygote是android系统的一个主要特征,它通过COW(copy on write)方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效的降低了内存的使用量(foot print)。具体简介如下:
                    Android Zygote_第4张图片
           如上图,Zygote进程调用fork()函数创建出 Zygote`子进程,子进程Zygote` 共享父进程Zygote的代码区域与连接信息。注意新的进程A并非通过fork()来重新装载已有进程的代码区,而是被动态地加载到复制出的Dalvik虚拟机上。而后,Zygote`进程将执行流程交给应用程序A类的方法,android应用程序开始运行。新生的应用程序A会使用已有的Zygote进程的库与资源的连接信息,所以运行速度很快。下图为Zygote运行后,新的android应用程序A的运行过程。
           Android Zygote_第5张图片
           如上图所述,Zygote启动后,初始化并运行Dalvik虚拟机,而后将需要的类与资源加载到内存中。随后调用fork()创建出Zygote`子进程,接着 Zygote`子进程动态加载并运行android应用程序A。运行android应用程序A会使用Zygote已经初始话并启动的Dalvik虚拟机,通过使用已加载至内存中的类与资源来加快运行速度。
****************************************************************************************************
      TIP:  COW ( copy on write)
      在创建新进程后,新进程会共享父进程的内存空间,即新的子进程会复制所有与父进程内存空间相关的信息并使用它。COW就是针对内存复制的一种技术。一般,复制内存的开销非常大,因此,创建的子进程在引用父进程的内存空间时,不要进行复制,而要直接共享父进程的内存空间。而当需要修改共享内存中的信息时,子进程才会将父进程中相关的内存信息复制到自身的内存空间,并进行修改,这就是COW技术。
       注意当调用fork()直接运行 exec()时,新进程的内存空间与父进程的内存空间内容不同,此时复制符进程内存空间的做法就毫无意义,并且会增加新进程运行的系统开销。
****************************************************************************************************
(2)由 app_process运行 ZygoteInit class
         与本地服务或Daemon不同的是,Zygote由java编写而成,不能直接由Init进程启动运行。若想运行Zygote类,必须先生成 Dalvik虚拟机,再在Dalvik虚拟机上装载运行ZygoteInte类,执行这一任务的就是 app_process 进程。
         Android Zygote_第6张图片
         分析 /system/bin/app_process 源码(具体路径在 frameworks/base/cmds/app_process/app_main.cpp) 中的main()函数,可发现app_process进程首先生成一个 AppRuntime 对象,而后分析main()函数传递进来的参数,并传递给AppRuntime对象。然后生成并初始化Dalvik虚拟机,再调用执行 ZygoteInit 类的main方法。
         分析 app_main()源码
        ****************************************************************************************************
             int  main( int  argc, const char* const argv[] ){
                      .......
                      AppRuntime  runtime;  //1AppRuntime类继承自AndroidRuntime类, AndroidRuntime类用于初始化并运行Dalvik虚拟机,为运行android应用程序做好准备,
                      ......
                      int  i = runtime.addVmArguments( argc, argv );  //2在运行Dalvik虚拟机之前,通过AppRuntime对象,分析环境变量以及运行的参数,并以此生成虚拟机选项。
                      //Next  arg  is  parent  directory
                      if ( i < argc ){
                               runtime.mParentDir = argv[ i++ ];   //3
                      }
             }
         ****************************************************************************************************
            分析 init.rc文件,可以发现Init进程在运行app_process时,根据如下规则传递参数,app_process参数形式如下:
                         app_process  [java-options]  cmd-dir  start-class-name  [options]
                    [ java-options ]: 传递给虚拟机的选项,必须以“-”开始
                    cmd-dir: 所有运行的进程所在的目录。
                    start-class-name :  要在虚拟机中运行的类的名称。 app_process会将指定的类加载到虚拟机中,而后调用类的main()方法。
                    [ options ]:要传递给类的选项
           app_process 服务运行时,init.rc文件中的运行命令如下:
                         /system/bin/app_process  -Xzygote  /system/bin  --zygote  --start-system-server
                    说明: -Xzygote指要传递给VM的选项,用来区分在虚拟机中运行的类是Zygote,还是 Zygote中运行的其他android应用程序,它会被保存到 AppRuntime 的mOption变量中,如在上述代码2中。在代码3中,运行目录参数 “/system/bin”被保存到 AppRuntime 的 mParentDir变量中。第三个参数用来指定加载到虚拟机中的类的名称,“--zygote”表示加载 com.android.internal.os.ZygoteInit类。最后一个参数“--start-system-server”作为选项传递给生成的类,用于启动运行系统服务器。
            调用AppRuntime 对象
           分析完传递给虚拟机的参数,并保存到AppRuntime类的对象中,而后加载对象,调用对象的main()方法。
        ****************************************************************************************************
               Android Zygote_第7张图片
      ****************************************************************************************************
        上述代码中 1中检查类名称是否为“--zygote“,处理过程过程略微不同,但最后都是将给定的类加载至虚拟机中。加入传递过来的是”--zygote“,程序将继续执行1中的代码。 3中代码,调用AppRuntime的start()成员函数,生成并初始化虚拟机,而后将ZygoteInit类加载至虚拟机中,执行其中的main()函数。
         传递给runtime.start()函数的第一个参数为"com.android.internal.os.ZygoteInit" 是完全限定名(FQN),在FQN中包含类所在的包以及类的名称,当FQN被传递给类加载器时,类加载器就会将包名称解析为相应的路径,而后在改路径下查找并加载名称为”ZygoteInit“ 的类。”--start-system-server“作为最后一个参数传递给app_process。
        创建Dalvik虚拟机
        在运行Dalvik虚拟机之前,除了接收从app_process传递过来的虚拟机选项外,AppRuntime的start()函数还要获取与虚拟机运行相关的各种系统属性与环境变量,而后更改虚拟机的运行选项。
       ****************************************************************************************************
                int  property_get ( const char  *key,  char  *value,  const char *default_value )
       ****************************************************************************************************
        为了设置虚拟机的运行选项,需要调用 property_get()函数来访问系统中设置的相关值,上述代码为property_get()函数的函数原型,用来访问系统的属性域。系统属性与通过 init.rc 的 setprop语句有init进程或其他进程进行设置。若想变更 Dalvik虚拟机的运行选项,只需在运行虚拟机之前,参照调用 property_get()函数的代码,设置 init.rc 相关属性或通过 app_process 参数传递虚拟机的运行选项即可。
          ****************************************************************************************************
                    void AndroidRuntime::start( const  char*  className,  const bool startSystemServer ){
                             if ( JNI_CreateJavaVM( &mJavaVM,  &env,  &initArgs ) < 0 ){
                                      goto  bail;
                             }
                    }
        ****************************************************************************************************
           start()函数中,调用 JNI_CreateJavaVM() 函数来创建并运行虚拟机,JNI_CreateJavaVM() 函数原型如下: 
                    jint  JNI_CreateJavaVM ( JavaVM** p_vm,  JNIEnv**  p_env,  void*  vm_args )
               >第一个参数:生成的javaVM类的接口指针
               >第二个参数:JNIEnv类的接口指针,方便访问虚拟机
               >第三个参数:已设置的虚拟机选项
           接下来,注册要在虚拟机中使用的JNI函数。
       ****************************************************************************************************
            Android Zygote_第8张图片
          ****************************************************************************************************
          调用2中的startReg()函数,将调用1中 static  const  RegJNIRecgRegJNI[] 数组中的函数。而后运行在虚拟机中的java类就可以调用本地函数了。
         运行 ZygoteInit类
         在创建完VM之后,接着加载要运行的类。如前所述,根据参数的不同,app_process也会加载执行Zygote以外的其他类。如下面代码,AppRuntime的start()函数会查找指定的类,并调用指定类的main()方法。
         ****************************************************************************************************
             Android Zygote_第9张图片
       ****************************************************************************************************
        此时程序的执行,转至了虚拟机上执行的java程序。后面本地域中的C++代码执行流会一直等待,知道虚拟机运行停止。
(4)ZygoteInit类的功能
  至此已经创建好了虚拟机,将zygoteInit类加载到了虚拟机中。接下来看看zygoteInit类的作用。
       Android Zygote_第10张图片
        ZygoteInit的main()方法的功能如上图所示。其源码如下: 
     **********************************************************************************************************
               public  static  void  main( String  argv[ ] ){
                        try{
                                 //绑定套接字,接收新android应用程序运行请求
                                 registerZygoteSocker(); //为了从ActivityManager接收新android应用程序的运行请求,Zygote使用UDS,init进程在运行app_process时,使用init.rc文件中以”/dev/zygote“形式注册的套接字。
                                 //加载android Application Framework使用的类与资源
                                 proloadClasses(); //用于将应用程序框架中的类,平台资源预先加载到内存中。新进程直接使用这些类与资源,不需要重新加载他们。
                                 proloadResources();
                                 //运行SystemServer
                                 if ( argv[1].equals("true") ) {
                                          startSystemServer(); //通过app_process运行zygote时,参数"--start-system-server"会调用startSystemServer()方法启动系统服务器,系统服务器用来运行Android平台需要的一些主要的本地服务。
                                 }
                                 if ( ZYGOTE_FORK_MODE ){
                                          runForkMode();
                                 }else{
                                          //处理新android应用程序运行请求
                                          runSelectLoopMode(); //监视UDS,若收到新android应用程序生成请求,则进入处理循环
                                 }
                                 closeServerSocket();
                        }catch( MethodAndArgsCaller  caller ){
                                 caller.run();
                        }catch( RuntimeException  ex ){
                                 closeServerSocket();
                                 throw  ex;
                        }
               }
      **********************************************************************************************************
       下面详细分析这些功能
   a. 绑定/dev/socket/zygote套接字
      ZygoteInit 类使用由 /dev/socket/zygote 生成的UDS套接字,从ActivityManager 接收新android应用程序的生成请求。该套接字在系统启动过程中由 init 进程生成,在 init.rc 文件中有生成该套接字的相关内容。下面是关于 zygote service 定义部分,套接字的名称,种类,访问权限在第二行中标出了。  *******************************************************************************************************
          service  zygote  /system/bin/app_process  -Xzygote  /system/bin  --zygote  --start-system-server
                   socket  zygote stream 666
                   onrestart  write  /sys/android_power/request_state  wake
                   onrestart  write  /sys/power/state  on
    ******************************************************************************************************
         ZygoteInit类中的main()方法首先调用 registerZygoteSocket()方法,代码如下:
    ******************************************************************************************************
           Android Zygote_第11张图片
        *****************************************************************************************************
            在2中创建一个LocalServerSocket类的对象并将其赋值给sServerSocket静态变量中。代码1中调用System.getenv()方法,获取套接字的文件描述符,该套接字由init进程记录在ANDROID_SOCKET_zygote环境变量中。应用程序Framework使用套接字文件描述符创建 LocalServerSocket 类的实例,并将其与 /dev/socket/zygote绑定在一起。
         当创建新android进程的请求到达ZygoteInit对象时,创建出LocalServerSocket实例接收生成新android进程的信息,并在最后循环语句中进行处理。
   b.  加载应用程序Framework 中的类与平台资源
         ZygoteInit 类会调用 preloadClasses()与 preloadResources()两个方法,这两个方法分别用于将应用程序Framework中的类,以及图标,图像,字符串等资源加载到内存中,并对装载的类与资源生成链接信息。新生成的android应用程序在使用这些已经装载的类或资源时,直接使用即可,不需要重新生成链接信息。
             preloadClasses()方法主要代码如下:
  ******************************************************************************************************
                  Android Zygote_第12张图片
  ******************************************************************************************************
             代码解析:1中获取一个输入流,以便读取”preloaded-classes“文件中记录的类。”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
              SQLite.Blob
              SQLite.Database
              SQLite.FunctionContext
              SQLite.Stmt
              SQLite.Vm
              android.R$styleable
              android.accounts.IAccountsService$Stub
              android.app.Acitivity
              .......
 ******************************************************************************************************
       2代码,在获取输入流的基础上,创建BufferedReader对象,并读取”preloaded-classed“文件的内容
       3代码中,忽略所读取内容中的注释与空行,而后开始读取下一行。
       4代码中,调用Class.forName()方法,将读到的类动态地加载到内存中。Class.forName()方法并非真在内存中生成指定类的实例,它只是把类的信息加载到内存中,并初始化静态变量。
        新应用程序运行时,由于使用的类已经加载到内存中,所以程序的启动运行速度回相当快。

        加载应用程序Framework中包含的资源
        在android应用程序Framework中使用的字符串,颜色,图像文件,音频文件等都成为资源。应用程序不能直接访问这些资源,需要通过Android开发工具自动生成的R类来访问。通过R类可访问的资源组成信息记录在XML中。
       preloadResources方法是用于加载资源,代码如下: 
  ******************************************************************************************************
             Android Zygote_第13张图片
  ******************************************************************************************************
       资源分为系统资源与应用程序资源,使用系统资源时,需要先调用Resources类的方法getSystem(),而后使用其返回对象,预加载系统资源。 
         至此,Dalvik虚拟机已经启动并完成初始化,还绑定了套接字,以便接收应用程序创建请求。并且Framework中的类与资源也被加载到内存中,由此ZygoteInit类做好了接收请求创建应用程序并运行的准备。但ZygoteInit类在处理应用程序创建请求之前,还要运行SystemServer工作。

    c. 运行 SystemServer
              Android Zygote_第14张图片
          Zygote启动Dalvik虚拟机后,会再生成一个Dalvik虚拟机实例,以便运行名称为 SystemServer的java服务,SystemServer 用于运行 Audio Flinger与 Surface  Flinger本地服务。在运行完所需的本地服务后, SystemServer 开始运行Android Framework的服务,如ActivityManager, PackageManager等。
           startSystemServer的代码如下:
  ******************************************************************************************************
           private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
        long capabilities = posixCapabilitiesAsBits(
            OsConstants.CAP_BLOCK_SUSPEND,
            OsConstants.CAP_KILL,
            OsConstants.CAP_NET_ADMIN,
            OsConstants.CAP_NET_BIND_SERVICE,
            OsConstants.CAP_NET_BROADCAST,
            OsConstants.CAP_NET_RAW,
            OsConstants.CAP_SYS_MODULE,
            OsConstants.CAP_SYS_NICE,
            OsConstants.CAP_SYS_RESOURCE,
            OsConstants.CAP_SYS_TIME,
            OsConstants.CAP_SYS_TTY_CONFIG
        );
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
            "--capabilities=" + capabilities + "," + capabilities,
            "--runtime-init",
            "--nice-name=system_server",
            "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 */
            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)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

        return true;
    }
   
    *****************************************************************************************************
            该方法用于运行SystemServer。代码1定义保存SystemServer的启动参数的字符串数组,属于硬编码数组。最后一个参数指定SystemServer类。  与运行其他应用程序不同,该方法会调用 forkSystemServer()方法来创建新进程,并运行SystemServer。系统在运行普通android应用程序时,只负责创建应用程序进程,至于进程是否创建成功并不检查。与此不同,SystemServer是必须运行的,因此在 forkSystemServer()方法中必须检查生成的SystemServer 进程工作是否正常。3代码中,运行SystemServer类的main()方法,此时会加载android_servers本地库。
 ******************************************************************************************************
              Android Zygote_第15张图片
    ******************************************************************************************************
        本地库加载完毕以后,然后调用JNI本地方法nativeInit(),加载系统需要的本地服务。然后设置android.server.SystemServer主线程,加载framework层主要服务,启动消息处理循环。

   d. 运行新的android应用程序
         在systemserver运行后,程序会进入一个循环,,处理来自所绑定的套接字的请求。程序运行runSelectLoopMode()方法启动消息处理循环,处理套接字请求。
   Android Zygote_第16张图片

         上图描述了zygoteInit类运行新应用的过程,接下来代码详细分析:
   ******************************************************************************************************
       private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList fds = new ArrayList();
        ArrayList peers = new ArrayList();
        FileDescriptor[] fdArray = new FileDescriptor[4];

        fds.add(sServerSocket.getFileDescriptor());//将套接字与dev/socket/zygote绑定在一起,本行首先将套接字的描述符添加到描述符数组中,保存在数组的第0个index中,程序将使用该描述符处理来自外部的连接请求。
        peers.add(null);

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

            if (loopCount <= 0) {
                gc();
                loopCount = GC_LOOP_COUNT;
            } else {
                loopCount--;
            }


            try {
                fdArray = fds.toArray(fdArray);
                index = selectReadable(fdArray);//本身是一个被注册为JNI本地方法的本地函数。用来监视参数传递过来的文件描述符数组,若描述符目录中存在相关事件,则返回其在数组中的索引。
            } catch (IOException ex) {
                throw new RuntimeException("Error in select()", ex);
            }

            if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {//处理index为0的套接字描述符中发生的输入输出事件。为了处理传递给dev/socket/zygote套接字的链接请求,程序先创建了Z对象。在zygoteConnection构造方法中创建输入输出流,而后生成Credentials,检查请求连接一方的访问权限,为了处理zygoteConnection对象的输入输出事件,将套接字描述符添加到套接字描述符数组fds中。被添加的套接字描述符的输入输出事件在下一个循环中,由selectReadable()方法进行检查。
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDescriptor());
            } else {
                boolean done;
                done = peers.get(index).runOnce();//用于处理新连接的输入输出套接字,并生成新的android应用程序。

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

               ******************************************************************************************************
 在runOnce()方法中,方法用来创建新的进程。
      以上是ZygoteInte类加载新应用程序类并调用main()方法执行的整个过程。新运行的应用程序由ZygoteInit类动态加载,共用装载到父进程生成的虚拟机中的代码。共用应用程序Framework中的类与资源的链接信息,大大加快了应用程序创建于启动的速度。





















   

你可能感兴趣的:(Android Zygote)