Android系统启动分析(Init->Zygote->SystemServer->Home activity)
整个Android系统的启动分为Linux Kernel的启动和Android系统的启动。Linux Kernel启动起来后,然后运行第一个用户程序,在Android中就是init程序。
-------------------------------------------------
以下的内容应该算是学习笔记,特地整理成文。
-------------------------------------------------
1 init程序
init是linux系统中用户空间的第一个进程。由于Android是基于linux内核的,所以init也是Android系统中用户空间的第一个进程,它的进程号是1。init程序并不是由一个源文件组成的,而是由一组源代码文件的目标文件链接而成,这些文件位于如下目录:
<android source code directory>/system/core/init/
它的主要职责在于:
1)、挂载目录,比如/sys, /dev, /proc
mkdir("/dev", );
mkdir("/proc", );
mkdir("/sys", );
......
2)、初始化属性,提供property service(属性服务)管理Android系统中的属性
3)、处理配置文件命令(主要是init.rc脚本文件)
4)、性能分析和执行其它进程
2.1 init分析
init进程的入口函数是main,主要做了这些工作:
1)、解析配置文件,主要是系统配置文件init.rc和与硬件平台相关的配置文件(如init.xxx.rc),在此阶段,也会解析service。
init.rc文件包含五个类型的声明:
- Actions(动作以命令流程命名,有一个触发器决定动作是否发生)
on <trigger>
<command>
<command>- Commands
class_start <serviceclass>
Start all services of the specified class if they are not already running- Services(是init进程启动的程序,以及当服务退出时init进程会视情况重启服务)
- Options(选项是对服务的描述,它们影响init进程如何以及何时启动服务)
service <name> <pathname> [ <argument> ]*
<option>
<option>- Imports
import <path>
Parse an init config file, extending the current configuration.
Action | Service | 描述 |
on | early-init | 设置init进程以及它创建的子进程的优先级,设置init进程的安全环境 |
on | init | 设置全局环境,为cpu accounting创建cgroup(资源控制)挂载点 |
on | fs | 挂载mtd分区 |
on | post-fs | 改变系统目录的访问权限 |
on | post-fs-data | 改变/data目录以及它的子目录的访问权限 |
on | boot | 基本网络的初始化,内存管理等等 |
service | servicemanager | 启动系统管理器管理所有的本地服务,比如位置、音频、Shared preference等等 |
service | zygote | 启动zygote作为应用进程 |
2)、执行各个阶段的动作,创建zygote的工作就是在其中某个阶段完成。
3)、调用property_init初始化属性相关的资源,并且通过property_start_service启动属性服务。
4)、init进入一个无限循环,然后等待响应。
2.2 属性服务
应用程序可以通过这个属性机制,查询或设置属性。这个属性服务是怎么实现的呢?其中与init.c和属性服务有关的代码:
property_init();
property_set_fd = start_property_service();
2.2.1 属性服务初始化
首先创建存储空间,property_service.c中有property_init函数
void property_init(void){
init_property_area(); // 初始化属性存储区域
// 加载default.prop文件
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}
虽然属性区域是由init进程创建的,但Android系统希望其它进程也能读取这块内存里的东西。为了做到这一点,它做了以下的工作:
1)、把属性区域创建在共享内存上,而共享内存是可以跨进程的。
2)、Android利用gcc的constructor属性,指明了一个__libc_prenit函数,当bionic libc库被加载时,将自动调用这个__libc_preni,这个函数内部就将完成共享内存到本地进程的映射工作。这样一来,其它进程就知道了这个共享内存。
接着,客户端进程获取存储空间。
2.2.2 属性服务器的分析
2.2.2.1 启动属性服务器
init进程会启动一个属性服务器,而客户端只能通过与属性服务器交互来设置属性。
2.2.2.2 处理设置属性请求
接受请求的地方在init进程中,当属性服务器收到客户端请求时,init会调用handle_property_set_fd进行处理。
if(ufds[].revents == POLLIN){
handle_property_set_fd(property_set_fd);
}
当客户端的权限满足要求时,init就调用property_set进行相关处理。
if(check_perms(msg.name, cr.uid, cr.gid)){
property_set((char *) msg.name, (char *) msg.value);
}
2.2.2.3 客户端发送请求
客户端通过property_set发送请求,property_set由libcutils库提供。
int property_set(const chatr *key, const char *value){
prop_msg msg;
unsigned resp;
// other code
......
msg.cmd = PROP_MSG_SETPROP; // 设置消息码
strcpy((char *) msg.name, key);
strcpy((char *) msg.value, value);
// 发送请求
return send_prop_msg(&msg);
}
static int send_prop_msg(prop_msg *msg){
int s;
int r;
// 建立和属性服务器的socket链接
s = socket_local_client(PROP_SERVICE_NAME, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
if(s < ){
return -;
}
// 通过socket发送
while((r = send(s, msg, sizeof(prop_msg), )) < ){
if((errno == EINTR) || (errno == EAGAIN)){
continue;
}
break;
} if(r == sizeof(prop_msg)){
r = ;
} else {
r = -;
} close(s);
return r;
}
2 zygote分析
zygote本身是一个Native的应用程序,与驱动、内核等无关。它是由init进程根据init.rc文件中的配置创建的。zygote最初的名字叫“app_process”,这个名字是在Android.mk文件中指定的。在运行中,app_process通过linux下的pctrl系统调用将自己的名字换成了zygote。
在Java中,我们知道不同的虚拟机实例会为不同的应用分配不同的内存。假如Android应用应该尽可能快地启动,但如果Android系统为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了”zygote”。zygote让Dalvik虚拟机共享代码、低内存占用以及最小的启动时间成为可能。
zygote进程由init通过fork而来,在init.rc中设置的启动参数如下:
-Xzygote /system/bin --zygote --start-system-server
zygote的原型app_process对应的源文件是App_main.cpp。
int main(int argc, const char* const argv[]){
......
AppRuntime runtime;
......
int i = runtime.addVmArguments(argc, argv);
if(i < argc){
runtime.mParentDir = argv[i++];
} if(i < argc){
arg = argv[i++];
if( == strcmp("--zygote", arg)){
bool startSystemServer = (i < argc) ? strcmp(argv[i], "--start-system-server") == : false;
setArgv0(argv0, "zygote");
set_process_name("zygote"); // 设置进程名
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);
}
......
}
......
}
2.1 AppRuntime分析
AppRuntime从AndroidRuntime类派生,它的声明和实现在App_main.cpp中。AppRuntime重载了onStarted, onZygoteInit和onExit函数。其中AndroidRuntime::start(const char* className, const bool startSystemServer)函数做了几件事情:
2.1.1 创建虚拟机--startVm
int AndroidRuntime::startVm(JavaVm** pJavaVM, JNIEnv** pEnv){
// JNI check是指Native层调用JNI函数时,系统所做的一些检查工作。例如,调用NewUTFString函数时,系统会检查传入的字符串是不是符合UTF-8的要求。
property_get("dalvik.vm.checkjni", propBuf, "");
......
// 设置虚拟机的heapsize,默认为16MB
strcpy(heapsizeOptsBuf, "-Xmx");
property_get("dalvik.vm.heapsize", heapsizeOptsBuf + , "16m");
......
if(checkJni){
opt.optionsString = "-Xcheck:jni";
mOptions.add(opt);
// JNI check中的资源检查,系统中创建的Global Reference个数不能超过2000
opt.optionString = "-Xjnigreflimit:2000";
mOptions.add(opt);
} // 调用JNI_CreateJavaVM创建虚拟机,pEnv返回当前线程的JNIEnv变量
if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < ){
goto bail;
} return ; bail:
free(stackTraceFile);
return result;
}
2.1.2 注册JNI函数--startReg
int AndroidRuntime::startReg(JNIEnv* env){
......
if(register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < ){
env->PopLocalFrame(NULL);
return -;
}
env->PopLocalFrame(NULL);
return ;
}
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env){
for(size_t i = ; i < count; i++){
if(array[i].mProc(env) < ){
return -;
}
}
return ;
}
statci const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_debug_JNITest),
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_android_os_SystemClock),
......
}
上面的mProc就是为Java类注册JNI函数。
2.1.3 通过JNI调用Java函数,进入Java世界
env->CallStaticVoidMethod(startClass, startMeth, strArray);
CallStaticVoidMethod最终将调用com.android.internal.os.ZygoteInit的main函数。
public static void main(String argv[]){
try{
SamplingProfilerIntegration.start();
// 1. 注册zygote用的socket. 基于AF_UNIX类型,是一个本机socket
registerZygoteSocket();
// 2. 预加载类和资源
preloadClasses();
preloadResources();
......
// 强制执行一次垃圾回收
gc(); if(argv[1].equals("true")){
startSystemServer(); // 3. 启动system_server进程。该进程是framework的核心。
} else if(!argv[1].equals("false")){
throw new RuntimeException(argv[0] + USAGE_STRING);
} if(ZYGOTE_FORK_MODE){
runForkMode();
} else {
runSelectLoopMode(); // 4. zygote调用这个函数,进入等待唤醒的状态
}
closeServerSocket();
} catch(MethodAndArgsCaller caller){
caller.run();
} catch(RuntimeException ex){
closeServerSocket();
throw ex;
}
......
}
3 SystemServer分析
SystemServer是由Zygote通过Zygote.forkSystemServer函数fork出来的。
pid = Zygote.forkSystemServer();
if(pid == 0){ // 子进程返回0,即systemServer
handleSystemServerProcess(parseArgs);
}
在handleSystemServerProcess函数中,首先会关闭从Zygote继承下来的socket,并设置SystemServer进程的一些参数,然后调用RuntimeInit.java中的ZygoteInit函数。
public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller {
commonInit();
// native层的初始化
zygoteInitNative();
......
invokeStaticMain(startClass, startArgs); // 调用com.android.server.SystemServer类的main函数
}
SystemServer调用了zygoteInitNative后,将与Binder通信系统建立联系,这样SystemServer就可以使用Binder了。zygoteInitNative中调用了onZygoteInit()
virtual void onZygoteInit(){
sp<ProcessState> proc = ProcessState::self();
if(proc->supportsProcesses()){
proc->startThreadPool(); // 启动一个线程,用于Binder通信
}
}
而另外一个关键函数invokeStaticMain
private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller {
// 参数传入,className = "com.android.server.SystemServer"
......
Method m;
try{
m = cl.getMethod("main", new Class[] { String[].class }); // 找到com.android.server.SystemServer类的main函数
} catch(NoSuchMethodException ex){
......
} catch(SecurityException ex){
......
} ......
throw new ZygoteInit.MethodAndArgsCaller(m, argv); // 主动抛出一个异常
}
抛出的异常在哪里被捕获呢?上面已经给出过了。也就是在ZygoteInit.java中
......
catch(MethodAndArgsCaller caller){
caller.run(); // 调用caller的run函数
}
而MethodAndArgsCaller的run函数如下:
public void run(){
try{
mMethod.invoke(null, new Object[] { mArgs }); // mMethod为com.android.server.SystemServer的main函数
} catch(IllegalAccessException ex){
......
}
}
为什么要主动抛出这样一个异常来执行main函数呢?
《深入理解Android 卷I》中是这样解释的:这个调用是在ZygoteInit.main中,相当于Native的main函数,也即入口函数,位于堆栈的顶层。如果不采用抛异常的方式,而是在invokeStaticMain那调用,则会浪费之前函数调用所占用的一些调用堆栈。
那么,这个main函数又做了什么工作呢?
public static void main(String[] args){
System.loadLibrary("android_servers"); // 加载libandroid_servers.so
init1(args); // 调用native的init1函数
}
其中init1函数创建了一些系统服务,然后把调用线程加入到Binder通信中。期间还通过JNI调用了com.android.server.SystemServer类的init2函数,通过单独创建一个线程,用以启动系统的各项服务,如PowerManagerService, BatteryService, WindowManagerService, ActivityManagerService等。
public static final void init2(){
Slog.i(TAG, "Entered the Android system server!");
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
}
这个函数的主要功能是创建新的线程ServerThread,它的override的run方法在SystemServer.java中,主要做了以下一些工作:
private void run() {
......
// Initialize native services.
System.loadLibrary("android_servers");
......
// Initialize the system context.
createSystemContext();
......
startCoreServices();
startOtherServices();
......
}
/**
* Starts some essential services that are not tangled up in the bootstrap process.
*/
private void startCoreServices() {
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class); // Tracks application usage stats.
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
// Update after UsageStatsService is available, needed before performBootDexOpt.
mPackageManagerService.getUsageStatsIfNoPackageUsageInfo(); // Tracks whether the updatable WebView is in a ready state and watches for update installs.
mSystemServiceManager.startService(WebViewUpdateService.class);
}
/**
* Starts a miscellaneous grab bag of stuff that has yet to be refactored
* and organized.
*/
private void startOtherServices() {
......
try {
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance(); traceBeginAndSlog("StartSchedulingPolicyService");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); mSystemServiceManager.startService(TelecomLoaderService.class); traceBeginAndSlog("StartTelephonyRegistry");
telephonyRegistry = new TelephonyRegistry(context);
ServiceManager.addService("telephony.registry", telephonyRegistry);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
......
}
......
}
至此,让我们总结一下SystemServer
1)、ZygoteInit调用startSystemServer创建system_server进程。
2)、SystemServer调用handleSystemServerProcess完成自己的使命。
3)、handleSystemServerProcess抛出异常,最终调用com.android.server.SystemServer的main函数。
4)、main函数加载libandroid_server.so并调用native的init1函数。
5)、init1函数通过JNI调用com.android.server.SystemServer类的init2函数。init2函数创建一个线程,用于加载各种service。
6)、init1函数最终加入到Binder通信系统。
4 Zygote的分裂
zygote分裂出system_server后,就等待runSelectLoopMode等待并处理来自客户的请求。那么,谁会向zygote发送消息呢?这里以一个activity的启动为例作分析。
4.1 ActivityManagerService发送请求
ActivityManagerService由SystemServer创建。假设通过startActivity来启动一个新的acitivity,而这个activity属于一个还未启动的进程,那么这个进程又如何启动呢?先看ActivityManagerService中的startProcessLocked函数
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr){
......
int pid = Process.start("android.app.ActivityThread", mSimpleProcessManagement ? app.processName : null, uid, uid, gids, debugFlags, null);
......
}
Process.start位于android.os.Process中,而它又调用了startViaZygote, zygoteSendArgsAndGetPid。而在zygoteSendArgsAndGetPid中,首先打开了和zygote通信的socket,接着把请求的参数发到zygote,然后读取zygote处理完的结果,得知是某个进程的pid。
由于ActivityManagerService驻留于SystemServer进程中,所以正是SystemServer向Zygote发送了消息。
还记得前面提过的runSelectLoopMode函数么?它在里面又调用了ZygoteConnection的runOnce
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
try{
args = readArgumentList(); // 读取SystemServer发送过来的参数
descriptors = mSocket.getAncillaryFileDescriptors();
} ...... pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits);
......
if(pid ==0){
handleClientProc(parsedArgs, descriptors, newStderr); //子进程处理
return true;
} else {
return handleParentProc(pid, descriptors, parsedArgs);
}
}
由此可见,zygote分裂子进程后,自己将在handleParentProc中做一些扫尾工作,然后继续等待请求进行下一次分裂。
4.2 zygote响应请求的流程
1)、Zygote进程调用runSelectLoopMode
2)、SystemServer进程发送消息到Zygote
3)、Zygote通过fork创建子进程
4)、子进程调用android.app.ActivityThread的main函数
5 Home界面启动
待Application Framework层的ActivityManagerService准备就绪后,就会通知各个模块,继续执行上层应用。
先附一张体系结构图:
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
// where third party code can really run (but before it has actually
// started launching the initial applications), for us to complete our
// initialization.
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
......
try {
mActivityManagerService.startObservingNativeCrashes();
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
......
try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Score Service ready", e);
}
......
try {
if (networkManagementF != null) networkManagementF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Managment Service ready", e);
}
......
try {
if (networkStatsF != null) networkStatsF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Stats Service ready", e);
}
......
try {
if (networkPolicyF != null) networkPolicyF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Policy Service ready", e);
}
......
try {
if (connectivityF != null) connectivityF.systemReady();
} catch (Throwable e) {
reportWtf("making Connectivity Service ready", e);
}
......
try {
if (audioServiceF != null) audioServiceF.systemReady();
} catch (Throwable e) {
reportWtf("Notifying AudioService running", e);
}
......
}
});
public void systemReady(final Runnable goingCallback) {
......
// Start up initial activity.
mBooting = true;
startHomeActivityLocked(mCurrentUserId, "systemReady");
......
}
这样一来,就启动了Home界面,完成了整个Android启动流程。
下面是启动流程图:
参考:
《深入理解Android 卷I》 邓凡平 著
Android系统启动分析(Init->Zygote->SystemServer->Home activity)的更多相关文章
- [Android]Android系统启动流程源码分析
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...
- android系统启动框架、Activity界面显示过程详解
一.Android系统框架 android的系统架构和其操作系统一样,采用了分层的架构.从架构图看,android分为四个层,从高层到低层分别是应用程序层.应用程序框架层.系统运行库层和linux核心 ...
- 源码级分析Android系统启动流程
首先看一下Android系统的体系结构,相信大家都不陌生 1.首先Bootloader引导程序启动完Linux内核后,会加载各种驱动和数据结构,当有了驱动以后,开始启动Android系统,同时会加载用 ...
- Android系统启动流程(二)解析Zygote进程启动过程
1.Zygote简介 在Android系统中,DVM(Dalvik虚拟机).应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器.它通过 ...
- Android系统启动流程(一)解析init进程启动过程
整体流程大致如下: 1.init简介 init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等.in ...
- Android群英传笔记——第八章:Activity与Activity调用栈分析
Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命 ...
- Android系统启动过程分析
Android系统启动过程分析 一.Android平台架构 首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知. 可以看出整个架构由5部分 ...
- android系统启动
首页 资讯 精华 论坛 问答 博客 专栏 群组 更多 ▼ 您还未登录 ! 登录 注册 Ant space 博客 微博 相册 收藏 留言 关于我 android启动过程再研 Androi ...
- Android架构分析之使用自定义硬件抽象层(HAL)模块
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本:2.3.7_r1 Linux内核版本:android-goldfish-2.6.29 在上一篇博 ...
随机推荐
- goldengate初始化
对丢弃已久的goldengate环境重新配置,使其重新开始跑起来 环境是一个主机上的两个库,都是单机,所以也就没配pump进程了,trail file都是在一个文件夹下的,extract写trail ...
- hibernate.xml文件详解
<!--标准的XML文件的起始行,version='1.0'表明XML的版本,encoding='gb2312'表明XML文件的编码方式--> <?xml version='1.0' ...
- 模块module
python中的Module相当于C++中头文件和命名空间的组合体,便于代码的组织,任何一个python代码的文件都是一个Module,都可以被其他模块import import,from...imp ...
- perl操作sybase
设置环境变量 export PERL5LIB=:/redhat/perl/lib64/perl5 安装DBI tar -xzvf DBI-1.631.tar.gz cd DBI-1.631 perl ...
- C语言基本类型之long long int
大家都知道int在linux系统下默认是占4个字节,数值表示范围是:-2147483648~2147483647.即使是无符号unsigned int类型表示范围:0-4294967295,大约42亿 ...
- 描述Linux系统开机到登陆界面的启动过程(计时2分钟)
简述: 1.开机BIOS自检 2.MBR引导 3.grub引导菜单 4.加载内核kernel 5.启动init进程 6.读取inittab文件,执行rc.sysinit,rc等脚本 7.启动minge ...
- 利用NuSoap开发WebService(PHP)
利用NuSoap开发WebService(PHP) 分类: php 2010-09-08 12:00 5005人阅读 评论(1) 收藏 举报 webservicephpsoapstringencodi ...
- git config命令使用
1. git config简介 我们知道config是配置的意思,那么git config命令就是对git进行一些配置.而配置一般都是写在配置文件里面,那么git的配置文件在哪里呢?互动一下,先问下大 ...
- MYSQL基础知识总结
!注释方式 # -- 单行 /* */ 多行 1.SELECT column1,column2,column3 FROM tablename WHERE id <= 5 ...
- LoadRunner 12.02 安装教程及中文语言包安装
注意事项: 安装前,把所有的杀毒软件和防火墙关闭. 若以前安装过LoadRunner,则将其卸载. 安装路径不要带中文字符. LoadRunner 12已经不再支持xp系统,仅支持win7和win8系 ...