从源码角度看Android系统Zygote进程启动过程
在Android系统中,DVM、ART、应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为“孵化器”。它是通过fork的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建一个DVM或者ART,因此通过fork而创建的应用程序进程和SystemServer进程可以在内部获取一个DVM或ART的实例副本。
备注:本文将结合Android8.0的源码看Zygote进程的启动过程以及Zygote进程做了哪些重要工作。
1. Zygote进程启动脚本
在init.rc中采用的是import类型语句来引入Zygote启动脚本,这些启动脚本都是由Android初始化语言来写的:
import /init.${ro.zygote}.rc
可以看出:init.rc中不会直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件。
从Android5.0开始,Android开始支持64位程序,Zygote就有了32位和64位的区别。所以这里就用ro.zygote属性来控制使用不同的Zygote启动脚本,从而就启动不同版本的Zygote进程。
ro.zygote属性的值有如下四种:
init.zygote32.rc
init.zygote32_64.rc
init.zygote64.rc
init.zygote64_32.rc
这些Zygote启动脚本都是放在system/core/rootdir/目录下。
1.1 init.zygote32.rc
表示支持纯32位程序,内容如下所示:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasks
根据Service类型语句的格式可知:
Zygote进程名称为zygote
执行程序是app_process
classname为main
如果audioserver、cameraserver、media、netd、wificond等进程终止时,Zygote进程会重启。
1.2 init.zygote32_64.rc
表示既支持32位程序也支持64位程序,内容如下所示:
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygoteclass mainpriority -20user rootgroup root readprocsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/tasksservice zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondaryclass mainpriority -20user rootgroup root readprocsocket zygote_secondary stream 660 root systemonrestart restart zygotewritepid /dev/cpuset/foreground/tasks
可以发现,脚本中有两个Service类型语句,说明会启动两个Zygote进程:
一个进程名为:zygote;执行程序为:app_process32,作为主模式
另一个进程名为:zygote_secondary;执行程序为:app_process64,作为辅模式
2. Zygote进程启动过程
在《从源码角度看Android系统init进程启动过程》一文中可知:init启动Zygote调用的是app_main.cpp的main函数中AppRuntime的start方法来启动Zygote进程。
代码路径:frameworks/base/cmds/app_process/app_main.cpp
main函数如下:
int main(int argc, char* const argv[])
{...省略...//传到的参数argv是“-Xzygote /system/bin --zygote --start-system-server”AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//忽略第一个参数 argc--;argv++;...省略...// 参数解析bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) { //注释1zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) { //注释2startSystemServer = true;} else if (strcmp(arg, "--application") == 0) {application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) {// 运行application或tool程序args.add(application ? String8("application") : String8("tool"));runtime.setClassNameAndArgs(className, argc - i, argv + i);...省略...} else {//进入zygote模式,创建 /data/dalvik-cache路径maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);for (; i < argc; ++i) {args.add(String8(argv[i]));}}//设置进程名if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string(), true /* setProcName */);}if (zygote) { //注释3runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}
}
注释解析:
注释1处判断参数arg中是否包含了“–zygote”,如果包含了则说明main函数是运行在Zygote进程中。将参数zygote设置为true;并给niceName赋值,64位系统nice_name为zygote64、32位系统nice_name为zygote
注释2处判断参数arg中是否包含了“–start-system-server”,如果包含了则说明main函数是运行在SystemServer进程中。将参数startSystemServer设置为true。
注释3出判断zygote是否为 true,如果为true,就说明运行在Zygote进程中,然后就调用AppRuntime中的start函数
深入到AppRuntime中的start函数中:
代码路径:frameworks/base/core/jni/AndroidRuntime.cpp
start函数如下:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{...省略...JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;//启动java虚拟机if (startVm(&mJavaVM, &env, zygote) != 0) {return;}onVmCreated(env);//为java虚拟机注册JNI方法if (startReg(env) < 0) {ALOGE("Unable to register all android natives\n");return;}...省略...//从app_main的main函数中可以传过来的className是com.android.internal.os.ZygoteInitclassNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr);for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}//将com.android.internal.os.ZygoteInit转换为com/android/internal/os/ZygoteInitchar* slashClassName = toSlashClassName(className);//找到ZygoteInitjclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {//找到ZygoteInit的main方法jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {//通过JNI调用ZygoteInit的main方法env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);...省略...
}
从上面的注释可以看出,start函数做了如下工作:
调用startVm函数来创建Java虚拟机
调用startReg函数为Java虚拟机注册JNI方法
将传进来的参数com.android.internal.os.ZygoteInit转换为com/android/internal/os/ZygoteInit,并赋值给slashClassName
通过slashClassName找到ZygoteInit
通过JNI调用ZygoteInit的main方法
在通过JNI调用ZygoteInit的main方法后,Zygote就进入了Java框架层。
深入到ZygoteInit方法中:
代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
main函数如下:
public static void main(String argv[]) {ZygoteServer zygoteServer = new ZygoteServer();...省略...try {...省略...//创建一个Server端的Socket,socketName的值为“zygote”zygoteServer.registerServerSocket(socketName); //注释1// In some configurations, we avoid preloading resources and classes eagerly.// In such cases, we will preload things prior to our first fork.if (!enableLazyPreload) {bootTimingsTraceLog.traceBegin("ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());//预加载类和资源preload(bootTimingsTraceLog); //注释2EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());bootTimingsTraceLog.traceEnd(); // ZygotePreload} else {Zygote.resetNicePriority();}...省略...//启动SystemServer进程if (startSystemServer) {startSystemServer(abiList, socketName, zygoteServer); //注释3}Log.i(TAG, "Accepting command socket connections");//等待AMS请求zygoteServer.runSelectLoop(abiList); //注释4zygoteServer.closeServerSocket();} catch (Zygote.MethodAndArgsCaller caller) {caller.run();} catch (Throwable ex) {Log.e(TAG, "System zygote died with exception", ex);zygoteServer.closeServerSocket();throw ex;}
}
注释解析:
注释1处通过registerServerSocket方法来创建一个Server端的Socket,这个name为zygote的Socket用于等待ActivityManagerService请求Zygote创建新的应用程序进程
注释2预加载类和资源
注释3启动SystemServer进程
注释4处调用ZygoteServer的runSelectLoop方法来等待AMS请求创建新的应用程序进程
综上所述:ZygoteInit的main方法主要做了4件事:
创建一个Server端的Socket
预加载类和资源
启动SystemServer进程
等待AMS请求创建新的应用程序进程
下面分别对这四件事跟踪源码分析下。
2.1 registerServerSocket
代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
深入到registerServerSocket函数中:
void registerServerSocket(String socketName) {if (mServerSocket == null) {int fileDesc;//拼接Socket的名称final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; //注释1try {//得到Socket的环境变量的值String env = System.getenv(fullSocketName); //注释2//将Socket环境变量的值转换为文件描述符的参数fileDesc = Integer.parseInt(env); //注释3} catch (RuntimeException ex) {throw new RuntimeException(fullSocketName + " unset or invalid", ex);}try {//创建文件描述符FileDescriptor fd = new FileDescriptor();fd.setInt$(fileDesc);//创建服务器端的SocketmServerSocket = new LocalServerSocket(fd); //注释4} catch (IOException ex) {throw new RuntimeException("Error binding to local socket '" + fileDesc + "'", ex);}}
}
注释解析:
注释1拼接Socket的名称,其中ANDROID_SOCKET_PREFIX的值为“ANDROID_SOCKET_”,socketName的值是传进来的“zygote”,因此fullSocketName的值为“ANDROID_SOCKET_zygote”
注释2处将fullSocketName转换为环境变量的值
注释3将环境变量的值转换为文件描述符参数
注释4创建LocalServerSocket,也就是服务器端的Socket
在Zygote进程将SystemServer进程启动后,就会在这个服务器端的Socket上等待AMS请求Zygote进程来创建新的应用程序进程。
2.2 preload
代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
深入到preload函数中:
static void preload(BootTimingsTraceLog bootTimingsTraceLog) {Log.d(TAG, "begin preload");bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");beginIcuCachePinning();bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinningbootTimingsTraceLog.traceBegin("PreloadClasses");//预加载位于/system/etc/preloaded-classes文件中的类preloadClasses();bootTimingsTraceLog.traceEnd(); // PreloadClassesbootTimingsTraceLog.traceBegin("PreloadResources");预加载资源,包含drawable和color资源preloadResources();bootTimingsTraceLog.traceEnd(); // PreloadResourcesTrace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");//预加载OpenGLpreloadOpenGL();Trace.traceEnd(Trace.TRACE_TAG_DALVIK);//通过System.loadLibrary()方法,//预加载"android","compiler_rt","jnigraphics"这3个共享库preloadSharedLibraries();//预加载 文本连接符资源preloadTextResources();//仅用于zygote进程,用于内存共享的进程WebViewFactory.prepareWebViewInZygote();endIcuCachePinning();warmUpJcaProviders();Log.d(TAG, "end preload");sPreloadComplete = true;
}
类加载,采用的是反射机制Class.forName()方法来加载。
加载的资源主要是com.android.internal.R.array.preloaded_drawables和com.android.internal.R.array.preloaded_color_state_lists,在应用程序中以com.android.internal.R.xxx开头的资源,就是在此时由Zygote加载到内存中的。
zygote进程内加载了preload()方法中的所有资源,当需要fork新进程时,采用copy on write技术,如下图所示:
从上图可以看出在创建新进程时,会共享父进程Zygote地址空间。
2.3 startSystemServer
代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
深入到startSystemServer函数中:
private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)throws Zygote.MethodAndArgsCaller, RuntimeException {...省略...//参数准备,args数组中保存启动SystemServer的启动参数String args[] = { //注释1"--setuid=1000","--setgid=1000","--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010","--capabilities=" + capabilities + "," + capabilities,"--nice-name=system_server","--runtime-args","com.android.server.SystemServer",};ZygoteConnection.Arguments parsedArgs = null;int pid;try {//用于解析参数,生成目标格式parsedArgs = new ZygoteConnection.Arguments(args); //注释2ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);//fork子进程,也是创建SystemServer进程pid = Zygote.forkSystemServer( //注释3parsedArgs.uid, parsedArgs.gid,parsedArgs.gids,parsedArgs.debugFlags,null,parsedArgs.permittedCapabilities,parsedArgs.effectiveCapabilities);} catch (IllegalArgumentException ex) {throw new RuntimeException(ex);}//运行在子进程中if (pid == 0) {if (hasSecondZygote(abiList)) {waitForSecondaryZygote(socketName);}//关闭zygote原有的socketzygoteServer.closeServerSocket();//处理SystemServer进程handleSystemServerProcess(parsedArgs); //注释4}return true;
}
注释解析:
- 注释1创建args数组,主要用来保存启动SystemServer的启动参数。
其中:
可以看出SystemServer进程的的用户id和用户组id都被设置为了1000,并且拥有用户组1001-1010、1018、1021、1032、3001-3010的权限。
进程名为:system_server
启动的类名为:com.android.server.SystemServer
注释2处将args数组封装成Arguments对象,并给注释3出的forkSystemServer函数调用
注释3处调用Zygote的forkSystemServer方法,其内部会调用nativeForkSystemServer这个Native方法,nativeForkSystemServer会通过fork函数在当前进程创建一个子进程,即SystemServer进程。
注释4处理SystemServer进程
2.4 runSelectLoop
在启动SystemServer进程后,会执行ZygoteServer的runSelectLoop方法。
代码路径:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
深入到runSelectLoop函数中:
void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();//sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]fds.add(mServerSocket.getFileDescriptor()); //注释1peers.add(null);//无限循环等待AMS的请求while (true) {StructPollfd[] pollFds = new StructPollfd[fds.size()];for (int i = 0; i < pollFds.length; ++i) { //注释2pollFds[i] = new StructPollfd();pollFds[i].fd = fds.get(i);pollFds[i].events = (short) POLLIN;}try {//处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里Os.poll(pollFds, -1);} catch (ErrnoException ex) {throw new RuntimeException("poll failed", ex);}for (int i = pollFds.length - 1; i >= 0; --i) { //注释3//采用I/O多路复用机制,当接收到客户端发出连接请求或者数据处理请求时,则往下执行;//否则进入continue,跳出本次循环if ((pollFds[i].revents & POLLIN) == 0) {continue;}if (i == 0) {//即fds[0],代表的是sServerSocket,则意味着有客户端连接请求;//则创建ZygoteConnection对象,并添加到fdsZygoteConnection newPeer = acceptCommandPeer(abiList); //注释4peers.add(newPeer);fds.add(newPeer.getFileDesciptor());} else {boolean done = peers.get(i).runOnce(this); //注释5if (done) {peers.remove(i);fds.remove(i);}}}}
}
注释解析:
注释1:处的mServerSocket就是在registerZygoteSocket函数中创建的服务端的Socket,调用getFileDescriptor()方法获取该Socket的fd字段的值,并添加到fd列表fds中。
注释2处通过遍历将fds存储的信息转移到pollFds中
注释3处对pollFds进行遍历,如果i==0,说明服务端Socket与客户端连接上了,即:当前Zygote进程与AMS建立了连接
注释4处通过acceptCommandPeer方法得到ZygoteConnection类并添加到Socket连接列表peers中,然后将ZygoteConnection的fd添加到fd列表fds中,以便接收到AMS发送过来的请求
如果i不等于0,则说明AMS向Zygote进程发送了一个创建应用进程的请求,然后调用注释5处ZygoteConnection的runOnce函数来创建一个新的应用程序进程,并在成功创建后,将这个连接从Socket连接列表peers和fds中移除。
3. 总结
3.1 Zygote启动过程调用流程图
3.2 Zygote进程启动总结
Zygote进程启动主要做了如下工作:
解析init.${ro.zygote}.rc中的参数,创建AppRuntime并调用它的start方法
调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
通过JNI调用ZygoteInit的main函数,进入Zygote的Java框架层
通过registerZygoteSocket方法建立Socket通道,zygote作为通信的服务端,用于响应客户端请求
preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
启动SystemServer进程
调用runSelectLoop(),随时待命,当接收到请求创建新进程时,立即唤醒Zygote并执行相应工作
非常感谢您的耐心阅读,希望我的文章对您有帮助。欢迎点评、转发或分享给您的朋友或技术群。
从源码角度看Android系统Zygote进程启动过程相关推荐
- 从源码角度看Android系统SystemServer进程启动过程
SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...
- 从源码角度看Android系统init进程启动过程
init进程是Linux系统中用户空间的第一个进程,进程号为1.Kernel启动后,在用户空间启动init进程,并调用/system/core/init.cpp中的main方法执行一些重要的工作. 备 ...
- 从源码角度看Android系统Launcher在开机时的启动过程
Launcher是Android所有应用的入口,用来显示系统中已经安装的应用程序图标. Launcher本身也是一个App,一个提供桌面显示的App,但它与普通App有如下不同: Launcher是所 ...
- android 多线程创建texture,从源码角度剖析Android系统EGL及GL线程
本文转载自天天P图攻城狮微信公众号,作者:天天P图Android工程师kenneyqin(覃华峥),原文链接https://mp.weixin.qq.com/s/j_N5_C7iQUPWENdRYfj ...
- 【Android源码分析】Android系统关键服务启动简析
一.关于Android系统重要的进程 (1).init进程:init进程是Linux内核启动完成之后,启动的第一个用户进程,Android系统就是在这个进程的基础上启动起来的,进程pid为1.init ...
- 从源码角度分析Android系统的异常捕获机制是如何运行的
我们在开发的时候经常会遇到各种异常,当程序遇到异常,便会将异常信息抛到LogCat中,那这个过程是怎么实现的呢? 我们以一个例子开始: import android.app.Activity; imp ...
- 从源码角度解析Android中APK安装过程
从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...
- 从源码角度看CPU相关日志
简介 (本文原地址在我的博客CheapTalks, 欢迎大家来看看~) 安卓系统中,普通开发者常常遇到的是ANR(Application Not Responding)问题,即应用主线程没有相应.根本 ...
- 从JDK源码角度看Long
概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其 ...
最新文章
- 程序单一实例实现 z
- BJUI验证后弹窗不显示
- 实战:搭建CA认证中心,使用CA证书搭建HTTPS
- sql limit 的用法
- DHCP_SNOOPING_ DAI_IPSG实验
- 什么原因导致芯片短路_华为为什么突然大量用起了联发科芯片,或是这三个产品策略原因...
- bootstrap-按钮的创建
- MikroTik RouterOS电子克隆盘原理收集
- cmd无法运行python_为什么CMD无法运行python程序
- centos7 设备 mariadb-10
- arduino 部分有用的函数
- 创建Java源代码文件----开始编写代码
- python随机抽取样本1500个_python 随机抽取数据
- easyar 实现模型的旋转和缩放
- PowerDesigner16.5 逆向生成物理模型
- unity文字转语音插件(中文版)教程
- CFileDialog的使用(MFC-C++)
- 如何制定学习计划 - 褪墨
- java 日历计算农历和节假日的工具类
- 也来分析BloomFilter