美文网首页
zygote android 应用启动之路

zygote android 应用启动之路

作者: MickCaptain | 来源:发表于2019-08-09 22:34 被阅读0次

    android, zygote, systemservice启动流程

    创建应用

    1. 创建zygote socket

    在zygoteInit.java的main方法中通过zygoteServer.runSelectLoop(abiList);监听应用创建的消息

    /**
         * 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.
         */
        Runnable runSelectLoop(String abiList) {
        // fds保存监听socket
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
           // peers 储存监听的session
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    
            fds.add(mServerSocket.getFileDescriptor());
            peers.add(null);
    
            while (true) {
                 pollFds = new StructPollfd[fds.size()];
                for (int i = 0; i < pollFds.length; ++i) {
                    pollFds[i] = new StructPollfd();
                    pollFds[i].fd = fds.get(i);
                    pollFds[i].events = (short) POLLIN;
                }
                // 将session的位置信息储存在StructPollfd[]
                try {
                    Os.poll(pollFds, -1);
                } catch (ErrnoException ex) {
                    throw new RuntimeException("poll failed", ex);
                }
                for (int i = pollFds.length - 1; i >= 0; --i) {
                    if ((pollFds[i].revents & POLLIN) == 0) {
                        continue;
                    }
    // 有消息返回 ℹ=0 应用要创建创建一个session, 将session添加在peers中,将文件新的文件描述符添加到fds中,用于信息的监听
                    if (i == 0) {
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        fds.add(newPeer.getFileDesciptor());
                    } else {
                        try {
                            ZygoteConnection connection = peers.get(i);
                            // 创建应用pid和进程,
                            final Runnable command = connection.processOneCommand(this);
    
                            if (mIsForkChild) {
                                // We're in the child. We should always have a command to run at this
                                // stage if processOneCommand hasn't called "exec".
                                if (command == null) {
                                    throw new IllegalStateException("command == null");
                                }
    
                                return command;
                            } else {
                                // We're in the server - we should never have any commands to run.
                                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(i);
                                    fds.remove(i);
                                }
                            }
                        } catch (Exception e) {
                            if (!mIsForkChild) {
                                // We're in the server so any exception here is one that has taken place
                                // pre-fork while processing commands or reading / writing from the
                                // control socket. Make a loud noise about any such exceptions so that
                                // we know exactly what failed and why.
    
                                Slog.e(TAG, "Exception executing zygote command: ", e);
    
                                // Make sure the socket is closed so that the other end knows immediately
                                // that something has gone wrong and doesn't time out waiting for a
                                // response.
                                ZygoteConnection conn = peers.remove(i);
                                conn.closeSocket();
    
                                fds.remove(i);
                            } else {
                                // We're in the child so any exception caught here has happened post
                                // fork and before we execute ActivityThread.main (or any other main()
                                // method). Log the details of the exception and bring down the process.
                                Log.e(TAG, "Caught post-fork exception in child process.", e);
                                throw e;
                            }
                        }
                    }
                }
            }
        }
    
    4. 创建应用
      /**
         * Reads one start command from the command socket. If successful, a child is forked and a
         * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
         * process. {@code null} is always returned in the parent process (the zygote).
         *
         * If the client closes the socket, an {@code EOF} condition is set, which callers can test
         * for by calling {@code ZygoteConnection.isClosedByPeer}.
         */
        Runnable processOneCommand(ZygoteServer zygoteServer) {
            String args[];
            Arguments parsedArgs = null;
            FileDescriptor[] descriptors;
        // 读取消息
            try {
                args = readArgumentList();
                descriptors = mSocket.getAncillaryFileDescriptors();
            } catch (IOException ex) {
                throw new IllegalStateException("IOException on command socket", ex);
            }
    
            // readArgumentList returns null only when it has reached EOF with no available
            // data to read. This will only happen when the remote socket has disconnected.
            if (args == null) {
                isEof = true;
                return null;
            }
    
            int pid = -1;
            FileDescriptor childPipeFd = null;
            FileDescriptor serverPipeFd = null;
    
            parsedArgs = new Arguments(args);
    
            if (parsedArgs.abiListQuery) {
                handleAbiListQuery();
                return null;
            }
    
            if (parsedArgs.preloadDefault) {
                handlePreload();
                return null;
            }
    
            if (parsedArgs.preloadPackage != null) {
                handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
                        parsedArgs.preloadPackageCacheKey);
                return null;
            }
    
            if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
                throw new ZygoteSecurityException("Client may not specify capabilities: " +
                        "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
                        ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
            }
    
            applyUidSecurityPolicy(parsedArgs, peer);
            applyInvokeWithSecurityPolicy(parsedArgs, peer);
    
            applyDebuggerSystemProperty(parsedArgs);
            applyInvokeWithSystemProperty(parsedArgs);
    
            int[][] rlimits = null;
    
            if (parsedArgs.rlimits != null) {
                rlimits = parsedArgs.rlimits.toArray(intArray2d);
            }
    
            int[] fdsToIgnore = null;
    
            if (parsedArgs.invokeWith != null) {
                try {
                    FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                    childPipeFd = pipeFds[1];
                    serverPipeFd = pipeFds[0];
                    Os.fcntlInt(childPipeFd, F_SETFD, 0);
                    fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
                } catch (ErrnoException errnoEx) {
                    throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
                }
            }
    
            /**
             * In order to avoid leaking descriptors to the Zygote child,
             * the native code must close the two Zygote socket descriptors
             * in the child process before it switches from Zygote-root to
             * the UID and privileges of the application being launched.
             *
             * In order to avoid "bad file descriptor" errors when the
             * two LocalSocket objects are closed, the Posix file
             * descriptors are released via a dup2() call which closes
             * the socket and substitutes an open descriptor to /dev/null.
             */
    
            int [] fdsToClose = { -1, -1 };
    
            FileDescriptor fd = mSocket.getFileDescriptor();
    
            if (fd != null) {
                fdsToClose[0] = fd.getInt$();
            }
    
            fd = zygoteServer.getServerSocketFileDescriptor();
    
            if (fd != null) {
                fdsToClose[1] = fd.getInt$();
            }
    
            fd = null;
        // fork pid 创建新进程
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);
    
            try {
            // 子进程
                if (pid == 0) {
                
                    // in child
                    zygoteServer.setForkChild();
    
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
            // 创建应用
                    return handleChildProc(parsedArgs, descriptors, childPipeFd);
                } 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);
            }
        }
    
    
    5. 启动应用
     /**
         * 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.
         */
        private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
                FileDescriptor pipeFd) {
            /**
             * By the time we get here, the native code has closed the two actual Zygote
             * socket connections, and substituted /dev/null in their place.  The LocalSocket
             * objects still need to be closed properly.
             */
    
            closeSocket();
            if (descriptors != null) {
                try {
                    Os.dup2(descriptors[0], STDIN_FILENO);
                    Os.dup2(descriptors[1], STDOUT_FILENO);
                    Os.dup2(descriptors[2], STDERR_FILENO);
    
                    for (FileDescriptor fd: descriptors) {
                        IoUtils.closeQuietly(fd);
                    }
                } catch (ErrnoException ex) {
                    Log.e(TAG, "Error reopening stdio", ex);
                }
            }
    
            if (parsedArgs.niceName != null) {
                Process.setArgV0(parsedArgs.niceName);
            }
    
            // End of the postFork event.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            if (parsedArgs.invokeWith != null) {
            // 启动应用
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        VMRuntime.getCurrentInstructionSet(),
                        pipeFd, parsedArgs.remainingArgs);
    
                // Should not get here.
                throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
            } else {
            // 与systemservicer 创建类似,见上一章systemservice的创建流程分析
                return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                        null /* classLoader */);
            }
        }
    
        public static void execApplication(String invokeWith, String niceName, int targetSdkVersion, FileDescriptor pipeFd, String[] args) {
            StringBuilder command = new StringBuilder(invokeWith);
            command.append(" /system/bin/app_process /system/bin --application");
            if (niceName != null) {
                command.append(" '--nice-name=").append(niceName).append("'");
            }
    
            command.append(" com.android.internal.os.WrapperInit ");
            command.append(pipeFd != null ? pipeFd.getInt$() : 0);
            command.append(' ');
            command.append(targetSdkVersion);
            Zygote.appendQuotedShellArgs(command, args);
            Zygote.execShell(command.toString());
        }
    
    

    相关文章

      网友评论

          本文标题:zygote android 应用启动之路

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