• Android系统启动流程概览


    第一节 Android系统启动流程

    Boot Rom —— Bootloader —— Linux Kernel —— init进程 —— Zygote进程(dalvik/ art)—— systemServer —— Apps

    init进程

    init 进程是Linux系统中,用户空间启动的第一个进程。

    1. 创建并挂载一些文件目录
    2. 启动属性服务
    3. 解析 init.rc 配置文件,启动 Zygote 进程

    挂载 seLinux 文件目录,创建seLinux,加载 安全策略
    启动内核日志
    启动属性系统,从文件读取属性
    创建epoll

    第二节 init.rc 解析

    包含五种类型语句:

    1. Action(包含command)
    2. Commands
    3. Services(将由 init进程 启动的服务)
    4. Options(服务的选项)
    5. Import (其他配置文件)

    Zygote服务 也在init.rc中配置:
    //进程名为Zygote,执行的真正程序是 /system/bin/app_process

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server           
        class main        //classname为main
        priority -20
        user root
        group root readproc reserved_disk
        socket zygote stream 660 root system
        socket usap_pool_primary stream 660 root system
        onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
        onrestart write /sys/power/state on
        onrestart restart audioserver
        onrestart restart cameraserver
        onrestart restart media
        onrestart restart netd
        onrestart restart wificond
        writepid /dev/cpuset/foreground/tasks
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第三节 Zygote进程的启动过程

    zygote受精卵,用于孵化子进程。所有APP及Systemserver进程 都由zygote 通过Linux的fork() 函数孵化出来的。
    Zygote进程是Android系统中第一个带有art 虚拟机的进程,Zygote 与 SystemServer 进程以socket的方式进行通信。
    Zygote是C/S模型的服务端,主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 及其他进程。

    启动一个应用的流程:
    点击应用图标 —— AMS —— 发出socket —— Zygote —— linux fork() 新进程

    /frameworks/base/cmds/app_process/app_main.cpp
    App_main.main() 有两种启动模式:

    1. Zygote初始化模式
    2. application模式

    • Zygote初始化模式,会启动 SystemServer ,并指定自己的 socket 名字为 zygote
    • application模式,就是启动普通应用,参数有 class 名字及参数

    (C++世界)(Zygote Service)App_main() —— AndroidRumtime.start() 【startVm() —— startReg() —— callMain() 】—— (Java世界开始)ZygoteInit.main() 【registerZygoteSocket() —— preload() —— gc() —— startSystemServer() —— runSelectLoop()】

    1. AndroidRumtime.start() —— startVm() —— startReg():AndroidRumtime::Start() 函数中将启动 JavaVM,并注册所有FWK相关的系统 JNI 接口。为 Java 世界做好准备。
    2. ZygoteInit.main() registerZygoteSocket() —— preload() —— gc() —— startSystemServer() —— runSelectLoop():第一次进入Java世界,运行 ZygoteInit.java::main() 初始化Zygote,并创建 Socket 的 Server 端。preload 常用的 Java 类库、系统 resources,执行 gc() 清理内存,为 fork 子进程做好准备。然后 fork 出子进程,在子进程中 初始化 SystemServer,初始化的过程中 启动 Android 系统所有的 Service。
    3. (与此同时,Zygote 继续在后台监听 Socket 等待新的应用启动请求。)
    4. (与此同时,Zygote 监听 SystemServer 的 SIGHID信号,一旦SystemServer挂掉,立即 kill 掉 Zygote 自己。init 会重启 Zygote,再启动 SystemServer,使系统恢复正常。 )
    5. AMS ready后,寻找系统的 “Startup” Application,向 Zygote 发请求。
    6. Zygote fork 出“Startup” Application,也就是启动 Launcher 应用,然后显示出Home页面。

    在fork SystemServer之前执行了 gc 操作,使得被 fork 出来的子进程有尽可能少的垃圾内存。


    ZygoteInit的启动入口是:
    app_main.cpp(也就是Zygote的C++进程) ::main() —— AppRuntime.start(“com.android.internal.os.ZygoteInit”)

    AndroidRuntime包括了:从下到上 libc、JNI、Java-VM、Java-lib

    3.1 AndroidRuntime.start 做了三件事:

    1. startVm():
    2. startReg():
    3. callMain():

    3.1.1 startVm:

    cmds/app_process.cpp
    base/core/jni/AndroidRuntime.cpp startvm()
    art/runtime/java_vm_ext.cc JNI_CreateJavaVM() 返回JniEnv JavaVm给Native代码,这样就可以访问java接口了
    art/runtime/runtime.cc 提供art虚拟机运行时:创建堆管理对象gc::Heap、创建Java虚拟机对象JavaVmExt、为vm添加JniEnvHandler、连接主线程、创建类连接器

    3.1.2 startReg:

    主要做两件事:

    1. 设置创建可访问Java的线程方法
    2. 注册所有的JNI方法
    3.1.2.1 设置创建可访问Java的线程方法

    system/core/libutils/Threads.cpp::run() native层可以创建两种Thread:一种可以调用java方法的线程,一种是纯native的线程。
    可调用java的线程,会调用vm 将线程与 JNIEnv 绑定,使之可通过JNIEnv调用java方法。

    // base/core/jni/androidRuntime.cpp
    /*
     * Makes the current thread visible to the VM.
     *
     * The JNIEnv pointer returned is only valid for the current thread, and
     * thus must be tucked into thread-local storage.
     */
    static int javaAttachThread(const char* threadName, JNIEnv** pEnv)
    {
        JavaVMAttachArgs args;
        JavaVM* vm;
        vm = AndroidRuntime::getJavaVM();
        result = vm->AttachCurrentThread(pEnv, (void*) &args);
        if (result != JNI_OK)
            ALOGI("NOTE: attach of thread '%s' failed\n", threadName);
    
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    从数据结构上看,thread.h 类中,有一个 JNIEnvExt* 的指针变量 jni_env, 表明一个thread与一个jni_env关联。

    // runtime/thread.h
    struct PACKED(sizeof(void*)) tls_ptr_sized_values {
        // Every thread may have an associated JNI environment
        JNIEnvExt* jni_env;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    3.1.2.2 注册所有的JNI方法

    除了系统的JNI接口(javacore nativehelper)FWK还有大量的Native实现,所有这些接口一次性通过startReg()来完成
    base/core/jni/androidRuntime.cpp::startReg()

    /*
     * Register android native functions with the VM.
     */
    /*static*/ int AndroidRuntime::startReg(JNIEnv* env)
    {
    	......
    	register_jni_procs(gRegJNI, NELEM(gRegJNI), env)
    }
    static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
    {
        for (size_t i = 0; i < count; i++) {
            if (array[i].mProc(env) < 0) { ... }
        }
    }
    static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_os_RuntimeInit),
        REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
        REG_JNI(register_android_os_SystemClock),
        REG_JNI(register_android_util_EventLog),
        REG_JNI(register_android_util_Log),
        REG_JNI(register_android_util_MemoryIntArray),
        REG_JNI(register_android_util_PathParser),
        REG_JNI(register_android_util_StatsLog),
        REG_JNI(register_android_app_admin_SecurityLog),
        REG_JNI(register_android_content_AssetManager),
        REG_JNI(register_android_content_StringBlock),
        REG_JNI(register_android_content_XmlBlock),
        REG_JNI(register_android_content_res_ApkAssets),
        REG_JNI(register_android_text_AndroidCharacter), 
        ... ...
    }
    
    // frameworks/base/core/jni/android_util_Log.cpp
    int register_android_util_Log(JNIEnv* env)
    {
        jclass clazz = FindClassOrDie(env, "android/util/Log");
        levels.verbose = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "VERBOSE", "I"));
        levels.debug = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "DEBUG", "I"));
        levels.info = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "INFO", "I"));
        levels.warn = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "WARN", "I"));
        levels.error = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ERROR", "I"));
        levels.assert = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ASSERT", "I"));
        return RegisterMethodsOrDie(env, "android/util/Log", gMethods, NELEM(gMethods));
    }
    
    /*
     * Register native methods using JNI.
     */
    /*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
        const char* className, const JNINativeMethod* gMethods, int numMethods)
    {
        return jniRegisterNativeMethods(env, className, gMethods, numMethods);
    }
    
    // libnativehelper/JNIHelp.cpp
    extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, constJNINativeMethod* gMethods, int numMethods){
        JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
        (*env)->RegisterNatives(e, c.get(), gMethods, numMethods)
    	... ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    3.1.3 callMain()

    // frameworks/base/core/java/com.android.internal.os.RuntimeInit.java
    public static final void main(String[] argv) {
    		......
            /*
             * Now that we're running in interpreted code, call back into native code
             * to run the system.
             */
            nativeFinishInit();
            if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // frameworks/base/core/jni/AndroidRuntime.cpp
    /*
     * Code written in the Java Programming Language calls here from main().
     */
    static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
    {
        gCurRuntime->onStarted();
    }
    
    // frameworks/base/cmds/app_process/app_main.cpp
    virtual void onStarted()
    {
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgs);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    callMain() 将调起Java世界的 com.android.internal.os.ZygoteInit 类的main()方法。

    3.2 (Java世界开始)ZygoteInit.main()

    public static void main(String argv[]) {
            ZygoteServer zygoteServer = new ZygoteServer();
    
            final Runnable caller;
            try {
                boolean startSystemServer = false;
                String socketName = "zygote";
                String abiList = null;
                boolean enableLazyPreload = false;
                for (int i = 1; i < argv.length; i++) {
                    if ("start-system-server".equals(argv[i])) {
                        startSystemServer = true;
                    } else if ("--enable-lazy-preload".equals(argv[i])) {
                        enableLazyPreload = true;
                    } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                        abiList = argv[i].substring(ABI_LIST_ARG.length());
                    } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                        socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                    } else {
                        throw new RuntimeException("Unknown command line argument: " + argv[i]);
                    }
                }
    
    			// 注册Server socket
                zygoteServer.registerServerSocketFromEnv(socketName); 
    			if (!enableLazyPreload) {
                    bootTimingsTraceLog.traceBegin("ZygotePreload");
                    // 加载类到内存
                    preload(bootTimingsTraceLog); 
                }
    			preloadTextResources();
    			// 垃圾回收
    			gcAndFinalize();
                if (startSystemServer) {
                    Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
    
                    // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                    // child (system_server) process.
                    if (r != null) {
                        r.run();
                        return;
                    }
                }
    
                Log.i(TAG, "Accepting command socket connections");
    
                // The select loop returns early in the child process after a fork and
                // loops forever in the zygote.
                caller = zygoteServer.runSelectLoop(abiList);
            } catch (Throwable ex) {
                Log.e(TAG, "System zygote died with exception", ex);
                throw ex;
            } finally {
                zygoteServer.closeServerSocket();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    preload是Android 启动最耗时的部分之一

    /**
         * Prepare the arguments and forks for the system server process.
         *
         * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
         * child process, and {@code null} in the parent.
         */
        private static Runnable forkSystemServer(String abiList, String socketName,
                ZygoteServer zygoteServer) {
            /* 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.applyDebuggerSystemProperty(parsedArgs);
                ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
    
                boolean profileSystemServer = SystemProperties.getBoolean(
                        "dalvik.vm.profilesystemserver", false);
                if (profileSystemServer) {
                    parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
                }
    
                /* Request to fork the system server process */
                pid = Zygote.forkSystemServer(
                        parsedArgs.uid, parsedArgs.gid,
                        parsedArgs.gids,
                        parsedArgs.runtimeFlags,
                        null,
                        parsedArgs.permittedCapabilities,
                        parsedArgs.effectiveCapabilities);
            } catch (IllegalArgumentException ex) {
                throw new RuntimeException(ex);
            }
            /* For child process */
            if (pid == 0) {
                if (hasSecondZygote(abiList)) {
                    waitForSecondaryZygote(socketName);
                }
    
                zygoteServer.closeServerSocket();
                return handleSystemServerProcess(parsedArgs);
            }
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    第四节 SystemServer 启动流程

    SystemServer 是 Zygote fork出来的第一个 Java 进程,此进程非要重要,提供了 Android 系统所有的核心服务。
    SystemServer 中运行着AMS,WMS 等系统服务,Binder-x 之类的服务线程,UI-thread InputReader

    SystemServer 创建:ActivityThread、SystemContext、SystemUIContext
    ContextImpl 是 Context 的实现类,其中封装了生成 4 种常用的 createContext 方法:

    • createSystemContext()
    • createSystemUiContext()
    • createAppContext()
    • createActivityContext()

    启动Service

    startBootstrapServices();
    startCoreServices();
    startOtherServices();
    
    • 1
    • 2
    • 3
    • startBootstrapServices():启动系统启动所需的一小堆关键服务(AMS,PMS,RecoverySystemService,LightsService,DisplayManagerService,UserManagerService,OverlayManagerService)。这些服务具有复杂的相互依赖关系,这就是为什么我们在这里将它们全部初始化的原因。除非您的服务也与这些依赖关系纠缠在一起,否则它应该在其他函数中进行初始化。
    • startCoreServices():启动一些在引导过程中没有互相交互的基本服务(BatteryService, UsageStatsService, WebViewUpdateService, BinderCallsStatsService)。
    • startOtherServices():启动一些杂七杂八的东西(ConnectivityService,WMS,InputManagerService,TelephonyRegistry)

    Zygote会在后台观察 SystemServer ,一旦 System 挂掉了,将其回收,然后重启 Zygote 自己。
    Watchdog 也可能因某种原因,将 SystemServer Kill 掉,并重启。屏幕会黑屏一段时间。

    在 Unix-like 系统,父进程必须用 waitpid 等待子进程退出,否则子进程将变成 Zombie(僵尸)进程,不仅系统资源泄漏,而且系统将崩溃。没有 SystemServer 所有 Android 应用程序都无法运行。
    waitpid() 是一个阻塞函数,所以通常处理是 在 signal 函数里无阻塞调用。
    每个子进程退出时 —— 发出 SIGCHID 信号 —— Zygote 会杀掉自己 —— 系统给所有子进程发送 SIGHUP 信号 —— 各子进程杀掉自己退出当前进程(子进程中的 Daemon 进程设置 SIG_IGN参数忽略 SIGHUP 信号继续运行)。

    第五节 其他小点

    5.1 什么情况下 Zygote 进程会重启呢?

    ServiceManager、SystemServer、SurfaceFlinger、Zygote自己 进程被杀

    5.2 fork函数

    返回值 == 0:成功创建子进程,并进入子进程执行
    返回值 > 0:成功创建子进程,返回子进程id,并继续沿原父进程执行
    返回值 < 0:创建失败
    失败原因主要有:进程数超过系统上限(EAGAIN),系统内存不足(ENOMEM)
    fork() 出的子进程,是父进程的一个copy,继承了整个进程的地址空间:包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。与父进程不同的只有进程号、计时器等少量信息。由上可见,fork() 函数的开销是很大的。

    5.3 写时拷贝(copy-on-write)

    Linux 的 fork 采用写时拷贝。写时拷贝是一种可能推迟甚至避免拷贝的技术。内核此时并不复制整个进程的地址空间,而是让子进程共享父进程的地址空间。只有在需要写入时,才会复制地址空间,使子进程拥有自己的地址空间。

    panic
    内核出现异常会导致1 进程死亡,如果是中断上下文的异常或系统关键资源受到破坏,通常会导致2 内核挂起,不再响应任何事件。

    5.4 内核空间、用户空间

    在linux中, 将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址 0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间)
    内核非法使用了用户空间的地址故存在问题。

    5.5 epoll

    epoll
    epoll_create、epoll_wait、epoll_ctl

    5.6 内核的异常级别

    1. bug
    2. Oops
    3. panic
    
    • 1
    • 2
    • 3
    • Bug:是指那些不符合内核的正常设计,但内核能够检测出来并且对系统运行不会产生影响的问题,比如在原子上下文中休眠。
    • Oops:程序在内核态时,进入一种异常情况,比如引用非法指针导致的数据异常,数组越界导致的取指异常,此时异常处理机制能够捕获此异常,并将系统关键信息打印到串口上,正常情况下Oops消息会被记录到系统日志中去。
      Oops发生时,进程处在内核态,很可能正在访问系统关键资源,并且获取了一些锁,当进程由于Oops异常退出时,无法释放已经获取的资源,导致其他需要获取此资源的进程挂起,对系统的正常运行造成影响。通常这种情况,系统处在不稳定的状态,很可能崩溃。
    • panic:当Oops发生在中断上下文中或者在进程0和1中,系统将彻底挂起,因为中断服务程序异常后,将无法恢复,这种情况即称为内核panic。另外当系统设置了panic标志时,无论Oops发生在中断上下文还是进程上下文,都将导致内核Panic。由于在中断复位程序中panic后,系统将不再进行调度,Syslogd将不会再运行,因此这种情况下,Oops的消息仅仅打印到串口上,不会被记录在系统日志中。
  • 相关阅读:
    使用vue3前端开发的一些知识点
    QGraphicsView使用要点
    提高 Python 代码可读性的 5 个基本技巧
    面试逻辑题,有8个小球和一个天平,一个小球偏重,其它小球一样重,问最少称几次可以找出那个重球?
    网络通信错误代码列表 HTTP 、FTP
    在有springSecurity或者若依项目中获取当前系统登录的用户信息
    Educational Codeforces Round 129 (Rated for Div. 2)
    涉密信息系统集成资质
    Linux基本命令
    Hadoop3教程(三):HDFS文件系统常用命令一览
  • 原文地址:https://blog.csdn.net/jx0260/article/details/130787240