Android 架构学习
六一儿童节快乐!祝大小儿童们永葆童心,简单快乐!
前面分散的学习了一些Android知识,还没有从整体上审视安卓的整体架构,学习一下架构可以更好地理解安卓。
整体架构
下图是Android操作系统的架构,包括5层,从上到下依次是应用程序层、应用程序框架层、核心库和安卓运行环境层、硬件抽象层、Linux内核层。
Android Open Source Project官网上最新的给出的架构图如下:
应用程序层(Apps)
顾名思义,就是我们日常使用的一些应用,包括SMS 短消息程序、日历、地图、浏览器、联系人管理程序和其他程序。
下面这张架构图将上图的应用程序层更细分为了普通的Android应用(应用商店下载的应用)、特权应用(使用Android和系统API组合创建的应用,详情见特许权限许可名单 | Android Open Source Project (google.cn))、设备制造商应用(结合使用 Android API、系统 API 并直接访问 Android 框架实现而创建的应用。由于设备制造商可能会直接访问 Android 框架中的不稳定的 API,因此这些应用必须预安装在设备上,并且只能在设备的系统软件更新时进行更新。)
Android应用开发四大组件
- 活动(Activity):用户操作的可视化界面,可以理解为前台能看到的界面都是活动
- 服务(Service):后台处理耗时的一些逻辑,服务的运行不依赖用户界面,分为startService(不交互用,与activity没有关联,不受影响,独立运行)和bindService(可以交互,与activity存在关联,退出activity时必须调用unbindService方法)
- 广播接收者(Broadcast Receive):接受外部信息
- 内容提供者(Content Provider):保存和获取数据
应用程序框架层(Application Framework/Java API Framework)
应用程序的体系结构旨在简化组件的重用,任何应用程序都能发布它的功能且任何其他应用程序都可以使用这些功能(需要服从框架执行的安全限制),这一机制允许用户替换组件。开发者完全可以访问核心应用程序所使用的 API 框架。通过提供开放的开发平台,Android 使开发者能够编制极其丰富和新颖的应用程序。开发者可以自由地利用设备硬件优势访问位置信息、运行后台服务、设置闹钟、向状态栏添加通知等。(这一层主要提供了构建应用程序时可能用到的各种API,Android自带的一些核心应用就是使用这些API完成的,开发者也可以自行使用这些API进行自己App的开发)这一层是编写Google发布的核心应用时所使用的API框架,开发人员同样可以使用这些框架来开发自己的应用,这样便简化了程序开发的结构设计,但是必须要遵守其框架的开发原则。
下面这张图将应用程序框架层细分为系统API(系统 API 表示仅供合作伙伴和 OEM 纳入捆绑应用的 Android API。这些 API 在源代码中被标记为 @SystemApi)、Android API(Android API 是面向第三方 Android 应用开发者的公开 API Android API 参考文档)、Android框架(构建应用所依据的一组 Java 类、接口和其他预编译代码。框架的某些部分可通过使用 Android API 公开访问。框架的其他部分只能由 OEM 通过系统 API 来访问。Android 框架代码在应用进程内运行。)
系统运行库层(C/C++库及Android运行库)
Android 本地框架是由 C/C++ 实现的,包含 C/C++ 库,以供 Android 系统的各个组件使用。这些功能通过 Android 的应用程序框架为开发者提供服务。这一层通过一些C/C++库来为Android提供主要的特性支持。同样在这一层还要Android运行时库,它主要提供了一些核心库,能够允许开发者使用Java语言来编写Android程序。不仅如此,它还包括了Dalvik虚拟机(android5.0后改为ART运行环境),它使得每一个android程序可以运行在独立的进程中,并且拥有自己的Dalvik虚拟机实例。相较于Java虚拟机,Dalvik是专门为移动设备定制的,它针对手机内存、CPU性能有限等情况做了优化处理。
硬件抽象层(Hardwar Abstraction Layer,HAL)
硬件抽象层提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。
HAL 包含多个库模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如:Audio音频模块,BlueTooth:蓝牙模块,Camera:相机模块,Sensors:传感器。系统内置对传感器的支持达13种,他们分别是:加速度传感器(accelerometer)、磁力传感器(magnetic field)、方向传感器(orientation)、陀螺仪(gyroscope)、环境光照传感器(light)、压力传感器(pressure)、温度传感器(temperature)和距离传感器(proximity)等。当框架 API 要求访问设备硬件时,Android 系统将为该硬件组件加载库模块。厂商会在这层定义自己的HAL接口。
可以注意到下面这张图,在HAL层以下还有一层原生守护程序和库,该层中的原生守护程序包括 init
、healthd
、logd
和 storaged
。这些守护程序直接与内核或其他接口进行交互,并且不依赖于基于用户空间的 HAL 实现。该层中的原生库包括 libc
、liblog
、libutils
、libbinder
和 libselinux
。这些原生库直接与内核或其他接口进行交互,并且不依赖于基于用户空间的 HAL 实现。
Linux内核层(Linux Kernel)
Android 内核基于上游 Linux 长期支持 (LTS) 内核。在 Google,LTS 内核会与 Android 专用补丁结合,形成所谓的“Android 通用内核 (ACK)”。较新的 ACK(版本 5.4 及更高版本)也称为 GKI 内核。GKI 内核支持将与硬件无关的通用核心内核代码和 GKI 模块与硬件专用供应商模块分离开来。
系统启动过程
Secure Boot
安全启动可以保证每一级加载的image都是没有被篡改的,从boot rom到kernel为止的安全启动,统一使用一把RSA公钥完成安全校验,并且当前这级的RSA Key已经作为自身固件的一部分,由前一级loader完成了安全校验,从而保证了Key的安全。
RSA公钥一般写在OTP或EFUSE的区域,这种区域一般只可以写入一次,但是由于这些区域存储空间可能较小,一般不直接将公钥写入该区域,而是将公钥的hash值写入该区域,在使用公钥时校验hash值就可以保证公钥没有被篡改。
secure boot签名的大致流程:
- 计算镜像的hash值
- 利用私钥对hash值签名
- 签名结果存在FIT Image[4] 中。
secure boot验签的大致流程:
- 读取FIT Image
- 获得pubkey
- 从FIT Image 提取签名
- 计算镜像的hash
- 使用公钥验签获得hash值,与计算得到的hash值进行对比
Boot ROM:
此步骤称为上电和系统启动。这意味着每当我们按下电源按钮时,Boot ROM 代码就会从 ROM 中硬连线的预定义位置开始执行。 Boot ROM 将 BootLoader 加载到 RAM 中并开始执行。
BootLoader:
Bootloader是低级代码,包含告诉设备如何启动并找到系统内核的指令。引导加载程序是制造商放置锁和限制的地方。
引导加载程序是在任何操作系统开始运行之前执行的代码。 BootLoader 分两个阶段执行:
- 在第一阶段,它检测外部 RAM 并加载有助于第二阶段的程序。
- 在第二阶段,引导加载程序设置运行内核所需的网络、内存等。
内核内核启动后,它会启动设置缓存、受保护的内存、调度、加载驱动程序、启动内核守护程序、挂载根文件系统、初始化输入/输出、启动中断、初始化进程表。内核是最低级别的易于替换的软件,与我们设备中的硬件交互。当内核完成系统设置时,它首先会在系统文件中查找“init”并启动根进程或系统的第一个进程。
注:MTK平台的bootloader
在MTK平台,bootloader第一阶段是preloader加载lk(u-boot)阶段,另一个是lk加载kernel阶段:
1-3:设备上电起来后,Boot code把preloader加载起到ISRAM, 因为当前DRAM(RAM分SRAM跟DRAM,简单来说SRAM就是cache,DRAM就是普通内存)还没有准备好,所以要先把pre-loader load到芯片内部的ISRAM(Internal SRAM)中;
4-6:preloader初始化好DRAM后就将lk从flash(nand/emmc)中加载到DRAM中运行;
7-8:解压bootimage成ramdisk跟kernel并载入DRAM中;
9-11:lk跳转到kernl初始化, kernel初始化完成后fork出init进程, 然后拉起ramdisk中的init程序,进入用户空间初始化。
Init
Init是第一个进程,或者我们可以说它是所有进程的祖父。
Init 进程有 2 个职责:
- 挂载 /sys、/dev 或 /proc 等目录
- 运行 /init.rc 脚本。 init.rc 负责系统的初始设置。
init 进程将设置所有本机服务,与常规 Linux 系统启动类似。
Zygote 和 Dalvik VM
Zygote是一个虚拟机进程,在系统启动时启动。当app_process启动Zygote时,它首先创建Dalvik虚拟机,然后调用Zygote的main()方法。Zygote通过/dev/socket/ Zygote收到启动应用程序的请求,一旦收到请求,它就会触发fork()调用。
当一个进程是一个分支时,它会创建一个自己的克隆。它在另一个存储空间复制自己,效率很高。当这种情况发生在Zygote上时,它会创建一个精确而干净的新Dalvik虚拟机作为线程,预加载任何应用程序所需的所有必要类和资源。这使得创建VM和加载资源的过程非常有效。
它支持跨Dalvik VM的代码共享,这有助于实现最小的启动时间。
系统服务
Zygote 预加载所有必需的 Java 类和资源后,它启动系统服务。系统服务是Android系统的核心。首先发生的事情是服务器将加载一个名为 android_servers 的本机库,该库提供本机功能的接口。
然后调用将设置本机服务的本机init方法。设置本机服务后,它会创建服务器线程。该线程将按照必要的启动顺序启动系统中剩余的服务。
每个服务都在 SystemServer 中的单独 Dalvik 线程中运行。
一旦系统服务在内存中启动并运行,Android就完成了启动过程,此时“ACTION_BOOT_COMPLETED”标准广播动作将被触发。
参考
架构概览 | Android Open Source Project (google.cn)
管理启动时间 | Android Open Source Project (google.cn)
https://www.linkedin.com/pulse/android-booting-process-from-power-p-srinivas-rao