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,如下表所示;                
   Zygote进程启动后,android的服务与应用程序都由Zygote进程启动运行。 连接手机,使用adb工具,ps查看系统运行的进程。 根据父进程的PID,android设备中运行的进程大致有 Daemon 进程以及 Dalvik虚拟机中运行的android应用程序两大类。下图中,PID 为1 的进程。还有 PPID(父进程)为 1的进程,就是init 进程启动的 Daemon进程。Zygote进程就是其中之一,其进程的PID 为 30,下图可以看到,而父进程为30的进程,都是Zygote进程的子进程,由其创建并启动,大都是android应用程序。               
注:linxu系统创建并运行一个进程,与android系统中通过Zygote来创建并运行一个进程的区别                       
        上图为linux创建并运行一个进程的过程,父进程A调用fork()函数创建新的子进程A`。新创建的进程A` 共享父进程的内存结构信息和库连接信息。而后子进程A`调用 exec('B'),将新进程B的代码加载到内存中。此时父进程A的内存信息被清除,并重新分配内存,以便运行被装载的B进程,接着形成新的库连接信息,以供进程B使用。若进程B使用的共享库已被装载至内存,则只需更新连接信息。不然,还要添加一个步骤,即使存储器中的相关库装入内存中。每当运行新进程时,就会重复以上过程。         那么,android怎么创建并运行一个新进程呢?         Zygote是android系统的一个主要特征,它通过COW(copy on write)方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效的降低了内存的使用量(foot print)。具体简介如下:                    
           如上图,Zygote进程调用fork()函数创建出 Zygote`子进程,子进程Zygote` 共享父进程Zygote的代码区域与连接信息。注意新的进程A并非通过fork()来重新装载已有进程的代码区,而是被动态地加载到复制出的Dalvik虚拟机上。而后,Zygote`进程将执行流程交给应用程序A类的方法,android应用程序开始运行。新生的应用程序A会使用已有的Zygote进程的库与资源的连接信息,所以运行速度很快。下图为Zygote运行后,新的android应用程序A的运行过程。           
           如上图所述,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 进程。         
         分析 /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()方法。         ****************************************************************************************************               
      ****************************************************************************************************         上述代码中 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函数。        ****************************************************************************************************            
          ****************************************************************************************************           调用2中的startReg()函数,将调用1中 static  const  RegJNIRecgRegJNI[] 数组中的函数。而后运行在虚拟机中的java类就可以调用本地函数了。          运行 ZygoteInit类          在创建完VM之后,接着加载要运行的类。如前所述,根据参数的不同,app_process也会加载执行Zygote以外的其他类。如下面代码,AppRuntime的start()函数会查找指定的类,并调用指定类的main()方法。          ****************************************************************************************************             
       ****************************************************************************************************         此时程序的执行,转至了虚拟机上执行的java程序。后面本地域中的C++代码执行流会一直等待,知道虚拟机运行停止。
(4)ZygoteInit类的功能
  至此已经创建好了虚拟机,将zygoteInit类加载到了虚拟机中。接下来看看zygoteInit类的作用。
      
        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()方法,代码如下:     ******************************************************************************************************           
        *****************************************************************************************************             在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()方法主要代码如下:   ******************************************************************************************************                  
  ******************************************************************************************************              代码解析: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方法是用于加载资源,代码如下:    ******************************************************************************************************             
  ******************************************************************************************************        资源分为系统资源与应用程序资源,使用系统资源时,需要先调用Resources类的方法getSystem(),而后使用其返回对象,预加载系统资源。           至此,Dalvik虚拟机已经启动并完成初始化,还绑定了套接字,以便接收应用程序创建请求。并且Framework中的类与资源也被加载到内存中,由此ZygoteInit类做好了接收请求创建应用程序并运行的准备。但ZygoteInit类在处理应用程序创建请求之前,还要运行SystemServer工作。

    c. 运行 SystemServer              
          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本地库。  ******************************************************************************************************              
    ******************************************************************************************************         本地库加载完毕以后,然后调用JNI本地方法nativeInit(),加载系统需要的本地服务。然后设置android.server.SystemServer主线程,加载framework层主要服务,启动消息处理循环。

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

         上图描述了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中的类与资源的链接信息,大大加快了应用程序创建于启动的速度。




















   

更多相关文章

  1. Android(安卓)studio 下的aidl编程实现Android的夸进程间通信
  2. Android应用中通过AIDL机制实现进程间的通讯实例
  3. Android实现屏幕旋转方法总结
  4. Android中通过Messenger与Service实现进程间双向通信
  5. 浅入浅出 Android(安卓)安全:第三章 Android(安卓)本地用户空间层
  6. android service
  7. Android进程间通信IPC机制Binder
  8. 将Android的Camera源码导入到eclipse中进行开发
  9. Android实现图表绘制和展示

随机推荐

  1. 疯狂Android讲义目录结构
  2. android 通过wifi 获取经纬度和获取渠道
  3. Android(安卓)SDCard UnMounted 流程分析
  4. 实现能定点移动的seekbar
  5. 关于Android软键盘弹出的问题
  6. 判断Android系统时间是否是24小时制
  7. Android 个人记账程序源码
  8. Android给View画边框
  9. Android下获取手机屏幕大小
  10. Android获取本机Mac地址和IP地址