安卓系统层次框架图如下

Android10 程序实现开机启动 android开机启动流程_应用进程

图片清晰地展示了Android的五层架构,从上到下依次是:应用层、应用框架层、库层、HAL层以及Linux内核层。

Android的启动流程是自下向上的,大体上可以分为三个阶段:1、BootLoader引导启动内核;2、启动Linux内核;3、启动Android系统。

Android 启动流程图如下:

Android10 程序实现开机启动 android开机启动流程_html_02

1 Boot ROM

        我们长按电源按键,引导 ROM 代码从 ROM 中预定义位置开始执行。它将引导加载程序加载到 RAM 中并开始执行。存储在Boot ROM中的 Primary Bootloader (PBL)是引导过程第一阶段,此代码由芯片制造商编写;PBL验证下一阶段的真实性,如果辅助引导加载程序验证失败,则进入EDL(Emergency Download)。

        由于Boot ROM 空间有限,因此将辅助引导加载程序存储在eMMC中

2 Bootloader

        Bootloader主要是在系统加载前,初始化硬件设备,建立内存空间的映像图,为最终调用系统内核准备好环境。在 Android运行环境中里没有硬盘,而是 ROM,它类似于硬盘存放操作系统,用户程序等。ROM 跟硬盘一样也会划分为不同的区域,用于放置不同的程序

        Bootloader程序分两个阶段执行。第一个阶段,检测外部的RAM以及加载对第二阶段有用的程序;第二阶段,设置网络、内存等等。这些对于运行内核是必要的,为了达到特殊的目标,bootloader可以根据配置参数或者输入数据设置内核。

 3 初始化Kernel

        kernel开始启动时,设置缓存、被保护存储器、计划列表,加载驱动。当初始化内核之后,就会启动一个相当重要的祖先进程,也就是init进程。关于init进程有兴趣的小伙伴可以去研究下system//core/init/init.cpp 。

 4  servicemanager、zygote、mediaserver、surfaceflinger等进程创建

        当init进程创建之后,会创建进程服务,其中包括servicemanager、mediaserver、surfaceflinger、Zygote等一系列服务。init进程如何创建各种服务的,有兴趣的可以去跟下代码:

system//core/init/init.cpp :LoadBootScripts去加载解析/system/etc/init下对应的.rc

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

如下图是我用androidstudio 开的虚拟设备中看到的rc文件,init 就是根据这些rc创建对应的服务     

Android10 程序实现开机启动 android开机启动流程_应用进程_03

 

rc 部分解析执行主要代码路径:system\core\init

        解析流程: main.cpp(SecondStageMain)->init.cpp(LoadBootScripts)->Parser.cpp(ParseConfig)

        Action执行流程:main.cpp(SecondStageMain)->->init.cpp(LoadBootScripts)->action_manager.cpp(ExecuteOneCommand)

        service 会略微复杂,有兴趣的可以自己去看看。

Zygote:

        Zygote 系统服务的创建,在init.rc的中,在初始化时会启动虚拟机,并加载一些系统资源。这样 zygote fork 出子进程后,子进程也继承了能正常工作的虚拟机和各种系统资源,接下来只需装载 apk 文件的字节码就可以运行应用程序了,可以大大缩短应用的启动时间,这就是 zygote 进程的主要作用。

        zygote服务的启动是通过init.rc启动服务,执行的是app_process,app_process是由frameworks/base/cmds/app_process/app_main.cpp编译出来的。

#init.rc service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

        调用Android start ->startVm函数来创建虚拟机,调用startReg函数为java虚拟机注册JNI方法,通过toSlashClassName找到ZygoteInit,通过GetStaticMethedID函数找到main方法然后调用,ZygoteInit的main方法是由Java语言编写的,当前的运行逻辑在Native中,这就需要JNI来调用Java,这样Zygote就从Native层进入了Java框架层(ZygoteInit)。

        在ZygoteInit中创建zygoteServer,为其他进程初始化创建时与zygote通信做准备,

fork出了SystemServer进程

  • 孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote?
  • 我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。孵化出来之后,SystemServer进程和应用进程就可以各干各的事了
  • Zygote的IPC通信机制为什么不采用binder?如果采用binder的话会有什么问题么?
  • 第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。第二个原因,如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。
ZygoteInit.java(main)->forkSystemServer ->Zygote.java(forkSystemServer)->nativeForkSystemServer->com_android_internal_os_Zygote.cpp(com_android_internal_os_Zygote_nativeForkSystemServer)->ForkCommon

    servicemanager

        servicemanager是通过servicemanager.rc创建服务,servicemanager的实现可以看

service servicemanager /system/bin/servicemanager

framework/native/cmds/servicemanager/main.cpp 部分源码如下:

int main(int argc, char** argv) {
    if (argc > 2) {
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
    }

    const char* driver = argc == 2 ? argv[1] : "/dev/binder";
    //启动Binder线程池。
    sp<ProcessState> ps = ProcessState::initWithDriver(driver);
    ps->setThreadPoolMaxThreadCount(0);
    ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);

    //启动ServiceManager服务并获取ServiceManager服务binder代理对象
    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
    //将ServiceManager服务的Binder代理对象注册到servicemanager服务中
    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
    }

    将servicemanager服务自己注册为上下文管理者
    IPCThreadState::self()->setTheContextObject(manager);
    ps->becomeContextManager();

    启动binder线程池Looper并接收处理消息
    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);

    BinderCallback::setupTo(looper);
    ClientCallbackCallback::setupTo(looper, manager);

    while(true) {
        looper->pollAll(-1);
    }

    // should not be reached
    return EXIT_FAILURE;
}

servicemanager 启动后就等待binder驱动发来消息进行处理。