首页 > 代码库 > Zygote进程【2】——Zygote的分裂
Zygote进程【2】——Zygote的分裂
欢迎转载,转载请注明:http://blog.csdn.net/zhgxhuaa
在Zygote的诞生一文中init进程是怎样一步步创建Zygote进程的。也了解了Zygote的进程的作用。
Zygote进程的诞生对于整个Java世界能够说有着”开天辟地“的作用。它创建了Java虚拟机,而且生殖了Java世界的核心服务system_server进程。在完毕Java世界的初创工作以后,Zygote并没有死去,它仅仅是临时的沉睡(socket事件阻塞)在那里,一旦有须要(有client请求的到来)。它便立即起来工作。本文接下来就将分析一下Zygote是怎样监听和处理socket事件的。
首先让我们一起来回顾一下Zygote的main方法:
@frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) { try { registerZygoteSocket();//注冊zygote用的socket ...... runSelectLoop();//变成守护进程,接收socket信息进行处理 closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
main()方法首先在registerZygoteSocket中注冊了Zygote的 服务端Socket对象。然后在完毕一系列初创工作后调用runSelectLoop进入到死循环中。等待client事件的到来。到了这里我们不禁会问,Zygote进程作为服务端,那client是谁呢?Zygote接收到client连接以后又是怎样处理的呢?以下我们就带着这两个问题一起来分析。
client请求
@/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) { ...... try { ...... // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, null); ....... }
@/frameworks/base/core/java/android/os/Process.java
/** * Start a new process. * * <p>If processes are enabled, a new process is created and the * static main() function of a <var>processClass</var> is executed there. * The process will continue running after this function returns. * * <p>If processes are not enabled, a new thread in the caller's * process is created and main() of <var>processClass</var> called there. * * <p>The niceName parameter, if not an empty string, is a custom name to * give to the process instead of using processClass. This allows you to * make easily identifyable processes even if you are using the same base * <var>processClass</var> to start them. * * @param processClass The class to use as the process's main entry * point. * @param niceName A more readable name to use for the process. * @param uid The user-id under which the process will run. * @param gid The group-id under which the process will run. * @param gids Additional group-ids associated with the process. * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SE Android information for the new process. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. * @throws RuntimeException on fatal start failure * * {@hide} */ public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } }startViaZygote()方法的实现例如以下:
/** * Starts a new process via the zygote mechanism. * * @param processClass Class name whose static main() to run * @param niceName 'nice' process name to appear in ps * @param uid a POSIX uid that the new process should setuid() to * @param gid a POSIX gid that the new process shuold setgid() to * @param gids null-ok; a list of supplementary group IDs that the * new process should setgroup() to. * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SE Android information for the new process. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { ArrayList<String> argsForZygote = new ArrayList<String>(); // --runtime-init, --setuid=, --setgid=, // and --setgroups= must go first argsForZygote.add("--runtime-init"); argsForZygote.add("--setuid=" + uid); argsForZygote.add("--setgid=" + gid); if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) { argsForZygote.add("--enable-jni-logging"); } if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { argsForZygote.add("--enable-safemode"); } if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { argsForZygote.add("--enable-debugger"); } if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { argsForZygote.add("--enable-checkjni"); } if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { argsForZygote.add("--enable-assert"); } if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) { argsForZygote.add("--mount-external-multiuser"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) { argsForZygote.add("--mount-external-multiuser-all"); } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); //TODO optionally enable debuger //argsForZygote.add("--enable-debugger"); // --setgroups is a comma-separated list if (gids != null && gids.length > 0) { StringBuilder sb = new StringBuilder(); sb.append("--setgroups="); int sz = gids.length; for (int i = 0; i < sz; i++) { if (i != 0) { sb.append(','); } sb.append(gids[i]); } argsForZygote.add(sb.toString()); } if (niceName != null) { argsForZygote.add("--nice-name=" + niceName); } if (seInfo != null) { argsForZygote.add("--seinfo=" + seInfo); } argsForZygote.add(processClass); if (extraArgs != null) { for (String arg : extraArgs) { argsForZygote.add(arg); } } return zygoteSendArgsAndGetResult(argsForZygote); } }startViaZygote的绝大部分代码都在处理传递到Zygote中的參数,与Zygote通信通过zygoteSendArgsAndGetResult()方法完毕:
/** * Sends an argument list to the zygote process, which starts a new child * and returns the child's pid. Please note: the present implementation * replaces newlines in the argument list with spaces. * @param args argument list * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args) throws ZygoteStartFailedEx { openZygoteSocketIfNeeded();//确保和Zygote通信的socket已被打开 try { /** * See com.android.internal.os.ZygoteInit.readArgumentList() * Presently the wire format to the zygote process is: * a) a count of arguments (argc, in essence) * b) a number of newline-separated argument strings equal to count * * After the zygote process reads these it will write the pid of * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ sZygoteWriter.write(Integer.toString(args.size())); sZygoteWriter.newLine(); int sz = args.size(); for (int i = 0; i < sz; i++) {//发送请求參数到Zygote String arg = args.get(i); if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx( "embedded newlines not allowed"); } sZygoteWriter.write(arg); sZygoteWriter.newLine(); } sZygoteWriter.flush(); // Should there be a timeout on this?到这里位置。client请求Zygote创建进程的请求就发送出去了,Zygote会返回进行的pid给client(ActivityMangerService)。因为ActivityMangerService在SystemServer进程中,所以这里即SystemServer进程通过socket向Zygote发送了信息。ProcessStartResult result = new ProcessStartResult(); result.pid = sZygoteInputStream.readInt();//Zygote处理完毕会返回子进程的pid(即要创建的进程) if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } result.usingWrapper = sZygoteInputStream.readBoolean(); return result; } catch (IOException ex) { try { if (sZygoteSocket != null) { sZygoteSocket.close(); } } catch (IOException ex2) { // we're going to fail anyway Log.e(LOG_TAG,"I/O exception on routine close", ex2); } sZygoteSocket = null; throw new ZygoteStartFailedEx(ex); } }
接下来。我们看一下看一下Zygote是怎样处理client请求的。
处理client请求
/** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. * * @throws MethodAndArgsCaller in a child process when a main() should * be executed. */ private static void runSelectLoop() throws MethodAndArgsCaller { <span style="white-space:pre"> </span>...... <span style="white-space:pre"> </span> while (true) {//死循环 ...... if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) {//index==0表示selcet接收到的是Zygote的socket的事件 ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else {//调用ZygoteConnection对象的runOnce方法,ZygoteConnection是在index == 0时被加入到peers的 boolean done; done = peers.get(index).runOnce(); if (done) { peers.remove(index); fds.remove(index); } } } }
每当有请求过来时,Zygote都会调用ZygoteConnection的runOnce()方法处理:
@/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
/** * Reads one start command from the command socket. If successful, * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} * exception is thrown in that child while in the parent process, * the method returns normally. On failure, the child is not * spawned and messages are printed to the log and stderr. Returns * a boolean status value indicating whether an end-of-file on the command * socket has been encountered. * * @return false if command socket should continue to be read from, or * true if an end-of-file has been encountered. * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() * method in child process */ boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; try { args = readArgumentList();//读取client发送过来的參数 descriptors = mSocket.getAncillaryFileDescriptors(); } catch (IOException ex) { Log.w(TAG, "IOException on command socket " + ex.getMessage()); closeSocket(); return true; } if (args == null) { // EOF reached. closeSocket(); return true; } /** the stderr of the most recent request, if avail */ PrintStream newStderr = null; if (descriptors != null && descriptors.length >= 3) { newStderr = new PrintStream( new FileOutputStream(descriptors[2])); } int pid = -1; FileDescriptor childPipeFd = null; FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyCapabilitiesSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext); applyDebuggerSystemProperty(parsedArgs); applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; if (parsedArgs.rlimits != null) { rlimits = parsedArgs.rlimits.toArray(intArray2d); } if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { FileDescriptor[] pipeFds = Libcore.os.pipe(); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; ZygoteInit.setCloseOnExec(serverPipeFd, true); } //fork一个新进程 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName); } catch (IOException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (ErrnoException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); } try { if (pid == 0) {//子进程 // in child IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // should never get here, the child is expected to either // throw ZygoteInit.MethodAndArgsCaller or exec(). return true; } else {//父进程 // in parent...pid of < 0 means failure IoUtils.closeQuietly(childPipeFd); childPipeFd = null; return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); } }
Zygote在处理client请求时会fork一个新的进程,接下来首先看一下handleChildProc()方法:
/** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. * @param pipeFd null-ok; pipe for communication back to Zygote. * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * * @throws ZygoteInit.MethodAndArgsCaller on success to * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { closeSocket();//关闭子进程中,从Zygote fork过来的服务端socket ZygoteInit.closeServerSocket(); ..... if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); } if (parsedArgs.runtimeInit) {//从startViaZygote可知传入了--runtime-init參数,所以这里为true if (parsedArgs.invokeWith != null) {//没有传入--invoke-with。所以这里走的是else的逻辑 WrapperInit.execApplication(parsedArgs.invokeWith, parsedArgs.niceName, parsedArgs.targetSdkVersion, pipeFd, parsedArgs.remainingArgs); } else { RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs); } } else { ...... } }zygoteInit()方法的实如今RuntimeInit类中:
@/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
/** * The main function called when started through the zygote process. This * could be unified with main(), if the native code in nativeFinishInit() * were rationalized with Zygote startup.<p> * * Current recognized args: * <ul> * <li> <code> [--] <start class name> <args> * </ul> * * @param targetSdkVersion target SDK version * @param argv arg strings */ public static final void zygoteInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); redirectLogStreams();//将System.out 和 System.err 输出重定向到Android 的Log系统 /* * 初始化了一些系统属性,当中最重要的一点就是设置了一个未捕捉异常的handler。 * 当代码有不论什么未知异常,就会运行它, * 调试过Android代码的同学常常看到的"*** FATAL EXCEPTION IN SYSTEM PROCESS" 打印就出自这里 */ commonInit(); /* * 终于会调用app_main的onZygoteInit函数 * 这里的作用是在新进程中引入Binder。也就说通过nativeZygoteInit以后,新的进程就能够使用Binder进程通信了 */ nativeZygoteInit(); applicationInit(targetSdkVersion, argv);//应用初始化 }接下来继续分析applicationInit(),走完进程启动的整个过程。然后再回头分析一下commonInit和nativeZygoteInit的实现。
applicationInit的实现例如以下:
private static void applicationInit(int targetSdkVersion, String[] argv) throws ZygoteInit.MethodAndArgsCaller { // If the application calls System.exit(), terminate the process // immediately without running any shutdown hooks. It is not possible to // shutdown an Android application gracefully. Among other things, the // Android runtime shutdown hooks close the Binder driver, which can cause // leftover running threads to crash before the process actually exits. nativeSetExitWithoutCleanup(true); // We want to be fairly aggressive about heap utilization, to avoid // holding on to a lot of memory that isn't needed. VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args; try { args = new Arguments(argv); } catch (IllegalArgumentException ex) { Slog.e(TAG, ex.getMessage()); // let the process exit return; } // Remaining arguments are passed to the start class's static main invokeStaticMain(args.startClass, args.startArgs); }在applicationInit()的最后,会通过调用invokeStaticMain来调用args.startClass这个类的main()方法。
在前面介绍socket的client代码时,在startProcessLocked()中传入的这个类为"android.app.ActivityThread"。
所以接下来invokeStaticMain()的功能相信大家都已经知道了,就是调用ActivityThread类的main(),以下是代码:
/** * Invokes a static "main(argv[]) method on class "className". * Converts various failing exceptions into RuntimeExceptions, with * the assumption that they will then cause the VM instance to exit. * * @param className Fully-qualified class name * @param argv Argument vector for main() */ private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { Class<?这种方法本身功能就是调用ActivityThread类的main(),没什么可说的。> cl; try { cl = Class.forName(className); } catch (ClassNotFoundException ex) { throw new RuntimeException( "Missing class when invoking static main " + className, ex); } Method m; try { m = cl.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException ex) { throw new RuntimeException( "Missing static main on " + className, ex); } catch (SecurityException ex) { throw new RuntimeException( "Problem getting static main on " + className, ex); } int modifiers = m.getModifiers(); if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) { throw new RuntimeException( "Main method is not public and static on " + className); } /* * 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); }
只是须要注意一下的是这里的调用方式十分特别,并没有採取常规的反射调用,而是通过抛出异常的方式调用ActivityThread的main()函数。
这里抛出的ZygoteInit.MethodAndArgsCaller异常会在ZygoteInit.main()中被捕获处理。
public static void main(String argv[]) { try { ...... } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { ...... } }这里须要注意的是:捕获异常是在子进程(即新的进程,不是Zygote进程)中的动作。还记得前面介绍的runOnce()方法吗?我们在runOnce中创建了一个新的进程。假设读者还有不明确这里为什么是在子进程,能够自行学习Linux fork()的原理。好了,继续..... 看一下MethodAndArgsCaller的代码:
@/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
/** * Helper exception class which holds a method and arguments and * can call them. This is used as part of a trampoline to get rid of * the initial process setup stack frames. */ public static class MethodAndArgsCaller extends Exception implements Runnable { /** method to call */ private final Method mMethod; /** argument array */ private final String[] mArgs; public MethodAndArgsCaller(Method method, String[] args) { mMethod = method; mArgs = args; } public void run() { try { mMethod.invoke(null, new Object[] { mArgs }); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { Throwable cause = ex.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } throw new RuntimeException(ex); } } }能够看出最后还是会调用invoke方法通过反射的方式调用ActivityThread的main方法。
到了这里,相信大家都会有一个疑问:既然最后还是通过invoke来反射调用main方法,那绕这一大圈子究竟在折腾什么?
有疑问的读者。有没有去思考过另外一个问题:我们为什么要通过Zygote去创建进程,而不是直接创建一个新的进程出来呢?这就要从Zygote创建进程的机制来解释。相信我们还记得在ZygoteInit的main函数中我们通过preload来预载入类和资源。所以这些被预载入的类和资源都存在于Zygote进程中。在通过Zygote创建进程时,我们是通过fork来创建的。 一个进程调用fork()函数后。系统先给新的进程分配资源,比如存储数据和代码的空间。
然后把原来的进程的全部值都拷贝到新的新进程中,仅仅有少数值与原来的进程的值不同。相当于克隆了一个自己。所以,Zygote通过fork的方式创建新的应用进程的同一时候。会将对系统的(主要是framework中的)一些类和资源的引用同一时候复制给子进程。这样子进程中就能够使用这些资源了。这也是为什么全部的应用程序能够共享Framework中的类和资源的原因。
在明确了Zygote机制后,相应为什么这里会以抛出异常的方式调用ActivityThread(应用程序入口)的main()方法,就变得easy理解了。在Java中。我们进行一些列的方法调用的时候。系统会为我们在内存中维护一个调用栈。栈底是发起调用的方法,栈顶是当前正在被调用的方法。
在栈顶的方法调用完毕后,会逐级返回调用它的方法。最后调用会回到最開始的方法中,然后调用栈就被销毁了。在Zygote处理client事件的时候。沿着各种方法一路调用下来,假设在调用ActivityThread中的main()时,直接使用invoke。那么在invoke结束后,整个调用过程会递归返回,最后发起方得到调用结果,调用栈被销毁。
然后每次的调用都会反复刚才的那个过程,这就造成了极大的浪费。採用异常的方式,能够保证调用栈不会被销毁。这样每次fork时就能够共享。
好了。到此为止Zygote的分裂过程就介绍完毕了。
Zygote进程【2】——Zygote的分裂