Zygote进程是Android系统中的一个重要进程,其主要作用是预热Java虚拟机和启动应用进程。本文将着重分析Zygote进程的启动过程,结合代码注释和示例代码,让读者更好地理解Zygote的内部工作原理。
Zygote进程的启动过程
Zygote进程的启动过程包含几个关键步骤,主要涉及Binder通信、双亲委派模型、系统属性加载和App启动等。下面我们将逐一进行讲解。
主入口函数
Zygote进程的主函数是ZygoteInit.main(),它的主要职责是启动Zygote进程并为其提供额外的参数。该函数主要由以下两部分组成:
public static void main(String[] args) {
...
// 设置JAVA_OPTS环境变量,指定堆大小等Java虚拟机参数
Os.setenv("JAVA_OPTS", "-Dfile.encoding=UTF-8 -Xmx64m", true);
...
// 创建ZygoteInit对象,并调用其registerZygoteSocket()方法
// 该方法会创建一个ServerSocket对象,用于监听应用进程的连接请求
ZygoteInit zygoteInit = new ZygoteInit();
zygoteInit.registerZygoteSocketIfNeeded(envVars);
...
// 开始监听应用进程的连接请求
zygoteInit.runSelectLoop(TextUtils.join(",", abis));
}
如上所示,ZygoteInit.main()中主要进行了环境变量、ZygoteInit对象创建以及注入参数等操作,其中registerZygoteSocketIfNeeded()是ZygoteInit对象中最关键的方法之一。
注册Zygote Socket
ZygoteInit.registerZygoteSocketIfNeeded()是ZygoteInit启动过程中最重要的方法之一,它主要用于创建一个ServerSocket对象并将其绑定到一个本地Unix域socket上,这样Zygote进程就可以监听来自应用进程的连接请求了。
@SuppressWarnings("UnusedReturnValue")
private boolean registerZygoteSocketIfNeeded(@NonNull String[] envVars)
throws IllegalStateException, IOException {
// 获取Zygote Socket的文件路径
mZygoteSocketName = Zygote.getConfigurationProperty("ro.zygote", null);
if (mZygoteSocketName == null) {
Log.e(TAG, "Error starting Zygote: required property ro.zygote not set");
throw new RuntimeException("Error starting Zygote: required property ro.zygote not set");
}
// 如果文件已存在,则删除相关的unix域socket
File socketFile = new File(mZygoteSocketName);
if (socketFile.exists()) {
socketFile.delete();
}
// 创建ServerSocket对象,绑定到本地unix域socket上
try {
mZygoteServerSocket = new LocalServerSocket(mZygoteSocketName);
} catch (IOException ex) {
Log.e(TAG, "Error binding to local socket '" + mZygoteSocketName + "'", ex);
throw ex;
}
// 获取androidboot.classpath和androidboot.libs属性的值
String classPath = Zygote.getConfigurationProperty("androidboot.classpath", "");
String libraryPath = Zygote.getConfigurationProperty("androidboot.libs", "");
// 设置环境变量BOOTCLASSPATH和ANDROID_ROOT
final String bootClassPath = !classPath.isEmpty() ? "-Xbootclasspath:" + classPath.replace(':', ',') : "";
envVars = new String[] {
computeZygoteHeapSize(),
"BOOTCLASSPATH=" + bootClassPath,
"ANDROID_ROOT=" + "/system",
"ANDROID_DATA=" + "/data",
"ANDROID_PROPERTY_WORKSPACE=" + "/data/local/tmp",
"LD_LIBRARY_PATH=" + libraryPath};
// 设置好环境变量之后关闭Socket,等待应用进程的连接
mZygoteServerSocket.close();
return true;
}
如上所示,该方法主要实现了创建ServerSocket对象并接收应用进程的连接请求。在函数内部,最核心的部分是LocalServerSocket类的使用,它是一个继承自ServerSocket的本地socket类,因此它不需要使用网络中断和解封装技术,能够更快地处理传输数据。
接下来,该方法会获取androidboot.classpath和androidboot.libs属性的值,这些属性值是在启动Zygote进程时通过init.rc脚本获取的。
然后,该方法会设置BOOTCLASSPATH和ANDROID_ROOT等环境变量,并调用ZygoteInit中的computeZygoteHeapSize()方法计算出Zygote进程的堆大小。
最后,该方法会关闭ServerSocket对象并等待应用进程的连接请求。
应用进程的连接请求
应用进程的连接请求主要是通过ZygoteInit.runSelectLoop()方法实现的。在该方法中,Zygote进程会不断地监听ServerSocket对象是否有新的socket连接请求,并在接收到新的socket连接请求时,调用ZygoteInit.forkAndSpecialize()方法来创建新的应用进程,并通过socket文件描述符的方式将新进程启动起来。
void runSelectLoop(String abiList) {
while (true) {
...
// 接收新的连接请求
ZygoteConnection newPeer = acceptCommandPeer(mZygoteServerSocket);
// 创建新的进程
ZygoteProcess zygoteProcess = ZygoteProcess.getZygoteProcess();
// 通过Socket文件描述符的方式启动新进程
socketDescriptor = newPeer.getFileDescriptor();
zygoteProcess.start(socketDescriptor, newArgs, runtimeFlags, null,
nativeDebuggable, newStdin, newStdout, newStderr, null,
instructionSet, cdnEnabled, capabilities, permittedCapabilities, effectiveCapabilities);
}
}
如上所示,ZygoteInit.runSelectLoop()方法主要是循环监听ServerSocket对象,并在接收到新的连接请求时,通过ZygoteProcess.start()方法启动新的App进程。
ZygoteProcess.start()方法是一个比较庞大的方法,其主要流程如下:
public int start(int socketFd, String[] argv, int runtimeFlags,
String seInfo, boolean debuggerEnabled, FileDescriptor[] descriptors,
String instructionSet, String invokeWith, long[] fdsToClose, int[] fdsToIgnore,
boolean startChildZygote, String[] acceptedAbis) {
...
// 创建App进程
for (String abi : abis) {
...
// 创建新进程,并创建对应的应用类加载器
try {
return doPreForkInit(newZygote, socketFd, contentType, argc,
argv, instructionSet, invokeWith, acceptedAbis, runtimeFlags, seInfo,
stdio_fds, fdsToClose, fdsToIgnore, isTopZygote);
} catch (IOException e) {
if (e.getMessage().contains("Too many open files")) {
// If the kernel runs out of file descriptors, it may start throwing EIO on socket
// accept(). Since we're likely to recover from an EIO, log it only at a
// VERBOSE level.
Log.v(TAG, "IOException on accept: ", e);
} else {
Log.e(TAG, "Exception creating new zygote: ", e);
}
}
}
...
}
如上所示,doPreForkInit()方法是创建新进程的重要方法之一,它主要作用是创建应用进程的类加载器和执行系统属性文件。
系统属性文件的加载
在Zygote进程启动过程中,加载系统属性文件是一个非常重要的过程。系统属性文件中包含了许多Android系统运行所需的关键信息,例如堆大小、系统密度、语言环境等。
ZygoteInit.doPreload()方法是加载系统属性文件的核心方法。在该方法中,系统会首先扫描/system/build.prop文件和/prop目录中的所有*.prop文件,并将它们的属性信息读取到Properties对象中。然后,针对某些特殊的系统属性,比如ro.build.fingerprint,系统会调用SystemProperties.get()方法获取对应的属性值。
private static void doPreload() {
// Get a set of default properties from /system/build.prop and /prop files.
Properties preloadedProps = new Properties();
preloadedProps.load(new FileInputStream(new File("/system/build.prop")));
File[] propFiles = new File("/").listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".prop");
}
});
if (propFiles != null) {
for (File file : propFiles) {
try {
FileInputStream stream = new FileInputStream(file);
preloadedProps.load(stream);
stream.close();
} catch (IOException e) {
Log.w(TAG, "Unable to read " + file.getName() + ".prop: " + e.getMessage());
}
}
}
// Look for non-native debuggability override only if we haven't already gotten the value
// through a native property.
String nonDebuggable =
SystemProperties.get(PROP_NONDEBUGGABLE_APPS);
if (nonDebuggable == null) {
nonDebuggable = preloadedProps.getProperty(PROP_NONDEBUGGABLE_APPS);
}
// Look for a non-sdk graylist override only if we haven't already gotten the value
// through a native property.
String nonSdkGreylist =
SystemProperties.get(PROP_NON_SDK_GREYLIST_APPS);
if (nonSdkGreylist == null) {
nonSdkGreylist = preloadedProps.getProperty(PROP_NON_SDK_GREYLIST_APPS);
}
...
}
如上所示,doPreload()方法是ZygoteInit中一个比较重要的方法,它主要是将/system/build.prop和/prop/*.prop中的属性读取到preloadedProps这个Properties对象中。同时,该方法也会调用SystemProperties.get()方法获取ro.build.fingerprint等特殊属性的值。
接下来,将介绍应用进程类加载器的创建过程。
应用进程类加载器的创建
ZygoteProcess.getZygoteProcess().start()方法的主要作用是创建新的应用进程和相应的应用类加载器。ZygoteProcess通过调用NativeStart.main()的方式来启动新进程,并将当前ClassLoader传递给新进程。新进程中调用的 ClassLoader.getSystemClassLoader() 方法就会返回当前的ClassLoader,也就是Zygote进程的应用进程类加载器。
public static int loadZygoteChild(@NonNull ZygoteArguments parsedArgs,
@Nullable FileDescriptor[] descriptors, ZygoteServer zygoteServer) {
...
// 创建新的进程,调用的是Object.clone()方法
Process child = Zygote.fork();
if (child == null) {
return 1;
} else if (child == 0) {
// This is the child process.
setArgv0(parsedArgs.processName);
Os.setpgid(0, 0);
try {
// 通过类加载器和服务器端Socket描述符的方式启动新应用进程。
Runnable r = forkAndSpecialize(parsedArgs, Zygote.getForkServerSocket(),
Zygote.getUsapPoolSocket(), descriptors, runtimeFlags, gidList);
r.run();
} finally {
Zygote.nativeCloseServerSocket();
Zygote.nativeCloseUsapPoolSocket();
}
}
return handleChildProc(child, parsedArgs, zygoteServer);
}
如上所示,新应用进程中会通过forkAndSpecialize()方法创建一个应用类加载器,在运行时期,Zygote进程将通过Socket描述符的方式将应用进程类加载器传递给应用进程,并启动应用进程。
应用进程中通过ActivityThread类的main()方法来启动应用,它会在应用进程中启动ActivityThread主线程。
总结
本文介绍了Zygote进程的启动过程,包括Binder通信、双亲委派模型、系统属性加载和App启动等多个方面,通过代码和示例的方式,详细地讲解了Zygote进程启动的内部原理。在Zygote进程启动过程中,系统会加载系统属性文件,创建应用进程类加载器,并传递给应用进程。通过深入研究Zygote进程的启动过程,可以更好地理解Android系统的内部工作原理,并对Android应用的开发、优化和调试等方面有所帮助。
参考资料:
-
Android Framework源码:https://cs.android.com/android/platform/superproject/
-
Android Developers官方文档:https://developer.android.com/docs