Android 系统启动 - Zygote 进程
前言
前面我们在 Android 系统启动 - init 进程 中提到,init 进程在解析完 init.rc 脚本后,会启动 Zygote 进程。具体来讲,Zygote 是以服务(service)的形式配置在 init.rc 中,其对应的可执行文件为 /system/bin/app_processXX。init 进程会通过 fork
为每个服务创建一个独立的进程。对于 Zygote 来说, /system/bin/app_processXX 就会被启动,然后该进程内部最终会通过 AppRuntime.start
真正启动 Zygote 进程。
接下来,我们就来分析下 Zygote 具体的启动过程:
Zygote 启动过程
我们先来看下启动 Zygote 服务源码:
framework/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
···
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}
···
}
看到调用的是 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
,看到 "com.android.internal.os.ZygoteInit"
这种包名形式,就大概知道 runtime.start
该函数内部应该会通过 JNI 来调用 Java 层的类了。究竟是不是,我们进入看一下:
frameworks/base/core/jni/AndroidRuntime.cpp
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
···
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
// 创建 Java 虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
// JNI 方法注册
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
// 相当于 strArray= new String[options.size() + 1];
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
// 相当于 strArray[0] = "com.android.internal.os.ZygoteInit"
classNameStr = env->NewStringUTF(className);
env->SetObjectArrayElement(strArray, 0, classNameStr);
// 等价 strArray[1] = "start-system-server";
// strArray[2] = "--abi-list=xxx";
// 其中xxx为系统响应的cpu架构类型,比如arm64-v8a.
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);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
// 将"com.android.internal.os.ZygoteInit"转换为"com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className);
// 找到类 "com/android/internal/os/ZygoteInit"
jclass startClass = env->FindClass(slashClassName);
···
// 找到类 "com/android/internal/os/ZygoteInit"的静态方法main(String[])
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
···
// 调用 ZygoteInit.main(String[])
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
//释放相应对象的内存空间
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
该方法主要就是启动 Android 运行时,主要做了两件事:创建虚拟机 startVm
和 注册 JNI 方法 startReg
并启动 ZygoteInit.main
函数。
到这里,Zygote 服务进程通过 JNI 反射调用 Java 层代码,正式将 Android 运行环境从 c++ 层转移到 Java 层。Zygote 进程也同时成为 Android 系统第一个 Java 进程。
那我们接下来看看 ZygoteInit.main
函数源码:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
try {
// 开启DDMS功能
RuntimeInit.enableDdms();
···
// 为Zygote注册Socket
registerZygoteSocket(socketName);
···
// 预加载类和资源
preload();
···
// Do an initial gc to clean up after startup
// GC 释放资源
gcAndFinalize();
···
if (startSystemServer) {
// 启动 SystemServer 进程
startSystemServer(abiList, socketName);
}
// 进入监听状态,等待客户端请求
runSelectLoop(abiList);
// 关闭Socket
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run(); // 真正启动 SystemServer
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
// 关闭Socket
closeServerSocket();
throw ex;
}
}
可以看到,ZygoteInit.main
方法主要做了四件事:
- 为 Zygote 进程注册一个服务端 Socket:
registerZygoteSocket
; - 预加载类和资源:
preload
; - 启动 SystemServer 进程:
startSystemServer
; - 开启 Socket 监听,等待客户端请求:
runSelectLoop
下面我们依次分析上述四个操作:
首先来看下 Zygote 注册服务端 Socket 具体过程:registerZygoteSocket
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static void registerZygoteSocket(String socketName) {
if (sServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
// 从环境变量中获取Socket套接字描述符
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc); // 设置文件描述符
sServerSocket = new LocalServerSocket(fd);// 创建Socket的本地服务端
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
其实就是从环境变量中获取 Socket 套接字文件描述符(此处的 socket 是由 service_start
创建的,并且创建完后会通过 publish_socket
方法发布出去,发布的途径就是设置到环境变量中),然后创建 Socket 本地服务端。
接下来看下 Zygote 预加载类和资源的具体过程:preload
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload() {
Log.d(TAG, "begin preload");
// 预加载常用类:/system/etc/preloaded-classes
preloadClasses();
// 预加载资源文件,包含drawable和color资源
preloadResources();
// 预加载OpenGL
preloadOpenGL();
// 预加载共享库:"android","compiler_rt" 和 "jnigraphics"
preloadSharedLibraries();
// 预加载文本连接符资源
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
// 为了达到内存共享,WebViewFactory 需要进行的初始化操作
WebViewFactory.prepareWebViewInZygote();
Log.d(TAG, "end preload");
}
该方法没有太多好说的,就是预加载了一些常用类库和资源文件等,这样后续创建新的虚拟机进程时,直接 fork
Zygote 进程,就无需再次预加载这些资源了,节省时间,使虚拟机能更快启动。
ZygoteInit.main
第三个主要操作就是启动 SystemServer 进程,该部分详细内容请查看:Android 系统启动 - SystemServer 进程
最后我们来看下 Zygote 进程实现响应客户端请求的具体过程:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// sServerSocket就是Zygote自己创建的Socket:registerZygoteSocket
// 即 zygote 的 socket 通信服务端,保存到fds[0
fds.add(sServerSocket.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 {
// 处理轮询状态,当pollFds有事件到来时则往下执行,否则阻塞
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
// 采用I/O多路复用机制,当接收到客户端发出连接请求 或者 数据处理请求到来,则往下执行
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
// 即fds[0],代表的是sServerSocket,意味着有客户端连接请求,则会创建ZygoteConnection对象,并添加到fds。
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
// i>0,说明接收到一个创建应用进程的请求,则调用ZygoteConnection的runOnce函数来创建一个新的应用程序进程。
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}
该方法采用 I/O 多路复用机制,高效地实现了 Zygote 进程的 Socket 监听。当有客户端的连接请求或数据处理时,则响应客户端请求。特别的,当接收到客户端请求创建应用进程时,就会调用 ZygoteConnection.runOnce
方法创建一个新的应用进程,具体的进程创建方法,请查看:理解Android进程创建流程
至此,Zygote 进程的启动流程分析结束。
总结
Zygote 进程是由 init 进程启动的,其对应的可执行文件为 /system/bin/app_processXX,其内部最终会通过 AppRuntime.start
方法启动 Zygote 进程:具体来说,该方法内部会创建 Java 虚拟机(startVm
) 和 注册 JNI 本地方法(startReg
),最后通过 JNI 调用启动 Java 层 Zygote 进程(ZygoteInit.main
),从而让 Android 系统正式开启第一个 Java 进程,即 Zygote。
Java 框架层的 Zygote 主要做的就是:创建服务端 Socket 并进行监听(registerZygoteSocket
,runSelectLoop
),预加载常用类和资源,启动 SystemServer 进程。