Android架构剖析

来源:互联网 发布:2016年网络热点事件 编辑:程序博客网 时间:2024/06/15 03:25

开篇:

首先在网上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

1.2  启动Service Manager这个服务管理进程,此进程会打开Binder驱动,负责Service的注册和查找。

注意:在这篇演讲稿中Zygote的启动在Service Manager之前,但根据我对Android 2.3源码中init.rc的分析,Service Manager的启动是在Zygote之前,甚至于1.1提到的一些daemondebuggerdrild都在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.3System Server并不是这样启动的,并不是Runtime进程通过socket发送请求到Zygote要求forkSystem Server,而是Zygote进程在启动后主动通过fork调用分裂出System Server

 

1.5  启动到了System Server,现在看看System Server做了哪些事。

首先启动两个Native Service:Surface Flinger和Audio Flinger。Android 2.3中实际上先启动的是Surface FlingerSensor Service。这两个Service都要通过BinderService 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系统知识,用户空间和内核空间之间的通信。

原创粉丝点击