第十四章 android启动过程详解
14.1 系统的启动过程
在android中,在Bootloader记载系统映像后,会通过 system\core\rootdir\ 目录下的 init.rc 脚本进行初始化配置。
在init.rc中可以配置系统时区,设置日志等级,设置全局环境变量,挂载文件系统,初始化网络配置,配置系统属性,启动守护进程等,具体过程如下:
启动过程中的配置是系统正常运行的基本保证。
14.1.1 系统属性配置
系统属性包括多个方面,在开机启动时,android将进行一系列的配置:
》配置系统时区。android默认设置系统时区为GMT 0。设置系统时区的方法如下: sysclktz 0 //东8区
》设置日志等级。在android中,日志分为多个等级,设置控制台输出的日志等级,默认为3,设置方法: loglevel 3
》设置全局变量。系统启动过程中,android需设置全局变量,具体入下:
》初始化网路配置。在android中,网络配置包括对lo和其他网络接入点的信息进行配置,配置网络参数的工具是 ifup, 具体如下:
ifup lo
hostname localhost
demainname localdomain
》配置系统属性。可在编译脚本中对其进行设置。ActivityManagerSerivice中用到的一些配置 oom_adj 值的系统属性如下:
oom_adj值通常被android特有的内存管理驱动 Low memory killer使用,它会在系统内存低于设定值时释放响应的进程,保证系统的稳定运行。
Low memory killer 根据两个原则(进程的重要性和释放这个进程可获取的空闲内存数量)来决定释放的进程。 oom_adj 值越小,表示该类型的重要性越高。 oom_adj 相同的情况下,占用内存到的进程有限被撤销,进程占用的内存可用 get_mm_rss 进行判断。
14.1.2 文件系统挂载
配置好文件系统分区并设置好分区表后,在设备实际启动前,需要挂载文件系统。
1. 创建挂载点并设置权限
完成环境变量设置后,接下来就是创建挂载点并根据安全需要设置相应的权限。对于敏感信息,应避免普通用于对其用于可写甚至可读的权限。权限的主要设置方法如下;
以上可看出,android 有两个比较重要的用户权限,即 root, system ,其中root 权限最高,对于系统和读写的路径至少应设置system权限。
2. 挂载文件系统
挂载点配置完成后,挂载文件系统。目前android默认的几个文件系统为system, data, cache等。挂载方法如下:
默认情况下,android支持的问价系统类型为 yaffs2 。
14.1.3 守护进程启动
守护进程是运行在后台的android核心进程,主要包括 servicemanager, vold, netd, debuggerd, ril-daemon, zygote, drm, drmio, media, bootanim, dbus, bluetoothd, hfag, hsag, opush, pbap, installd, falsh_recovery, ravonn, mtpd, keystore, dumpstate 等。
1. 守护进程的配置(在init.rc中完成)
由于守护进程之间可能存在依赖关系,或守护进程对其他配置存在依赖,在启动进程时,需要做些配置。 在system\core\init\目录下的 readme.txt 中介绍了包括守护进程在内的启动项配置方法,其中守护进程的配置方法如下:
service <name> <pathname> [<argument>] *
service <name> <pathname> [<argument>] *
<option>
......
上述代码中,option有多种情况:
2. 守护进程的启动
下面介绍各守护进程的启动和配置和作用。
(1)servicemanager
系统服务的管理器,通过一个HashMap<String, IBinder>来管理系统服务,当
servicemanager重启时,会导致zygote和media重启。 下面是servicemanager的启动配置:
(2)vold的配置
vold主要用来处理热插拔,其实际上就是负责完成系统的CDROM, USB大容量存储和MMC卡等扩展存储的挂载任务的守护进程。vold和linux标准的udev类似,均通过sysfs为内核和用于层提供通信。下面是vold的启动配置:
热插拔的处理框架:
和vold相关的代码主要位于 system/vold目录下。
(3)netd
主要用来监控网络状态,进行网络管理,其实现位于system\netd目录下,其启动配置如下:
(4)debuggerd
主要用于调试,启动后会监听UNIX套接字 android:debuggerd, 其实现位于system\core\debuggerd目录下, 其启动配置如下:
(5)ril-daemon
RIL守护进程会初始化芯片厂商的RIL, 管理所有来自Android通信服务的通信,通过套件字实现与芯片厂商的RIL的通信,其实现与hardware/ril/rild无关,配置过程如下:
Android的RIL位于
应用程序
框架与
内核之间,分成了两个部分,一个部分是rild,它负责socket与应用程序框架进行通信。另外一个部分是Vendor RIL,这个部分负责向下是通过两种方式与radio进行通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的
上网功能。
对于RIL的java框架部分,也被分成了两个部分,一个是RIL模块,这个模块主要用于与下层的rild进行通信,另外一个是Phone模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。
(6)zygote
是android启动后启动的第一个linux进程,其他的linux进程,比如各个应用,均是由zygote产生的。关于zygote的实现可参考ZygoteInit.java。zygote服务的重启会导致media和netd服务的重启,其启动配置如下:
(7)drm
用于数字版权保护,其启动配置如下:
(8)drmio
同样用于数字版权保护,配置如上
(9)media
提供多媒体服务的守护进程,它会启动 AudioFlinger, MediaPlayerSerivce, CameraService, AudioPolicySerivce等服务。media服务的入口实现如下:
media服务的启动配置如下:
(10)bootanim
即所谓的开机动画服务,其具体实现位于BootAnimation.cpp中。 SurfaceFlinger 在完成准备工作后会在reayToRun()方法中,通过 porperty_set("ctl.start", "bootanim") 启动bootanim服务。在
bootanim服务启动过程中,会加载用户开机动画和系统开机动画,均为zip格式,所在的位置为 data\local\bootanimation.zip, 和 \system\media\bootanimation.zip。动画中图片的格式应为RGB565,其实现位于frameworks\base\cmds\bootanimation目录中,bootanim服务的启动配置如下:
(11)dbus
dbus和OpenBinder一样都是进程间的通信机制,android进程间通信主要利用的是OpenBinder, dbus仅用于蓝牙协议栈Bluez中。启动配置如下:
(12)bluetoothd
为bluez的守护进程,默认是不启动的。启动配置入下:
(13)hfag
也用于Bluez, 作用是启动蓝牙免提音频网关,默认不启动。
(14) hsag
也用于Bluez, 作用是启动蓝牙耳机音频网关,默认不开启
(15)opush
实现 Exchange ActiveSync服务器协议,同样也用于Bluez。默认不启动
(16)installd
安装守护线程,用于apk的安装
(17)flash_recovery
用于系统发生故障时的恢复。
(18)racoon
是VPN的守护进程。
14.2 应用的启动过程
AAPT允许开发者查看,创建,更新与ZIP箭筒的压缩文件(ZIP, JAR, APK),同时还将资源编译到断言(asset)中。
APK包中,通常可以看到res, AndroidManifest.xml, classes.dex, resource.arsc, META-INF 和libs等几项。其中
META-INF 中存放的是Android的数字签名证书。
需要说明的是,在android中,采用的java混淆器为开源的ProGuard4.4。ProGuard可以在一定程度上防止别人的窥视,但对于需要保护的敏感信息,其安全性是无法保证的。
android生成的java字节码为DEX字节码,而非传统的CLASS字节码,但编译过程中,是先将java 文件编译为 CLASS字节码,然后将 CLASS字节码转化为 DEX字节码。
14.2.1 应用的启动配置
android应用由一个继承了ContextWrapper的Application构成,其中Application由 Activity, Service, Receiver, Provider, uses-library等组件构成。组件之间的关系:
需要注意的是,应用程序并非必须具有activity,如果仅是后台程序,仅有application也是可以的。
如果不希望应用在系统低内存时被系统销毁,需将application标签的 android:persistent 属性设置为 true.
14.2.2 应用的启动过程
要启动一个应用,首先要创建一个进程,然后启动UI主线程,接着打开Activity,具体流程如下:
android进程分为zygote进程和普通进程。当启动一个应用时,Dalvik虚拟机会先通过 NativeStart 接口初始化JNI,为通过 zygote进程创建新进程做好准备。原生代码的启动位于: app_main.app中
第十五章 深入解析android系统管理
15.1 内存管理
在创建进程时,Dalvik虚拟机会为每个进程分配一定量的堆内存。占用内存较多的程序很容易引起OutOfMemoryError等异常。
在数据交换比较频繁的场景中,多用SQLite来进行缓存。
15.1.1 对应引用
java对象的引用被分为 强引用(HardReference), 弱引用(WeakReference), 软引用(SoftReference)和 虚引用(PhantomReference)等4个级别。
强引用表示 即使虚拟机内存“吃紧”抛出了 OutOfMemoryError 异常,该类型的对象也不会被回收;
软引用表示 在虚拟机内存“吃紧”抛出了 OutOfMemoryError 异常前,该类型对象会被回收;
弱引用 更适合那些数量不多,但体积较庞大的对象,弱引用对象最容易被回收
虚引用一般没有实际意义,仅观察GC的活动状态,对于 测试比较实用,必须和引用队列(ReferenceQueue)一起使用。
弱引用,软引用,虚引用均可与引用队列联合使用,当引用的对象被回收时,Dalvik会把该引用对象加入到与之关联的引用队列。
默认情况下,创建的java对象均为强引用。创建引用队列的方法如下:
ReferenceQueue<String> rq = new ReferenceQueue<String>()
创建一个弱引用,并将对象和引用对象关联的方法如下:
WeakReference<String> wf = new WeakReference<String>( str, rq );
15.1.2 垃圾回收策略
android中,每个应用占据一个虚拟机,所以android的垃圾回收是基于应用进行的。
在应用层,通过调用 System.gc() 可以调用垃圾回收器回收垃圾。
事实上,在android的原生代码层,通过引入引用计数机制,Android 同样实现了自动垃圾回收机制,相关的实现位于 \frameworks\base\include\utils\RefBase.h 中。几乎所有的原生类均继承了 RefBase类,RefBase类会维护对象的强引用和弱引用计数,一旦强引用计数为0, 对象自动释放自己。原生代码的垃圾回收机制如下图所示:
其中维护强引用的指针为 sp, 维护弱引用的指针为 wp(weak pointer), sp 和 wp 均依赖于 RefBase类,因此继承与
RefBase类的原生类才具有自动垃圾回收的能力。
1. sp 的实现
待完善