基于Android 9.0的源码浅析Zygote进程的创建过程,android应用进程的过程,和系统为应用进程准备android环境的过程。
Zygote进程创建
在android中所有的应用进程和系统进程SystemServer都是由Zygote进程fork出来的,先看看zygote进程为分析应用进程创建做好准备
在native层,init进程通过解析init.zygote32.rc / init.zygote64.rc文件后创建Zygote进程,下面是它的代码执行入口
app_main.cpp
int main(int argc, char* const argv[])
{
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// 配置虚拟机参数
// ...
// 参数解析
// ...
if (zygote) {
//一、AndroidRuntime.start 启动虚拟机
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}
}
AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
//适配层JniInvocation在旧版4.4 可以根据系统属性选择是加载Dalvik虚拟机还是ART运行时
//https://blog.csdn.net/Luoshengyang/article/details/18006645
JniInvocation jni_invocation;
//加载对应虚拟机的so
jni_invocation.Init(NULL);
JNIEnv* env;
//解析设置的虚拟机参数并初始化虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
//1、设置线程创建方法为javaCreateThreadEtc(当native的线程需要使用jni时,这个方法会
// 在创建线程时将jni上下文环境与线程绑定,因而可以执行Java代码)
//2、注册系统类的jni方法
if (startReg(env) < 0) {
return;
}
//"com.android.internal.os.ZygoteInit" -》"com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
// 调用ZygoteInit.main()
env->CallStaticVoidMethod(startClass, startMeth, strArray);
mJavaVM->DetachCurrentThread() ;
mJavaVM->DestroyJavaVM();
}
从native层调用java层ZygoteInit的main方法
ZygoteInit
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
final Runnable caller;
try {
//二、通过环境变量获取在native创建好的socket描述符(socket的类型为unix domain socket,
//地址是/dev/socket/zygote)
zygoteServer.registerServerSocket(socketName);
if (!enableLazyPreload) {
//三、预加载android进程通用的资源,类,共享库等
preload(bootTimingsTraceLog);
}
if (startSystemServer) {
//四、启动SystemServer进程,启动android核心服务
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
//fork之后父进程即zygote本身会返回null,而systemServer会返回runnable执行SystemServer.main()
if (r != null) {
r.run();
return;
}
}
//五、
//通过pollI/O多路复用机制监听套接字连接和数据可读。当客户端建立了一个连接后,
//调用accept获取已连接描述符添加到监听的数组。
//当数据可读时则处理对端的数据(这里是创建新进程需要的参数)
//并调用 Zygote.forkAndSpecialize创建新进程
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
//在上面runSelectLoop中如果收到systemServer进程创建新进程的请求,fork后子进程返回的
//caller!=null。一般来说这里是执行ActivityThread.main。而zygote进程在runSelectLoop中继续监听socket描述符
if (caller != null) {
caller.run();
}
}
Zygote 进程的五个关键步骤
一、在native(AndroidRuntime.cpp中的start方法)启动虚拟机并调用startReg()注册JNI函数,然后jni调用回java层的ZygoteInit的main方法来执行后续方法-----虚拟机这里可以展开
二、zygoteServer.registerServerSocket(socketName)通过环境变量获取在native创建好的socket描述符(socket的类型为unix domain socket,地址是/dev/socket/zygote)
三、preload()预加载通用类、drawable和color资源和共享库等。因为fork进程时是copy on write(只要没有进程试图写它自己的私有区域,它们就可以继续共享物理内存中对象的那一个副本),从而提高app启动效率,减少内存的消耗(从整个系统来看);
四、在ZygoteInit.forkSystemServer中fork SystemServer进程并调用ZygoteInit的handleSystemServerProcess进行初始化工作。其中最主要的是调用了ZygoteInit.nativeZygoteInit()进行Binder机制的初始化工作还有调用SystemServer的main方法来启动android中的核心服务PackageManagerService和ActivityManagerService等。
五、zygoteServer.runSelectLoop()监听fork子进程的socket请求
应用进程创建
当我们要启动四大组件,而组件所在的进程还没启动时,系统就会调用ActivityManagerService.startProcessLocked来启动进程。ActivityManagerService.startProcessLocked(这里有好几层省略了)->Process.start ->ZygoteProcess.start
ZygoteProcess.start
private Process.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 invokeWith,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList argsForZygote = new ArrayList();
//省略的是添加传给应用进程的参数,比较熟悉的参数有uid,gid,targetSdkVersion等
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
接下来先看openZygoteSocketIfNeeded,它的关键语句是ZygoteState.connect(mSocket);其中mSocket的值为"zygote";
之后的zygoteSendArgsAndGetResult 就是往socket写入参数传给zygote。
public static ZygoteState connect(String socketAddress) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
//zygoteSocket.connect 里面创建了socket和调用了connect
//过程和我们网络通信时的socket一样,不过类型不同,
//一个的网域是AF_INET传地址为ip地址,一个的网域为AF_UNIX传地址为文件名
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;
}
//从zygote那边的服务端获取支持的ABI架构
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
}
看完客户端的建立和连接,写入。再看看在zygote的socket服务端,在zygote创建的步骤二获取了在native创建的socket套接字(已经调用过socket()、bind()、listen()),在步骤五调用了runSelectLoop()来监听进程的创建,下面就看下具体的代码。
ZygoteServer.runSelectLoop
Runnable runSelectLoop(String abiList) {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
//poll io复用,与普通的阻塞的io调用区别是可以等待多个io事件,任意一个可读时返回。
//详细的可以看《UNIX网络编程卷1第三版》第六章
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
//第0个固定为监听套接字,当有新的连接准备好时通知poll,走到这里
if (i == 0) {
//调用accept 获取已完成的连接
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
//将连接加入监听的列表,当有数据可读时再走else分支
fds.add(newPeer.getFileDesciptor());
} else {
//现有连接有数据可读时通知poll,走到这个分支
try {
ZygoteConnection connection = peers.get(i);
//处理客户端传来的数据,fork在这里进行
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
//fork后的子进程的mIsForkChild 为true
return command;
} else {
//zygote进程走这个分支
//清理工作
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(i);
fds.remove(i);
}
}
} catch (Exception e) {
if (!mIsForkChild) {
//清理工作
ZygoteConnection conn = peers.remove(i);
conn.closeSocket();
fds.remove(i);
} else {
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
}
}
}
}
}
ZygoteConnection.processOneCommand处理客户端传来的参数
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
Arguments parsedArgs = null;
//读取socket传来的参数
args = readArgumentList();
//处理参数
parsedArgs = new Arguments(args);
//对应在客户端看到的getAbiList获取支持的ABI架构
//写入支持的abi后return null,等到客户端再写入参数时,poll监听会再次进到这个方法
if (parsedArgs.abiListQuery) {
handleAbiListQuery();
return null;
}
//省略一些参数的处理和文件描述符的处理
//里面调用了fork系统调用来创建子进程
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
parsedArgs.appDataDir);
if (pid == 0) {
// 新创建的进程走这个分支
// 设置mIsForkChild = true
zygoteServer.setForkChild();
//fork之后socket文件描述符共享了,而新进程并不需要,所以关闭。
zygoteServer.closeServerSocket();
return handleChildProc(parsedArgs, descriptors, childPipeFd);
} else {
//zygote进程走这里,返回的pid为新进程的id,通过socket写入传给ams所在的客户端
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
}
新的进程创建后主要逻辑在handleChildProc
ZygoteConnection.handleChildProc
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd) {
//这里关闭的是与客户端连接的socket套接字,前面的zygoteServer.closeServerSocket();
//关闭的是监听客户端连接建立的socket套接字。
closeSocket();
//处理标准输入、标准输出、标准错误的文件描述符
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
//设置进程名字
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
//进程创建后的主要初始化工作
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
null /* classLoader */);
}
ZygoteInit.zygoteInit
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
//重定向System.out 和System.err到android 的log
RuntimeInit.redirectLogStreams();
//设置一个默认的未截获异常处理器KillApplicationHandler( 把crash信息添加到DropBox,弹框退出还
//是退出并报告。最终调用Process.killProcess杀死进程。ActivityThread.main会调用ams的attach方
//法里面调用thread.asBinder().linkToDeath注册一个死亡回调,从而应用进程挂了之后ams可以调用
//cleanUpApplicationRecordLocked清理应用的相关信息比如activity,service等信息)
RuntimeInit.commonInit();
// jni方法最终调用了app_main.cpp中的onZygoteInit(),代码下面两句
// 打开binder驱动
//sp proc = ProcessState::self();
// 启动binder的线程池
// proc->startThreadPool();
//更多binder知识看老罗的binder系列https://blog.csdn.net/luoshengyang/article/details/6618363
ZygoteInit.nativeZygoteInit();
//解析argv获取className,反射允许main方法。如果是普通应用进程这里就是ActivityThread
// 如果是systemServer进程就是SystemServer类
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
应用进程创建小结
ams通过socket向zygote进程发起fork请求,zyogte进程调用完fork后应用进程便创建好了,应用进程进行初始化工作(设置未截获异常处理器,打开binder驱动,启动binder线程池等),然后调用ActivityThread.main
android环境
在android中我们更多接触的是四大组件和Application,而它们都离不开Context,通过分析Context理解android环境
Context
Context作为android环境上下文提供了与android系统打交道的方法
而ContextImpl作为其实现类我们看下它的方法与关键成员变量,就能大概知道这个类的作用
四大组件相关,封装了ActivityManager.getService()的调用即请求ActivityManagerService的方法
bindServicexxx
registerReceiverxxx
getContentResolver
startActivityxxx
sendBroadcastxxx
startServicexxx
操作SharedPreferences,实质是读写xml文件,简单的配置持久化时我们使用SharedPreferences
xxxSharedPreferences
创建Context
createxxxContext
检测权限,如果不是系统应用或者root,请求ActivityManagerService检测权限。
checkxxxPermission
获取系统服务,常见的getSystemService(Context.LAYOUT_INFLATER_SERVICE)
context.getSystemService(Context.CONNECTIVITY_SERVICE)等
getSystemService
获取Resources从而获取资源文件
getResources
看一下关键的成员变量
//运行在应用进程主线程的对象,记录了应用当前运行的状态(各种组件的状态,application),
//application和四大组件生命周期的执行(与ActivityManagerService通信时传一个binder即
//ApplicationThread作为回调,然后执行相应组件的生命周期方法)
ActivityThread mMainThread
//apk的基本信息apk的文件路径等,AndroidManifest.xml上的信息(待调试截图)
LoadedApk mPackageInfo
//ClassLoader 通过加载dex字节码将目标类加载到内存,在android中ClassLoader一般为
//PathClassLoader,主要用于系统和app的类加载器,
//也可以使用DexClassLoader来加载系统没有安装的apk或者jar文件
ClassLoader mClassLoader
//AssetManager 默认会加载位于"/system/framework/framework-res.apk"的系统资源,
//然后再再加载应用的资源。资源的加载通过ApkAssets.loadFromPath 解析apk中的
//resources.arsc(apk编译打包的时候会给每一个非assets资源一个ID值存在R.java文件中,
//最后生成资源索引表resources.arsc,用来描述那些具有ID值的资源的配置信息)。
//当我们根据资源id获取资源时,从这个索引表根据id找到对应资源项再根据当前配置信息
//(dpi、语言等)选择最匹配的资源项
Resources mResources
下面分析Activity和Application在创建时与Context的关系
Application创建
ActivityThread.handleBindApplication中调用LoadedApk的makeApplication创建Application
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
Application app = null;
String appClass = mApplicationInfo.className;
//如果在mainfest没有设置自定义的application就使用默认的
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
//创建ContextImpl
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//反射实例化application或自定义的application,并调用attach方法
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
}
return app;
}
Activity创建和Context的关系
ActivityThread.performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//只保留创建Activity时和Context有关系的代码
//调用ContextImpl的createActivityContext方法创建ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
//反射实例化activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
if (activity != null) {
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
}
}
1、可以看到的是Activity和Application的创建过程都实例化了ContextImpl,只是调用的静态工厂方法不同,createAppContext和createActivityContext二者主要区别在于后者传入了activityToken和overrideConfiguration。(activityToken的主要作用是获取资源时和activityToken关联起来,overrideConfiguration的主要作用是使得某个activity可以有不同的配置信息,这样获取资源时根据这个配置信息来取。总的来说就是资源获取可以根据activity达到差异化的效果。)
2、Activity和Application都调用了attach方法最后都调用了ContextWrapper的attachBaseContext方法。两个类都是继承自ContextWrapper,顾名思义,Context和ContextImpl使用了装饰模式,ContextWrapper通过attachBaseContext持有ContextImpl,并且方法的实现都交给了ContextImpl,ContextWrapper子类通过重写方法增强功能。ContextThemeWrapper重写主题相关方法,Activity与UI密切相关,于是继承了ContextThemeWrapper,Application与UI关系不大所以直接继承ContextWrapper。
总结:
1、进程创建的过程(zygote的创建、ams通过socket请求zygote创建新进程),在zygote进程android部分比较耗时的类提前加载,部分系统资源提前加载,之后创建应用进程的时候,fork子进程不需要再次加载从而提高效率。向zygote请求后的进程初始化:普通应用进程创建后调用ActivityThread.main()作为入口,SystemServer进程入口为SystemServer.main。最后为新创建的进程打开binder驱动,启动binder线程池,后续就可以使用binder进行进程间的通信了。
2、Context(ContextImpl)作为android环境上下文提供给我们与四大组件交互的能力(封装对AMS的请求)、获取ClassLoader、获取apk的信息(通过LoadedApk)、获取资源(Resource)等等。application和四大组件的创建时都会创建ContextImpl或使用application的ContextImpl attach到组件对应的Context中去。
参考:
Android系统进程Zygote启动过程的源代码分析:https://blog.csdn.net/Luoshengyang/article/details/6768304
Android应用程序进程启动过程的源代码分析:https://blog.csdn.net/Luoshengyang/article/details/6747696
Dalvik虚拟机的启动过程分析:https://blog.csdn.net/Luoshengyang/article/details/8885792