前言:
已经记不清楚是什么时候第一次听说有Android这个系统,唯一清楚的是Android系统是Google开发的基于Linux kernel的智能手机操作系统,直到从去年从淘宝上花了2000多大洋买了一台Galaxy Nexus,到现在用了有半年的时间了,版本也从最初的4.0升级到现在的4.2.2,对这个系统也越来越爱不释手。渐渐地再也不满足于平常的使用,作为多年的嵌农,早就想深入研究Android,一窥其中的奥秘。在此记录下我学习的整个历程,希望能坚持下来。
开篇:
首先在网上google了一下,发现对Android进行源码分析的文章并不少,不过大都着重于细节描述,初学者反而会陷入代码的细节中缺乏对全局的把握。 以我学习的经验初学新知识时先从大局入手,等了解系统框架的构成后再逐步深入,下图是Android系统的架构图:这幅图是Google的原创图,网上很多的架构图应该都是基于此图。完整的演讲稿和视频来自于2008 GoogleI/O大会(https://sites.google.com/site/io/anatomy--physiology-of-an-android),基础好的同学建议结合演讲稿认真看下整个演讲视频,强烈推荐!
看完此视频基本上对Android架构心中有数了。下面基于这篇演讲稿“Android Anatomy andPhysiology”从底层到上层逐步解剖:
Layer1: Linux Kernel
Linux内核,对应上图中红色部分。这是Android的操作系统层,主要用C语言编写。
1.1 Android基于Linux Kernel,但不是Linux。演讲稿上说当时是基于2.6.24,而从Linux 3.3开始Android对内核代码改动部分已经融入kernel主线。据说最新的Android 5.0是基Linux 3.8。
Android不是Linux。在我看来Android本质上就是Linux的一个变种,不管是上图绿色部分的Libraries还是黄色部分的Dalvik Virtual Machine,以及蓝色Application Framework的部分Service都直接使用了Linux kernel提供的API。甚至于虚拟机上层的Java库最终也是下层API的面向对象的封装(比如你可以自己编写一个Java类库接口提供到特定硬件的访问)。好像基本上所有的基于Linux的智能手机系统如Meego,Tizen等都不会在命名中提到Linux,可能长期以来在普通用户心中Linux就是界面不友好的代名词,厂商考虑到产品形象不会在产品名称中包含Linux字样。
1.2 没有本地窗口系统。这是肯定的,没有带X Window这种在Linux发行版中广泛使用的GUI系统。
1.3 没有glibc支持。废话,这是Linux kernel。
1.4 没有包含标准Linux中一系列工具集。一般的嵌入式Linux都不会包含,都是裁剪后的能满足需求的最小系统。
1.5 提供了内核增强补丁以支持Android。包括Alarm(时钟),Ashmem(匿名共享内存),Binder(IPC驱动),Power Management(电源管理),Logger(日志记录),Low Memory Killer(替代标准Linux中的OOM Killer,在内存不足时有选择性地杀掉非活跃进程)。补充:从Android 3.0开始在kernel的netfilter模块中又增加了xt_qtaguid支持应用程序流量计数,引入xt_quota2支持流量限制告警。
1.5.1 Binder IPC。为什么不用常规的IPC?这里给出了答案:常规IPC会引入过重的处理开销和安全漏洞,并且通过共享内存实现的IPC性能更高。
1.5.2 PM电源管理。Android的电源管理是基于标准的Linux电源管理模块,并提供的更多的策略和不同类型的wake lock。
Layer2: Native Libraries
本地库,对应架构图中绿色部分。大量使用了开源软件,主要用C/C++编写。
2.1 Bionic Libc库。为什么要使用Bionic Libc库而不是标准的Libc库,这里提到了几个优点:Bionic库是专为CPU和内存受限的嵌入式系统而实现的libc库,其代码短小运行速度很快,而且有很精简的pthread多线程库实现。另外其License不是GPL而是BSD,以此规避了GPL License的感染性。其缺点是:不支持某些POSIX标准功能,并不完全兼容glibc。
2.2 Function Libraries。Android所需要的各种功能库,如Webkit提供浏览器所需的各种基本功能,Media Framework提供音频视频的各种编解码,SQLite提供轻量级的关系型数据库。
2.3 Native Servers。本地服务器。如Surface Manager负责图像显示管理,Audio Manager负责音频管理。
2.4 Hardware Abstraction Libraries。这一层就是所谓的硬件抽象层,是用C/C++编写的动态库文件。这一层相当于是用户态程序和底层驱动交互的一个中间层,本质上还是在用户空间。其目的主要是为了规避GPL License,保护硬件设备商的知识产权。Layer3: Android Runtime
Android运行时库,对应架构图中黄色部分。主要用C/C++编写。
3.1 Dalvik Virtual Machine。Java世界的基础Dalvik虚拟机,Java代码所编写的.class/.jar文件最终都在编译时转换为.dex文件格式。专为嵌入式环境设计的设计,支持每个应用进程一个虚拟机实例,高度优化的字节码解释器,能更高效地使用运行时内存。在我看来,实际上每个Java应用在启动后都会动态链接到libdvm.so这个动态库文件,然后由这个动态库文件对.dex文件进行解释执行Java字节码。
3.2 Core Libraries。Java的基本核心库,提供Java常用的一些基本类,如标准I/O,文件访问,网络访问,图形界面等。
Layer4: Application Framework
应用层框架,对应架构图从上到下的第二层蓝色部分。
这一层为最上层的Java应用提供一些公共的服务和框架,主要用Java和C/C++混合编码。
其中公共服务主要分成两大类:核心平台服务和硬件服务。
4.1 核心平台服务包括Activity Manager(Java应用程序激活管理),Package Manager(Java应用程序安装卸载管理),Window Manager(图形界面窗口管理),Resource Manager(资源管理),Content Provider(数据共享服务),View System(也和图形界面管理有关)。
4.2 硬件服务包括Telephony Service(电话相关服务,如拨号,短信,彩信,GPRS等),Location Service(GPS定位服务),Bluetooth Service(蓝牙服务),WiFi Service(WiFi服务),USB Service(USB服务),Sensor Service(传感器服务,如重力感应,温度计等)。更多介绍请参考Google I/O的另一次演讲“Inside the Android Application Framework”。
前面部分是从软件系统架构上剖析Android的静态组成,下面是从运行时入手,剖析Android的系统的启动过程和各层间的交互。
Part1: Start-up Walkthrough
1.1 和常见的嵌入式Linux一样Kernel启动完成后会调用/sbin/init作为第一个启动的进程(进程pid为1)。此后init进程会解析/etc/init.rc文件启动一些daemon服务如usbd,adbd,debuggerd,rild。
service servicemanager /system/bin/servicemanager user system critical onrestart restart zygote onrestart restart media service vold /system/bin/vold socket vold stream 0660 rootmount ioprio be 2 service netd /system/bin/netd socket netd stream 0660 rootsystem service debuggerd /system/bin/debuggerd service ril-daemon /system/bin/rild socket rild stream 660 rootradio socket rild-debug stream 660radio system user root group radio cache inet miscaudio sdcard_rw service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server socket zygote stream 666 onrestart write/sys/android_power/request_state wake onrestart write /sys/power/stateon onrestart restart media onrestart restart netd |
注意:在这篇演讲稿中Zygote的启动在Service Manager之前,但根据我对Android 2.3源码中init.rc的分析,Service Manager的启动是在Zygote之前,甚至于1.1提到的一些daemon如debuggerd,rild都在ServiceManager之后。这篇演讲稿应该是基于比较老的Android版本,以下分析都是以Android 2.3源码为准。
1.3 启动Zygote进程,这个进程主要做哪些事情呢:
- 初始化Java虚拟机环境
- 预加载公共的Java类并在一个unix socket上监听
- 若收到请求则为指定的Java应用程序fork出一个新的虚拟机实例
- 通过fork调用加快了虚拟机实例的创建速度,并利用copy-on-write最大化利用有限的内存资源
这意味着所有的Java应用程序都是通过Zygote这个受精卵快速创建出新的Java应用,并且fork出来后Java的runtime环境以及公共的Java类已经初始化完毕了。不得不说在这里fork被应用得相当的精妙。
1.4 Runtime进程发送请求到Zygote启动System Service。(原文:Runtime process sends request for Zygote to start System Server)注意:在Android 2.3中System Server并不是这样启动的,并不是Runtime进程通过socket发送请求到Zygote要求fork出System Server,而是Zygote进程在启动后主动通过fork调用分裂出System Server。
1.5 启动到了System Server,现在看看System Server做了哪些事。
首先启动两个Native Service:Surface Flinger和Audio Flinger。在Android 2.3中实际上先启动的是Surface Flinger和Sensor Service。这两个Service都要通过Binder向Service Manager注册(下图中虚线部分表示注册Service)。
然后,System Server继续启动其他各种各样的Service,并且这些Service都要向Service Manager注册。
1.6 至此,系统环境准备完毕。下图中的RUNTIME代表Service Manager。
1.7 当系统准备完毕后,最终会启动HOME程序,这就是大家看到的手机桌面。可以看出,HOME也是一个独立的Linux进程,有自己的Dalvik虚拟机实例。
Part2: Layer Interaction
层间交互方式主要有三种:
· App -> Runtime Service -> lib
· App -> Runtime Service -> Native Service-> lib
· App -> Runtime Service -> Native Daemon ->lib
看起来很抽象吧,下面逐个分解:
2.1 第一种交互方式:App ->Runtime Service -> lib
在这里Runtime Service是一个独立的服务进程,这个服务进程会通过JNI直接调用Native Library中提供的服务接口函数并动态加载HAL Library(硬件抽象层的动态库)。这个硬件抽象层的动态库可能是厂商提供的闭源的.so文件,也可能需要调用底层的Kernel Driver完成其功能。由于Runtime Service是独立的进程,Application想要后这个进程交互就要用Binder IPC完成进程间通讯。
上图以位置定位服务作为例子:如果某个Java应用需要使用定位服务器,比如说xx地图需要知道当前的地理位置(经纬度),首先需要通过Binder IPC向Service Manager查询定位服务获取定位服务的Handle,然后再通过Binder IPC发送请求到Location Manager Service,这个Service会通知GpsLocationProvider并通过JNI调用GpsLocationProvider库,这个库会动态加载HAL层的libgps.so,并调用相关的函数接口去获取经纬度,当然这个函数接口可能还需要调用kernel driver中函数最终控制GPS硬件去获取位置信息。
2.2 第二种交互方式:App ->Runtime Service -> Native Service -> lib
在这种交互方式中,Runtime Service应该是一个Java类库而不是一个独立的进程。当然这个类库封装了一些JNI接口,这些接口(Native Service Binding)实际上通过Binder驱动获取Native Service的Handle,并通过Binder IPC与Native Service进行通信。Native Service是用C/C++写的服务进程,其最终会调用HAL Library甚至Kernel Driver来完成Application的请求。
MediaPlayer的交互方式就是这样的,具体可以查看原文档。
2.3 第三种交互方式:App ->Runtime Service -> Native Daemon -> lib
这种交互方式和第二种交互方式非常相似,区别是第二种方式中服务进程是Native Service,而这种方式的服务进程是Daemon;第二种方式是通过Binder IPC和服务进程通信,而现在是用socket。
后面用Telephony Manager为例描述了这种带socket的交互方式,具体请查看原文档。
小结:层间交互涉及到几个部分:
1. Applications和 Application Framework的交互。
1.1 在同一个Dalvik虚拟机进程中,Application可以直接调用Framework提供的公共类库。
1.2 在不同的Dalvik虚拟机进程中,Application通过Binder IPC调用Framework提供的类库。
2. Application Framework和Libraries的交互。
本质上就是Dalvik虚拟机和其他Native Library的交互,这里Dalvik虚拟机提供了JNI接口(Java Native Interface)实现这两层的双向通信。
3. Libraries和Linux Kernel的交互。
涉及到Linux系统知识,用户空间和内核空间之间的通信。
结语:
在研究这篇Google I/O文档的过程中,结合了 Android2.3的源码进行阅读,并根据自己的理解写下了这篇学习笔记。由于笔者并没从事Android相关的开发工作,认识上难免狭隘,如有错误之处,欢迎指正。