zoukankan      html  css  js  c++  java
  • Android Zygote进程是如何fork一个APP进程的

    进程创建流程

    • 不管从桌面启动应用还是应用内启动其它应用,如果这个应用所在进程不存在的话,都需要发起进程通过Binder机制告诉system server进程的AMS
    • system server进程的AMS调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求
    • 在zygote进程的ZygoteInit.main方法中,有一个runSelectLoop循环体,通过acceptCommandPeer方法获取链接过来的客户端,再通过runOnce方法去创建进程
    • 新的进程执行handleChildProc方法,最后通过反射调用ActivityThread.main()方法,这样一个新的APP进程就创建完成了

    APP启动第三方应用

    startActivity

    当你在桌面启动一个应用或者在一个APP内启动一个应用,本质都是一样的,因为Android手机桌面其实就是一个APP(这点可参考Android之Activity启动流程源码深入解析),两种方式经过层层调用之后都会走到ActivityStackSupervisor.startSpecificActivityLocked方法,判断如果对方进程不存在,就需要去创建一个进程

    startService

    调用startService启动一个服务,该方法经过层层调用最终会走到ActiveServices.bringUpServiceLocked方法,如果判断对方进程不存在也会去创建一个新进程

    sendBroadcast

    调用sendBroadcast方法去发送一个广播,经过层层调用后走到BroadcastQueue.processNextBroadcast方法,也会判断BroadcastReceiver所在进程不存在就需要去创建进程

    query

    在ContentProvider的处理过程中,ContentResolver.query方法经过层层调用会走到ActivityManagerService.getContentProviderImpl方法,判断ContentProvider所在的进程不存在就会创建新进程

    System Server请求创建进程

    以下叙述中ActivityManagerService同意简称为AMS

    以下源码基于API 24

    上述四种途径最终都会走到如下方法

    AMS.startProcessLocked

    先来看ActivityManagerService的startProcessLocked方法,这是最开始入口

    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
            //......省略代码
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
           //......省略代码
    }

    代码是在太长,我们只看关键的地方,Process.start这个方法开始进行fork,ok那来看看它的内容,代码很长,可以直接看下面关于本段代码总结

    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 abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }
    
    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 abi,
                                  String instructionSet,
                                  String appDataDir,
                                  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);
            }
    
            if (instructionSet != null) {
                argsForZygote.add("--instruction-set=" + instructionSet);
            }
    
            if (appDataDir != null) {
                argsForZygote.add("--app-data-dir=" + appDataDir);
            }
    
            argsForZygote.add(processClass);
    
            if (extraArgs != null) {
                for (String arg : extraArgs) {
                    argsForZygote.add(arg);
                }
            }
    
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

    上面的startViaZygote方法,所做的事情是:把参数最终放到一个列表中,接着调用zygoteSendArgsAndGetResult方法,该方法中的第一个参数是调用了openZygoteSocketIfNeeded(abi)方法,那我们先来看下这方法的内容

    rivate static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {
                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
            }
        }
    
        //......省略代码
    }

    ZygoteState.connect(ZYGOTE_SOCKET)接着看下这方法

        /*与ZygoteInit的server socket建立链接通信*/
        public static ZygoteState connect(String socketAddress) throws IOException {
            DataInputStream zygoteInputStream = null;
            BufferedWriter zygoteWriter = null;
            final LocalSocket zygoteSocket = new LocalSocket();
    
            try {
                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                        LocalSocketAddress.Namespace.RESERVED));
    
                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
    
                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                        zygoteSocket.getOutputStream()), 256);
            } catch (IOException ex) {
                try {
                    zygoteSocket.close();
                } catch (IOException ignore) {
                }
    
                throw ex;
            }
    
            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
    
            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }

    上面的代码其实就是与ZygoteInit类中的ServerSocket建立连接,socket连接起来了,那就可以进行通信了。现在我们在返回到zygoteSendArgsAndGetResult方法

     private static ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        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.
             */
            final BufferedWriter writer = zygoteState.writer;
            final DataInputStream inputStream = zygoteState.inputStream;
    
            writer.write(Integer.toString(args.size()));
            writer.newLine();
    
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                if (arg.indexOf('
    ') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                writer.write(arg);
                writer.newLine();
            }
    
            writer.flush();
    
            // Should there be a timeout on this?
            ProcessStartResult result = new ProcessStartResult();
            result.pid = inputStream.readInt();
            /*pid小于0代表有问题,==0代表是子进程,》0代表是父进程*/
            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            result.usingWrapper = inputStream.readBoolean();
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

    代码也很简单,上面提到过client与server已经建立了socket连接,那这个方法,会把所有的参数通过socket发送到ZygoteInit的ServerSocket,发送完毕后,就等待ServerSocket把结果返回

    Zygote进程处理fork请求

    上一节提到过,client发送的建立socket连接最终会在ZygoteInit中创建一个ZygoteConnection对象,收到client发送的fork请求,会调用ZygoteConnection对象的runOnce方法,因此来看这方法

     boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    
            //.......省略代码
    
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);
    
            //......省略代码
    
        try {
            /*子进程执行pid==0情况,父进程执行else情况*/
            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;
                /*Process中的io流监听的pid等信息都是通过下面的代码发出去的*/
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }
    Zygote.forkAndSpecialize这个方法会调用native方法来fork app进程,fork成功后,子进程就复制了基本上父进程所有的数据等,这在本节开始的时候科普过这个知识,子进程fork出的pid==0,因此if(pid == 0){}else{}这段代码就特别有意思了,pid==0是子进程执行,else是父进程执行,父进程执行的代码我就不贴了,它主要是把fork成功的pid返回给client端,这时候ActivityManagerServicestartProcessLocked就可以继续执行。
    我们还是来看下子进程执行的代码,最终会执行handleChildProc方法
     private void handleChildProc(Arguments parsedArgs,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
            throws ZygoteInit.MethodAndArgsCaller {
        
         //......省略代码
    
         if (parsedArgs.runtimeInit) {
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        pipeFd, parsedArgs.remainingArgs);
            } else {
                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs, null /* classLoader */);
            }
        } else {
          // ......省略代码
        }
    }

    从传递过来的参数可以定位最终调用了RuntimeInit.zygoteInit方法,那就来看下

    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
    
        redirectLogStreams();
    
        commonInit();
        nativeZygoteInit();
    
        applicationInit(targetSdkVersion, argv, classLoader);
    }

    该方法我们只关注applicationInit(targetSdkVersion, argv, classLoader)这个方法

        private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            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, classLoader);
    }
    
    private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        Class<?> cl;
    
        try {
            cl = Class.forName(className, true, classLoader);
        } 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);
    }

    最终我们关注invokeStaticMain这个方法,该方法最终会抛出一个ZygoteInit.MethodAndArgsCaller(m, argv)异常,这个异常会把ActivityThreadmain方法反射出来。
    还记得上一节ZygoteInitmain方法吗

      public static void main(String argv[]){
        try{
              //.....省略代码
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
      }

    main方法最终会把MethodAndArgsCaller异常给捕获到,捕获到后其实最终就是调用ActivityThreadmain方法

    通过抛异常的方式来进行调用,主要目的是把当前线程的堆栈信息给置空

  • 相关阅读:
    CLR
    Cocos2dx 3.12 在AndroidStudio上编译配置
    MFC 调试方法
    列表初始化
    类型转换
    Cocos2d-x 3.4在AndroidStudio上编译配置
    出发 Let's Go
    LumiSoft.Net邮件接收乱码问题解决
    百度地图经纬度转换JS版
    百度经纬度和google经纬度互转
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/10384616.html
Copyright © 2011-2022 走看看