1 引言
本文作为Android系统架构的开篇,起到提纲挈领的作用,从系统整体架构角度概要讲解Android系统的核心技术点,带领大家初探Android系统全貌以及内部运作机制。虽然Android系统非常庞大且错综复杂,需要具备全面的技术栈,但整体架构设计清晰。Android底层内核空间以Linux Kernel作为基石,上层用户空间由Native系统库、虚拟机运行环境、框架层组成,通过系统调用(Syscall)连通系统的内核空间与用户空间。对于用户空间主要采用C++和Java代码编写,通过JNI技术打通用户空间的Java层和Native层(C++/C),从而连通整个系统。
为了能让大家整体上大致了解Android系统涉及的知识层面,先来看一张Google官方提供的经典分层架构图,从下往上依次分为Linux内核、HAL、系统Native库和Android运行时环境、Java框架层以及应用层这5层架构,其中每一层都包含大量的子模块或子系统。
上图采用静态分层方式的架构划分,众所周知,程序代码是死的,系统运转是活的,各模块代码运行在不同的进程(线程)中,相互之间进行着各种错终复杂的信息传递与交互流,从这个角度来说此图并没能体现Android整个系统的内部架构、运行机理,以及各个模块之间是如何衔接与配合工作的。
1.1 Linux内核层
Android以Linux操作系统内核为基础,借助Linux内核服务实现硬件设备驱动,进程和内存管理,网络协议栈,电源管理,无线通信、设备驱动、文件系统、Binder (IPC) 驱动等核心功能。Android4.0版本之前基于Linux2.6系列内核,4.0及之后的版本使用更新的Linux3.X内核,并且两个开源项目开始有了互通。Linux3.3内核中正式包括一些Android代码,可以直接引导进入Android。Linux3.4将会增添电源管理等更多功能,以增加与Android的硬件兼容性,使Android在更多设备上得到支持。
Android内核 对Linux内核进行了增强,增加了一些面向移动计算的特有功能。例如,低内存管理器LMK(Low Memory Keller),匿名共享内存(Ashmem),以及轻量级的进程间通信Binder机制等。这些内核的增强使Android在继承Linux内核安全机制的同时,进一步提升了内存管理,进程间通信等方面的安全性。下表列举了Android内核的主要驱动模块:
驱动名称 | 说明 |
---|---|
电源管理(Power Manager) | 针对嵌入式设备,基于标准Linux电源管理系统,轻量级的电源管理驱动 |
低内存管理器(Low Memory Killer) | 拓展了Linux的OOM机制,形成独特的LMK机制 |
匿名共享内存(Ashmem) | 为进程之间提供共享内存资源,同时为内核提供回收和管理内存的机制 |
日志(Android Logger) | 轻量级的日志设备 |
定时器(Android Alarm) | 用于把设备从睡眠状态唤醒 |
物理内存映射管理(Android PMEM) | DSP以及其他设备只能工作在连续的物理内存上,PMEM用于向用户空间提供连续的物理内存区域映射 |
Yaffs2文件系统 | Android采用大容量的NADN闪存作为储存设备,使用Yaffs2 作为文件系统管理大容量MTD NAND Flash;Yaffs2占用内存小,垃圾回收简洁迅速。 |
定时设备(Android Timed device) | 可以执行对设备的定时控制功能 |
Paranoid网络 | 对Linux内核的网络代码进行了改动,增加了网络认证机制。可在IPV4、IVP6和蓝牙中设置,由ANDROID_PARANOID_NETWORK宏来启用此特性。 |
1.2 HAL-硬件抽象层
许多硬件设备厂商不希望公开其设备驱动的源代码,如果能将android的应用框架层与linux系统内核的设备驱动隔离,使应用程序框架的开发尽量独立于具体的驱动程序,则android将减少对Linux内核的依赖,HAL由此而生。
HAL(Hardware Abstract Layer硬件抽象层)是Google因应厂商「希望不公开源码」的要求下,所推出的新观念。它对Linux内核驱动程序进行封装,将硬件抽象化,屏蔽掉底层的实现细节。规定了一套应用层对硬件层读写和配置的统一接口,本质上就是将硬件的驱动分为用户空间和内核空间两个层面,Linux内核驱动程序运行于内核空间,硬件抽象层运行于用户空间。HAL设备驱动抽象使得应用程序和驱动程序之间很明显地区分开来。 驱动抽象促进了应用程序代码的可重用性,应用程序和底层硬件的通信依靠统一的接口函数,底层硬件的改动对应用程序的代码没有影响。而且,HAL标准使得和已有外设相匹配的新外设的驱动程序编写起来更加简单。
1.3 Android Runtime
Java核心库
核心库提供了Java5 se API的多数功能,并提供Android的核心API,如android.os,android.net,android.media等。该核心库提供了JAVA编程语言核心库的大多数功能。Dalvik VM(5.0之前)
Dalvik VM在2010年5月21日,Google公司发布的Android2.2版本中正式使用,一直沿用到Android5.0版本。借助新的Dalvik JIT编译器(Just In Time,即时编译器),CPU密集型应用的速度比Android2.1快2-5倍,Android2.2还改进了App可以安装在SD卡(外部存储)上。
Dalvik VM前身基于apache的java虚拟机,并被改进以适应低内存,低处理器速度的移动设备环境。Dalvik VM基于寄存器,使用.dex可执行文件,依赖于Linux内核,实现进程隔离与线程调试管理,安全和异常管理,垃圾回收等重要功能。ART VM(5.0正式启用)
Dalvik虚拟机从DEX(Dalvik Executable)格式的文件中读取指令与数据,进行解释运行,JIT 在运行时将字节码翻译为本地机器指令,运行一次就要编译一次,性能还是很低。于是Android4.4版本支持了ART模式,并在Android5.0正式将编译模式由ART取代Dalvik VM成为默认选项。
ART 采用预编译AOT(ahead-of-time)技术,在应用程序安装时,将程序代码一次性转化为机器语言,省去了程序在运行时每次编译的时间,加快了程序运行速度,使得电量消耗更少,系统更加流畅。
1.4 Native C/C++ Libraries
系统类库大部分由C/C++编写,所提供的功能通过Android应用程序框架为开发者所使用,如资源文件管理、基础算法库。第三方类库,极大的加快了Android应用的开发周期。
主要的第三方类库及说明如下表:
库名称 | 说明 |
---|---|
Webkit | Web浏览器的软件引擎 |
OpenMAX AL | 多媒体应用程序的框架标准,由NVIDIA公司和Khronos在2006年推出 |
Libc | 继承自BSD的c函数库bionic libc,更适合基于嵌入式Linux的移动设备 |
Media Framework | 基于Packet Video Open Core的多媒体库,支持多种常用的音频和视频格式的录制和回放,所支持的编码格式包括MPEG4,MP3,H264,AAC,ARM |
OpenGL ES | 基于OpenGL ES1.0 API标准实现的3D跨平台图形库 |
Surface Manager | 执行多个应用程序时,管理子系统的显示,另外对2D和3D图形提供支持; |
SQLite | 本地小型关系数据库,android提供了一些新的SQLite数据库API,以替代传统的耗费资源的JDBC API |
FreeType | 用于显示位图和矢量字体 |
SGL | 底层的2D图形引擎 |
SSL | 安全套接层,为网络通信提供安全及数据完整性的一种安全协议 |
除上表列举的主要系统类库之外,Android NDK(Native Development Kit),即Android原生库,也十分重要。NDK为开发者提供了直接使用Android系统资源,并采用C或C++语言编写程序的接口。因此,第三方应用程序可以不依赖于Dalvik虚拟机进行开发。实际上,NDK提供了一系列从C或C++生成原生代码所需要的工具,为开发者快速开发C或C++的动态库提供方便,并能自动将生成的动态库和java应用程序一起打包成应用程序包文件。
1.5 Application Framework(应用框架层)
应用框架层提供了一些类的类库框架,方便开发人员调用,通过组件重用能够快速开发出应用程序。具体模块如下表所示:
管理器/组件 | 功能 |
---|---|
Content Provider(内容提供器) | 提供一个应用程序访问另一个应用程序数据的功能,或者实现应用程序之间的数据共享 |
View System(视图系统) | 提供各种视图控件,如列表、网格、文本框、按钮、可嵌套的web浏览器 |
Activity Manager(活动管理器) | 管理各种应用程序生命周期,为所有程序的窗口提供交互的接口 |
Location Manager(位置管理器) | 提供位置服务 |
Package Manager(包管理器) | 对应用程序进行管理,如安装应用程序、卸载应用程序、查询相关权限等 |
Notification Manager(通知管理器) | 使应用程序可以在状态栏中显示自定义的提示信息 |
Resource Manager(资源管理器) | 提供各种非代码资源供应用程序使用,如本地化字符串、图片、音频等 |
Telephony Manager(电话管理器) | 提供电话相关信息的查询/修改功能,以及状态监听功能 |
Window Mangaer(窗口管理器) | 对开启的窗口进行管理 |
1.6 Apps
该层提供一些核心应用程序包,例如电子邮件、短信、日历、地图、浏览器和联系人管理等。同时,开发者可以利用Java语言设计和编写属于自己的应用程序,而这些程序与那些核心应用程序彼此独立。
为了更深入地掌握Android整个架构思想以及各个模块在Android系统所处的地位与价值,计划以Android系统启动过程为主线,以进程的视角来诠释Android M系统全貌,全方位的深度剖析各个模块功能,争取各个击破。这样才能犹如庖丁解牛,解决、分析问题则能游刃有余。
2 Android架构
Google提供的5层架构图很经典,但为了更进一步透视Android系统架构,本文更多的是以进程的视角,以分层的架构来诠释Android系统的全貌,阐述Android内部的环环相扣的内在联系。
系统启动架构图
图解:Android系统启动过程由上图从下往上的一个过程是由Boot Loader引导开机,然后依次进入 -> Kernel -> Native -> Framework -> App,接来下简要说说每个过程:
关于Loader层:
- Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在 ROM里的预设代码开始执行,然后加载引导程序到 RAM;
- Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。
2.1 Linux内核层
Android平台的基础是Linux内核,比如ART虚拟机最终调用底层Linux内核来执行功能。Linux内核的安全机制为Android提供相应的保障,也允许设备制造商为内核开发硬件驱动程序。
- 启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作;
- 启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。 kthreadd进程是所有内核进程的鼻祖。
2.2 硬件抽象层 (HAL)
硬件抽象层 (HAL) 提供标准接口,HAL包含多个库模块,其中每个模块都为特定类型的硬件组件实现一组接口,比如WIFI/蓝牙模块,当框架API请求访问设备硬件时,Android系统将为该硬件加载相应的库模块。
2.3 Android Runtime & 系统库
每个应用都在其自己的进程中运行,都有自己的虚拟机实例。ART通过执行DEX文件可在设备运行多个虚拟机,DEX文件是一种专为Android设计的字节码格式文件,经过优化,使用内存很少。ART主要功能包括:预先(AOT)和即时(JIT)编译,优化的垃圾回收(GC),以及调试相关的支持。
这里的Native系统库主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程, init进程是所有用户进程的鼻祖。
- init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程;
- init进程还启动 servicemanager(binder服务管家)、 bootanim(开机动画)等重要服务;
- init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程),** Zygote是所有Java进程的父进程**,Zygote进程本身是由init进程孵化而来的。
2.4 Framework层
Zygote进程,是由init进程通过解析init.rc文件后fork生成的,Zygote进程主要包含:
- 加载ZygoteInit类,注册Zygote Socket服务端套接字
- 加载虚拟机
- 提前加载类preloadClasses
- 提前加载资源preloadResouces
- System Server进程,是由Zygote进程fork而来,** SystemServer是Zygote孵化的第一个进程**,System Server负责启动和管理整个Java framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服务。
- Media Server进程,是由init进程fork而来,负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service等服务。
2.5 App层
- Zygote进程孵化出的第一个App进程是Launcher,这是用户看到的桌面App;
- Zygote进程还会创建Browser,Phone,Email等App进程,每个App至少运行在一个进程上。
- 所有的App进程都是由Zygote进程fork生成的。
2.6 Syscall && JNI
- Native与Kernel之间有一层系统调用(SysCall)层,见Linux系统调用(Syscall)原理;
- Java层与Native(C/C++)层之间的纽带JNI,见Android JNI原理分析。
3 通信方式
无论是Android系统,还是各种Linux衍生系统,各个组件、模块往往运行在各种不同的进程和线程内,这里就必然涉及进程/线程之间的通信。对于IPC(Inter-Process Communication, 进程间通信),Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制。Android OS中的Zygote进程的IPC采用的是Socket机制,在上层system server、media server以及上层App之间更多的是采用Binder IPC方式来完成跨进程间的通信。对于Android上层架构中,很多时候是在同一个进程的线程之间需要相互通信,例如同一个进程的主线程与工作线程之间的通信,往往采用的Handler消息机制。
想深入理解Android内核层架构,必须先深入理解Linux现有的IPC机制;对于Android上层架构,则最常用的通信方式是Binder、Socket、Handler,当然也有少量其他的IPC方式,比如杀进程Process.killProcess()采用的是signal方式。下面说说Binder、Socket、Handler:
3.1 Binder
Binder作为Android系统提供的一种IPC机制,无论从系统开发还是应用开发,都是Android系统中最重要的组成,也是最难理解的一块知识点。深入了解Binder机制,最好的方法便是阅读源码,借用Linux鼻祖Linus Torvalds曾说过的一句话:Read The Fucking Source Code。下面简要说说Binder IPC原理。
Binder通信采用c/s架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。
想进一步了解Binder,可查看Binder系列—开篇,Binder系列花费了13篇文章的篇幅,从源码角度出发来讲述Driver、Native、Framework、App四个层面的整个完整流程。根据有些读者反馈这个系列还是不好理解,这个binder涉及的层次跨度比较大,知识量比较广,建议大家先知道binder是用于进程间通信,有个大致概念就可以先去学习系统基本知识,等后面有一定功力再进一步深入研究Binder机制。
3.2 Socket
Socket通信方式也是C/S架构,比Binder简单很多。在Android系统中采用Socket通信方式的主要有:
- zygote:用于孵化进程,system_server创建进程是通过socket向zygote进程发起请求;
- installd:用于安装App的守护进程,上层PackageManagerService很多实现最终都是交给它来完成;
- lmkd:lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成;
- adbd:这个不用说,用于服务adb;
- logcatd:这个不用说,用于服务logcat;
- vold:即volume Daemon,是存储类的守护进程,用于负责如USB、Sdcard等存储设备的事件处理。
等等还有很多,这里不一一列举,Socket方式更多的用于Android framework层与native层之间的通信。Socket通信方式相对于binder比较简单,这里省略。
3.3 Handler
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信,Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的,为了方便且称之为Handler消息机制。
有人可能会疑惑,为何Binder/Socket用于进程间通信,能否用于线程间通信呢?答案是肯定,对于两个具有独立地址空间的进程通信都可以,当然也能用于共享内存空间的两个线程间通信,这就好比杀鸡用牛刀。接着可能还有人会疑惑,那handler消息机制能否用于进程间通信?答案是不能,Handler只能用于共享内存地址空间的两个线程间通信,即同进程的两个线程间通信。很多时候,Handler是工作线程向UI主线程发送消息,即App应用中只有主线程能更新UI,其他工作线程往往是完成相应工作后,通过Handler告知主线程需要做出相应地UI更新操作,Handler分发相应的消息给UI主线程去完成,如下图:
由于工作线程与主线程共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程与主线程都能直接使用该对象,只需要注意多线程的同步问题。工作线程通过mHandler向其成员变量MessageQueue中添加新Message,主线程一直处于loop()方法内,当收到新的Message时按照一定规则分发给相应的handleMessage()方法来处理。所以说,Handler消息机制用于同进程的线程间通信,其核心是线程间共享内存空间,而不同进程拥有不同的地址空间,也就不能用handler来实现进程间通信。
上图只是Handler消息机制的一种处理流程,是不是只能工作线程向UI主线程发消息呢,其实不然,可以是UI线程向工作线程发送消息,也可以是多个工作线程之间通过handler发送消息。
参考资料:
[1] Android系统架构开篇 ★★★
[2] 从Android架构图看开发所需的知识点
[3] Android系统架构 ★