当前位置: 首页 > news >正文

Android启动流程源码分析(基于Android S)

从上图我们可以清楚的看到Android系统的启动分为以下几个步骤

  1. 启动电源以及启动系统

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

  1. 引导程序

引导程序是在Android操作系统开始运行前的一个小程序. 引导程序是运行的第一个程序, 因此它是针对特性的主板和芯片的. 设备制造商要么使用很受欢迎的引导程序比如redboot, uboot, qi bootloader或者开发自己的引导程序, 在此注意一下, 引导程序并不是Android操作系统的一部分. 引导程序是OEM厂商或运营商加锁和限制的地方.

引导程序分为两个阶段执行.

第一: 检查外部的RAM以及加载对第二阶段有用的程序

第二: 引导程序设置网络, 内存等等. 这些对于运行内核是有必要的. 为了达到特殊的目标, 引导程序可以根据配置参数或者输入数据设置内核.

传统得加载器主要是2个文件:

/external/arm-trusted-firmware/lib/romlib/init.s

这个主要是初始化堆栈, 清零BSS段, 调用main.c的_main()函数

/external/rootdev/main.c

初始化硬件(闹钟,主板,键盘, 控制台等), 创建Linux标签

  1. 内核

Android内核与桌面Linux内核的启动方式差不多, 内核启动时, 设置缓存 被保护的存储器 计划列表 加载驱动. 当内核完成系统设置时, 它首先在系统文件中寻找"init"文件, 然后启动root进程或者系统的第一个进程(据我之前的了解这个进程叫idle进程, pid=0, 如有记错的话请大家指出)

  1. init进程

init进程是Linux系统中用户空间的第一个进程, 进程号固定为1. Kernel启动后, 在用户空间启动init进程, 并调用init中的main()方法执行init进程的职责

  1. 启动Launcher App

init进程分析

init进程是Android系统中及其重要的用户空间的第一个进程. 接下来我们看看init进程做了些什么事情.

  1. 创建和挂载启动所需要的目录文件

  2. 初始化和启动属性服务

  3. 解析init.rc配置文件并启动Zygote进程

 

比较重要的两个文件

/system/core/init/init.cpp

/system/core/rootdir/init.rc

在这里init.cpp里面干了啥我们就不去详细解析了, 有兴趣得同学自己去研究

init.rc解析

init.rc是什么? 它是一个非常重要的配置文件, 由android初始化语言(Android Init Language)编写的脚本, 它里面包含了Action, Service, Command, Options. 这里就不过多讲解, 有兴趣了解的同学自己google

Zygote

  1. Zygote概述

Zygote的翻译为"受精卵", 如其名, 它的主要作用是用来孵化进程的, 在Android系统中主要有以下两种程序

Java应用程序---主要基于ART虚拟机, 所有的应用程序APK都是属于这一类程序

Native程序---也就是利用c/cpp开发的程序, 例如bootanimation.

所有的Java程序都以及系统服务进程SystemServer 都是由Zygote孵化而来的, 而native程序是由init程序创建启动的.

Binder 机制中存在 Binder 线程池, 是多线程的, 如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。 其实严格来说, Binder 机制不一定要多线程, 所谓的 Binder 线程只不过是在循环读 取 Binder 驱动的消息而已, 只注册一个 Binder 线程也是可以工作的, 比如 service manager 就是这样的。 实际 上 Zygote 尽管没有采取 Binder 机制, 它也不是单线程的, 但它在 fork() 前主动停止了其他线程, fork() 后重 新启动了。

  1. Zygote触发过程.

之前介绍了Zygote,那么Zygote是怎么被唤起来的?

在init.rc中, 上面有这一行代码:

import /system/etc/init/hw/init.${ro.zygote}.rc

${ro.zygote}这个会被替换成ro.zygote对应的属性值, 这个是由不同厂商自己定制的, 有4个值:

  • zygote32: zygote进程对应的执行程序是app_process(纯32bit模式)

  • zygote64:zygote进程对应的执行程序是app_process64(纯64bit模式)

  • zygote32_64: 启动两个zygote进程(名为zygote和zygote_secondary), 对应的执行程序是app_process32(主模式), app_process64

  • zygote64_32: 启动两个zygote进程(名为zygote和zygote_secondary), 对应的执行程序是app_process64(主模式), app_process32

Start zygote

system/core/rootdir/init.rc 960L

# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
    wait_for_prop odsign.verification.done 1
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start statsd
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    wait_for_prop odsign.verification.done 1
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start statsd
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    wait_for_prop odsign.verification.done 1
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start statsd
    start netd
    start zygote
    start zygote_secondary

app_processXXX对应的位置为:out/target/product/xxx/system/bin

源位置app_process 中有一个比较重要的文件:::::/frameworks/base/cmds/app_process/app_main.cpp

L336 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

这里, 就是由native的世界进入java世界的地方, 在此之前都是native和kernel的世界.

这里有个很重要的东西AppRunTime 从代码上看, 它是AnroidRunTime的子类, 也就是Android中比较核心的一个东西叫作ART /frameworks/base/core/jni/AndroidRunTime.cpp

既然是是进入了java世界, 那我们不是应该有虚拟机吗? 虚拟机是在哪里搞出来的?

AndroidRunTime.cpp start()函数中的L1246

if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

我们可以看到, 这里ART先给我们干了两件事 启动java虚拟机, 然后注册JNI

然后呢? 不是说好进入java世界的吗? 只干这个可不行, start函数L1287

jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }

OK, 我们应该都能看到了, 这里是通过反射去调用某个类的main方法, 哪个类? OK, 之前在app_main里面那一行 runtime.start 传进来的参数是ZygoteInit, 就是调的它.. 从这里.我们真正的进入了Java的世界

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

zygoteServer = new ZygoteServer(isPrimaryZygote);

if (startSystemServer) {
    Runnable r = forkSystemServer(abiList, zygoteSocketName, 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);
pid = Zygote.forkSystemServer(
        parsedArgs.mUid, parsedArgs.mGid,
        parsedArgs.mGids,
        parsedArgs.mRuntimeFlags,
        null,
        parsedArgs.mPermittedCapabilities,
        parsedArgs.mEffectiveCapabilities);

/* For child process */
if (pid == 0) {
    if (hasSecondZygote(abiList)) {
        waitForSecondaryZygote(socketName);
    }

    zygoteServer.closeServerSocket();
    return handleSystemServerProcess(parsedArgs);
}

这里对怎么启动SystemServer就不去过多的赘述了, 大概的套路是一样的, Zygote调用的是nativeFork之类的方法, 然后通过反射调用到SystemServer的main, 大概就是这个套路

当SystemServer启动完成之后, 当SystemReady, 就会启动Launcher, 到此 Android的大体启动流程到此结束

下面给出Zygote的流程图

我们都知道, 当我们点到Launcher中的某个icon要启动某个app的时候, Launcher通过Binder和AMS通信, AMS如果发现当前没有这个进程,则通知Zygote fork一个进程,然后AMS再通过Binder与ActivityThread进行交互, 这里不深究交互细节, 抛出一个问题: 既然Binder是Android中很重要的IPC, 那为什么Zygote和SystemServer之间用Socket通信? 用Binder是不是会更好?

其实并不然, Binder固然是好, 基于mmap能达到在内存中只做一次拷贝.. 但是Binder是多线程的.. 多线程环境下fork会不会出问题? 答案是肯定的.

别说什么在fork之前暂停所有线程, fork完之后再启动之类的话.. 这只是其中一个规避方案而已.

大家可以了解下 unix3 c++程序设计守则,

https://blog.csdn.net/wallwind/article/details/7036733

这个守则中有明确说明, 多线程程序里不准使用fork, 多线程环境下fork可能会导致死锁

Binder 机制中存在 Binder 线程池, 是多线程的, 如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。 其实严格来说, Binder 机制不一定要多线程, 所谓的 Binder 线程只不过是在循环读 取 Binder 驱动的消息而已, 只注册一个 Binder 线程也是可以工作的, 比如 service manager 就是这样的。 实际 上 Zygote 尽管没有采取 Binder 机制, 它也不是单线程的, 但它在 fork() 前主动停止了其他线程, fork() 后重 新启动了。

浅谈fork()导致死锁的原因

复制整个用户空间的数据(通常使用 copy-on-write 的策略, 所以可以 实现的速度很快) 以及所有系统对象, 然后仅复制当前线程到子进程。这里: 所有父进程中别 的线程, 到了子进程 中都是突然蒸发掉的 对于锁来说, 从 OS 看, 每个锁有一个所有者, 即最后一次 lock 它的线程。 假设这么一个环境, 在 fork 之前, 有 一个子线程 lock 了某个锁, 获得了对锁的所有权。 fork 以后, 在子进程中, 所有的额外线程都人间蒸发了。 而锁却 被正常复制了, 在子进程看来, 这个锁没有主人, 所以没有任何人可以对它解锁。 当子进程想 lock 这个锁时, 不再有任何手段可以解开了。 程序发生死锁

init脚本语言相关

https://blog.csdn.net/chaihuasong/article/details/50456113

汇编指ARM指令集

https://blog.csdn.net/TuxedoLinux/article/details/111462307

Unix c++程序设计守则

https://blog.csdn.net/xiaohangyu/article/details/3341135

Linux 进程托孤

沙盒机制

init脚本语言属性

Copy-on-write

mmap

相关文章:

  • 网站上传空间/网站排名软件包年
  • 科技网站开发/苏州做网站哪家比较好
  • 网站怎么做聚合/100个常用的关键词
  • 电影网站建设教程/高端网站建设公司排名
  • php动态网站制作流程图/便宜的seo官网优化
  • 网站建设与管理复习知识点/青岛seo网站关键词优化
  • Spring Cloud Gateway(黑马springcloud笔记)
  • 【ROS】—— 机器人导航(仿真)—导航实现(十八)[重要][重要][重要]
  • JAVA会员营销系统源码+数据库,实体店铺会员管理和营销系统源码,采用SpringBoot + Mysql+Mybatis
  • 商业智能 BI 跟业务系统的思维差异,跨越和提升
  • Http客户端 Feign 的使用 (黑马springcloud笔记)
  • Docker(黑马spring cloud笔记)
  • Wav2Vec HuBert 自监督语音识别模型
  • 【信息系统项目管理师】复盘沟通管理论文素材
  • 算法刷题打卡第67天:句子相似性 III
  • 抖音素人对接需要注意什么?合作时注意不要被薅羊毛了
  • 教程:Flutter 和 Rust混合编程,使用flutter_rust_bridge自动生成ffi代码
  • MyBatis-Plus基本操作