作者:贾东风
转载地址:https://juejin.cn/post/7093412026483933215
1. zygote是什么?
在 Android 系统中,JavaVM(Java 虚拟机)
、应用程序进程
以及运行系统关键服务的 SystemServer 进程
都是由 Zygote
来创建的,我们也将它称为 孵化器
。它通过 fock
(复制进程)的形式来创建 "应用程序进程"
和 "SystemServer 进程"
,由于 Zygote 进程在启动时会创建 JavaVM,因此通过 fock 而创建的 "应用程序进程" 和 "SystemServer 进程" 可以在内部获取一个 JavaVM 的实例拷贝
。
2. zygote启动脚本
zygote的rc脚本是包含在system/core/rootdir/init.rc中的
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // ${ro.zygote} 由厂商定义,与平台相关
init.zygote32.rc init.zygote64.rc 只会启动一个朱进程 init.zygote32_64.rc init.zygote64_32.rc 会分别启动主副zygote进程
init.zygote32_64.rc:启动两个 zygote 进程 (zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64
//init.zygote64_32.rc
service zygote /system/bin/app_process64
-Xzygote /system/bin
--zygote
--start-system-server
--socket-name=zygote
service zygote_secondary /system/bin/app_process32
-Xzygote /system/bin
--zygote
--socket-name=zygote_secondary
--enable-lazy-preload
2.1 主要流程:
- 在init中通过init进程解析zytoe.rc中的配置执行脚本,通过FindService方法找到rc中配置的zygote服务, fork出zygote进程
- 在fork出的zygote进程中,执行service对应的/system/bin/app_process64脚本,进入app_main.cpp对应的main方法
详情参考: segmentfault.com/a/119000002…
3. app_main#main
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;//处于zygote进程中
bool startSystemServer = false;
bool application = false;//处于application进程
String8 niceName;
String8 className;
//1\. 解析参数
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = 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;
}
}
//2\. 准备参数
Vector<String8> args;
if (!className.isEmpty()) {
if (!LOG_NDEBUG) {
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
}
} else {//处于zytote进程模式
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer) {
args.add(String8("start-system-server"));
}
...
String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
//3\. 根据所处进程的不同,执行不同的初始化
if (zygote) {
runtime.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.");
}
}
3.1 主要流程:
- 解析main方法的调用参数,判断当前所处的进程模式
- 如果是处于zygote进程,则完成zygote的初始化,并创建systemserver进程
- 如果是处于应用进程,则完成运行时初始化
4. ZygoteInit初始化流程
4.1 AndroidRuntime.start
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...
//启动虚拟机
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
/*
* 2、Java虚拟机注册JNI方法
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
// 3
classNameStr = 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);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
// 4
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
// 6
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 {
// 6
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
...
}
在main方法中通过runtime.start方法,启动虚拟机,然后通过JNI的方式,调用java 层的ZygoteInit的main方法
首先,在AndroidRuntime的start函数中,主要处理流程如下:
- 使用startVm函数来启动弄Java虚拟机,
- 使用startReg函数为Java虚拟机注册JNI方法。
解析com.android.internal.os.ZygoteInit字符串,通过jni调用该类的main方法,从native进入java世界
4.2 ZygoteInit.main
4.2.1 preload
- preloadClasses
PRELOADED_CLASSES = "/system/etc/preloaded-classes";
使用class.forname在虚拟机中中预加载preloaded-classes
aosp:/ # cat /system/etc/preloaded-classes
un.util.logging.LoggingProxy
sun.util.logging.LoggingSupport
sun.util.logging.LoggingSupport$1
sun.util.logging.PlatformLogger
sun.util.logging.PlatformLogger$1
sun.util.logging.PlatformLogger$Level
....
- preloadResources
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
try {
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
...
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(ar);
...
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(ar);
...
if (mResources.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
N = preloadDrawables(ar);
...
}
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
}
}
分别调用preloadDrawables,preloadColorStateLists,preloadDrawables预加载各种图像和颜色配置资源
- preloadSharedLibraries();
private static void preloadSharedLibraries() {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
try {
System.loadLibrary("sfplugin_ccodec");
} catch (Error | RuntimeException e) {
// tolerate missing sfplugin_ccodec which is only present on Codec 2 devices
}
}
4.2.2 createManagedSocketFromInitSocket
创建socke连接
4.2.3 runSelectLoop
主要通过linux poll机制,轮询监听socket客服端请求连接情况。 主要流程:
- 将本地socket服务创建的套接字文件描述符添加到被监测的文件描述符socketFDs中去
- 调用Os.poll,等待文件描述符就绪
- 如果超时,停留一段时间再监听
- 如果返回正常的,则判断请求类型,共三中类型:
4.1 则判断是socket Connect,则通过accept答应请求,创建一个客户端的连接 4.2 如果是闯将子应用请求,则返回创建命令 4.3 如果是断开请求,则断开该文件描述符与server的连接
关于linux poll机制,请参考linux poll,java层的使用跟native层机制完全一致。
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
ArrayList<ZygoteConnection> peers = new ArrayList<>();
//1\. 将创建socket生成的文件描述符,添加到数组
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
int[] usapPipeFDs = null;
StructPollfd[] pollFDs;
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
//2\. 初始化poll()的参数fds
int pollIndex = 0;
for (FileDescriptor socketFD : socketFDs) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = socketFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
final int usapPoolEventFDIndex = pollIndex;
...
//3\. 阻塞等待请求事件
int pollReturnValue;
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
//超时
if (pollReturnValue == 0) {
// The poll timeout has been exceeded. This only occurs when we have finished the
// USAP pool refill delay period.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else {
boolean usapPoolFDRead = false;
//4\. 轮询遍历检测client[],处理有就绪事件的文件描述符
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {//客户端connect连接请求,非数据发送和读取请求
// Zygote server socket
ZygoteConnection newPeer = acceptCommandPeer(abiList);//接受客户端连接
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
// TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {//fork 子进程请求
// We're in the child. We should always have a command to run at
// this stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {//关闭连接请求
// We're in the server - we should never have any commands to run.
if (command != null) {
throw new IllegalStateException("command != null");
}
// We don't know whether the remote side of the socket was closed or
// not until we attempt to read from it from processOneCommand. This
// shows up as a regular POLLIN event in our regular processing
// loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(pollIndex);
socketFDs.remove(pollIndex);
}
}
} catch (Exception e) {
...
}
...
} else {
...
}
}
...
}
...
}
}
5. 总结
从以上的分析可以得知,Zygote进程启动中承担的主要职责如下
- 创建AppRuntime,执行其start方法。
- 创建JVM并为JVM注册JNI方法。
- 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
- 调用preload完成系统资源的预加载。主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries
- 通过ZygoteServer创建本地服务端socke连接(LocalServerSocket), 调用Os.socket, bindLocal绑定socket文件和进程
- 启动SystemServer进程。
- 通过runSelectLoop调用linux poll机制,阻塞等待客户端请求连接,创建应用,关闭连接请求
网友评论