Zygote

Posted ayanwan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zygote相关的知识,希望对你有一定的参考价值。

相关源码:

/frameworks/base/cmds/app_process/App_main.cpp (内含AppRuntime类)
/frameworks/base/core/jni/androidRuntime.cpp
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
/frameworks/base/core/java/com/android/internal/os/Zygote.java
/frameworks/base/core/java/android/net/LocalServerSocket.java

         

         在Android中,zygote是整个系统创建新进程的核心进程。zygote进程在内部会先启动Dalvik虚拟机,继而加载一些必要的系统资源和系统类,最后进入一种监听状态。

         在之后的运作中,当其他系统模块(比如AMS)希望创建新进程时,只需向zygote进程发出请求,zygote进程监听到该请求后,会相应地fork出新的进程,于是这个新进程在初生之时,就先天具有了自己的Dalvik虚拟机以及系统资源。

         zygote进程是由init进程启动起来,由init.rc 脚本中关于zygote的描述可知:zygote对应的可执行文件就是/system/bin/app_process,也就是说系统启动时会执行到这个可执行文件的main()函数里。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

【解析】

zygote作为一个名字标识了这个service(可执行程序);

/system/bin/app_process表示可执行文件的位置;

class、user、group、onrestart这些关键字所对应的行都被称为options, options是用来描述的service一些特点,不同的service有着不同的options。

第三行表示在Zygote启动过程中,要在其内部创建一个名为zygote的socket,它在Linux下的权限是666,即所有用户多可以对它进行读写。

       下面看一个Zygote的启动流程图:


一、Zygote启动函数调用类的栈关系:
App_main.main
      AndroidRuntime.start
           startVm
           startReg
           ZygoteInit.main
                 registerZygoteSocket
                 preload
                 startSystemServer
                 runSelectLoop

       zygote服务的main()函数位于frameworks\\base\\cmds\\app_process\\App_main.cpp。关键代码如下:

int main(int argc, char* const argv[])

    . . . . . .
    AppRuntime runtime;
    const char* argv0 = argv[0];    // -Xzygote
    argc--;
    argv++;
    . . . . . .
    int i = runtime.addVmArguments(argc, argv);
    . . . . . .
    while (i < argc) 
        const char* arg = argv[i++];		// 应该是/system/bin目录
        if (!parentDir) 
            parentDir = arg;
         else if (strcmp(arg, "--zygote") == 0) 
            zygote = true;
            niceName = "zygote";
         else if (strcmp(arg, "--start-system-server") == 0) 
            startSystemServer = true;
         
        . . . . . .
    

    if (niceName && *niceName) 
        setArgv0(argv0, niceName);
        set_process_name(niceName);     // 一般改名为“zygote”
    
    runtime.mParentDir = parentDir;
    if (zygote) 
      runtime.start("com.android.internal.os.ZygoteInit", args);
    else if (className) 
      runtime.start("com.android.internal.os.RuntimeInit", args);
    else 
      fprintf(stderr, "Error: no class name or --zygote supplied.\\n");
      app_usage();
      LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
      return 10;
   

   

       main()函数里先构造了一个AppRuntime对象,即AppRuntime runtime;而后把进程名改成“zygote”,并利用runtime对象,把工作转交给java层的相应的***Init类处理。

      根据传入参数的不同可以有两种启动方式,一个是 "com.android.internal.os.RuntimeInit", 另一个是 ”com.android.internal.os.ZygoteInit", 对应RuntimeInit 和 ZygoteInit 两个类, 这两个类的主要区别在于Java端,可以明显看出,ZygoteInit 相比 RuntimeInit 多做了很多事情,比如说 “preload", "gc" 等等。但是在Native端,他们都做了相同的事, startVM() 和 startReg() 

简单地说就是:Zygote进程的main()函数在启动Dalvik虚拟机后,会调用另一个ZygoteInit类的main()静态函数。

1、 zygote在native层的调用示意图如下:

2、 zygote在java层的调用主要是由ZygoteInit.java完成的,调用示意图如下:



   ZygoteInit.java关键代码如下(frameworks/base/core/java/com/android/internal/os/ZygoteInit.java)

public class ZygoteInit 
	......

	public static void main(String argv[]) 
		try 
			......

			registerZygoteSocket();
			
			......

			......

			if (argv[1].equals("true")) 
				startSystemServer();
			 else if (!argv[1].equals("false")) 
				......
			

			......

			if (ZYGOTE_FORK_MODE) 
				......
			 else 
				runSelectLoopMode();
			

			......
		 catch (MethodAndArgsCaller caller) 
			......
		 catch (RuntimeException ex) 
			......
		
	

	......

主要做了四件事情:

(1)调用registerZygoteSocket函数创建了一个socket接口,用来和ActivityManagerService等通讯;

(2)预加载一些类与资源;

(3)调用startSystemServer函数来启动SystemServer组件;

         startSystemServer()并不是在函数体内直接调用Java类的main()函数的,而是通过抛异常的方式,在startSystemServer()之外加以处理的。

【为什么要以异常方式启动】

(a)首先、我们要先清楚,抛异常这一操作会引发什么? 

       我们知道,当一个函数抛出异常后,这个异常会依次传递给调用它的函数,知道这个异常被捕获,如果这个异常一直没有被处理,最终就会引起程序的崩溃。

(b)其次、在传递异常的时候,应用程序的栈发生了什么变化? 

        这就要牵涉到函数的执行模型了,我们知道,程序都是有一个个函数组成的(除了汇编程序),c/c++/java/..等高级语言编写的应用程序,在执行的时候,他们都拥有自己的栈空间(是一种先进后出的内存区域),用于存放函数的返回地址和函数的临时数据,每调用一个函数时,就会把函数的返回地址和相关数据压入栈中,当一个函数执行完后,就会从栈中弹出,cpu会根据函数的返回地址,执行上一个调用函数的下一条指令。 

        所以,在抛出异常后,如果异常没有在当前的函数中捕获,那么当前的函数执行就会异常的退出,从应用程序的栈弹出,并将这个异常传递给上一个函数,直到异常被捕获处理,否则,就会引起程序的崩溃。

       因此,这里通过抛异常的方式启动主要是清理应用程序栈中ZygoteInit.main以上的函数栈帧,以实现当相应的main函数退出时,能直接退出整个应用程序。 当当前的main退出后,就会退回到MethodAndArgsCaller.run而这个函数直接就退回到ZygoteInit.main函数,而ZygoteInit.main也无其他的操作,直接退出了函数,这样整个应用程序将会完全退出。

(4)调用runSelectLoopMode函数进入一个无限循环在前面创建的socket接口上等待ActivityManagerService等的请求。


2.1 startSystemServer()

       其并不是在函数体内直接调用Java类的main()函数的,而是通过抛异常的方式,在startSystemServer()之外加以处理的。

private static boolean startSystemServer()
        throws MethodAndArgsCaller, RuntimeException 

    . . . . . .
    /* 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,1021,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);

        // fork出系统服务对应的进程
        pid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,
                                            parsedArgs.gids, parsedArgs.debugFlags, null,
                                            parsedArgs.permittedCapabilities,
                                            parsedArgs.effectiveCapabilities);
     catch (IllegalArgumentException ex) 
        throw new RuntimeException(ex);
    

    // 对新fork出的系统进程,执行handleSystemServerProcess()
    if (pid == 0) 
        handleSystemServerProcess(parsedArgs);
    
    return true;
其中:

(1)Zygote.forkSystemServer()会通过jni调用linux的fork函数;

(2)startSystemServer()会在新fork出的子进程中调用handleSystemServerProgress(),进而抛出异常MethodAndArgsCaller,通过caller.run()启动com.android.server.SystemServer的main 方法。

可参考:Systemserver


2.2、runSelectLoop()

private static void runSelectLoop() throws MethodAndArgsCaller 

    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    FileDescriptor[] fdArray = new FileDescriptor[4];

    fds.add(sServerSocket.getFileDescriptor());
    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);
         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);
            
        
    

2.2.1、在一个while循环中,不断调用selectReadable()。

selectReadable函数是个native函数,主要就是调用select()而已。在Linux的socket编程中,select()负责监视若干文件描述符的变化情况。内部调用select等待客户端 的连接,客户端连接上之后就会返回。
返回值:
<0: 内部发生错误
=0: 该客户端第一次连接到服务端。服务端调用accept与客户 端建立连接。客户端在zygote中以ZygoteConnection对象表示。
>0: 客户端与服务端已经建立连接,并开始发送数据。表明发送数据的客户端的index,peers.get(index)取得 发送数据客户端的ZygoteConnection对象,之后调用runOnce 函数处理具体的请求。

2.2.2、runOnce()

boolean runOnce( ) 
       Arguments parsedArgs = null;
       FileDescriptor[] descriptors;

       //Reads one start command from the command socket.
       args = readArgumentList();
       descriptors = mSocket.getAncillaryFileDescriptors();

       //创建/Forks a new VM instance /process.
       //使用Jni 调用nativeFork
       pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
              parsedArgs.gids, parsedArgs.debugFlags, rlimits);

       //返回两次
       if (pid == 0) 
              // in child    
              serverPipeFd = null;
              handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

              // should never get here, the child is expected to either
              return true;
        else 

              // in parent...pid of < 0 means failure
              childPipeFd = null;
              return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
       
        从上面的代码中可以看到创建进程之后返回:
子进程:handleChildProc
父进程:handleParentProc
        我们关心的是子进程的执行,继续到handleChildProc中。

// Handles post-fork setup of child proc
private void handleChildProc(Arguments parsedArgs,...)
       ……
       if (parsedArgs.runtimeInit) 
           if (parsedArgs.invokeWith != null) 
        //通过系统调用执行进程
        WrapperInit.execApplication(parsedArgs.invokeWith,
          parsedArgs.niceName, parsedArgs.targetSdkVersion,
          pipeFd, parsedArgs.remainingArgs);

            else 
        //通过寻找到相应目标类的main()函数并执行
        RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
          parsedArgs.remainingArgs);
           
       
       ……
看到子进程的执行有两种方式:
  WrapperInit.execApplication和RuntimeInit.zygoteInit

1)通过系统调用的方式执行进程 WrapperInit.execApplication:

public static void execApplication(……) 
  ……
  Zygote.execShell(command.toString());


public static void execShell(String command) 
  // using the exec() system call
  nativeExecShell(command);
2)通过寻找到相应目标类的main()函数并执行 RuntimeInit.zygoteInit:
// The main function called when started through the zygote process.
public static final void zygoteInit( )
zygoteInitNative();
applicationInit(targetSdkVersion, argv);


private static void applicationInit( ) 
// Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs);

通过RuntimeInit调用startClass的main函数,进而以异常的方式启动新的进程。

 static void invokeStaticMain(ClassLoader loader,
            String className, String[] argv)
            throws ZygoteInit.MethodAndArgsCaller 
    ....
        /*
         * This throw gets caught in ZygoteInit.main(), which responds
         * by invoking the exception's run() method. This arrangement
         * clears up all the stack frames that were required in setting
         * up the process.
         */
        throw new ZygoteInit.MethodAndArgsCaller(m, argv);
    





以上是关于Zygote的主要内容,如果未能解决你的问题,请参考以下文章

Android Zygote进程

Android4.4的zygote进程(上)

Android4.4的zygote进程(上)

对android中Zygote的理解

对android中Zygote的理解

了解 Android:Zygote 和 DalvikVM