第三章 Android系统启动流程

第三章 Android系统启动流程

面试题预览

  1. 你了解Android系统启动流程吗?⭐⭐⭐⭐⭐
  2. system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢?⭐⭐⭐
  3. 能说说具体是怎么导致死锁的吗?⭐⭐⭐
  4. Zygote 为什么不采用 Binder 机制进行 IPC 通信?⭐⭐⭐⭐
  5. 请简述从点击图标到app启动的流程。⭐⭐⭐⭐⭐
  6. 说说Activity加载的流程。⭐⭐⭐⭐⭐
  7. Zygote为什么需要用到Socket通讯而不是Binder?⭐⭐⭐⭐
  8. Zygote进程最原始的进程是什么进程(或者Zygote进程是怎么来的)?⭐⭐⭐

1 概述

Android系统启动流程图

在init进程启动前,如上面“Android系统启动流程图”所示:

1)按Power键启动电源及系统启动

当按下电源键,引导芯片代码开始从固化在ROM中预定义的地方开始执行,加载引导程序Bootloader到RAM,然后执行引导程序。

2)引导程序Bootloader

引导程序BootLoader是Android系统运行前的第一个程序,它的主要作用是把系统OS拉起来并运行

3)Linux内核启动

Linux kernel启动. kernel(内核)启动时会设置缓存、被保护存储器、计划列表、加载驱动.然后在系统文件中寻找init文件,并启动init进程

4)init进程启动

详细见本章第二节分析。

2 Init启动进程详解

2.1 init进程概述

在运行Android程序后首先会启动init 进程,此进程是Linux 系统中用户空间的第一个进程,进程编号为1。init被赋予了很多重要的职责,比如我们熟悉的Zygote孵化器进程就是由init进程启动的。

2.2 启动前分析

在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。

init进程被赋予了很多重要工作,init进程启动主要分为两个阶段:

第一个阶段完成以下内容:

  • ueventd/watchdogd跳转及环境变量设置
  • 挂载文件系统并创建目录
  • 初始化日志输出、挂载分区设备
  • 启用SELinux安全策略
  • 开始第二阶段前的准备

第二个阶段完成以下内容:

  • 初始化属性系统
  • 执行SELinux第二阶段并恢复一些文件安全上下文
  • 新建epoll并初始化子进程终止信号处理函数
  • 设置其他系统属性并开启属性服务

Android 是一个基于Linux 内核的系统,与Linux、Fedora Linux最大的区别是,Android在应用层专门为移动设备添加了一些特有的支持。目前 Linux 有很多通信机制可以在用户空间和内核空间之间交互,例如设备驱动文件(位于/dev目录中)、内存文件(/proc、/sys 目录等)。Android在加载Linux基本内核后,就开始运行一个初始化进程init。从Android加载Linux内核时设置了如下参数。

Kemelcommand line: noinitrd root=/dev/nfs console=ttySACO init=/initnfsroot=192.168.1.103:/nfsbootip=192.168.1.20:192.168.1.103:192.168.1.1:255.255.255.0::eth0:on

在上述命令中,Android系统一般会在根目录下放一个init的可执行文件,也就是说Linux系统的init进程在内核初始化完成后,就直接执行init这个文件。Init 进程的代码位于源码的目录 system/core/init,在分析 init 的核心代码之前,还需要做如下工作。 

  • 初始化属性。 
  • 处理配置文件的命令(主要是init.rc 文件),包括处理各种 Action。 
  • 性能分析(使用b
  • ootchart 工具)。 
  • 无限循环执行 command(启动其他的进程)。

2.3 init进程启动过程代码分析

init程序并不是由一个源代码文件组成的,而是由一组源代码文件的目标文件链接而成的。这些文件位于目录/system/core/init 中,例如system/core/init/init.cpp。 主要的 JNI代码放在路径frameworks/base/core/jni/中。 

init进程的入口函数

在Linux内核加载完成之后,它首先在系统文件中寻找init.rc文件,并启动init进程,然后查看init进程的入口函数main,代码如下所示:

system/core/init/init.cpp

int main(int argc, char** argv) {
    // ... 1
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    // ... 2
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
    // ... 3
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    // 添加环境变量
    add_environment("PATH", _PATH_DEFPATH);
 
    // 获取本次启动是否是系统启动的第一阶段,如果是第一阶段,
    // 则进入下面的if语句中,创建并挂载相关的文件系统。
    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
    //创建文件并挂载
    if (is_first_stage) {
        ....
        // 创建和挂载启动所需的文件目录
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
 
        // 初始化Kernel的Log,这样就可以从外界获取Kernel的日志
        InitKernelLogging(argv);
 
        LOG(INFO) << "init first stage started!";
        ...
        // Set up SELinux, loading the SELinux policy.
        // 启动SELinux。
        selinux_initialize(true);
        ... 
    // 初始化属性服务
    property_init();   //  .......  5
 
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline();
 
    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();
 
    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
 
    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
    // Clean up our environment.
    unsetenv("INIT_SECOND_STAGE");
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");
 
    // Now set up SELinux for second stage.
    selinux_initialize(false);
    selinux_restore_context();
 
 
    // 创建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(ERROR) << "epoll_create1 failed";
        exit(1);
    }
 
    // 用于设置子进程信号处理函数,如果子进程(Zygote进程)异常退出,init进程会调用该函数中
    // 设定的信号处理函数来进行处理
    signal_handler_init();   // ...... 6
 
    // 导入默认的环境变量
    property_load_boot_defaults();
    export_oem_lock_status();
 
    // 启动属性服务
    start_property_service();  // ...... 7
 
    set_usb_controller();
 
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
 
    ActionManager& am = ActionManager::GetInstance();
    ServiceManager& sm = ServiceManager::GetInstance();
    Parser& parser = Parser::GetInstance();
 
    parser.AddSectionParser("service", std::make_unique(&sm));
    parser.AddSectionParser("on", std::make_unique(&am));
    parser.AddSectionParser("import", std::make_unique(&parser));
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        // 解析init.rc配置文件
        parser.ParseConfig("/init.rc"); // ...... 8
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }
 
    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();
    am.QueueEventTrigger("early-init");
    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    // ... so that we can start queuing up actions that require stuff from /dev.
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
    am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");
    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
 
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
    // Run all property triggers based on current state of the properties.
    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;
        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }
        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
            // 遍历执行每个action中携带的command对应的执行函数
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || sm.IsWaitingForExec())) {
            if (!shutting_down) 
                // 重启死去的进程 
                restart_processes(); // ...... 9
            // If there's a process that needs restarting, wake up in time for that.
            if (process_needs_restart_at != 0) {
                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
            }
 
            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }
        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }
    return 0;
}

另外,还涉及了其他目录中的如下文件。 

  • 涉及源码文件:
  • /system/core/init/main.cpp
  • /system/core/init/init.cpp
  • /system/core/init/ueventd.cpp
  • /system/core/init/selinux.cpp
  • /system/core/init/subcontext.cpp
  • /system/core/base/logging.cpp
  • /system/core/init/first_stage_init.cpp
  • /system/core/init/first_stage_main.cpp
  • /system/core/init/first_stage_mount.cpp
  • /system/core/init/keyutils.h
  • /system/core/init/property_service.cpp
  • /external/selinux/libselinux/src/label.c
  • /system/core/init/signal_handler.cpp
  • /system/core/init/service.cpp

3 Zygote进程详解

在 Android系统中有如下3个十分重要的进程系统。 

  • Zygote 进程: 被称为"孵化"进程或"孕育"进程,功能和Linux系统的fork类似,用于"孕 育"产生出不同的子进程。 
  •  System进程∶系统进程,是整个 Android Framework所在的进程,用于启动 Android 系统。其 核心进程是 system server,其父进程就是Zygote。 
  • 应用程序进程∶每个 Android 应用程序运行后都会拥有自己的进程,这和Windows 资源管理器中体现的进程是同一含义。 

本节接下来将详细分析 Zygote 进程系统的基本知识。 

3.1 Zygote介绍

我们的Android系统是基于Linux系统,所以当我们开机的时候,第一个启动的是Init进程,而后面所有的进程都是Init的子进程,Zygote就是Init进程通过解析init.rc文件之后,启动的一个进程。Zygote是Android系统的核心进程之一,被认为是Framework大家族的祖先。事实上,JavaVM(Java 虚拟机)、应用程序进程以及运行系统关键服务的 SystemServer 进程都是由 Zygote 来创建的。从总体架构上看, Zygote是一个简单的典型 C/S 结构。其他进程作为一个客户端向 Zygote发出"孕育"请求,当Zygote 接收到命令后就"孕育"出一个 Activity 进程。

这里回答本地的面试题1(Zygote为什么需要用到Socket通讯而不是Binder?

  • 先后时序问题:zygote比service manager先启动,而Binder引用都是保存在ServiceManager进程中的,从这个意义上,他没有service manager可以注册,所以没法用binder。
  • 多线程问题:zygote是通过fork生成进程的,而多线程是不允许使用fork的,否则可能造成死锁,同时binder又是多线程的,所以干脆不同binder而使用socket。
  • 效率问题:AMS和Zygote之间使用的LocalSocket,相对于网络Socket,减少了数据验证等环节,所以其实效率相对于正常的网络Socket会大幅的提升。虽然还是要经过两次拷贝,但是由于数据量并不大,所以其实影响并不明显。所以,LocalSocket效率其实也不低
  • 安全问题:LocalSocket其实也有权限校验,并不意味着可以被所有进程随意调用

Zygote的进程的主要作用只有两个,可见下图:

  1. 启动SystemServer进程。SystemServer是一个用于启动手机内部各种服务的进程,我们常说的PMS,AMS等都是由SystemServer所启动。
  2. 在系统运行过程中,即时的去孵化APP进程,也就是我们每次点击APP图标启动的APP的时候,zygote就开始运作了。

 3.2 Zygote的启动流程

3.2.1 概述

Zygote进程启动中承担的主要职责如下:

  1. 创建AppRuntime,执行其start方法。
  2. 创建JVM并为JVM注册JNI方法。
  3. 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
  4. 调用preload完成系统资源的预加载。主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries
  5. 通过ZygoteServer创建本地服务端socke连接(LocalServerSocket), 调用Os.socket, bindLocal绑定socket文件和进程
  6. 启动SystemServer进程。
  7. 通过runSelectLoop调用linux poll机制,阻塞等待客户端请求连接,创建应用,关闭连接请求

具体分析如下:

如下图,Zygote的主要启动流程为:

3.2.2 init.rc

在init中通过init进程解析zytoe.rc中的配置执行脚本,通过FindService方法找到rc中配置的zygote服务, fork出zygote进程在fork出的zygote进程中,执行service对应的/system/bin/app_process64脚本,进入app_main.cpp对应的main方法;

详情参考: segmentfault.com/a/119000002…

3.2.3 app_main.main()

    1. 解析main方法的调用参数,判断当前所处的进程模式
    2. 如果是处于zygote进程,则完成zygote的初始化,并创建systemserver进程
    3. 如果是处于应用进程,则完成运行时初始化

3.2.4 ZygoteInit初始化进程

4 APP启动流程

4.1 概述

从用户手指点击桌面上的应用图标到屏幕上显示出应用主Activity界面而完成应用启动,快的话往往都不需要一秒钟,但是这整个过程却是十分复杂的,其中涉及了Android系统的几乎所有核心知识点。同时应用的启动速度也绝对是系统的核心用户体验指标之一,多少年来,无论是谷歌或是手机系统厂商们还是各个Android应用开发者,都在为实现应用打开速度更快一点的目标而不断努力。但是要想真正做好应用启动速度优化这件事情,我想是必须要对应用启动的整个流程有充分的认识和理解的.

可以先看下下面的启动流程图:

4.2 APP启动流程

  1. 点击桌面APP图标时,Launcher通过Binder通信,调用system_server进程中AMS服务的startActivity方法,发起启动请求
  2. system_server进程接收到请求后,向Zygote进程发送创建进程的请求
  3. Zygote进程fork出App进程,并执行ActivityThread的main方法,创建ActivityThread线程,初始化MainLooper,主线程Handler,同时初始化ApplicationThread用于和AMS通信交互
  4. App进程,通过Binder向sytem_server进程发起attachApplication请求,这里实际上就是APP进程通过Binder调用sytem_server进程中AMS的attachApplication方法,上面我们已经分析过,AMS的attachApplication方法的作用是将ApplicationThread对象与AMS绑定
  5. system_server进程在收到attachApplication的请求,进行一些准备工作后,再通过binder IPC向App进程发送handleBindApplication请求(初始化Application并调用onCreate方法)和scheduleLaunchActivity请求(创建启动Activity)
  6. App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送BIND_APPLICATION和LAUNCH_ACTIVITY消息,这里注意的是AMS和主线程并不直接通信,而是AMS和主线程的内部类ApplicationThread通过Binder通信,ApplicationThread再和主线程通过Handler消息交互。 ( 这里猜测这样的设计意图可能是为了统一管理主线程与AMS的通信,并且不向AMS暴露主线程中的其他公开方法,大神可以来解析下)
  7. 主线程在收到Message后,创建Application并调用onCreate方法,再通过反射机制创建目标Activity,并回调Activity.onCreate等方法
  8. 到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染后显示APP主界面。

5 参考资料

Android系统启动流程(一)—— init进程的启动流程

ANDROID系统启动系列2 INIT进程

zygote启动过程

知识分享—Android APP 启动流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统启动流程可以分为以下几个步骤: 1. 启动电源以及系统启动:当电源按下时,引导芯片代码开始执行,加载引导程序Bootloader到RAM,并执行该程序。 2. 引导程序Bootloader:引导程序是一个小程序,其主要作用是将系统OS启动起来并运行。 3. Linux内核启动:内核启动时,进行系统设置,包括设置缓存、被保护存储器、计划列表,并加载驱动。内核会在系统文件中寻找"init"文件,并启动init进程或系统的第一个进程。 4. init进程启动:init进程是Android系统的第一个用户空间进程,它负责启动和管理其他系统进程。init进程会读取init.rc文件,根据其中的配置启动系统服务和应用进程。 5. Zygote进程和SystemServer进程启动:Zygote进程是一个特殊的进程,它作为应用进程的模板,用于快速创建新的应用进程。SystemServer进程是Android系统的核心服务进程,负责启动和管理各种系统服务。 6. 应用层进程启动:在Android系统中,应用层进程包括Launcher进程,即主屏幕的进程,以及其他应用程序的进程。这些进程会根据用户的操作和应用的需求来启动和管理。 总结起来,Android系统启动流程包括引导程序Bootloader的加载和执行、Linux内核的启动、init进程的启动、Zygote进程和SystemServer进程的启动,以及应用层进程的启动。这些步骤共同完成了Android系统的初始化和启动。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Android系统启动流程(一)解析init进程启动过程](https://blog.csdn.net/itachi85/article/details/54783506)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Android系统启动流程](https://blog.csdn.net/xhmj12/article/details/128149490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android 面试必备 - 系统、App、Activity 启动过程“一锅端”](https://blog.csdn.net/zzz777qqq/article/details/115698795)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值