Android 9 (P) Zygote进程启动源码分析指南二
Android 9 Zygote进程启动源码分析指南二前言 各位老司机们,现在闲下来终于有时间接着续写Android 9 Zygote进程启动源码分析指南二了,在前面的篇章Android P Zygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化Android世界做的前期准备工作,大概的内容如下所示:Zygote进程启动流程整体概括Zygote 进
Android 9 Zygote进程启动源码分析指南二
Android 9 (P) 系统启动及进程创建源码分析目录:
Android 9 (P)之init进程启动源码分析指南之一
Android 9 (P)之init进程启动源码分析指南之二
Android 9 (P)之init进程启动源码分析指南之三
Android 9 (P)核心服务和关键进程启动
Android 9 (P)Zygote进程启动源码分析指南一
Android 9 (P)Zygote进程启动源码分析指南二
Android 9 (P)系统启动之SystemServer大揭秘上
Android 9 (P)系统启动之SystemServer大揭秘下
Android 9 (P)应用进程创建流程大揭秘
引言
各位老司机们,现在闲下来终于有时间接着续写Android 9 Zygote进程启动源码分析指南二了,在前面的篇章Android 9(P)Zygote进程启动源码分析指南一中,我们已经讲解了zygote启动的前面阶段主要是为了孵化Android世界做的前期准备工作,大概的内容如下所示:
- Zygote进程启动流程整体概括
- Zygote 进程从何而来
- zygote创建参数解析
- 创建虚拟机
- 注册JNI函数
都说zygote开创了Android的世界,创造了五彩缤纷的Android生态,孵化了各式的上层App。那么本篇将会带领大伙看看,我们的zygote是怎么做到的!要的罗,让我们开干。
注意:本文演示的代码是Android P高通msm8953平台源码。其中涉及的源码路径如下:
frameworks/base/include/android_runtime/AndroidRuntime.h
frameworks/base/core/jni/AndroidRuntime.cpp
frameworks/base//core/java/com/android/internal/os/ZygoteInit.java
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
一. 通过JNI反射启动ZygoteInit的main方法
在前面zygote启动阶段已经创建好了虚拟机,注册好了系统JNI函数,一切就绪只欠东风了!在接下来通过JNI的反射调用Java的类了。关于JNI的语法如果大伙有不熟悉的可以参见系列篇章JNI/NDK入门一锅端。
1.1 JNI反射启动ZygoteInit的main
还是熟悉的味道,还是原来的配方,经过一顿猛如虎的操作最终调用CallStaticVoidMethod反射从而调用ZygoteInit的main函数,
char* AndroidRuntime::toSlashClassName(const char* className)
{
char* result = strdup(className);
for (char* cp = result; *cp != '\0'; cp++) {
if (*cp == '.') {
*cp = '/';
}
}
return result;
}
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
......
/*
* 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;//申明一些jni变量,没有什么好说的
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
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.
*/
/*
*将字符中的.转换为/,我们前面传递过来的字符串是com.android.internal.os.ZygoteInit,
*通过这里转换为com/android/internal/os/ZygoteInit,不要问我为啥要这样这个是JNI的规范
*/
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 {
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 {
env->CallStaticVoidMethod(startClass, startMeth, strArray);//调用main方法
#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");
......
}
二. 登陆Java世界
兜兜转转,调用CallStaticVoidMethod终于登陆Java的世界了,Java世界真香,让我们畅游一番。
2.1 ZygoteInit.main
该代码定义在frameworks/base//core/java/com/android/internal/os/ZygoteInit.java中,这里不过多详细解释,在main方法中主要做了如下几件事情:
- 解析参数
- 调用zygoteServer.registerServerSocketFromEnv创建zygote通信的服务端
- 调用forkSystemServer启动system_server
- zygoteServer.runSelectLoop进入循环模式
private static final String SOCKET_NAME_ARG = "--socket-name=";
public static void main(String argv[]) {
....
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {//解析参数
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
zygoteServer.registerServerSocketFromEnv(socketName);//注册zygote进程和AMS交互的通道
......
preload(bootTimingsTraceLog);//预加载类和资源
gcAndFinalize();//GC操作
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);//启动system_server
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);//进入循环模式
......
}
2.2 zygoteServer.registerServerSocketFromEnv
该代码的路径是frameworks/base/core/java/com/android/internal/os/ZygoteServer.java,这里主要是通过解析zygote启动传入的变量值得到 zygote 文件描述符,根据该文件描述符创建 socket,用来和 ActivityManagerService 通信。AMS 通过 Process.start 来创建新的进程,而Process.start 会先通过 socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。这里传入的-为-socket-name=zygote.。
/**
* Registers a server socket for zygote command connections. This locates the server socket
* file descriptor through an ANDROID_SOCKET_ environment variable.
*
* @throws RuntimeException when open fails
*/
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
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);//设置文件描述符
mServerSocket = new LocalServerSocket(fd);//创建socket本地服务端
mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
LocalSocket是Android的妈咪谷歌为我们带来了,比Java本身的socket效率要高,没有经过协议栈,是Android自己实现的类似共享内存一样的东东,在传输大量数据的时候就需要用到,关于LocalSocket的介绍和使用大伙可以参考篇章Android Framework层和Native层通过LocalSocket实现通信。
我们知道ActivityManagerService 通过 Process.start 来创建新的进程,而 Process.start 会先通过socket 连接到 zygote 进程,并最终由 zygote 完成进程创建。如下是App进程创建请求Zygote创建新的进程。
//定义在frameworks/base/core/java/android/os/Process.java
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
//定义在frameworks/base/core/java/android/os/ZygoteProcess.java
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
private Process.ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
boolean startChildZygote,
String[] extraArgs)
throws ZygoteStartFailedEx {
......
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
......
}
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
primaryZygoteState = ZygoteState.connect(mSocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
我不知道大伙读到这里是否有一个疑问,为啥AMS和Zygote通信使用的是socket而不是Android的杜门绝技Binder,这个当初学习源码的时候也是我思考的,来大伙思考思考为啥Android的设计者是这么设计的呢,我认为主要有如下几个方面的考虑:
- zygote比service manager先启动,从这个意义触发,zygote没有service manager可以注册binder,所以没有办法binder
- zygote进程和service manager进程都是由init进程启动的,那怕先启动service manager,也不能保证zygote起来的时候service manager启动好了,这样就需要额外的同步
- 同时这个socket的所有者是root,用户组是system,只有系统权限的用户才能读写,这又多了一个安全保障(这里的socket是unix域的socket,而不是internet域的socket)
- 最最主要的是zygote是通过fork生成进程的,而多线程是不允许fork的,可能造成死锁,同时Binder又是多线程,为了避免这些麻烦所以干脆使用socket
2.3 preload预加载
预加载类和资源,android Java 进程都是由 zygote 进程 fork,zygote 通过预加载类和资源可以加快子进程的执行速度和优化内存。因为预加载的类和资源较多,在开机优化过程中也需要重点关注 preload 的耗时。
//预加载位于/system/etc/preloaded-classes文件中的类
preloadClasses();
//预加载资源,包含drawable和color资源
preloadResources();
//预加载OpenGL
preloadOpenGL();
//通过System.loadLibrary()方法,
//预加载"android","compiler_rt","jnigraphics"这3个共享库
preloadSharedLibraries();
//预加载 文本连接符资源
preloadTextResources();
//仅用于zygote进程,用于内存共享的进程
WebViewFactory.prepareWebViewInZygote();
- preloadClasses()预加载的文件路径为/system/etc/preloaded-classes,预加载比较多有大概4000多个类,这里对于类的加载主要通过Class.forName()方法来进行的。
[Landroid.accounts.Account;
[Landroid.animation.Animator;
[Landroid.animation.Keyframe$FloatKeyframe;
[Landroid.animation.Keyframe$IntKeyframe;
[Landroid.animation.Keyframe$ObjectKeyframe;
[Landroid.animation.Keyframe;
[Landroid.animation.PropertyValuesHolder;
[Landroid.app.LoaderManagerImpl;
[Landroid.content.ContentProviderResult;
[Landroid.content.ContentValues;
[Landroid.content.Intent;
[Landroid.content.UndoOwner;
[Landroid.content.pm.ActivityInfo;
[Landroid.content.pm.ConfigurationInfo;
[Landroid.content.pm.FeatureGroupInfo;
[Landroid.content.pm.FeatureInfo;
[Landroid.content.pm.InstrumentationInfo;
[Landroid.content.pm.PathPermission;
- preloadResources()主要预加载的是com.android.internal.R.array.preloaded_drawables和com.android.internal.R.array.preloaded_color_state_lists的,那这些资源的载体是什么,主要是加载framework-res.apk中的资源,各位也可以反编译看看。在应用程序中以com.android.internal.R.xxx开头的资源,便是此时由Zygote加载到内存的。
06-04 15:39:01.041 2901 2901 D Zygote : begin preload
06-04 15:39:01.041 2901 2901 I Zygote : Installing ICU cache reference pinning...
06-04 15:39:01.041 2901 2901 I Zygote : Preloading ICU data...
06-04 15:39:01.061 2901 2901 I Zygote : Preloading classes...
06-04 15:39:01.065 2901 2901 W Zygote : Class not found for preloading: [Landroid.view.Display$ColorTransform;
06-04 15:39:01.150 2901 2901 E Typeface: Error mapping font file /system/fonts/DroidSansFallback.ttf
06-04 15:39:01.291 2901 2901 I art : Thread[1,tid=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"
06-04 15:39:01.291 2901 2901 D MtpDeviceJNI: register_android_mtp_MtpDevice
06-04 15:39:01.292 2901 2901 I art : Thread[1,tid=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"
06-04 15:39:01.292 2901 2901 I art : Thread[1,tid=2901,Native,Thread*=0x7f77696a00,peer=0x12c060d0,"main"] recursive attempt to load library "/system/lib64/libmedia_jni.so"
06-04 15:39:01.316 2901 2901 D : Environment::getOverlayDir() = /system/vendor/Default/system/vendor/overlay
06-04 15:39:01.352 2901 2901 W Zygote : Class not found for preloading: android.view.Display$ColorTransform
06-04 15:39:01.352 2901 2901 W Zygote : Class not found for preloading: android.view.Display$ColorTransform$1
06-04 15:39:01.449 2901 2901 I System : Loaded time zone names for "" in 32ms (30ms in ICU)
06-04 15:39:01.469 2901 2901 I System : Loaded time zone names for "en_US" in 19ms (16ms in ICU)
06-04 15:39:01.491 2901 2901 I System : Loaded time zone names for "zh_CN" in 22ms (20ms in ICU)
06-04 15:39:01.518 2901 2901 I Zygote : ...preloaded 4158 classes in 457ms.
06-04 15:39:01.518 2901 2901 I art : VMRuntime.preloadDexCaches starting
06-04 15:39:01.572 1374 1419 E slim_daemon: [NDK] bindNDKSensors: Sensor server is unavailable.
06-04 15:39:01.664 2901 2901 I art : VMRuntime.preloadDexCaches strings total=292199 before=40345 after=40345
06-04 15:39:01.665 2901 2901 I art : VMRuntime.preloadDexCaches types total=24111 before=8040 after=8075
06-04 15:39:01.665 2901 2901 I art : VMRuntime.preloadDexCaches fields total=115222 before=41429 after=41660
06-04 15:39:01.665 2901 2901 I art : VMRuntime.preloadDexCaches methods total=201879 before=83038 after=83782
06-04 15:39:01.665 2901 2901 I art : VMRuntime.preloadDexCaches finished
06-04 15:39:01.666 2901 2901 I Zygote : Preloading resources...
06-04 15:39:01.682 2901 2901 W Resources: Preloaded drawable resource #0x108025d (android:drawable/dialog_background_material) that varies with configuration!!
06-04 15:39:01.683 2901 2901 W Resources: Preloaded drawable resource #0x1080299 (android:drawable/editbox_dropdown_background_dark) that varies with configuration!!
06-04 15:39:01.686 2901 2901 W Resources: Preloaded color resource #0x10600e2 (android:color/material_grey_800) that varies with configuration!!
06-04 15:39:01.686 2901 2901 W Resources: Preloaded drawable resource #0x10802ca (android:drawable/floating_popup_background_dark) that varies with configuration!!
06-04 15:39:01.694 2901 2901 W Resources: Preloaded drawable resource #0x108030c (android:drawable/ic_clear_disabled) that varies with configuration!!
06-04 15:39:01.694 2901 2901 W Resources: Preloaded drawable resource #0x1080311 (android:drawable/ic_clear_normal) that varies with configuration!!
06-04 15:39:01.698 2901 2901 W Resources: Preloaded drawable resource #0x108035a (android:drawable/ic_go) that varies with configuration!!
06-04 15:39:01.704 2901 2901 W Resources: Preloaded drawable resource #0x1080038 (android:drawable/ic_menu_close_clear_cancel) that varies with configuration!!
06-04 15:39:01.707 2901 2901 W Resources: Preloaded drawable resource #0x1080045 (android:drawable/ic_menu_more) that varies with configuration!!
06-04 15:39:01.714 2901 2901 W Resources: Preloaded drawable resource #0x10804f1 (android:drawable/menu_background_fill_parent_width) that varies with configuration!!
06-04 15:39:01.744 2901 2901 W Resources: Preloaded drawable resource #0x1080787 (android:drawable/text_edit_paste_window) that varies with configuration!!
06-04 15:39:01.748 2901 2901 W Resources: Preloaded drawable resource #0x1080096 (android:drawable/toast_frame) that varies with configuration!!
06-04 15:39:01.748 2901 2901 I Zygote : ...preloaded 114 resources in 82ms.
06-04 15:39:01.752 2901 2901 W Resources: Preloaded color resource #0x106010b (android:color/background_cache_hint_selector_material_dark) that varies with configuration!!
06-04 15:39:01.753 2901 2901 W Resources: Preloaded color resource #0x1060110 (android:color/btn_default_material_dark) that varies with configuration!!
06-04 15:39:01.755 2901 2901 I Zygote : ...preloaded 41 resources in 7ms.
06-04 15:39:01.757 2901 2901 D libEGL : loaded /vendor/lib64/egl/libEGL_adreno.so
06-04 15:39:01.773 2901 2901 D libEGL : loaded /vendor/lib64/egl/libGLESv1_CM_adreno.so
06-04 15:39:01.785 2901 2901 D libEGL : loaded /vendor/lib64/egl/libGLESv2_adreno.so
06-04 15:39:01.803 2901 2901 I Zygote : Preloading shared libraries...
06-04 15:39:01.815 2901 2901 I Zygote : Uninstalled ICU cache reference pinning...
06-04 15:39:01.816 2901 2901 I Zygote : Installed AndroidKeyStoreProvider in 2ms.
06-04 15:39:01.830 2901 2901 I Zygote : Warmed up JCA providers in 13ms.
06-04 15:39:01.830 2901 2901 D Zygote : end preload
06-04 15:39:01.831 2901 2901 I art : Starting a blocking GC Explicit
06-04 15:39:01.856 2901 2901 I art : Explicit concurrent mark sweep GC freed 58740(6MB) AllocSpace objects, 134(2MB) LOS objects, 71% free, 3MB/11MB, paused 152us total 24.977ms
06-04 15:39:01.858 2901 2901 I art : Starting a blocking GC Explicit
06-04 15:39:01.870 2901 2901 I art : Explicit concurrent mark sweep GC freed 4484(191KB) AllocSpace objects, 1(24KB) LOS objects, 72% free, 2MB/10MB, paused 127us total 11.445ms
06-04 15:39:01.873 2901 2908 I art : Starting a blocking GC HeapTrim
06-04 15:39:01.876 2901 2901 I art : Starting a blocking GC Background
当经过上述的步骤后,zygote进程内加载了preload()方法中的所有资源,当需要fork新进程时,采用COW(copy-on-write)技术,这里盗用已不在Android界的gityuan的一张图来说明:
copy-on-write即写时拷贝技术,zygote在这里使用了copy-on-write技术可以提高应用运行速度,因为该种方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效降低了内存的使用量。也就是说当新的App通过fork()创建的的时候不进行内存的复制,这是因为复制内存的开销是很大的,此时子进程只需要共享父进程的内存空间即可,因为这个时候他们没有差异。而当子进程需要需要修改共享内存信息时,此时才开始将内存信息复制到自己的内存空间中,并进行修改。感觉很高大上啊,这也就是为啥我们的App里面也能使用预加载的资源,so库等。这下大伙明白为啥我们能import com.android.internal.R.xxx的资源了吗。
2.4 forkSystemServer
该代码定义在frameworks/base//core/java/com/android/internal/os/ZygoteInit.java,其逻辑主要就是准备参数,并启动 system_server 进程,后续启动的Android Java 系统服务都将驻留在该进程中,它是是Android framework核心。这里主要设置了system_server 进程uid和gid,process name,class name。并且从zygote进程fork新进程后,需要关闭zygote原有的socket,另外,对于有两个zygote进程情况,需等待第2个zygote创建完成。
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_PTRACE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG,
OsConstants.CAP_WAKE_ALARM,
OsConstants.CAP_BLOCK_SUSPEND
);
/* Containers run without some capabilities, so drop any caps that are not available. */
StructCapUserHeader header = new StructCapUserHeader(
OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
StructCapUserData[] data;
try {
data = Os.capget(header);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to capget()", ex);
}
capabilities &= ((long) data[0].effective) | (((long) data[1].effective) << 32);
/* Hardcoded command line to start the system server */
String args[] = {//参数准备
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
//用于解析参数,生成目标格式
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver", false);
if (profileSystemServer) {
parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
/* Request to fork the system server process */
//fork子进程,用于运行system_server
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {//进入子进程system_server
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
// 完成system_server进程剩余的工作
return handleSystemServerProcess(parsedArgs);
}
return null;
}
06-04 15:39:01.920 2901 2901 I Zygote : System server process 2910 has been created
06-04 15:39:01.923 2901 2901 I Zygote : Accepting command socket connections
06-04 15:39:01.940 2910 2910 I Zygote : Process: zygote socket opened, supported ABIS: armeabi-v7a,armeabi
06-04 15:39:01.941 2910 2910 I InstallerConnection: connecting...
06-04 15:39:01.942 771 771 I : new connection
06-04 15:39:01.945 2910 2910 I InstallerConnection: disconnecting...
06-04 15:39:01.945 771 771 E : eof
06-04 15:39:01.945 771 771 E : failed to read size
06-04 15:39:01.945 771 771 I : closing connection
06-04 15:39:01.955 2910 2910 V appproc : App process: starting thread pool.
06-04 15:39:01.959 2910 2910 I SystemServer: Entered the Android system server!
这里我有一个疑问就是设置的system_server进程的uid=1000,gid=1000可是实际ps查看的并不是,这个希望大伙一起探讨。各位不好意思误导大伙了,这里的USER为sytem就代表uid为1000,这里把PID和UID混为一谈了。
127|msm8953_64:/ # ps 1515
USER PID PPID VSIZE RSS WCHAN PC NAME
system 1515 762 2384060 133700 SyS_epoll_ 0000000000 S system_server
2.5 ZygoteServer.runSelectLoop
该代码定义在frameworks/base/core/java/com/android/internal/os/ZygoteServer.java,在这个阶段zygote将进入循环状态等待AMS来和zygote进行通信,从而孵化新的App。
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
//每次循环,都重新创建需要监听的pollFds
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);
}
/*注意这里是倒序处理的,网上有的博客说是优先处理已建立连接的信息,后处理新建连接的请求
* 我觉得这个表述不是很正确,我觉得采用倒序是为了先处理已经建立连接的请求,但是这个优先反而是后面建立连接的请求有数据到来是优先处理了
* 然后接着最后处理sServerSocket,此时即有新的客户端要求建立连接
*/
for (int i = pollFds.length - 1; i >= 0; --i) {
//采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;
// 否则进入continue,跳出本次循环。
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
//即fds[0],代表的是sServerSocket因为它最先加入,则意味着有客户端连接请求;
// 则创建ZygoteConnection对象,并添加到fds。
ZygoteConnection newPeer = acceptCommandPeer(abiList);
//加入到peers和fds,下一次也开始监听
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
//i>0,则代表通过socket接收来自对端的数据,并执行相应操作
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
// 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(i);
fds.remove(i);//处理完则从fds中移除该文件描述符
}
}
} catch (Exception e) {
if (!mIsForkChild) {
// We're in the server so any exception here is one that has taken place
// pre-fork while processing commands or reading / writing from the
// control socket. Make a loud noise about any such exceptions so that
// we know exactly what failed and why.
Slog.e(TAG, "Exception executing zygote command: ", e);
// Make sure the socket is closed so that the other end knows immediately
// that something has gone wrong and doesn't time out waiting for a
// response.
ZygoteConnection conn = peers.remove(i);
conn.closeSocket();
fds.remove(i);
} else {
// We're in the child so any exception caught here has happened post
// fork and before we execute ActivityThread.main (or any other main()
// method). Log the details of the exception and bring down the process.
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
} finally {
// Reset the child flag, in the event that the child process is a child-
// zygote. The flag will not be consulted this loop pass after the Runnable
// is returned.
mIsForkChild = false;
}
}
}
}
}
从上面的代码可以看出,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。从前面可以看到初始时fds中仅有server socket,因此当有数据到来时,将执行i等于0的分支。此时,显然是需要创建新的通信连接,因此acceptCommandPeer将被调用。让我们接着分析看看它究竟干了些啥!
2.6 ZygoteServer.acceptCommandPeer
让我们接着分析这段代码,如下:
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
// socket编程中,accept()调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET
// 它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符
// 新建立的套接字不在监听状态,原来所监听的套接字的状态也不受accept()调用的影响,这个就是套接字编程的基础了
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
通过上面的代码我们可以看到,acceptCommandPeer主要是基础的socket套接字编程,调用了server socket的accpet函数等待客户端的连接。当有新的连接建立时,zygote进程将会创建出一个新的socket与其通信,并将该socket加入到fds中。所以一旦和客户端进程的通信连接建立后,fds中将会有多个socket至少会有两个。
当poll监听到这一组sockets上有数据到来时,就会从阻塞中恢复。于是,我们需要判断到底是哪个socket收到了数据。
2.7 ZygoteConnection.processOneCommand
该代码定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中,解析socket客户端即AMS传递过来的参数,然后调用forkAndSpecialize创建App进程。
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
//读取socket客户端发送过来的参数列表
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
...
throw new IllegalStateException("IOException on command socket", ex);
}
...
//将socket客户端传递过来的参数,解析成Arguments对象格式
parsedArgs = new Arguments(args);
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
if (pid == 0) {
// in child
//子进程执行
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
//父进程执行
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
好了到此处runSelectLoop已经讲解完毕了,我们可以看出它采用的是倒序的方式进行轮询。且由于server socket第一个被加入到fds,所以它是最后被轮询到的。因此最后轮询到的socket才需要处理新建连接的操作;其它socket收到数据时,仅需要调用zygoteConnection的processOneCommand函数执行数据对应的操作。若一个连接处理完所有对应消息后,该连接对应的socket和连接等将被移除。
总结
到这里zygote启动就基本告一段落了,zygote启动的调用流程图如下所示:
细数下来,zygote进程启动主要干了如下的相关大事:
- 解析init.zygotexxx.rc传递过来的参数,创建AppRuntime并调用AppRuntime.start()方法;
- 调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数
- 虚拟机和JNI环境构建后以后,通过JNI方式调用ZygoteInit.main(),正式进入的Java世界
- 调用registerServerSocketFromEnv()建立socket通道,zygote作为通信的服务端,用于响应客户端请求,这里大伙可以思考一个问题就是为啥用的是zygote通信而不是binder
- preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率
- zygote完毕大部分工作,接下来再通过forkSystemServer(),fork得力帮手system_server进程,也是上层framework的运行载体
- zygote功成身退,调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。
写在最后
Android 9 zygote启动就告一段落了,但是这个只是一个开端,因为其中的forkSystemServer启动system_server和forkAndSpecialize启动App并没有讲解,这个牵涉的东西太多了,现在功力不足以将其写清楚,所以接下来会修炼内功,在接下来的篇章中力求讲上述两个遗留的问题说透彻。如果对给位有帮助欢迎点赞一个,如果写得有问题也欢迎多多指正。未完待续,下个篇章再见。好了system_server启动的博客终于来了,参见Android 9 (P)系统启动之SystemServer大揭秘上篇章!
更多推荐
所有评论(0)