美文网首页
4.App进程的启动

4.App进程的启动

作者: 梦想黑客 | 来源:发表于2020-03-07 14:34 被阅读0次

    进程是如何启动的?

    Android中进程的启动是被动的,当其他进程调用其他组件(四大组件)的时候,如果发现该组件所在的进程还没有启动,就会先向Zygote进程请求启动新的进程,再进行后续组件的启动。

    所以进程启动的流程是:App -> AMS -> Zygote -> 新App

    image
    1. App发起进程:当从桌面启动应用,则发起进程便是Launcher所在进程;当从某App内启动远程进程,则发送进程便是该App所在进程。发起进程先通过binder发送消息给system_server进程;
    2. system_server进程:调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求;
    3. zygote进程:在执行ZygoteInit.main()后便进入runSelectLoop()循环体内,当有客户端连接时便会执行ZygoteConnection.runOnce()方法,再经过层层调用后fork出新的应用进程;
    4. 新进程:执行handleChildProc方法,最后调用ActivityThread.main()方法。

    因为第一步发起的途径有很多startActivity、startService,这些后面单独介绍,本章先从system_server进程调用Process.start()方法开始。

    Process.start()流程

    1.Process.start()

     public static ProcessStartResult start(@NonNull final String processClass,
                                               @Nullable final String niceName,
                                               int uid, int gid, @Nullable int[] gids,
                                               ... ... 
                                               ) {
            return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                        runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                        abi, instructionSet, appDataDir, invokeWith, packageName,
                        /*useUsapPool=*/ true, zygoteArgs);
        }
    

    接着会调用到startViaZygote()方法,可知这里要和Zygote通讯了;

        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,
                                                          ... ...)
                                                          throws ZygoteStartFailedEx {
            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");
            }
    
            ... ...
            
            synchronized(mLock) {
                // The USAP pool can not be used if the application will not use the systems graphics
                // driver.  If that driver is requested use the Zygote application start path.
                return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                                  useUsapPool,
                                                  argsForZygote);
            }
        }
    

    该方法主要是准备参数,openZygoteSocketIfNeeded()方法会连接Zygote暴露的本地socket服务,最后调用zygoteSendArgsAndGetResult()将准备好的创建请求参数发出,最终调用attemptZygoteSendArgsAndGetResult()完成发送和读取:

    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
                ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
            try {
                final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
                final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
    
                zygoteWriter.write(msgStr);
                zygoteWriter.flush();
    
                // Always read the entire result from the input stream to avoid leaving
                // bytes in the stream for future process starts to accidentally stumble
                // upon.
                Process.ProcessStartResult result = new Process.ProcessStartResult();
                result.pid = zygoteInputStream.readInt();
                result.usingWrapper = zygoteInputStream.readBoolean();
    
                if (result.pid < 0) {
                    throw new ZygoteStartFailedEx("fork() failed");
                }
    
                return result;
            } catch (IOException ex) {
                zygoteState.close();
                Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                        + ex.toString());
                throw new ZygoteStartFailedEx(ex);
            }
        }
    

    到此Process的工作就做完了,接下来看看Zygote服务端是怎么处理的。

    Zygote创建进程的流程

    我们知道Zygote进程创建后会通过jni调用启动ZygoteInit.java的main()方法,在main()方法里面会做如下事情:
    1.预加载:preloadClasses()和preloadResources()
    2.forkSystemServer,最终会调用SystemServer.java的main()方法;
    3.创建ZygoteService,进入runSelectLoop;

    这里的第三步就循环监听socket请求:

     Runnable runSelectLoop(String abiList) {
        
             //循环
            while (true) {
                
               ... ... 
    
                try {
                    //监听请求,阻塞
                    Os.poll(pollFDs, -1);
                } catch (ErrnoException ex) {
                    throw new RuntimeException("poll failed", ex);
                }
    
                boolean usapPoolFDRead = false;
    
                while (--pollIndex >= 0) {
                    if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                        continue;
                    }
    
                    if (pollIndex == 0) {
                        // Zygote server socket
    
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        socketFDs.add(newPeer.getFileDescriptor());
    
                    } else if (pollIndex < usapPoolEventFDIndex) {
                        // 接收到新的请求
    
                        try {
                            ZygoteConnection connection = peers.get(pollIndex);
                            
                            //请求处理
                            final Runnable command = connection.processOneCommand(this);
    
                        
                            if (mIsForkChild) {
                               //fork出来的子进程处理
    
                                if (command == null) {
                                    throw new IllegalStateException("command == null");
                                }
    
                                return command;
                            } else {
                               //父进程处理
    
                                if (command != null) {
                                    throw new IllegalStateException("command != null");
                                }
    
                                // We don't know whether the remote side of the socket was closed or
                                // not until we attempt to read from it from processOneCommand. This
                                // shows up as a regular POLLIN event in our regular processing loop.
                                if (connection.isClosedByPeer()) {
                                    connection.closeSocket();
                                    peers.remove(pollIndex);
                                    socketFDs.remove(pollIndex);
                                }
                            }
                        } catch (Exception e) {
                            ...
                        } finally {
                       
                            mIsForkChild = false;
                        }
                    } 
                }
    
                
            }
        }
    

    从以上代码可以知道,当有新的请求到达的时候,会创建ZygoteConnection,并调用processOneCommand方法来处理:

    Runnable processOneCommand(ZygoteServer zygoteServer) {
            String args[];
            ZygoteArguments parsedArgs = null;
            FileDescriptor[] descriptors;
    
            try {
                args = Zygote.readArgumentList(mSocketReader);
    
                // TODO (chriswailes): Remove this and add an assert.
                descriptors = mSocket.getAncillaryFileDescriptors();
            } catch (IOException ex) {
                throw new IllegalStateException("IOException on command socket", ex);
            }
    
         
    
            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) {
                    // in child
                    zygoteServer.setForkChild();
    
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
    
                    return handleChildProc(parsedArgs, descriptors, childPipeFd,
                            parsedArgs.mStartChildZygote);
                } else {
                    // In the parent. A pid < 0 indicates a failure and will be handled in
                    // handleParentProc.
                    IoUtils.closeQuietly(childPipeFd);
                    childPipeFd = null;
                    handleParentProc(pid, descriptors, serverPipeFd);
                    return null;
                }
            } finally {
                IoUtils.closeQuietly(childPipeFd);
                IoUtils.closeQuietly(serverPipeFd);
            }
        }
    

    可以看到会调用Zygote.forkAndSpecialize()方法进行fork操作,该方法会调用native方法进行fork,然后返回pid,根据pid分别进入不同的处理:

    • pid == 0: 子进程,调用handleChildProc();
    • pid > 0:父进程,调用handleParentProc();

    我们先看看handleChildProc会做什么?

    private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
              FileDescriptor pipeFd, boolean isZygote) {
          
          closeSocket();
      
          return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                          parsedArgs.mRemainingArgs, null /* classLoader */);
      }
    

    该方法主要就是关闭socket,因为新创建的进程是不需要zygote的服务来,然后调用ZygoteInit.zygoteInit();

        public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
                ClassLoader classLoader) {
            if (RuntimeInit.DEBUG) {
                Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
            RuntimeInit.redirectLogStreams();
            
            RuntimeInit.commonInit();    //环境初始化
            ZygoteInit.nativeZygoteInit();
            return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
        }
    

    这里主要有三个步骤:

    • RuntimeInit.commonInit():做一些通用的初始化;
    • ZygoteInit.nativeZygoteInit():调用native方法开启binder服务;
    • RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader):反射调用ActivityThread.main()方法。

    到这里新的App进程就创建起来了。

    总结

    我们总结一下一个app进程的创建主要的流程:
    1.调用AMS进行组件调用(startActivity、startService);
    2.如果目标组件的进程未启动,则调用Process.start()向Zygote进程请求fork进程;
    3.进程fork成功后会执行ActivityThread的main()方法,之后会调用IActivityManager.attachApplication(IApplicationThread)方法来告诉AMS新的进程已经启动了;
    4.AMS接收到attachApplication请求后,就会继续步骤1的操作,进行组件启动;

    本章只介绍到了1,2步骤,接下来会继续分析进程创建成功之后的操作。

    相关文章

      网友评论

          本文标题:4.App进程的启动

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