分析Android 9.0的系统启动

作者: 聪葱忙忘 | 来源:发表于2020-05-13 08:58 被阅读0次

    系统启动大致流程

    流程这个我用一段文字来大概概括,因为流程并不是重点,一方面是大部分blog都是在讲流程我啰嗦了也没意思,其次是流程又长又臭一般记不住。还是研究点有意思的吧。
    大概流程如下

    1. system/core/init/init.cpp 里面解析system/core/rootdir/init.rc
    2. 以64位处理器为例,在被拆分放在system/core/rootdir/init.zygote64.rc里面有启动zygote的配置
    3. sytem/core/init/service.cpp 的start函数会创建zygote的子进程并调用其main函数
    4. 对应就是frameworks/base/cmds/app_process/app_main.cpp的main函数中执行了runtime.start(...)
    5. frameworks/base/core/jni/AndroidRuntime.cpp中启动java虚拟机和注册JNI方法打开了Java新世界的大门
      ------------------Java新世界的大门分割线--------------------
    6. 再通过JNI调用ZygoteInit的main方法
    7. main方法在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java里面,执行registerServerSocketFromEnv,注册socket
    8. main方法接着通过forkSystemServer启动SystemServer
    9. main方法接着执行runSelectLoop等待AMS的请求。
      SystemServer的main函数会启动一些重要的系统服务如AMS,和启动Launcher。

    大致流程就是上面这样,看过很多文章看完后,得到的知识跟上面的差不多,挺枯燥的没什么意思(没卵用)。

    了解registerServerSocketFromEnv

    void registerServerSocketFromEnv(String socketName) {
            if (mServerSocket == null) {
                int fileDesc;
                final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
                try {
                    String env = System.getenv(fullSocketName);
                    fileDesc = Integer.parseInt(env);
                } catch (RuntimeException ex) {
                    throw new RuntimeException(fullSocketName + " unset or invalid", ex);
                }
    
                try {
                    FileDescriptor fd = new FileDescriptor();
                    fd.setInt$(fileDesc);
                    //通过了fileDesc的唯一来保证了mServerSocked的唯一
                    mServerSocket = new LocalServerSocket(fd);
                    mCloseSocketFd = true;
                } catch (IOException ex) {
                    throw new RuntimeException(
                            "Error binding to local socket '" + fileDesc + "'", ex);
                }
            }
        }
    

    这里主要是通过fd参数实例化了一个LocalServerSocket。

    public LocalServerSocket(FileDescriptor fd) throws IOException
        {
            impl = new LocalSocketImpl(fd);
            impl.listen(LISTEN_BACKLOG);
            localAddress = impl.getSockAddress();
        }
    

    LocalServerSocket用了代理模式,实际上是由LocalSocketImpl来实现的。

    class LocalSocketImpl
    {
        private SocketInputStream fis;
        private SocketOutputStream fos;
        private Object readMonitor = new Object();
        private Object writeMonitor = new Object();
    
        /** null if closed or not yet created */
        private FileDescriptor fd;
        /** whether fd is created internally */
        private boolean mFdCreatedInternally;
    
        // These fields are accessed by native code;
        /** file descriptor array received during a previous read */
        FileDescriptor[] inboundFileDescriptors;
        /** file descriptor array that should be written during next write */
        FileDescriptor[] outboundFileDescriptors;
    
    

    看的出具体的socket的读写都封装在LocalSocketImpl了。

    了解runSelectLoop

    Runnable runSelectLoop(String abiList) {
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
            //实际上这里mServerSocket.getFileDescriptor()就是上面的fd,只有一个
            fds.add(mServerSocket.getFileDescriptor());
            peers.add(null); //很巧妙的在初始化的时候add了一个null
    
            while (true) {
                StructPollfd[] 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;
                }
                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;
                    }
    
                    if (i == 0) {
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        fds.add(newPeer.getFileDesciptor());
                    } else {
                        try {
                            ZygoteConnection connection = peers.get(i);
                            final Runnable command = connection.processOneCommand(this);
    

    开始pollFds的长度为1,所以如果有客户端请求连接,会来到i==0的逻辑,实例化好ZygoteConnection 添加到peers,所以当继续有信息过来即可认为是i==1的情况,此时peer.get(1)就是在i==0的时候实例好的ZygoteConnection 。然后接着执行processOneCommand,之前的版本叫runOnce,其实一样是解析socket传过来的参数来执行对应的操作。

    了解processOneCommand

    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);
            }
    

    先是通过String数组存放readAgumentList解析从socket传过来的信息

       private String[] readArgumentList()
                throws IOException {
            int argc;
    
            try {
                //第一行存放的参数数量
                String s = mSocketReader.readLine();
    
                if (s == null) {
                    // EOF reached.
                    return null;
                }
                //再把数量string转成integer类型
                argc = Integer.parseInt(s);
            } catch (NumberFormatException ex) {
                Log.e(TAG, "invalid Zygote wire format: non-int at argc");
                throw new IOException("invalid wire format");
            }
    
            // See bug 1092107: large argc can be used for a DOS attack
          //  通过判断其数量大小判断是不是dos攻击,防止被dos攻击
            if (argc > MAX_ZYGOTE_ARGC) {
                throw new IOException("max arg count exceeded");
            }
            //最后遍历参数,存放再result 的String数组
            String[] result = new String[argc];
            for (int i = 0; i < argc; i++) {
                result[i] = mSocketReader.readLine();
                if (result[i] == null) {
                    // We got an unexpected EOF.
                    throw new IOException("truncated request");
                }
            }
    
            return result;
    }
    

    在封装在变量中parsedArgs = new Arguments(args);
    最后执行forkAndSpecialize,参数都是来自parsedArgs的,有个返回值pid

    pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
                    parsedArgs.instructionSet, parsedArgs.appDataDir);
    
     if (pid == 0) {
                    // in child
                    zygoteServer.setForkChild();
    
                    zygoteServer.closeServerSocket();
                    IoUtils.closeQuietly(serverPipeFd);
                    serverPipeFd = null;
    
                    return handleChildProc(parsedArgs, descriptors, childPipeFd,
                            parsedArgs.startChildZygote);
    

    当返回值pid 为0代表fork的是zygote的子进程,通知zygoteServer更改一些状态和关掉socket,因为任务完成了,接着执行handleChildProc,这里参数上继续传了parsedArgs等参数。
    来到

      if (!isZygote) {
                    return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
                            null /* classLoader */);
                } else {
                    return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
                            parsedArgs.remainingArgs, null /* classLoader */);
                }
    

    会执行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();// zygote初始化 
            return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);// 应用初始化
        }
    

    我们接着跟踪argv的参数传过去的地方看看applicationInit

      //args 存放了应用的startClass 和startArgs
            return findStaticMain(args.startClass, args.startArgs, classLoader);
    
     //通过反射来获得startClass的main方法
            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);
            }
            return new MethodAndArgsCaller(m, argv);
    

    最后return这一步以前是通过throw操作来回到ZygoteInit的main函数,为了能清空栈,新版本是通过一堆的runnable来衔接,最后在ZygoteInit执行caller.run来实现。
    关于MethodAndArgsCaller最后里面的run。

     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);
                }
            }
        }
    

    到底执行了哪个类的哪个方法?这个肯定要从arg里面的信息才知道,arg归根到底是从发送方通过socket传过来的,那么问题来了发送方是谁?发了什么?
    弄清楚这个之前先来看一下上面还没有说的forkSystemServer。

    了解forkSystemServer

    这个本质上也是zegote的fork子进程的,虽然跟fork应用的子进程有点不一样,但其实差不多。
    跟fork应用不同的是,因为这个forkSystemServer方法的调用是在zegoteinit的main函数上的,就没必要再通过socket来通知zegote fork子进程了。

      /* Hardcoded command line to start the system server */
            String args[] = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",
            };
            ZygoteConnection.Arguments parsedArgs = null;
    
            int pid;
    
            try {
                parsedArgs = new ZygoteConnection.Arguments(args);
    ...
    

    上面的代码看出来,还是使用了 ZygoteConnection.Arguments来包装参数,证明这两种fork的传参方式是一样的。

    static jint com_android_internal_os_Zygote_nativeForkSystemServer(
            JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
            jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
            jlong effectiveCapabilities) {
      pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
                                          runtime_flags, rlimits,
                                          permittedCapabilities, effectiveCapabilities,
                                          MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
                                          NULL, false, NULL, NULL);
    

    在JNI调用里面看的出实际上也调用跟fork应用子进程一样的ForkAndSpecializeCommon的方法。显然fork子进程的方式也一样。
    现在再看看包装和解析参数的过程。

     private void parseArgs(String args[])
                    throws IllegalArgumentException {
                int curArg = 0;
    
                boolean seenRuntimeArgs = false;
    
                boolean expectRuntimeArgs = true;
                if (arg.equals("--")) {
                        curArg++;
                        break;
                    } else if (arg.startsWith("--setuid=")) {
                        if (uidSpecified) {
                            throw new IllegalArgumentException(
                                    "Duplicate arg specified");
                        }
                        uidSpecified = true;
                        uid = Integer.parseInt(
                                arg.substring(arg.indexOf('=') + 1));
                    } else if (arg.startsWith("--setgid=")) {
                        if (gidSpecified) {
                            throw new IllegalArgumentException(
                                    "Duplicate arg specified");
                        }
                        gidSpecified = true;
                        gid = Integer.parseInt(
                                arg.substring(arg.indexOf('=') + 1));
                    }
    ...
    //剩下非选项类参数,即不是--开头的参数,用remainingArgs存起来。
                else if (expectRuntimeArgs) {
                    if (!seenRuntimeArgs) {
                        throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
                    }
    
                    remainingArgs = new String[args.length - curArg];
                    System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
                }
    

    forkSystemServer的过程的参数里非“--.."开头的只有"com.android.server.SystemServer",所以remainingArgs存放的就是这个字符串了,也就是类名。
    包装起来的parsedArgs在子进程systemserver创建成功后会接着传入handleSystemServerProcess执行

    /* For child process */
            if (pid == 0) {
                if (hasSecondZygote(abiList)) {
                    waitForSecondaryZygote(socketName);
                }
    
                zygoteServer.closeServerSocket();
                return handleSystemServerProcess(parsedArgs);
            }
    

    在handleSystemServerPorcess中最后执行这段代码

    if (systemServerClasspath != null) {
                    cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
    
                    Thread.currentThread().setContextClassLoader(cl);
                }
    
                /*
                 * Pass the remaining arguments to SystemServer.
                 */
                return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    

    显然这里传的是remainingArgs,对于systemserver而言就是只有一个类名的String数组。
    接着执行

    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();// zygote初始化
     //argv包含的是类名和其他参数
            return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);// 应用初始化
        }
    

    applicationInit里面通过Arguments把argv包装,然后执行

     final Arguments args = new Arguments(argv);
    
            // The end of of the RuntimeInit event (see #zygoteInit).
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    
            // Remaining arguments are passed to the start class's static main
            //选项的参数都是--开头的,第一个非选项的参数就类名,然后startArgs就是类名之后的参数
            //args 存放了应用的startClass 和startArgs
            return findStaticMain(args.startClass, args.startArgs, classLoader);
    

    看一下Arguments 的代码

     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;
                    }
                }
    
                if (curArg == args.length) {
                    throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
                }
                 //这里curArg的位置就是非选项类的第一个,即类名,curArg ++ 指向类名之后的参数位置
                startClass = args[curArg++];
    //把类名之后的参数复制到startArgs的数组中
                startArgs = new String[args.length - curArg];
                System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
            }
        }
    

    经过解析后得到的startClass 就是类名,startArgs就是类名之后的参数。

    到这里我们就知道最后执行了com.android.server.SystemServer的main函数。

    相关文章

      网友评论

        本文标题:分析Android 9.0的系统启动

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