美文网首页
安卓手机启动时发生的那些事儿——下篇

安卓手机启动时发生的那些事儿——下篇

作者: 邱穆 | 来源:发表于2020-11-08 16:59 被阅读0次

    上篇文章我们谈到进入安卓系统的启动流程,大致梳理了从Native到Framework的流程,到达Framework阶段后,到应用程序启动,这之间仍然有一些工作需要处理,这次,我们就一起学习这部分的工作。

    一、概览

    上篇文章学习到了Zygote进程,但并未有具体分析Zygote进程的工作,本篇文章,我们就一起学习Zygote进程的具体工作,以及第一个App——Launcher的启动过程。

    二、Zygote进程的工作

    ZygoteInit的main方法方法大概就做了六件事情:

    1. 创建 ZygoteServer,Android O 上把与 Socket 的操作都封装到了ZygoteServer 类中
    2. 解析app_main.cpp传来的参数 。
    3. 创建一个 Server 端的 Socket,作用是当 Zygote 进程将SystemServer 进程启动后,就会在这个 Socket 上来等待ActivityManagerService 请求,即请求创建我们自己 APP 应用程序进程
    4. 预加载类和资源,包括颜色、R文件、drawable 、 类等
    5. 启动 system_server 进程 这是上层 framework 的运行载体,ActivityManagerService 就是运行在这个进程里面的
    6. 开启一个循环,等待着接收ActivityManagerService 的请求,随时待命,当接收到创建新进程的请求时立即唤醒并执行相应工作

    我们主要学习其中的这几个步骤:

    1. 创建 Server 端的 Socket
    2. 启动SystemServer进程
    3. SystemServer处理过程
    4. 资源预加载
    5. 等待AMS请求创建新的应用程序进程

    2.1 创建 Server 端的 Socket

    在ZygoteInit.java的main方法,是这样来创建 Server 端的 Socket的:

    zygoteServer.registerServerSocket(socketName);
    

    那我们就来看看ZygoteServer的registerServerSocket方法到底做了什么。关键代码如下:

    void registerServerSocket(String socketName) {
            if (mServerSocket == null) {
                int fileDesc;
                //拼接Socket的名称
                final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
                try {
                     //得到Socket的环境变量的值
                    String env = System.getenv(fullSocketName);
                    //将Socket环境变量的值转换为文件描述符的参数
                    fileDesc = Integer.parseInt(env);
                } catch (RuntimeException ex) {
                    throw new RuntimeException(fullSocketName + " unset or invalid", ex);
                }
    
                try {
                    //创建文件描述符
                    FileDescriptor fd = new FileDescriptor();
                    fd.setInt$(fileDesc);
                    //创建服务器端 Socket
                    mServerSocket = new LocalServerSocket(fd);
                } catch (IOException ex) {
                    throw new RuntimeException(
                            "Error binding to local socket '" + fileDesc + "'", ex);
                }
            }
        }
    

    拼接Socket的名称时,socketName的值是传进来的“zygote”,因此fullSocketName的值是“ANDROID_SOCKET_zygote”,然后将这个值转换为环境变量的值,再转换为文件描述符的参数。之后创建文件描述符,并传入此前的文件描述符参数,最后创建LocalServerSocket(fd),也就是服务器端 Socket,并将文件描述符传进去。再Zygote进程将SystenServer进程启动后,就会在这个服务器端的Socket上等待AMS请求zygote进程创建新的应用程序进程。

    2.2 启动SystemServer进程

    ZygoteInit进程是通过startSystemServer(abiList, socketName, zygoteServer)函数来启动SystemServer进程的,接下来我们学习此部分关键代码:

    private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
                throws Zygote.MethodAndArgsCaller, RuntimeException {
    ······
            //创建args数组,这个数组用来保存启动SystemServer的启动参数
            String args[] = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "com.android.server.SystemServer",
            };
            ZygoteConnection.Arguments parsedArgs = null;
    
            int pid;
    
            try {
                parsedArgs = new ZygoteConnection.Arguments(args);
                ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
                ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
    
                //创建SystemServer子线程
                pid = Zygote.forkSystemServer(
                        parsedArgs.uid, parsedArgs.gid,
                        parsedArgs.gids,
                        parsedArgs.debugFlags,
                        null,
                        parsedArgs.permittedCapabilities,
                        parsedArgs.effectiveCapabilities);
            } catch (IllegalArgumentException ex) {
                throw new RuntimeException(ex);
            }
    
            //以下代码运行在子线程中
            if (pid == 0) {
                if (hasSecondZygote(abiList)) {
                    waitForSecondaryZygote(socketName);
                }
                zygoteServer.closeServerSocket();
                //处理SystemServer进程
                handleSystemServerProcess(parsedArgs);
            }
    
            return true;
        }
    

    可以看到,这个函数首先创建args数组,保存启动参数,并封装Arguments对象供forkSystemServer调用,而forkSystemServer函数内部会调用nativeSystemServer的这个Native方法,最终通过fork函数在当前进程创建一个子进程,也就是SystemServer进程,并执行pid == 0的代码,也就是关键的handleSystemServerProcess(parsedArgs)函数。

    2.3 SystemServer处理过程

    这一节紧接上节,主要学习handleSystemServerProcess()函数发生的事情。主要代码如下:

        private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
                throws Zygote.MethodAndArgsCaller {
           ······
            if (parsedArgs.invokeWith != null) {
                ······
            } else {
                ClassLoader cl = null;
                if (systemServerClasspath != null) {
                    //创建PathClassLoader
                    cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
                    Thread.currentThread().setContextClassLoader(cl);
                }
                //调用ZygoteInit的zygoteInit方法
                ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
            }
        }
    

    这里重点是调用了ZygoteInit的zygoteInit方法,关键代码如下:

    public static final void zygoteInit(int targetSdkVersion, String[] argv,
                ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
            if (RuntimeInit.DEBUG) {
                Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
            RuntimeInit.redirectLogStreams();
            RuntimeInit.commonInit();
            //启动binder线程池
            ZygoteInit.nativeZygoteInit();
            //进入SystemServer的main方法
            RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
        }
    

    这里主要调用了nativeZygoteInit()方法,作用是调用Native层的代码,来启动Binder线程池,这样SystemServer就可以使用Binder与其他进程进行通信。然后进入SystemServer的main方法。下面分别介绍这两部分:

    int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
    {
        const JNINativeMethod methods[] = {
            { "nativeZygoteInit", "()V",
                (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
        };
        return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
            methods, NELEM(methods));
    }
    

    nativeZygoteInit()是一个Native方法,需要查找其对应的JNI文件,通过JNI的gMethods数组,可以看到其对应的是AndroidRuntime.cpp的com_android_internal_os_ZygoteInit_nativeZygoteInit函数,如下:

    static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
    {
        gCurRuntime->onZygoteInit();
    }
    

    这里gCurRuntime是AndroidRuntime类型的指针,指向AppRuntime,在app_main_cpp中定义,关键代码如下:

        virtual void onZygoteInit()
        {
            sp<ProcessState> proc = ProcessState::self();
            ALOGV("App process: starting thread pool.\n");
           //启动一个Binder线程池
            proc->startThreadPool();
        }
    

    看到这里,我们大致就了解了nativeZygoteInit方法主要是启动Binder线程池的。

    再回到ZygoteInit的zygoteInit方法,我们还没结束这段代码:

    public static final void zygoteInit(int targetSdkVersion, String[] argv,
                ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
            if (RuntimeInit.DEBUG) {
                Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
            }
    
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
            RuntimeInit.redirectLogStreams();
            RuntimeInit.commonInit();
            //启动binder线程池
            ZygoteInit.nativeZygoteInit();
            //进入SystemServer的main方法
            RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
        }
    

    第二部分,调用了RuntimeInit的applicationInit方法,来进入SystemServer的main方法,applicationInit关键代码如下:

        protected static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
                throws Zygote.MethodAndArgsCaller {
           ······
            // Remaining arguments are passed to the start class's static main
            invokeStaticMain(args.startClass, args.startArgs, classLoader);
        }
    

    看到,在其中调用了invokeStaticMain方法,这个方法启动SystemServer的main方法过程比较复杂,为了清除堆栈帧,使用了抛出异常然后捕获异常的方法,总之会经过一串过程,最终进入SystemServer的main方法中。那么我们就看看这个main方法:

         public static void main(String[] args) {
            new SystemServer().run();
        }
    

    里面只有一行run方法,run方法主要代码如下:

        private void run() {
            try {
                ······
                //创建消息Looper
                Looper.prepareMainLooper();
                // 加载动态库libandroid_servers.so
                System.loadLibrary("android_servers");
                performPendingShutdown();
                // 创建系统的contex
                createSystemContext();
                // 创建SystemServiceManager
                mSystemServiceManager = new SystemServiceManager(mSystemContext);
                mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
                LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
                SystemServerInitThreadPool.get();
            } finally {
                traceEnd();  // InitBeforeStartServices
            }
            try {
                traceBeginAndSlog("StartServices");
                //启动引导服务
                startBootstrapServices();
                //启动核心服务
                startCoreServices();
                //启动其他服务
                startOtherServices();
                SystemServerInitThreadPool.shutdown();
            } catch (Throwable ex) {
                Slog.e("System", "******************************************");
                Slog.e("System", "************ Failure starting system services", ex);
                throw ex;
            } finally {
                traceEnd();
            }
            ······
        }
    

    可以看到,这段代码主要启动系统的各种服务,这些服务大致如下:

    • 引导服务:AMS,PowerMS,PachageMS
    • 核心服务:DropBoxManagerService,BatteryService,UsageStateService和web ViewUpdateService
    • 其他服务:Camera、Alarm、VrManager等等

    服务的总数有100多个,这里就不在详细讨论了。这些服务的启动逻辑是类似的,直接调用SystemServiceManager的startService方法,会将服务注册到服务列表中,然后调用服务的onStart函数完成启动。
    下面总结下SystemServer进程的工作:

    • 启动Binder线程池,可以与其他进程通信
    • 创建SystemServiceManager,用于对系统的服务进行创建、启动和生命周期管理
    • 启动各种系统服务

    2.4 资源预加载

    好,现在让我们回到ZygoteInit进程,接下来是资源的预加载过程,那么预加载是什么呢?Android 系统资源加载分两种方式,预加载和使用进程中加载。 预加载是指在 zygote 进程启动的时候就加载,这样系统只在 zygote 执行一次加载操作,所有 APP 用到该资源不需要再重新加载,减少资源加载时间,加快了应用启动速度,一般情况下,系统中 App 共享的资源会被列为预加载资源。

    预加载的原理很简单,就是在 zygote 进程启动后将资源读取出来,保存到 Resources 一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找。这主要靠 zygoteInit.java 类的man方法调用preload()方法完成,下面我们来看看这部分关键代码:

    static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
            Log.d(TAG, "begin preload");
            bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
            beginIcuCachePinning();
            bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
            bootTimingsTraceLog.traceBegin("PreloadClasses");
            //预加载类
            preloadClasses();
            bootTimingsTraceLog.traceEnd(); // PreloadClasses
            bootTimingsTraceLog.traceBegin("PreloadResources");
            //预加载资源
            preloadResources();
            bootTimingsTraceLog.traceEnd(); // PreloadResources
            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
            //预加载图形接口
            preloadOpenGL();
            Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
            preloadSharedLibraries();
            //预加载文字资源
            preloadTextResources();
            // Ask the WebViewFactory to do any initialization that must run in the zygote process,
            // for memory sharing purposes.
            WebViewFactory.prepareWebViewInZygote();
            endIcuCachePinning();
            warmUpJcaProviders();
            Log.d(TAG, "end preload");
    
            sPreloadComplete = true;
        }
    

    可以看到,这个方法主要是通过调用各种预加载方法去实现预加载的,具体的方法预加载方法在这里就不在细究,等有机会再深入学习。

    2.5 等待AMS请求创建新的应用程序进程

    启动SystemServer进程后,会执行ZygoteServer的zygoteServer.runSelectLoop(abiList)方法:

    zygoteServer.runSelectLoop(abiList);
    

    这个方法无限循环,等待AMS的请求,主要代码如下:

        void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
            //之前创建的服务器端Socket,获得该Socket的fd字段,并添加到列表
            fds.add(mServerSocket.getFileDescriptor());
            peers.add(null);
            //无限循环等待AMS请求
            while (true) {
                StructPollfd[] pollFds = new StructPollfd[fds.size()];
                //遍历fds,将信息转移到pollFds数组
                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);
                }
                //遍历pollFds数组
                for (int i = pollFds.length - 1; i >= 0; --i) {
                    if ((pollFds[i].revents & POLLIN) == 0) {
                        continue;
                    }
                    //i等于0,说明服务器端Socket与客户端连接上了,即当前Zygote与AMS建立连接
                    if (i == 0) {
                        //得到ZygoteConnection并添加到连接列表
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        peers.add(newPeer);
                        //添加fd列表,以便可以接收到AMS的请求
                        fds.add(newPeer.getFileDesciptor());
                    } else {
                        //i不等于0,说明AMS向Zygote发送了请求,则创建新的应用程序进程
                        //调用runOnce函数创建新的应用程序进程
                        boolean done = peers.get(i).runOnce(this);
                        //成功后,在Socket连接列表和fds中清除这个请求
                        if (done) {
                            peers.remove(i);
                            fds.remove(i);
                        }
                    }
                }
            }
        }
    

    根据上述代码,可以看到这个函数是通过无限循环来处理AMS的请求,每次成功创建新的应用程序进程,就将其从列表清除。

    三、Lanucher启动过程

    Laucher启动较为复杂,放在另外一篇文章中进行学习。

    四、总结

    结合这几篇文章,就可以总结出安卓启动的完整流程,主要有下列几部分:

    1. 启动电源以及系统启动
      按下电源,引导芯片代码从预设位置(ROM)执行。加载BootLoader到RAM,然后执行。
    2. 引导程序BootLoader
      拉起OS。
    3. Linux内核启动
      设置缓存、被保护寄存器、计划列表、加载驱动,完成系统设置后,在系统文件中寻找Init.rc文件,并启动init进程。
    4. init进程启动
      初始化和启动属性服务,启动Zygote进程
    5. Zygote进程启动
      创建java虚拟机并注册JNI方法,创建服务器端Socket,启动SystenmServer进程。
    6. SystemServer进程启动
      启动Binder线程池和SystemServiceManager,启动各种系统服务。
    7. Launcher启动
      AMS启动Launcher,将安装应用程序的快捷图标显示到界面上。

    给出图示如下:


    安卓系统启动流程

    文笔简陋,如有错误,还请见谅。

    相关文章

      网友评论

          本文标题:安卓手机启动时发生的那些事儿——下篇

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