美文网首页
Android应用进程的创建姿势

Android应用进程的创建姿势

作者: 4d3bf4cac28c | 来源:发表于2020-05-29 22:55 被阅读0次

    Android应用进程的创建

    在之前的Android启动流程中,我们最后提到了会通过ActivityManagerServicestartProcess方法来进行应用进程的创建。本篇文章我们就从这里开始着手,来进行相关源码的解析工作。

    ActivityManagerService#startProcess

        //ActivityManagerService#LocalService.java
        public void startProcess(String processName, ApplicationInfo info,
                boolean knownToBeDead, String hostingType, ComponentName hostingName) {
            try {
                //同步操作,避免死锁
                synchronized (ActivityManagerService.this) {
                    //调用startProcessLocked方法, Process的start,最终到ZygoteProcess的attemptUsapSendArgsAndGetResult()
                    // 用来fork一个新的Launcher的进程
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,new HostingRecord(hostingType, hostingName),false /* allowWhileBooting */, false /* isolated */,true /* keepIfLarge */);
                }
            ...
        }
        
        final ProcessRecord startProcessLocked(String processName,
                ApplicationInfo info, boolean knownToBeDead, int intentFlags,
                HostingRecord hostingRecord, boolean allowWhileBooting,
                boolean isolated, boolean keepIfLarge) {
            return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                    hostingRecord, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                    null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                    null /* crashHandler */);
        }
    

    这里最终调用了ProcessList的startProcessLocked方法。这里的ProcessList类的主要作用是用来处理进程。

    ProcessList#startProcessLocked

        //启动进程
        @GuardedBy("mService")
        boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
                boolean disableHiddenApiChecks, boolean mountExtStorageFull,
                String abiOverride) {
            //已经启动,则直接返回
            if (app.pendingStart) {
                return true;
            }
            //启动时间
            long startTime = SystemClock.elapsedRealtime();
            ...
                //设置程序的入口
                final String entryPoint = "android.app.ActivityThread";
                //***重点方法****
                return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                        runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
                        startTime);
           ...
        }
    

    调用了一个重载方法,注意我们这里的一个参数entryPoint,这个是我们的进程启动以后的入口类,当我们fork出进程以后,会调用这个类中的main方法来启进程。

        boolean startProcessLocked(HostingRecord hostingRecord,
                String entryPoint,
                ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
                String seInfo, String requiredAbi, String instructionSet, String invokeWith,
                long startTime) {
                ...
                        //***重点方法***
                        final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
                                entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
                                app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
            ...
        }
    
        private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
                ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
                String seInfo, String requiredAbi, String instructionSet, String invokeWith,
                long startTime) {
                    //*****重点方法*****最终调用的创建进程的方法
                    startResult = Process.start(entryPoint,
                            app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                            app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                            app.info.dataDir, invokeWith, app.info.packageName,
                            new String[] {PROC_START_SEQ_IDENT + app.startSeq});
        }
    

    最后将进程的创建交给Process类来进行处理,通过start方法创建,然后返回了ProcessStartResult启动的结果。

    Process#start

        public static ProcessStartResult start(@NonNull final String processClass,
                                               @Nullable final String niceName,
                                               int uid, int gid, @Nullable int[] gids,
                                               int runtimeFlags,
                                               int mountExternal,
                                               int targetSdkVersion,
                                               @Nullable String seInfo,
                                               @NonNull String abi,
                                               @Nullable String instructionSet,
                                               @Nullable String appDataDir,
                                               @Nullable String invokeWith,
                                               @Nullable String packageName,
                                               @Nullable String[] zygoteArgs) {
            //processClass为"android.app.ActivityThread",表示程序的入口类
            return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                        runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                        abi, instructionSet, appDataDir, invokeWith, packageName,
                        /*useUsapPool=*/ true, zygoteArgs);
        }
    

    调用ZygoteProcess的start方法

    ZygoteProcess#start

        //启动一个新的进程
        public final Process.ProcessStartResult start(@NonNull final String processClass,
                                                      final String niceName,
                                                      int uid, int gid, @Nullable int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      @Nullable String seInfo,
                                                      @NonNull String abi,
                                                      @Nullable String instructionSet,
                                                      @Nullable String appDataDir,
                                                      @Nullable String invokeWith,
                                                      @Nullable String packageName,
                                                      boolean useUsapPool,
                                                      @Nullable String[] zygoteArgs) {
            try {
                //***重点方法****
                return startViaZygote(processClass, niceName, uid, gid, gids,
                        runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                        abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                        packageName, useUsapPool, zygoteArgs);
            }
            ...
        }
    
        private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
                                                          @Nullable final String niceName,
                                                          final int uid, final int gid,
                                                          @Nullable final int[] gids,
                                                          int runtimeFlags, int mountExternal,
                                                          int targetSdkVersion,
                                                          @Nullable String seInfo,
                                                          @NonNull String abi,
                                                          @Nullable String instructionSet,
                                                          @Nullable String appDataDir,
                                                          @Nullable String invokeWith,
                                                          boolean startChildZygote,
                                                          @Nullable String packageName,
                                                          boolean useUsapPool,
                                                          @Nullable String[] extraArgs)
                                                          throws ZygoteStartFailedEx {
            ArrayList<String> argsForZygote = new ArrayList<>();
            //这是一些创建进程时候的参数信息
            ...
            //这个是程序的入口类,设置的是"android.app.ActivityThread"
            argsForZygote.add(processClass);
            ...
            synchronized(mLock) {
                //***重点方法***
                return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                                  useUsapPool,
                                                  argsForZygote);
            }
        }
    
    
    

    openZygoteSocketIfNeeded 会创建一个和Zygote的socket连接。

        //如果初始的Zygote的连接不存在或者未连接。则创建一个Socket连接,并将相关信息封装为ZygoteState
        @GuardedBy("mLock")
        private void attemptConnectionToPrimaryZygote() throws IOException {
            //如果没有连接
            if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
                primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
            }
        }
    

    如果之前没有建立过和Zygote之间的连接,那么会通过connect()方法进行连接

            static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,@Nullable LocalSocketAddress usapSocketAddress)throws IOException {
                DataInputStream zygoteInputStream;
                BufferedWriter zygoteOutputWriter;
                final LocalSocket zygoteSessionSocket = new LocalSocket();
                try {
                    //进行连接
                    zygoteSessionSocket.connect(zygoteSocketAddress);
                    //创建DataInputStream
                    zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
                    //创建BufferedWriter
                    zygoteOutputWriter = new BufferedWriter(new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),Zygote.SOCKET_BUFFER_SIZE);
                ...
                //封装为ZygoteState对象
                return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
                                       zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
                                       getAbiList(zygoteOutputWriter, zygoteInputStream));
            }
    

    所以openZygoteSocketIfNeeded的主要作用是保证和Zygote的socket连接的存在。当连接存在以后就可以通过socket进行消息的传输了。

    ZygoteProcess#zygoteSendArgsAndGetResult

    通过socket连接Zygote,并发送对应的fork进程所需要的信息

        private Process.ProcessStartResult zygoteSendArgsAndGetResult(
                ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args) throws ZygoteStartFailedEx {
            //****重点方法**** 尝试fork子线程
            return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
        }   
        
        private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
                ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
            try {
                //传入的zygoteState为openZygoteSocketIfNeeded(),里面会通过abi来检查是第一个zygote还是第二个
                final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
                final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
                //将参数的信息写给Zygote进程,包括前面的processClass ="android.app.ActivityThread"
                zygoteWriter.write(msgStr);
                //刷数据,全部写入Zygote进程,处于阻塞状态
                zygoteWriter.flush();
                //从socket中得到zygote创建的应用pid,赋值给 ProcessStartResult的对象
                Process.ProcessStartResult result = new Process.ProcessStartResult();
                //从socket中读取创建的进程的pid
                result.pid = zygoteInputStream.readInt();
                result.usingWrapper = zygoteInputStream.readBoolean();
                //如果pid<0,表示创建失败
                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("fork() failed");
                }
                return result;
            } 
            ...
        }
    

    最后会通过socket连接到Zygote进程,将对应的参数发送给Socket的Server端以后,由Server端来进行进程的fork操作,操作完成以后将创建的进程id返回。

    那么Zygote的Server端又是如何创建的呢?

    Zygote启动监听

    这个就涉及了Zygote的启动过程了。这部分我们后续可以详细分析,这里只提一下大体的流程。

    Zygote会先fork出SystemServer进程,然后会进入循环等待,用来接收Socket发来的消息,用来fork出其他应用所需要的进程信息。

        //ZygoteInit.java
        public static void main(String argv[]) {
            ZygoteServer zygoteServer = null;
            Runnable caller;
            try {
                //创建一个ZygoteServer对象,这个对象创建一个socket服务端,能够接收连接并且孵化对应的子进程
                zygoteServer = new ZygoteServer(isPrimaryZygote);
                if (startSystemServer) {
                    //Fork出第一个进程  SystemServer服务所需的进程
                    Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
                    if (r != null) {
                        //启动SystemServer服务
                        r.run();
                        return;
                    }
                }
                //***重点方法***   这里会进入循环等待,用来接收Socket发来的消息,用来fork出其他应用所需要的进程信息。并且返回fork出的进程的启动函数
                caller = zygoteServer.runSelectLoop(abiList);
            ....
            if (caller != null) {
                //调用caller的run方法,启动子进程(run方法会调用子进程的启动程序的main方法,也就是ActivityThread.java的main()方法)
                caller.run();
            }
        }
    

    连接的处理

    我们这里看一下runSelectLoop这个方法如何监听Socket连接以及接收消息的

        Runnable runSelectLoop(String abiList) {
            ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
            //socketFDs[0]是socketServer。
            socketFDs.add(mZygoteSocket.getFileDescriptor());
            peers.add(null);
            //死循环
            while (true) {
                {
                    //这里会进入阻塞,当有pollFDs事件到来的时候,则继续往下执行
                    Os.poll(pollFDs, -1);
                } catch (ErrnoException ex) {
                    throw new RuntimeException("poll failed", ex);
                }
                while (--pollIndex >= 0) {
                    if (pollIndex == 0) {
                        //采用I/O 多路复用机制,index==0表示selcet接收到的是Zygote的socket连接的事件.
                        // 客户端第一次请求服务端,服务端会调用accept方法与客户端建立连接,客户端在zygote以ZygoteConnection对象表示
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        socketFDs.add(newPeer.getFileDescriptor());
                    } else if (pollIndex < usapPoolEventFDIndex) {
                        //当连接以后,能够接收指令,这里根据
                        try {
                            //peers.get(index)取得发送数据客户端的ZygoteConnection对象。这个就是多路复用的机制了
                            ZygoteConnection connection = peers.get(pollIndex);
                            //收到Socket发来的消息,进行fork的创建工作。返回的command是MethodAndArgsCaller类
                            //其run方法,会调用通过socket接收到的启动类的main方法
                            final Runnable command = connection.processOneCommand(this);
                            ....
        }
    

    当启动循环以后,会一直遍历等待,等待接收Socket发来的连接以及消息请求。当获取到对应的客户端的ZygoteConnection对象以后,这里会调用processOneCommand指令来进行处理。

    到这里的话,就可以和我们刚才讲的创建Socket连接关联起来了。

    我们看一下processOneCommand这个方法是如何对发送的相关fork进程的参数来进行处理的。

        Runnable processOneCommand(ZygoteServer zygoteServer) {
            String args[];
            ZygoteArguments parsedArgs = null;
            FileDescriptor[] descriptors;
            try {
                //读取socket传来的参数信息
                args = Zygote.readArgumentList(mSocketReader);
            ...
            parsedArgs = new ZygoteArguments(args);
            fd = null;
            //*****重点方法**** fork一个子进程,得到一个对应的进程pid
            pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                    parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                    parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);
    
            try {
                if (pid == 0) {
                    //当pid=0,说明是fork的子进程
                    zygoteServer.setForkChild();
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
                    //****重点方法*****  处理子进程
                    return handleChildProc(parsedArgs, descriptors, childPipeFd,
                            parsedArgs.mStartChildZygote);
            ...
        }
    

    我们将这个方法分为3个大内容来处理

    1. 将接收到的数据进行解析处理,生成ZygoteArguments对象,这个对象里面包含了我们设置的进程创建以后的入口类(即启动类)
    2. 通过forkAndSpecialize方法fork出一个子进程
    3. 通过handleChildProc方法对fork出的子进程进行处理

    我们分别对上面的3部分进行分析:

    参数的解析

    这里接收的参数,是在我们的socket的client端来进行创建的。

    //ZygoteProcess.java #startViaZygote方法
            ArrayList<String> argsForZygote = new ArrayList<>();
            // --runtime-args, --setuid=, --setgid=,
            // and --setgroups= must go first
            //这是一些创建进程时候的参数信息
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
            argsForZygote.add("--runtime-flags=" + runtimeFlags);
            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
                argsForZygote.add("--mount-external-default");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
                argsForZygote.add("--mount-external-read");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
                argsForZygote.add("--mount-external-write");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
                argsForZygote.add("--mount-external-full");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
                argsForZygote.add("--mount-external-installer");
            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) {
                argsForZygote.add("--mount-external-legacy");
            }
    
            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
            argsForZygote.add(processClass);
            
            //ZygoteProcess.java # zygoteSendArgsAndGetResult方法
            String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
    
    

    这里的参数的样式是"--setuid=1",行与行之间以"\r"、"\n"或者"\r\n"分割,最后一个参数是进程的入口类。在解析的时候,会按照格式进行拆分。

    子进程的创建

    对于子进程的fork,是通过Zygote.forkAndSpecialize来处理的。

        //Zygote.java
        //fork一个子进程,如果这是子节点则返回0;如果这是父进程,则返回子进程的pid;发生异常则返回-1。
        public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
                int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
                int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
                int targetSdkVersion) {
            ...
            //调用native方法,fork出一个子进程。具体的位置在com_android_internal_os_Zygote.cpp
            int pid = nativeForkAndSpecialize(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,fdsToIgnore, startChildZygote, instructionSet, appDataDir);
            ...
            return pid;
        }
    

    这里调用了一个native方法来进行线程的fork操作。由于采用copy on write方式,这里执行一次,会返回两次。

    创建结果的处理

    当通过native进行了子进程的fork操作以后,会返回pid。这里的pid根据具体的值表示的是不同的类型

    • pid=0:表示处于子进程
    • pid>0:表示处于Zygote进程
    • pid<0:表示子进程的创建失败

    这里我们只关心子进程的处理。也就是handleChildProc方法。

        //ZygoteConnection.java
        //进程创建完成后的处理工作,适当的关闭socket,适当的重新打开stdio,返回成功或者失败信息等
        //返回的Runnable是一个封装了创建进程时,socket传进来的程序入口的方法以及对应的参数的类。其run方法会通过反射调用类的main方法
        private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor pipeFd, boolean isZygote) {
     
            //当执行到这的时候,connection已经关闭了关闭socket,用/dev/null替换它们。
            closeSocket();
            ...
                    //这个里面会通过反射创建socket传递的启动程序的入口类(ActivityThread),然后调用其main方法进行启动
                    return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                            parsedArgs.mRemainingArgs, null /* classLoader */);
                }
            }
        }
    

    这个方法在最后会调用childZygoteInit方法,然后返回一个Runnable对象。

        static final Runnable childZygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
            //根据argv获取到对应的运行的相关参数
            RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
            return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
        }
    

    这里根据解析后的参数信息,生成了Arguments对象。

            //RuntimeInit.java
            Arguments(String args[]) throws IllegalArgumentException {
                parseArgs(args);
            }
            
            //进行参数的解析
            private void parseArgs(String args[]) throws IllegalArgumentException {
                int curArg = 0;
                for (; curArg < args.length; curArg++) {
                    String arg = args[curArg];
                    if (arg.equals("--")) {
                        curArg++;
                        break;
                    } else if (!arg.startsWith("--")) {
                        break;
                    }
                }
                //在传递参数的时候,最后一项传递的是程序入口类的信息
                startClass = args[curArg++];
                //参数信息
                startArgs = new String[args.length - curArg];
                System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
            }
        }
    
    

    方法中会根据传入的参数确定了fork的子进程启动时的类startClass以及对应的参数startArgs

         //ZygoteInit.java
        //调用传入的className所对应的类的main方法
        protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) {
             Class<?> cl;
             //反射创建类
             cl = Class.forName(className, true, classLoader);
             Method m;
            //获取类的main方法
              m = cl.getMethod("main", new Class[] { String[].class });
             //封装一个Runnable类,将方法和参数都传递给了该类,其run方法会通过反射调用main方法
             return new MethodAndArgsCaller(m, argv);
        }
    
       static class MethodAndArgsCaller implements Runnable {
            //调用的方法
            private final Method mMethod;
            //方法的参数
            private final String[] mArgs;
    
            public MethodAndArgsCaller(Method method, String[] args) {
                mMethod = method;
                mArgs = args;
            }
    
            public void run() {
                try {
                    mMethod.invoke(null, new Object[] { mArgs });
                } 
                ...
            }
        }
    

    handleChildProc会根据将传入的参数信息,返回子进程启动时所使用的方法通过反射获取到,并放在一个Runnable的run方法中去执行。

    那么这个Runnable的启动是在哪儿呢?

    //ZygoteInit.java
        public static void main(String argv[]) {
            ...
               caller = zygoteServer.runSelectLoop(abiList);
            ...
            if (caller != null) {
                //调用caller的run方法,启动子进程(run方法会调用子进程的启动程序的main方法,也就是ActivityThread.java的main()方法)
                caller.run();
            }
    

    当通过runSelectLoop方法fork完对应的子进程以后,会将这个MethodAndArgsCaller返回并执行。我们一开始传入的ActivityThread的main方法就调用并执行了。

    总结

    1. 在fork子进程之后,直接执行了ActivityThread的main方法来启动的。
    2. 在系统启动时,开启了Zygote的Socket的Server端来监听,当需要创建进程时,直接通过Socket连接,然后传递参数来创建。
    3. fork采用了copy on write方式。
    4. Server端对于连接的处理,采用了I/O 多路复用机制。具体的这个机制,这个机制回头可以延伸一下。

    源码解析项目地址

    本文由 开了肯 发布!

    同步公众号[开了肯]

    相关文章

      网友评论

          本文标题:Android应用进程的创建姿势

          本文链接:https://www.haomeiwen.com/subject/vpjqzhtx.html