Android Zygote进程是如何fork一个APP进程的
进程创建流程
- 不管从桌面启动应用还是应用内启动其它应用,如果这个应用所在进程不存在的话,都需要发起进程通过Binder机制告诉system server进程的AMS
- system server进程的AMS调用Process.start()方法,通过socket向zygote进程发送创建新进程的请求
- 在zygote进程的ZygoteInit.main方法中,有一个runSelectLoop循环体,通过acceptCommandPeer方法获取链接过来的客户端,再通过runOnce方法去创建进程
- 新的进程执行handleChildProc方法,最后通过反射调用ActivityThread.main()方法,这样一个新的APP进程就创建完成了
APP启动第三方应用
startActivity
当你在桌面启动一个应用或者在一个APP内启动一个应用,本质都是一样的,因为Android手机桌面其实就是一个APP(这点可参考Android之Activity启动流程源码深入解析),两种方式经过层层调用之后都会走到ActivityStackSupervisor.startSpecificActivityLocked方法,判断如果对方进程不存在,就需要去创建一个进程
startService
调用startService启动一个服务,该方法经过层层调用最终会走到ActiveServices.bringUpServiceLocked方法,如果判断对方进程不存在也会去创建一个新进程
sendBroadcast
调用sendBroadcast方法去发送一个广播,经过层层调用后走到BroadcastQueue.processNextBroadcast方法,也会判断BroadcastReceiver所在进程不存在就需要去创建进程
query
在ContentProvider的处理过程中,ContentResolver.query方法经过层层调用会走到ActivityManagerService.getContentProviderImpl方法,判断ContentProvider所在的进程不存在就会创建新进程
System Server请求创建进程
以下叙述中ActivityManagerService同意简称为AMS
以下源码基于API 24
上述四种途径最终都会走到如下方法
AMS.startProcessLocked
先来看ActivityManagerService的startProcessLocked
方法,这是最开始入口
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
//......省略代码
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
//......省略代码
}
代码是在太长,我们只看关键的地方,Process.start
这个方法开始进行fork,ok那来看看它的内容,代码很长,可以直接看下面关于本段代码总结
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
} private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>(); // --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
argsForZygote.add("--enable-jni-logging");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
argsForZygote.add("--mount-external-multiuser");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
argsForZygote.add("--mount-external-multiuser-all");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion); //TODO optionally enable debuger
//argsForZygote.add("--enable-debugger"); // --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups="); int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
} argsForZygote.add(sb.toString());
} if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
} if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
} if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
} if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
} argsForZygote.add(processClass); if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
} return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
上面的startViaZygote
方法,所做的事情是:把参数最终放到一个列表中,接着调用zygoteSendArgsAndGetResult
方法,该方法中的第一个参数是调用了openZygoteSocketIfNeeded(abi)
方法,那我们先来看下这方法的内容
rivate static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
} //......省略代码
}
ZygoteState.connect(ZYGOTE_SOCKET)
接着看下这方法
/*与ZygoteInit的server socket建立链接通信*/
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket(); try {
zygoteSocket.connect(new LocalSocketAddress(socketAddress,
LocalSocketAddress.Namespace.RESERVED)); zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); zygoteWriter = new BufferedWriter(new OutputStreamWriter(
zygoteSocket.getOutputStream()), 256);
} catch (IOException ex) {
try {
zygoteSocket.close();
} catch (IOException ignore) {
} throw ex;
} String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString); return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
上面的代码其实就是与ZygoteInit
类中的ServerSocket建立连接,socket连接起来了,那就可以进行通信了。现在我们在返回到zygoteSendArgsAndGetResult
方法
private static ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream; writer.write(Integer.toString(args.size()));
writer.newLine(); int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
writer.write(arg);
writer.newLine();
} writer.flush(); // Should there be a timeout on this?
ProcessStartResult result = new ProcessStartResult();
result.pid = inputStream.readInt();
/*pid小于0代表有问题,==0代表是子进程,》0代表是父进程*/
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
代码也很简单,上面提到过client与server已经建立了socket连接,那这个方法,会把所有的参数通过socket发送到ZygoteInit
的ServerSocket,发送完毕后,就等待ServerSocket把结果返回
Zygote进程处理fork请求
上一节提到过,client发送的建立socket连接最终会在ZygoteInit
中创建一个ZygoteConnection
对象,收到client发送的fork请求,会调用ZygoteConnection
对象的runOnce
方法,因此来看这方法
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { //.......省略代码 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir); //......省略代码 try {
/*子进程执行pid==0情况,父进程执行else情况*/
if (pid == 0) {
/*子进程*/
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
/*Process中的io流监听的pid等信息都是通过下面的代码发出去的*/
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
Zygote.forkAndSpecialize
这个方法会调用native方法来fork app进程,fork成功后,子进程就复制了基本上父进程所有的数据等,这在本节开始的时候科普过这个知识,子进程fork出的pid==0,因此if(pid == 0){}else{}
这段代码就特别有意思了,pid==0是子进程执行,else是父进程执行,父进程执行的代码我就不贴了,它主要是把fork成功的pid返回给client端,这时候ActivityManagerService
的startProcessLocked
就可以继续执行。我们还是来看下子进程执行的代码,最终会执行
handleChildProc
方法private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller { //......省略代码 if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
// ......省略代码
}
}
从传递过来的参数可以定位最终调用了RuntimeInit.zygoteInit
方法,那就来看下
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); redirectLogStreams(); commonInit();
nativeZygoteInit(); applicationInit(targetSdkVersion, argv, classLoader);
}
该方法我们只关注applicationInit(targetSdkVersion, argv, classLoader)
这个方法
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
// Android runtime shutdown hooks close the Binder driver, which can cause
// leftover running threads to crash before the process actually exits.
nativeSetExitWithoutCleanup(true); // We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
// let the process exit
return;
} // Remaining arguments are passed to the start class's static main
invokeStaticMain(args.startClass, args.startArgs, classLoader);
} private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl; try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
} Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
} int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
} /*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
最终我们关注invokeStaticMain
这个方法,该方法最终会抛出一个ZygoteInit.MethodAndArgsCaller(m, argv)
异常,这个异常会把ActivityThread
的main
方法反射出来。
还记得上一节ZygoteInit
的main
方法吗
public static void main(String argv[]){
try{
//.....省略代码
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
main
方法最终会把MethodAndArgsCaller
异常给捕获到,捕获到后其实最终就是调用ActivityThread
的main
方法
通过抛异常的方式来进行调用,主要目的是把当前线程的堆栈信息给置空
Android Zygote进程是如何fork一个APP进程的的更多相关文章
- 如何利用php+android+新浪sae服务器做一个app下载应用
功能简介:提供一个app下载的平台,类似于appstore,上面有很多app可供下载 实现基本思路:利用android,在手机桌面建立一个图标,点击该图标不是打开app应用,而是跳转到一个web页面, ...
- Android Zygote进程启动分析
dvm,app进程,linux进程三者关系 DVM指 dalivk 的虚拟机.每一个 Android 应用程序都在它自己的进程中运行,都拥有一个独立的 Dalvik 虚拟机实例.而每一个 DVM 都是 ...
- Android性能优化系列---管理你的app内存
文章出处:http://developer.android.com/training/articles/memory.html#YourApp Random-access memory(RAM)在任 ...
- 一个APP从启动到主页面显示经历了哪些过程?
①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求: ②system_server进程接收到请求后,向zygote进程 ...
- 进程控制之fork函数
一个现有进程可以调用fork函数创建一个新进程. #include <unistd.h> pid_t fork( void ); 返回值:子进程中返回0,父进程中返回子进程ID,出错返回- ...
- Linux内核及分析 第六周 分析Linux内核创建一个新进程的过程
实验过程 1.github上克隆相应的mengning/menu.git 2.测试menuOS,测试fork直接执行结果 3.配置调试系统,进入gdb调试,利用file linux-3.18.6/vm ...
- 1.1 Linux中的进程 --fork、孤儿进程、僵尸进程、文件共享分析
操作系统经典的三态如下: 1.就绪态 2.等待(阻塞) 3.运行态 其转换状态如下图所示: 操作系统内核中会维护多个队列,将不同状态的进程加入到不同的队列中,其中撤销是进程运行结束后,由内核收回. 以 ...
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标: 复制进程映像 fork系统调用 孤儿进程.僵尸进程 写时复制 一,进程复制(或产生) 使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文.进程堆栈. ...
- linux系统编程之进程(八):守护进程详解及创建,daemon()使用
一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...
随机推荐
- FractalNet(分形网络)
-Argues that key is transitioning effectively from shallow to deep and residual representations are ...
- Eclipse中连接数据库错误:com.microsoft.sqlserver.jdbc.SQLServerException: 之类的错误
原创 错误:org.apache.jasper.JasperException: Unable to compile class for JSP 原因是页面指令中 import="java. ...
- EBS登陆界面个性化
把完整资料贴出来 Set the profile option Local Login Mask (FND_SSO_LOCAL_LOGIN_MASK). (This profile option is ...
- 【大数据之数据仓库】GreenPlum优化器对比测试
在< [大数据之数据仓库]选型流水记>一文中有提及,当时没有测试GreenPlum的quicklz压缩算法和ORCA查询优化器,考虑到quicklz压缩算法因为版权问题不会开源(详情请参阅 ...
- 二、搭建SpringBoot项目
与其说是搭建,还不如说去下载...(注意,在此之前要确保你的3000块钱的笔记本上安装了JDK8+已经最新的相对较新的maven:apache-maven-3.6.0,至于JDK以及maven的相关安 ...
- libcurl坑
code = curl_easy_setopt(conn, CURLOPT_URL, ca.strUrl.c_str()); 要char* 不能string
- 多态实现的原理------新标准c++程序设计
“多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定.例子: #include<iostream> using namespac ...
- javaee--学生成绩录入与显示--Struts2标签的使用
类Score.java:各课程的成绩及平均成绩 类Student.java:学生姓名.学号及Score类 类ScoreAction.java:将Student类存在一个List对象中, execute ...
- 【arc074e】RGB Sequence dp
Description 丰泽爷今天也在愉快地玩Minecraft! 现在丰泽爷有一块1∗N1∗N的空地,每个格子按照顺序标记为11到NN.丰泽爷想要在这块空地上铺上红石块.绿宝石块和钻石块作为 ...
- P3185 [HNOI2007]分裂游戏
$ \color{#0066ff}{ 题目描述 }$ 聪聪和睿睿最近迷上了一款叫做分裂的游戏. 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2.....n-1, 第 i 个瓶子中装有 p[i ...