Android启动流程_Zygote阶段
前言
上一篇文档中我们描述了 Android 启动中的 init 启动部分,本片文档将会继续 Android 启动流程的逻辑,继续梳理 Zygote 部分功能。
说明框架
对于 Zygote 进程,要从以下框架说明:
第一点,编译,zygote 运行的二进制编译文件
zygote32、zygote64 文件对应 app_process32、app_precess64 文件区别,对应的功能。
第二点,功能,zygote 的重要功能描述
从上面两个点展开说明,可以说到 AndroidRuntime 虚拟机、预加载资源、app_process 命令的使用等。
正文
对于 Zygote 进程的功能描述,准备从以下方面展开描述:
首先,会对于 Zygote 进程运行的二进制文件描述,从代码路径到编译结构展开,对 Zygote 进程的功能文件进行整理;
然后,整理 Zygote 进程在启动中的时序图。
接着从功能的角度对 Zygote 进程的运行进行整理,重要的部分详细描述
最后,总结 Zygote 进程的知识点。
在上一篇介绍 init 进程的文档中,我们提到在 init 的第二阶段会扫描系统路径下的 rc 文件,并解析执行所有的 rc 文件中定义功能。在 Android 中,很多的服务是在这个阶段被启动,作为系统的 Native 服务或者说是守护进程运行,在这些服务中,对于启动流程而言,必须要提到的一个服务进程就是 Zygote 进程。
1、rc 文件
Zygote 进程在 init 进程中以 service 的方式启动的。从 Android 5.0 开始,Zygote 还是有变动的,之前是直接放入 init.rc 中的代码块中,现在是放到了单独的文件中,通过 init.rc 中通过 “import” 的方式引入文件。
如下所示:
从上面的语句可以看到,init.rc 并不是直接引入某个固定的文件,而是根据属性 ro.zygote 的内容来引入不同的文件。这是因为从 Android 5.0 开始,Android 系统开始支持 64 位的编译,Zygote 进程本身也会有 32 位和 64 位版本的区别,因此,这里通过 ro.zygote 属性来控制启动不同版本的 Zyogte 进程。
ro.zygote 属性的取值可能有以下情况:
zygote32
zygote64
zygote32_64
zygote64_32
在系统路径 /system/core/rootdir 同级目录下,对应四个关于 zygote 的 rc 文件匹配 zygote 的启动。
/android/system/core/rootdir$ ls -l
-rw-r--r-- 1 domain users 959 Nov 9 2022 init.zygote32_64.rc
-rw-r--r-- 1 domain users 563 Nov 9 2022 init.zygote32.rc
-rw-r--r-- 1 domain users 981 Nov 9 2022 init.zygote64_32.rc
-rw-r--r-- 1 domain users 565 Nov 9 2022 init.zygote64.rc
那么现在我们的版本中对于 ro.zygote 属性值是如何定义的呢?如下所示:
项目:/ $ getprop | grep -i "ro.zygote"
[ro.zygote]: [zygote64_32]
现在的项目中使用的是 ro.zygote = zygote64_32,也就是对应使用 init.zygote64_32.rc 文件启动 Zygote 进程。
这四个 rc 文件对应了 Android 中支持的四种运行模式。
init.zygote32.rc : 纯32位模式
init.zygote32_64.rc : 混32位模式,即32位为主,64位为辅
init.zygote64.rc : 纯64位模式
init.zygote64_32.rc : 混64位模式,即64位为主,32位为辅
1.1 init.zygote32.rc
下面是 init.zygote32.rc 文件内容,系统使用此 rc 文件,zygote 将会以 32 位模式运行。
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
1.2 init.zygote32_64.rc
下面是 init.zygote32_64.rc 文件文件内容,系统使用此 rc 文件,会启动两个 zygote 进程,32 位和 64 位两种服务,32 位为主,64 位为辅。
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
1.3 init.zygote64.rc
下面是 init.zygote64.rc 文件内容,系统使用此 rc 文件,zygote 将会运行在 64 位模式。
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
1.4 init.zygote64_32.rc
下面是 init.zygote64_32.rc 文件内容,系统使用此 rc 文件,将会启动两个zygote 进程,64 位和 32 位,64 位为主,32 位为辅。
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
项目上现在使用的 rc 文件是 init.zygote64_32.rc 文件,我们来简单分析下文件内容
从 rc 文件中我们可以看出,定义了两个 zygote 服务:zygote 和 zygote_secondary。
zygote 是作为主服务,用于处理 64 位的程序,对应启动的可执行文件是 app_process64;
-
启动的参数是“-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote”;
-
定义了名为 zygote 的 Socket;
-
onrestart 指的是当 zygote 进程重启时,执行后面的指令,比如写入电源状态、重启系统服务等;
-
当创建子进程时,在 /dev/cpuset/foreground/tasks 里写入 pid。
zygote_secondary 是作为辅服务,用于处理 32 位的程序,对应启动的可执行文件是 app_process32;
-
启动的参数是“-Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload”;
-
定义了名为 zygote_secondary 的 Socket;
-
当 zygote_secondary 进程重启时,重启 zygote 服务;
-
当创建子进程时,在 /dev/cpuset/foreground/tasks 下写入 pid。
2、app_process
app_process 是启动 zygote 时执行的程序。app_process 是 Android 上一个原生程序,是 APP 进程的主入口点,不仅是说可以启动 zygote 进程,还可以启动任何其他的 APP,简单理解为是一个可以使用虚拟机运行 main 函数的程序。
2.1 代码路径
app_process 的代码路径位于 /android/frameworks/base/cmds/app_process 路径下。文件结构如下:
ubuntu16-017:~/workspace/src/android/frameworks/base/cmds/app_process$ ls -l
total 28
-rw-r--r-- 1 domain users 1992 Nov 9 2022 Android.mk
-rw-r--r-- 1 domain users 12019 Nov 9 2022 app_main.cpp
-rw-r--r-- 1 domain users 0 Nov 9 2022 MODULE_LICENSE_APACHE2
-rw-r--r-- 1 domain users 10695 Nov 9 2022 NOTICE
2.2 编译
编译文件是 /android/frameworks/base/cmds/app_process/Android.mk,下面我们来看下编译
LOCAL_PATH:= $(call my-dir)
// 编译所需依赖库
app_process_common_shared_libs := \
libandroid_runtime \
libbinder \
libcutils \
libdl \
libhidlbase \
liblog \
libnativeloader \
libutils \
# This is a list of libraries that need to be included in order to avoid
# bad apps. This prevents a library from having a mismatch when resolving
# new/delete from an app shared library.
# See b/21032018 for more details.
app_process_common_shared_libs += \
libwilhelm \
app_process_common_static_libs := \
libsigchain \
// 编译的文件
app_process_src_files := \
app_main.cpp \
app_process_cflags := \
-Wall -Werror -Wunused -Wunreachable-code
app_process_ldflags_32 := \
-Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
app_process_ldflags_64 := \
-Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(app_process_src_files)
LOCAL_LDFLAGS_32 := $(app_process_ldflags_32)
LOCAL_LDFLAGS_64 := $(app_process_ldflags_64)
LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs)
LOCAL_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs)
// 编译模块名为 app_process
LOCAL_MODULE:= app_process
/*
* LOCAL_MULTILIB 可以指定模块编译 32 位或者 64 位或者都编译
* 值可选
* “both”: build both 32-bit and 64-bit.
* “32”: build only 32-bit.
* “64”: build only 64-bit.
* “first”: build for only the first arch (32-bit in 32-bit devices and 64-bit in 64-bit devices).
* “”: the default; the build system decides what arch to build based on the module class and other LOCAL_ variables, such as LOCAL_MODULE_TARGET_ARCH, LOCAL_32_BIT_ONLY, etc.
*/
LOCAL_MULTILIB := both
// 32 位可执行文件名为 app_process32
LOCAL_MODULE_STEM_32 := app_process32
// 64 位可执行文件名为 app_process64
LOCAL_MODULE_STEM_64 := app_process64
LOCAL_CFLAGS += $(app_process_cflags)
# In SANITIZE_LITE mode, we create the sanitized binary in a separate location (but reuse
# the same module). Using the same module also works around an issue with make: binaries
# that depend on sanitized libraries will be relinked, even if they set LOCAL_SANITIZE := never.
#
# Also pull in the asanwrapper helper.
ifeq ($(SANITIZE_LITE),true)
LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
LOCAL_REQUIRED_MODULES := asanwrapper
endif
// 编译可执行文件
include $(BUILD_EXECUTABLE)
# Create a symlink from app_process to app_process32 or 64
# depending on the target configuration.
ifneq ($(SANITIZE_LITE),true)
include $(BUILD_SYSTEM)/executable_prefer_symlink.mk
endif
2.3 使用介绍
从参数介绍到进程的启动,创建虚拟机,通过反射启动。
用法如下:
app_process [vm-options] [工作目录] [options] 类名 [类的main方法参数] [类的main方法参数] ....
参数如下:
vm-options – VM 选项
work-dir –工作目录(如/system/bin,/data/app,/data/...)
options –运行的参数 :
–-zygote 启动 zygote 进程用的
–-start-system-server
–-application (api>=14) 启动应用程序
–-nice-name=nice_proc_name (api>=14) (只有非--zygote模式下该选项才会生效)
[启动类名] –包含main方法的主类 (如com.android.internal.os.WrapperInit)
main-options –启动时候传递到main方法中的参数
到这里我们简单来概述一下,app_process 是用来启动程序的工具,可以启动 zygote 这样的系统服务,也可以启动 apk 这样的应用进程,也可以执行 java 程序。这里根据传入的参数,可以分为 zygote 模式和 非 zygote 模式。有以下注意点:
- 传入 –zygote 会启动 com.android.internal.os.ZygoteInit,否则启动 com.android.internal.os.RuntimeInit。
- –start-system-server 只在启动 zygote 时有效。
- 在非 zygote 模式中,有无 –application 的选项的区别只是是否将 stdout 和 stderr 重定向到 AndroidPrintStream。
- 只有在非 zygote 的情况下,–nice-name= 选项有效。
示例:
1、启动 zygote 进程命令:
app_process -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
2、启动 com.android.test 应用命令:
app_process -Djava.class.path=/sdcard/classes.dex /data/local/tmp --application --nice-name=helloservice com.android.test.HelloWorld 1 2 a
2、启动 com.android.test 应用命令(通过 apk 的方式)
app_process -Djava.class.path=/sdcard/app.apk /data/local com.android.test.HelloWorld
2.4 代码分析
2.4.1 app_main
app_process 程序的执行文件是 app_main.cpp 文件,入口是 main() 函数。文件路径为:/android/frameworks/base/cmds/app_process/app_main.cpp。
#if defined(__LP64__)
// 如果为 64 位进程,则进程名为 "zygote64",否则为 "zygote"
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
int main(int argc, char* const argv[])
{
-----------------------------------------------------------------------------------------
1、创建 AppRuntime 对象,AppRuntime 类用于创建和初始化虚拟机。
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
ALOGV("app_process main with argv: %s", argv_String.string());
}
// 创建 AppRuntime 对象
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments argv[0] = "/system/bin/app_process"
// ignore argv[0] 跳过 argv[0] 参数
argc--;
argv++;
-----------------------------------------------------------------------------------------
2、从给定参数中解析 Java-Option 参数,并把参数添加到 AppRuntime 中
// Everything up to '--' or first non '-' arg goes to the vm.
// 直到遇到 '-' 或第一个非 '-' 的参数为止的所有内容都将提供给虚拟机作为 options。
// The first argument after the VM args is the "parent dir", which
// is currently unused.
//
// After the parent dir, we expect one or more the following internal
// arguments :
//
// --zygote : Start in zygote mode 启动到 zygote 模式
// --start-system-server : Start the system server. 启动 system server
// --application : Start in application (stand alone, non zygote) mode. 以应用程序模式启动 (独立启动, 非 zygote)
// --nice-name : The nice name for this process. 给进程起一个名字
//
// 对于非 zygote 启动,这些参数后面将是主类名,所有其余的参数都传递给此类的 main 方法;
// For non zygote starts, these arguments will be followed by
// the main class name. All remaining arguments are passed to
// the main method of this class.
//
// 对于 zygote 启动,所有剩余的参数都传递给 zygote 的 main 方法。
// For zygote starts, all remaining arguments are passed to the zygote.
// main function.
//
// Note that we must copy argument string values since we will rewrite the
// entire argument block when we apply the nice name to argv0.
//
// As an exception to the above rule, anything in "spaced commands"
// goes to the vm even though it has a space in it.
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;
int i;
for (i = 0; i < argc; i++) {
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}
for (int j = 0;
j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
// 将参数添加到 AppRuntime 中
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add option '%s'", argv[i]);
}
-----------------------------------------------------------------------------------------
这里继续解析参数,将信息保存在 zygote、startSystemServer、application 等变量中。
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++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;
}
}
-----------------------------------------------------------------------------------------
3、准备 ZygoteInit 或 RuntimeInit 启动所需要的参数
Vector<String8> args;
// 没有处于 zygote 模式
if (!className.isEmpty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
// The Remainder of args get passed to startup class main(). Make
// copies of them before we overwrite them with the process name.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
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 {
// We're in zygote mode.
// className 为空,处于 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);
// 获取支持的 abi 列表
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
// 在 zygote 模式下,将所有剩余参数传递给 zygote 的 main() 方法。
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
-----------------------------------------------------------------------------------------
4、设置进程名称
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
-----------------------------------------------------------------------------------------
5、启动 ZygoteInit 或者 RuntimeInit 程序。
if (zygote) {
// 启动 zygote 服务
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
// 启动 application 服务
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.");
}
}
那么这里总结 app_process 程序的功能:
- 初始化 AppRuntime 对象
- 从命令行中解析参数,并把参数都添加到 AppRuntime 中
- 判断启动功能,zygote、application,并且准备对应启动参数
- 设置进程名称
- 调用 AppRuntime.start 函数启动 zygote 或者 application
2.4.2 AndroidRuntime/AppRuntime
AppRuntime 直译的话叫做应用运行时
,开发时,我们写好 Java/Kotlin 代码,通过对应的编译器将代码编译为字节码,AppRuntime 的作用就是创建一个可以执行字节码的环境,这个环境主要由两部分内容构成:
- 一部分负责对象的创建与回收,譬如类的加载器,垃圾回收器等
- 一部分负责程序逻辑的运行,譬如即时编译系统,解释器等
AppRuntime 类定义在 app_main.cpp 中,继承了 AndroidRuntime 类,app_process 启动程序是会初始化一个 AndroidRuntime 对象,下面是 AndroidRuntime 对象的构造函数
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
// 初始化 skia 图形系统
SkGraphics::Init();
// Pre-allocate enough space to hold a fair number of options.
// 预先分配空间来存放传入虚拟机的参数
mOptions.setCapacity(20);
// 每个进程只能初始化一次
assert(gCurRuntime == NULL); // one per process
gCurRuntime = this;
}
app_process 调用 AppRuntime.start 函数会调用到 AndroidRuntime 中,AppRuntime 是在 app_main.cpp 文件中定义的 AndroidRuntime 的子类。
AndroidRuntime 的代码在 /android/frameworks/base/core/jni/AndroidRuntime.cpp 文件中,下面来看下 start 函数的功能
// AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 打印启动服务日志
ALOGD(">>>>>> START %s uid %d <<<<<<\n",
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}
// 获取 system 启动目录
// 系统目录从环境变量 ANDROID_ROOT 中读取。如果说取失败,则默认设置目录为"/system"。如果连"/system"也没有,则 Zygote 进程会退出。
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
// 通过 jni_invocation.Init(NULL) 完成 jni 接口的初始化
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
// 创建虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// onVimCreate() 是一个虚函数,调用它实际上调用的是继承类的 AppRuntime 中的重载函数。
onVmCreated(env);
// 注册系统类 JNI 方法
// startReg() 函数通过调用 register_jni_procs() 函数将全局的 gRegJNI 中的本地 JNI 函数在虚拟机中注册
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
// 为启动 Java 类的 main 函数做准备
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
// Java: strArray = new String[options.size() + 1];
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
// Java: strArray[0] = "com.android.internal.os.ZygoteInit";
env->SetObjectArrayElement(strArray, 0, classNameStr);
// 将相关参数收集至 options 中,下面会传递给 ZygoteInit
// --start-system-server, --abi-list=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);
}
// 转换为 JNI 格式类名:com/android/internal/os/ZygoteInit
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
} else {
// 通过 GetStaticMethodID 函数来获取 main() 方法的 id
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
} else {
// 调用 ZygoteInit.main() 调用 Java 层方法;
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
// 关闭 Java 虚拟机
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");
}
AndroidRuntime.start() 函数的功能如下:
- 创建了一个 JniInvocation 的实例,并且调用它的成员函数 init 来初始化 JNI 环境。
- startVm 函数创建虚拟机及对应的 JNI 接口,即 JavaVM 接口和 JNIEnv 接口。startVm 函数中主要是
设置虚拟机配置参数
以及创建虚拟机实例
,创建虚拟机后每一个进程应该具有一个 JavaVM 指针,而每一个线程都具有一个 JNIEnv 指针。 - startReg 函数注册系统 JNI 方法;
- 收集 options 参数,加载指定的 class
JavaVM(Java Virtual Machine)和 JNIEnv(Java Native Interface Environment)是 Java Native Interface(JNI)中两个重要的概念,它们在实现 Java 与本地代码之间的交互时起着重要的作用。
JavaVM:JavaVM 是一个代表 Java 虚拟机的结构体指针,它提供了一系列 JNI 函数,可以用于创建 Java 虚拟机、获取当前 Java 虚拟机等操作。JavaVM 提供了一种机制,可以在本地方法中获取对 Java 虚拟机的访问,从而可以在本地代码中操作 Java 对象、调用 Java 方法等功能。JavaVM 的作用是为本地代码提供对 Java 虚拟机的操作接口,使得本地代码能够与 Java 虚拟机进行交互。
JNIEnv:JNIEnv 是一个代表 JNI 环境的指针类型,它是通过 JavaVM 获取到的,每个线程都会有一个对应的 JNIEnv。JNIEnv 提供了一系列 JNI 函数,可以用于在本地代码中访问 Java 对象、调用 Java 方法、处理异常等操作。通过 JNIEnv,本地代码可以与 Java 代码进行通信,操作 Java 对象,并实现 Java 与本地代码之间的数据传递和交互。
总的来说,JavaVM 提供了对 Java 虚拟机的操作接口,而 JNIEnv 则提供了 JNI 环境的相关操作接口,这两者协同工作,使得本地代码能够与 Java 代码进行交互,实现 Java 与本地代码的互通。
时序图
这里考虑把时序图放在前面,总结描述。
这里可以画一张 app_main 和 AndroidRuntime 的初始化和交互逻辑图,包括再到 JNI 并通过反射找到对应函数的时序。
3、zygote 启动
zygote 启动时传入的参数是:com.android.internal.os.ZygoteInit。那么在通过 app_process 程序启动时,最终会通过反射找到 ZygoteInit 文件作为 Zygote 的入口文件。
本节将会从 ZygoteInit 文件开始,描述 Zygote 在启动过程中提供的功能。
3.1 时序图
时序图如下所示:
时序图简要描述:
从时序图中我们可以大致整理 Zygote 进程在 Android 初始化过程中提供的功能。
首先第一点,预加载资源。Android 中有很多资源是可以共享的,在启动阶段进行预加载可以在后续的进程中共享资源,也就避免了后续再去加载的动作,这样对于后续进程的启动、运行有很大的提升。这里的预加载并没有一次性完成,而是在线程中分步完成。这里是一次修改变更,由原来在一个函数中完成资源的预加载变更为开启不同的子线程去加载不同的资源,等待子线程完成预加载之后,zygote 进程再去执行后续的动作。
第二点,初始化 ZygoteServer 类。ZygoteServer 会去创建 Zygote 所需的 Socket 对象,用于后续的 socket 通信。
第三点,fork SystemServer 进程。启动 SystemServer 服务。
第四点,开启循环监听 Socket 通信。
3.2 Zygote 启动流程
前面提到在 app_process 会找到 ZygoteInit 作为 Zygote 的入口文件,执行 main() 函数。
ZygoteInit 文件路径为:/android/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
-----------------------------------------------------------------------------------------
1、设置进程 group id;记录时间和 Trace 信息;启动 DDMS 虚拟机监控服务
ZygoteServer zygoteServer = null;
// Mark zygote start. This ensures that thread creation will throw
// an error.
// 调用此方法后,虚拟机会拒绝线程的创建,如果此时创建线程会产生异常
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
// 设置 zygote 进程的 group id
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
// 记录一些时间信息,用于性能分析
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
MetricsLogger.histogram(null, "boot_zygote_init",
(int) SystemClock.elapsedRealtime());
}
// 记录 trace 信息
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
// 启动 DDMS 虚拟机监控调试服务
RuntimeInit.enableDdms();
-----------------------------------------------------------------------------------------
2、解析参数;预加载资源
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
// 判断是否开启 SystemServer
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
// 是否启动延时加载
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
// 获取 abi 类型,一个 CPU 对应一个 abi
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
// 获取 zygote socket 的名字
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
// 通过 zygote socket 的名字判断是否是主 zygote。
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
// 没有指定 abi 会有异常
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// 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());
//multithread to do preload start
//preload(bootTimingsTraceLog);
// 预加载资源
partialPreload(bootTimingsTraceLog);
//multithread to do preload end
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false, 0);
-----------------------------------------------------------------------------------------
3、初始化 ZygoteServer 服务类,用于注册 socket 监听;创建 SystemServer 进程;
// 执行一些初始化操作
Zygote.initNativeState(isPrimaryZygote);
// 与 startZygoteNoThreadCreation 函数对应,虚拟机可以创建线程了
ZygoteHooks.stopZygoteNoThreadCreation();
// 初始化 ZygoteServer 进程,管理 socket 注册监听
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
// fork SystemServer 进程
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
// 如果 r 为空,说明是父进程 zygote,无任何处理,继续执行
// 如果 r 不为空,说明是子进程 SystemServer,启动后直接返回
if (r != null) {
r.run();
return;
}
}
-----------------------------------------------------------------------------------------
4、在线程中预加载资源,包括 Class、Resources、SharedLibraries、openGL 资源等。开启一个无限循环,处理 socket 信息;
Log.i(TAG, "Accepting command socket connections");
//multithread to do preload start
if (!enableLazyPreload) {
Thread loadClassThread = new Thread(new Runnable() {
public void run() {
// 在线程中预加载 Classes 资源
preloadClasses();
}
});
loadClassThread.start();
Thread loadResourceThread = new Thread(new Runnable() {
public void run() {
// 在线程中预加载 Resources 资源
preloadResources();
}
});
loadResourceThread.start();
Thread loadLibraryThread = new Thread(new Runnable() {
public void run() {
// 在线程中预加载 SharedLibraries 资源
preloadSharedLibraries();
// 在线程中预加载 openGL 资源
maybePreloadGraphicsDriver();
}
});
loadLibraryThread.start();
try {
loadClassThread.join();
loadResourceThread.join();
loadLibraryThread.join();
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
} catch(Exception e){}
}
//multithread to do preload end
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
// 调用 ZygoteServer 开启一个无限循环,处理 socket 信息
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
// 执行新进程的主函数
if (caller != null) {
caller.run();
}
}
说明点:
1、预加载资源由原来的全部加载变为线程中加载,可以提高运行效率,减少开机时间。控制参数是 --enable-lazy-preload,仅有在 init.zygote64_32.rc 中的 zygote64 中有此参数,说明在现有代码逻辑中,仅有 zygote64 才会在启动时预加载资源。
首先第一点,预加载资源。Android 中有很多资源是可以共享的,在启动阶段进行预加载可以在后续的进程中共享资源,也就避免了后续再去加载的动作,这样对于后续进程的启动、运行有很大的提升。这里的预加载并没有一次性完成,而是在线程中分步完成。这里是一次修改变更,由原来在一个函数中完成资源的预加载变更为开启不同的子线程去加载不同的资源,等待子线程完成预加载之后,zygote 进程再去执行后续的动作。
第二点,初始化 ZygoteServer 类。ZygoteServer 会去创建 Zygote 所需的 Socket 对象,用于后续的 socket 通信。
第三点,fork SystemServer 进程。启动 SystemServer 服务。
第四点,开启循环监听 Socket 通信。
3.2.1 预加载资源
预加载资源主要是进行一些 类、资源、共享库的预加载工作,以提升运行时效率。封装的函数包括 preloadClasses()、preloadResources()、preloadSharedLibraries() 等,我们来简单看一下对于这些资源是如何加载的。
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
// /system/etc/preloaded-classes
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
Log.i(TAG, "Preloading classes...");
long startTime = SystemClock.uptimeMillis();
// Drop root perms while running static initializers.
final int reuid = Os.getuid();
final int regid = Os.getgid();
// We need to drop root perms only if we're already root. In the case of "wrapped"
// processes (see WrapperInit), this function is called from an unprivileged uid
// and gid.
boolean droppedPriviliges = false;
if (reuid == ROOT_UID && regid == ROOT_GID) {
try {
Os.setregid(ROOT_GID, UNPRIVILEGED_GID);
Os.setreuid(ROOT_UID, UNPRIVILEGED_UID);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to drop root", ex);
}
droppedPriviliges = true;
}
// Alter the target heap utilization. With explicit GCs this
// is not likely to have any effect.
float defaultUtilization = runtime.getTargetHeapUtilization();
runtime.setTargetHeapUtilization(0.8f);
try {
BufferedReader br =
new BufferedReader(new InputStreamReader(is), Zygote.SOCKET_BUFFER_SIZE);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
// Load and explicitly initialize the given class. Use
// Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
// (to derive the caller's class-loader). Use true to force initialization, and
// null for the boot classpath class-loader (could as well cache the
// class-loader of this class in a variable).
Class.forName(line, true, null);
count++;
} catch (ClassNotFoundException e) {
Log.w(TAG, "Class not found for preloading: " + line);
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, "Problem preloading " + line + ": " + e);
} catch (Throwable t) {
Log.e(TAG, "Error preloading " + line + ".", t);
if (t instanceof Error) {
throw (Error) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw new RuntimeException(t);
}
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
}
Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
IoUtils.closeQuietly(is);
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
// Fill in dex caches with classes, fields, and methods brought in by preloading.
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadDexCaches");
runtime.preloadDexCaches();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// Bring back root. We'll need it later if we're in the zygote.
if (droppedPriviliges) {
try {
Os.setreuid(ROOT_UID, ROOT_UID);
Os.setregid(ROOT_GID, ROOT_GID);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to restore root", ex);
}
}
}
}
上面是加载 类 资源,加载逻辑是读取 /system/etc/preloaded-classes 文件,并通过 Class.forName() 方法逐行加载文件中声明的类。提前预加载系统常用的类可以提升运行效率,但是这个预加载的过程耗时等资源消耗比较多。在源码目录 /android/frameworks/base/config 下,存在 preloaded-classes 文件,存放着启动启动时需要加载的常用类。下面是部分资源加载内容:
android.app.Activity
android.app.ActivityManager$1
android.app.ActivityManager$AppTask
android.app.ActivityManager$MemoryInfo$1
android.app.ActivityManager$MemoryInfo
android.app.ActivityManager$OnUidImportanceListener
android.app.ActivityManager$RecentTaskInfo$1
android.app.ActivityManager$RecentTaskInfo
android.app.ActivityManager$RunningAppProcessInfo$1
android.app.ActivityManager$RunningAppProcessInfo
android.app.ActivityManager$RunningServiceInfo$1
android.app.ActivityManager$RunningServiceInfo
android.app.ActivityManager$RunningTaskInfo$1
android.app.ActivityManager$RunningTaskInfo
android.app.ActivityManager$TaskDescription$1
android.app.ActivityManager$TaskDescription
android.app.ActivityManager$UidObserver
android.app.ActivityManager
下面是预加载资源 preloadResources() 函数:
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
try {
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
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);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resource in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
}
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
}
}
预加载资源是定义在 /android/frameworks/base/core/res/res/values/arrays.xml 文件下的,预加载资源如下:
com.android.internal.R.array.preloaded_drawables
com.android.internal.R.array.preloaded_color_state_lists
com.android.internal.R.bool.config_freeformWindowManagement
下面是 /android/frameworks/base/core/res/res/values/arrays.xml 文件中定义的部分内容:
<array name="preloaded_drawables">
<item>@drawable/action_bar_item_background_material</item>
<item>@drawable/activated_background_material</item>
...
</array>
<array name="preloaded_color_state_lists">
<item>@color/primary_text_dark</item>
<item>@color/primary_text_dark_disable_only</item>
...
</array>
<array name="preloaded_freeform_multi_window_drawables">
<item>@drawable/decor_maximize_button_dark</item>
<item>@drawable/decor_maximize_button_light</item>
</array>
下面是预加载共享库 preloadSharedLibraries() 函数:
private static void preloadSharedLibraries() {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
}
预加载共享库函数是预加载了三个共享库:libandroid.so、libcompiler_rt.so 和 libjnigraphics.so。
3.2.2 初始化 ZygoteServer
Zygote 通过 ZygoteServer 封装了对于 socket 通信的操作。
下面是 ZygoteServer 初始化函数:
ZygoteServer(boolean isPrimaryZygote) {
mUsapPoolEventFD = Zygote.getUsapPoolEventFD();
// 这里通过 Zygote.createManagedSocketFromInitSocket 创建 LocalServerSocket 对象。
if (isPrimaryZygote) {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
} else {
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
}
fetchUsapPoolPolicyProps();
mUsapPoolSupported = true;
}
ZygoteServer 函数创建 LocalServerSocket 对象来进行 Socket 通信。
static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
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("Socket unset or invalid: " + fullSocketName, ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
return new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error building socket from file descriptor: " + fileDesc, ex);
}
}
下面是 Android Socket 的通信架构图:
LocalSocket 就是作为客户端建立于服务端的连接,发送数据。LocalServerSocket 作为服务端使用,建立服务端的 socket 监听客户端请求。
3.2.3 复制 SystemServer 进程
Zygote 进程在会复制 SystemServer 继续 Android 的启动。调用函数是 forkSystemServer 函数。
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",
};
ZygoteArguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteArguments(args);
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
boolean profileSystemServer = SystemProperties.getBoolean(
"dalvik.vm.profilesystemserver", false);
if (profileSystemServer) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
// fork SystemServer 进程
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
// pid = 0 表示子进程,从这里开始进入 SystemServer 进程。
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
// 关闭并释放从 Zygote copy 过来的 socket
zygoteServer.closeServerSocket();
// 完成新创建的 system_server 进程的剩余工作
return handleSystemServerProcess(parsedArgs);
}
/**
* 注意 fork() 函数式一次执行,两次返回(两个进程对同一程序的两次执行)。
* pid > 0 说明还是父进程。pid = 0 说明进入了子进程
* 所以这里的 return null 依旧会执行
*/
return null;
}
从上面的启动参数可以看到,SystemServer 进程的 uid 和 gid 都是 1000,进程名是 system_server,其最后要加载的类名是 com.android.server.SystemServer。准备好一系列参数之后通过 ZygoteConnection.Arguments() 拼接,接着调用 Zygote.forkSystemServer() 方法真正的 fork 出子进程 system_server。
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
ZygoteHooks.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
// 这里最终调用 nativeForkSystemServer 去执行 fork 操作。
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
}
ZygoteHooks.postForkCommon();
return pid;
}
最后的 fork() 操作是在 native 层完成的。再回到 ZygoteInit.forkSystemServer() 中执行 fork() 之后的逻辑处理。对于 SystemServer 进程来说,fork 函数返回值是 0,会继续执行 handleSystemServerProcess() 函数,执行 SystemServer 的逻辑。对于 Zygote 进程,会启动下一步内容。
3.2.4 开启循环进行 Socket 通信
Zygote 进程在复制 SystemServer 进程后,SystemServer 继续执行启动逻辑,Zygote 进程会调用 zygoteServer.runSelectLoop() 函数开启循环进行 Socket 通信。
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// mServerSocket 是之前在 Zygote 中创建的
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null);
while (true) {
fetchUsapPoolPolicyPropsWithMinInterval();
int[] usapPipeFDs = null;
StructPollfd[] pollFDs = null;
// Allocate enough space for the poll structs, taking into account
// the state of the USAP pool for this Zygote (could be a
// regular Zygote, a WebView Zygote, or an AppZygote).
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
/*
* For reasons of correctness the USAP pool pipe and event FDs
* must be processed before the session and server sockets. This
* is to ensure that the USAP pool accounting information is
* accurate when handling other requests like API blacklist
* exemptions.
*/
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;
if (mUsapPoolEnabled) {
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = mUsapPoolEventFD;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
for (int usapPipeFD : usapPipeFDs) {
FileDescriptor managedFd = new FileDescriptor();
managedFd.setInt$(usapPipeFD);
pollFDs[pollIndex] = new StructPollfd();
pollFDs[pollIndex].fd = managedFd;
pollFDs[pollIndex].events = (short) POLLIN;
++pollIndex;
}
}
try {
// 有事件来时往下执行,没有时就阻塞
Os.poll(pollFDs, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
boolean usapPoolFDRead = false;
while (--pollIndex >= 0) {
if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
continue;
}
if (pollIndex == 0) {
// 有新客户端连接
// 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) {
// 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) {
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(pollIndex);
conn.closeSocket();
socketFDs.remove(pollIndex);
} 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;
}
} else {
// Either the USAP pool event FD or a USAP reporting pipe.
// If this is the event FD the payload will be the number of USAPs removed.
// If this is a reporting pipe FD the payload will be the PID of the USAP
// that was just specialized.
long messagePayload = -1;
try {
byte[] buffer = new byte[Zygote.USAP_MANAGEMENT_MESSAGE_BYTES];
int readBytes = Os.read(pollFDs[pollIndex].fd, buffer, 0, buffer.length);
if (readBytes == Zygote.USAP_MANAGEMENT_MESSAGE_BYTES) {
DataInputStream inputStream =
new DataInputStream(new ByteArrayInputStream(buffer));
messagePayload = inputStream.readLong();
} else {
Log.e(TAG, "Incomplete read from USAP management FD of size "
+ readBytes);
continue;
}
} catch (Exception ex) {
if (pollIndex == usapPoolEventFDIndex) {
Log.e(TAG, "Failed to read from USAP pool event FD: "
+ ex.getMessage());
} else {
Log.e(TAG, "Failed to read from USAP reporting pipe: "
+ ex.getMessage());
}
continue;
}
if (pollIndex > usapPoolEventFDIndex) {
Zygote.removeUsapTableEntry((int) messagePayload);
}
usapPoolFDRead = true;
}
}
// Check to see if the USAP pool needs to be refilled.
if (usapPoolFDRead) {
int[] sessionSocketRawFDs =
socketFDs.subList(1, socketFDs.size())
.stream()
.mapToInt(fd -> fd.getInt$())
.toArray();
final Runnable command = fillUsapPool(sessionSocketRawFDs);
if (command != null) {
return command;
}
}
}
}
mServerSocket
是 ZygoteInit.main()
中一开始就建立的服务端 socket,用于处理客户端请求。一看到 while(true)
就肯定会有阻塞操作。Os.poll()
在有事件来时往下执行,否则就阻塞。当有客户端请求过来时,调用 ZygoteConnection.processOneCommand()
方法来处理。
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
ZygoteArguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = Zygote.readArgumentList(mSocketReader);
// TODO (chriswailes): Remove this and add an assert.
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
throw new IllegalStateException("IOException on command socket", ex);
}
// readArgumentList returns null only when it has reached EOF with no available
// data to read. This will only happen when the remote socket has disconnected.
if (args == null) {
isEof = true;
return null;
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
parsedArgs = new ZygoteArguments(args);
if (parsedArgs.mAbiListQuery) {
handleAbiListQuery();
return null;
}
if (parsedArgs.mPidQuery) {
handlePidQuery();
return null;
}
if (parsedArgs.mUsapPoolStatusSpecified) {
return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
}
if (parsedArgs.mPreloadDefault) {
handlePreload();
return null;
}
if (parsedArgs.mPreloadPackage != null) {
handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
return null;
}
if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
Parcel appInfoParcel = Parcel.obtain();
appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
appInfoParcel.setDataPosition(0);
ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
appInfoParcel.recycle();
if (appInfo != null) {
handlePreloadApp(appInfo);
} else {
throw new IllegalArgumentException("Failed to deserialize --preload-app");
}
return null;
}
if (parsedArgs.mApiBlacklistExemptions != null) {
return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions);
}
if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
|| parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
return handleHiddenApiAccessLogSampleRate(zygoteServer,
parsedArgs.mHiddenApiAccessLogSampleRate,
parsedArgs.mHiddenApiAccessStatslogSampleRate);
}
if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: "
+ "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
+ ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
}
Zygote.applyUidSecurityPolicy(parsedArgs, peer);
Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null;
if (parsedArgs.mRLimits != null) {
rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
}
int[] fdsToIgnore = null;
if (parsedArgs.mInvokeWith != null) {
try {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
Os.fcntlInt(childPipeFd, F_SETFD, 0);
fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
} catch (ErrnoException errnoEx) {
throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
}
}
/**
* In order to avoid leaking descriptors to the Zygote child,
* the native code must close the two Zygote socket descriptors
* in the child process before it switches from Zygote-root to
* the UID and privileges of the application being launched.
*
* In order to avoid "bad file descriptor" errors when the
* two LocalSocket objects are closed, the Posix file
* descriptors are released via a dup2() call which closes
* the socket and substitutes an open descriptor to /dev/null.
*/
int [] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) {
fdsToClose[0] = fd.getInt$();
}
fd = zygoteServer.getZygoteSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
}
fd = null;
// 这里调用 Zygote.forkAndSpecialize 去执行 fork 进程的逻辑。
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);
try {
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
parsedArgs.mStartChildZygote);
} 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);
}
}
这里的进程复制跟 SystemServer 进程的复制类似,SystemServer 进程复制调用的是 Zygote.forkSystemServer 函数,Socket 通信时进程的复制调用的是 Zygote.forkAndSpecialize 函数,在 native 层的是实现逻辑类似。通过对应的 Socket 指令去执行对应的功能。
对于进程的复制,可以从下图中对 init 进程复制进程、zygote 进程复制进程的联系:
总结
本篇文档主要描述了 Android 启动流程中 Zygote 进程的内容。描述了从 Zygote 的启动,app_process 程序的启动以及 Zygote 初始化、预加载、进程的赋值等内容。