在本文中,我们主要介绍进程启动的内容,自我感觉有个不错的建议和大家分享下
对于Linux内核来说,其实Android系统就是一个一般的Linux应用程序而已,只是在init.rc中被设置为默许启动;
- Framework运行环境综述
任何系统启动进程的实质都是要建立一套系统运行所需的环境;android系统的启动其实就是建立dalvik虚拟机运行所需的环境,整体如下:
- android系统启动的第一个进程为zygote,意思为“受精卵”,因为接下来所有的Dalvik虚拟机进程都是通过它孵化出来的;该进程包含2个核心模块:
- Socket服务器:用于接收启动新的Dalvik进程的命令;
- Framework共享类与资源:所有Dalvik虚拟机进程都市需要的共享类及资源,其实就是android.jar中的大部分内容,待加载的类和资源列表分离通过preload-classes/preload-resources定义;
- zygote孵化出的第一个Dalvik虚拟机进程为SystemServer;该进程会负责加载android应用所需的所有基础服务(比如PowerManagerService、AlarmManagerService、WifiService...),并且会创立一个由Ams(详细的货色详见前面的第19章说明)管理的Socket客户端,该客户端用于向zygote中的服务端发送启动新Dalvik进程命令;
- 从之前的运行环境架构图可以看出有如下特色:
- 在android系统中每一个应用就是一个独立的dalvik进程,他们之间相互独立,这样就保证了单个应用的crash不会影响其他应用;而且可以设置单个应用的资源和权限制约,比如内存等;
- 公共的类和资源只会加载一份,这样对应用而言只要加载自身所需的类和资源即可,这样就有效的节省了对内存的消耗、加载效率也会提升;
这点和PC上运行多个JAVA应用是不一样的,在PC上一般不存在多个JAVA进程共享一份类和资源的情况;
- android系统启动的第一个进程为zygote,意思为“受精卵”,因为接下来所有的Dalvik虚拟机进程都是通过它孵化出来的;该进程包含2个核心模块:
- Zygote进程的启动进程
- 加载点为linux系统根目录下的init.rc中配置的zygote服务,该文件可以直接通过adb pull /init.rc .命令或者在DDMS视图中下载,对应于android源码中的路径为./system/core/rootdir/,详细配置为:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netted
该以上配置命令中核心信息包含:- 用/system/bin/app_process命令启动名称为zygote的系统服务(注意:该进程没法在DDMS中直接查看,他属于系统服务);
其作用可以简单理解为就是启动一个Dalvik虚拟机,并通过指定参数启动一个JAVA应用进程,和执行java命令实质上是一样的;
该命令的源码位于./frameworks/base/cmds/app_process/app_main.cpp
源码会依赖于./frameworks/base/include/android_runtime/AndroidRuntime.h
详细实现位于./frameworks/base/core/jni/AndroidRuntime.cpp- 启动Dalvik虚拟机的详细指令路径为:
app_main.cpp中的main() ==> AndroidRuntime.cpp中的start() ==> AndroidRuntime.cpp中的startVm()
==> ./dalvik/vm/Jni.cpp中的JNI_CreateJavaVM() ==> ./dalvik/vm/Init.cpp中的dvmStartup();
期间会实现多个dalvik基础服务启动以及dalvik.system.NativeStart.main()的执行; - Dalvik虚拟机环境预备终了以后,利用FindClass/CallStaticVoidMethod执行详细Java文件的main方法;
- 启动Dalvik虚拟机的详细指令路径为:
- --zygote标示以com.android.internal.os.ZygoteInit类为虚拟机执行入口;
- --start-system-server仅在指定--zygote参数下有效,标示ZygoteInit启动终了后孵化出第一个Dalvik进程SystemServer;
- socket标示zygote进程中启动的Socket服务端类型以及端口等信息;
- onrestart标示该服务重启后需要触发的操作,即需要唤醒电源、重启media/netted服务;
- 用/system/bin/app_process命令启动名称为zygote的系统服务(注意:该进程没法在DDMS中直接查看,他属于系统服务);
- ZygoteInit.main()核心加载逻辑
- registerZygoteSocket(); 构造LocalServerSocket启动Socket服务端,用以接收启动新的Dalvik进程的命令;
- preloadClasses(); 加载Framework共享类,待加载列表位于framework.jar/preloaded-classes文件;
preloaded-classes源文件位于./frameworks/base/preloaded-classes;
而该文件又可以通过java -Xss512M -cp ./out/host/darwin-x86/framework/preload.jar WritePreloadedClassFile ./frameworks/base/tools/preload/XXXXXX.compiled来生成;
preload源码位于./frameworks/base/tools/preload/,最后一个参数XXXXXX.compiled二进制文件等于真正的待加载列表;而至于这个.compiled二进制文件如何来的,还未知! - preloadResources(); 加载Framework共享资源,包含图片和颜色;待加载资源位于/frameworks/base/core/res/res/values/arrays.xml;
其中定义了2个array,分离为preloaded_drawables/preloaded_color_state_lists; - startSystemServer(); 孵化出第一个Dalvik虚拟机进程SystemServer;
- 指定新进程启动相关参数,主要是进程名称system_server(为什么DDMS中看到的进程名称为system_process呢?)、以及将要执行的第一个Java类:com.android.server.SystemServer
- 执行Zygote.forkSystemServer(…)创立新进程;
该进程现实上是通过Linux系统的一个系统调用fork(); 实现的,其作用是复制当前进程所包含的所有信息,并发生一个新进程;这样做的目标主要是为了确保Zygote进程中加载的共享类和资源只会存在一份,从而有效节省系统资源; - 执行handleSystemServerProcess(…);
- closeServerSocket(); 关闭从zygote进程中copy过去的Socket服务器;
- RuntimeInit.zygoteInit(…); 初始化一些额外的运行环境,并执行RuntimeInit.invokeStaticMain(…) 实现对SystemServer.main()函数的运行,至此就实现了新的Java进程SystemServer的创立了;
而至于main()中执行的详细逻辑如下:- 执行Looper.prepareMainLooper(); 为主线程做异步消息队列预备;
- 加载android应用所需的所有基础服务,比如:Ams、Wms、Pms、WifiService…
- 执行ActivityManagerService.self().systemReady(…);
- 首先会执行 startSystemUi(…); 启动 com.android.systemui.SystemUIService服务(可以通过DDMS查看到com.android.systemui进程);
该进程主要是实现顶部状态栏PhoneStatusBar、电池电量PowerUI、铃声RingtonePlayer这三个服务的加载;
注意:该部分逻辑在老版本中可能没有,书中也没提到,我是以4.2版本中的源码为准的; - 执行多个服务的systemReady()方法,因为很多服务需要等Ams初始化实现才能真正ready;
- 执行mMainStack.resumeTopActivityLocked(); 启动任务列表中的最上面的一个Activity;
在startHomeActivityLocked()执行进程中,系统会查询所有能响应category为CATEGORY_HOME的Activity列表,若涌现多个则可以由用户自己选择,并允许用户记住该选择;
至此android系统的主页面应当就加载出来了,包含系统状态栏、以及Home;
从上可以看出,任何人都可以做一个Laucher应用,只要申请可以响应对应的category即可,就像Facebook做的Home应用一样;
- 首先会执行 startSystemUi(…); 启动 com.android.systemui.SystemUIService服务(可以通过DDMS查看到com.android.systemui进程);
- 执行 Looper.loop(); 主线程进入接受消息状态;
- runSelectLoopMode(); 让之前新建好的Socket服务端进入非阻塞读操作,等待接收命令;
当接收到请求时会调用ZygoteConnection.runOnce(),该方法的核心作用就是孵化出新的应用进程;- 详细创立新进程是靠执行Zygote.forkAndSpecialize(…) 实现的;与SystemServer进程的创立对比可以看出,两者的方式并不完全相同,但都是基于fork模式来复制进程;
- 对复制出来进程善后处置,这个和SystemServer新建后的处置分歧,主要是关闭Socket服务器、以及启动新的目标Java文件ActivityThread中的main函数;
- 加载点为linux系统根目录下的init.rc中配置的zygote服务,该文件可以直接通过adb pull /init.rc .命令或者在DDMS视图中下载,对应于android源码中的路径为./system/core/rootdir/,详细配置为:
这个章节有非常多的细节待专研,目前的困惑包含:
- dalvik虚拟机启动时如何和dalvik.system.NativeStart关联上的?而且该class居然找不到对应的jni文件?
- startSystemUi时启动的是一个Service,但现实加载的顶部状态栏PhoneStatusBar却明显和前台UI相关,如何实现的前台UI可以不依赖于Activity?
- 系统默许启动后还会加载一些基础应用,比如电话phone、短信sms等,这些应用又是在什么时候自动启动的呢?
文章结束给大家分享下程序员的一些笑话语录:
关于编程语言
如果 C++是一把锤子的话,那么编程就会变成大手指头。
如果你找了一百万只猴子来敲打一百万个键盘,那么会有一只猴子会敲出一 段 Java 程序,而其余的只会敲出 Perl 程序。
一阵急促的敲门声,“谁啊!”,过了 5 分钟,门外传来“Java”。
如果说 Java 很不错是因为它可以运行在所有的操作系统上,那么就可以说 肛交很不错,因为其可以使用于所有的性别上。