继上一篇介绍语法后,接下来详细看看init进程解析init.rc后启动的一些关键的系统服务进程,其中最重要的有ServiceManager、Zygote和SystemServer。
一、Android的“DNS服务器”--------ServiceManager
ServiceManager是Binder机制中的“DNS服务器”,负责域名(某Binder服务在ServiceManager注册时提供的名称) 到IP地址(有底层Binder驱动分配的值)的解析。
在高通8155 LA.1.1 中 ServiceManager是在init.rc里由init进程启动。如下:
on init #init事件
.......略
# Start logd before any other services run to ensure we capture all of their logs.
# 在任何其他服务运行之前启动 logd 以确保我们捕获它们的所有日志。
start logd # 这个命令将启动一个服务 logd ,如果它没有处于运行状态的话
# Start essential services.
# 启动基本服务。
start servicemanager # 这个命令将启动一个服务 servicemanager ,如果它没有处于运行状态的话
start hwservicemanager # 这个命令将启动一个服务 hwservicemanager ,如果它没有处于运行状态的话
start vndservicemanager # 这个命令将启动一个服务 vndservicemanager ,如果它没有处于运行状态的话
......略
有servicemanager文件可看出,servicemanager是一个 Linux程序的执行文件。它在设备中的存储路径是“/system/bin/servicemanager”(需要进入root才能查询到,如下图),源码路径则是“frameworks/native/cmds/servicemanager”。
servicemanager的启动描述则在servicemanager.rc文件里,.rc文件位于“frameworks/native/cmds/servicemanager/servicemanager.rc”。高通8155的LA.1.1基线代码对于servicemanager的描述和启动的位置分在两个不同的.rc文件。servicemanager.rc具体启动描述内容如下:
service servicemanager /system/bin/servicemanager
class core animation #所属类是 core 和 animation
user system #在启动服务前将用户切换至 system ,默认是root
group system readproc #在启动服务前将用户组切换至 system 和 readproc
critical #表明这是对设备至关重要的一个服务,如果4分钟内退出超过4次,则设备将重启进入恢复模式
onrestart restart healthd #当servicemanager重启时,重启 healthd 进程
onrestart restart zygote #当servicemanager重启时,重启 zygote 进程
onrestart restart audioserver #当servicemanager重启时,重启 audioserver 进程
onrestart restart media #当servicemanager重启时,重启 media 进程
onrestart restart surfaceflinger #当servicemanager重启时,重启 surfaceflinger 进程
onrestart restart inputflinger #当servicemanager重启时,重启 inputflinger 进程
onrestart restart drm #当servicemanager重启时,重启 drm 进程
onrestart restart cameraserver #当servicemanager重启时,重启 cameraserver 进程
onrestart restart keystore #当servicemanager重启时,重启 keystore 进程
onrestart restart gatekeeperd #当servicemanager重启时,重启 gatekeeperd 进程
onrestart restart thermalservice #当servicemanager重启时,重启 thermalservice 进程
writepid /dev/cpuset/system-background/tasks # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用
#设置服务进程的关闭行为。如果未指定此选项,则在关闭过程中通过使用SIGTERM和SIGKILL终止服务。
#shutdown_behavior为"critical"时,服务在关闭期间不会被杀死,直到关闭超时为止。
#当服务进程关闭超时时,即使标记为"shutdown critical"的服务也将被杀死。
#当shutdown行为启动时,标有"shutdown critical"的服务未运行时,它将启动。
shutdown critical #服务在关闭期间不会被杀死,直到关闭超时为止
由servicemanager.rc可看出,servicemanager所属class是core,其它同类的系统进程包括ueventd、console(/system/bin/sh)、adbd等。根据core组的特性,这些进程会同时被启动或停止。另外,critical选项说明它是系统的关键进程,意味着如果进程不幸在4分钟异常退出4次,则设备将重启进入还原模式。每当servicemanager重启时,其它关键进程也会被重启,如:healthd、zygote、audioserver、media、surfaceflinger、inputflinger、drm、cameraserver、keystore、gatekeeperd、thermalservice等。
二、Zygote—“孕育”新的线程和进程
Zygote这个词的字面意思时“受精卵”,因而可以“孕育”出一个“新生命”。在Android中大多数应用进程和系统进程都是通过Zygote来生成的。下面具体分析Zygote时如何启动的。
同servicemanager类似,Zygote也是由init解析rc脚本时启动的。在早期的Android版本中Zygote的启动命令是直接被写在init.rc中。但随着硬件等的不断升级换代,Android系统不得不面对32位和64位机器同时存在的状况,因而,对Zygote的启动也需要根据不同的情况区分对待,在高通平台中,在init.rc中加载Zygote的脚本描述如下:
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.rc中加载不同的描述zygote的rc脚本,zygote的描述脚本包含“init.zygote32_64.rc”、“init.zygote32.rc”、“init.zygote64_32.rc”、“init.zygote64.rc”,具体文件如下:(目录:system/core/rootdir)
其中zygote32和zygote64分别对应32位和64位机器的情况,而zygote32_64和zygote64_32,则是Primary Arch和Secondary Arch的组合。
组合介绍如下:
路径:device/qcom/msmnile_au/BoardConfig.mk
BoardConfig.mk用于指定目标平台相关的不少属性,咱们能够在这个脚本中同时指定Primary和Secondary的CPU Arch和ABI:
与Primary Arch相关的变量有TARGET_ARCH、TARGET_ARCH_VARIANT、TARGET_CPU_VARIANT等,具体范例以下:
TARGET_ARCH := arm64 TARGET_ARCH_VARIANT := armv8-a TARGET_CPU_ABI := arm64-v8a TARGET_CPU_ABI2 := TARGET_CPU_VARIANT := generic
与Secondary Arch相关的变量有TARGET_2ND_ARCH、TARGET_2ND_ARCH_VARIANT、TARGET_2ND_CPU_VARIANT等,具体范例以下:
TARGET_2ND_ARCH := arm TARGET_2ND_ARCH_VARIANT := armv7-a-neon TARGET_2ND_CPU_ABI := armeabi-v7a TARGET_2ND_CPU_ABI2 := armeabi TARGET_2ND_CPU_VARIANT := cortex-a9
所以,由BoardConfig.mk可知,高通8155的LA.1.1基线中的“init.rc“-》“import /init.${ro.zygote}.rc“-》ro.zygote的值为“zygote64_32”,即Primary Arch为64,Secondary Arch为32。接下来详细看zygote64_32的内容,如下:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main #所属类是 main
priority -20 #设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置
user root #在启动服务前将用户切换至 root ,默认是 root
group root readproc reserved_disk #在启动服务前将用户组切换至 root 和 readproc 和 reserved_disk
socket zygote stream 660 root system #创建一个名为 /dev/socket/zygote 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
socket usap_pool_primary stream 660 root system #创建一个名为 /dev/socket/usap_pool_primary 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
onrestart write /sys/android_power/request_state wake #当 zygote 重启时,写 wake 到 /sys/android_power/request_state
onrestart write /sys/power/state on #当 zygote 重启时,写 on 到 /sys/power/state
onrestart restart audioserver #当 zygote 重启时,重启 audioserver 进程
onrestart restart cameraserver #当 zygote 重启时,重启 cameraserver 进程
onrestart restart media #当 zygote 重启时,重启 media 进程
onrestart restart netd #当 zygote 重启时,重启 netd 进程
onrestart restart wificond #当 zygote 重启时,重启 wificond 进程
writepid /dev/cpuset/foreground/tasks # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main #所属类是 main
priority -20 #设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置
user root #在启动服务前将用户切换至 root ,默认是 root
group root readproc reserved_disk #在启动服务前将用户组切换至 root 和 readproc 和 reserved_disk
socket zygote_secondary stream 660 root system #创建一个名为 /dev/socket/zygote_secondary 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
socket usap_pool_secondary stream 660 root system #创建一个名为 /dev/socket/usap_pool_secondary 的unix domian socket ,然后将它的fd值传给启动它的进程,有效的值为 stream (dgram,stream,seqpacket),权限 660 用户 root ,用户组 system
onrestart restart zygote #当 zygote_secondary 重启时,重启 zygote 进程
writepid /dev/cpuset/foreground/tasks # 当fork一个子进程时,写子进程的pid到一个给定的文件。是给cgroup/cpuset使用
从上面这段脚本描述可以看出:
存在两个服务 zygote 和 zygote_secondary;
zygote的path:/system/bin/app_process64
zygote_secondary的path:/system/bin/app_process32
zygote的Arguments:-Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
zygote_secondary的Arguments:-Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
zygote和zygote_secondary所属class都为main,而不是core。同class的系统进程有netd、debuggerd、rild等。从两者的路径可以看出,它们所在的程序名分别叫“ app_process64” 和 “ app_process32”,而不像ServiceManager一样在一个独立的程序中。通过指定–zygote 参数,app_process可以识别出用户是否需要启动zygote。那么app_process又是什么东东呢?app_process的源码路径在: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.
# 这是为了避免不良应用程序需要包含的库列表。 这可以防止库在从应用程序共享库中解析新/删除时出现不匹配。
# 详情请参阅 b/21032018。
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) #32位程序的ld标志
LOCAL_LDFLAGS_64 := $(app_process_ldflags_64) #64位程序的ld标志
LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs) #共享库
LOCAL_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs) #静态库
LOCAL_MODULE:= app_process #模块名称
LOCAL_MULTILIB := both #用于针对的硬件平台架构,可选项:"both":同时 "32":针对32 "64":针对64 " ":由系统根据其它变量决定
LOCAL_MODULE_STEM_32 := app_process32 #指定32位系统下的应用程序
LOCAL_MODULE_STEM_64 := app_process64 #指定64位系统下的应用程序
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.
# 在 SANITIZE_LITE 模式下,我们在单独的位置创建经过清理的二进制文件(但重用相同的模块)。
# 使用相同的模块也可以解决 make 的问题:依赖于已清理库的二进制文件将被重新链接,即使它们设置 LOCAL_SANITIZE := never。
# 同时引入 asanwrapper 助手。
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.
# 根据目标配置创建从 app_process 到 app_process32 或 64 的符号链接。
ifneq ($(SANITIZE_LITE),true)
include $(BUILD_SYSTEM)/executable_prefer_symlink.mk
endif
从上面的描述可看到,app_process其实扮演的类似一个外壳,实际是去调用 app_process64 或app_process32程序。那么app_process具体内容有哪些呢?下面分析app_process.cpp中的main主函数的实现,内容如下:
int main(int argc, char* const argv[])
{
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());
}
//Android运行时环境
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments 处理命令行参数
// ignore argv[0] 忽略 argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm.
//
// 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
// --start-system-server : Start the system server.
// --application : Start in application (stand alone, non zygote) mode.
// --nice-name : The nice name for this process.
//
// 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.
//
// 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.
// 直到 '--' 或第一个非 '-' arg 的所有内容都进入 vm。
// VM args 后面的第一个参数是“父目录”,目前未使用。
// 在父目录之后,我们期望一个或多个以下内部参数:
// --zygote : 以 zygote 模式启动
// --start-system-server : 启动系统服务器。
// --application : 以应用程序(独立,非 zygote)模式启动。
// --nice-name : 这个进程的好名字。
// 对于非 zygote 启动,这些参数后跟主类名。所有剩余的参数都传递给这个类的 main 方法。
// 对于 zygote 启动,所有剩余的参数都传递给 zygote。
// 主功能。
// 请注意,我们必须复制参数字符串值,因为当我们将 nice 名称应用于 argv0 时,我们将重写整个参数块。
// 作为上述规则的一个例外,“间隔命令”中的任何内容都会进入 vm,即使其中有空格。
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
// 允许“间隔命令”通过恰好 1 个参数(不管 -s)。
bool known_command = false;
int i;
for (i = 0; i < argc; i++) {
if (known_command == true) { //判断参数是否存在
runtime.addOption(strdup(argv[i])); //将参数传给runtime
// 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
// 静态分析器对我们没有释放上面的字符串感到不安。 由于分配来自 main,因此泄漏它似乎没有问题。 NOLINTNEXTLINE
ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}
for (int j = 0;
j < static_cast(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) { //比较判断argv中的参数是否存在 和 spaced_commands中一样的部分
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;
}
runtime.addOption(strdup(argv[i])); //将参数传给runtime
// 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
// 静态分析器对我们没有释放上面的字符串感到不安。 由于分配来自 main,因此泄漏它似乎没有问题。 NOLINTNEXTLINE
ALOGV("app_process main add option '%s'", argv[i]);
}
// 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 表示当前进程承载 zygote
zygote = true; //标记
niceName = ZYGOTE_NICE_NAME; //别名
} else if (strcmp(arg, "--start-system-server") == 0) { //判断参数存在 --start-system-server 表示需要启动 system server
startSystemServer = true; //标记
} else if (strcmp(arg, "--application") == 0) { //判断参数存在 --application 表示启动进入独立的程序模式
application = true; //标记
} else if (strncmp(arg, "--nice-name=", 12) == 0) { //判断参数存在 --nice-name= 表示设置进程别名为 = 后面的字串
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) { //判断参数存在 -- 表示设置className 增加 --后面字串 作为数据
className.setTo(arg);
break;
} else {
--i; //倒叙检索参数
break;
}
}
Vector args;
if (!className.isEmpty()) { //判断 非zygote
// 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.
// 我们不是在 zygote 模式下,我们需要传递给 RuntimeInit 的唯一参数是应用程序参数。
// args 的剩余部分被传递给启动类 main()。 在我们用进程名称覆盖它们之前制作它们的副本。
args.add(application ? String8("application") : String8("tool")); //如果参数 包含 application 则添加application作为数据 否则为tool
runtime.setClassNameAndArgs(className, argc - i, argv + i); //设置Android运行时环境 runtime 的类名和参数
//调试信息,输出类名和参数
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.
// 我们处于 zygote 模式。
maybeCreateDalvikCache();
if (startSystemServer) { //判断 startSystemServer 参数存在
args.add(String8("start-system-server")); //添加参数 start-system-server
}
char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) { //ABI属性列表获取 列表参数
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY); //无法从属性中确定 ABI 列表
return 11;
}
String8 abiFlag("--abi-list=");
abiFlag.append(prop); //abi标志列表 最加prop
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]));
}
}
if (!niceName.isEmpty()) { //判断别名
runtime.setArgv0(niceName.string(), true ); //设置runtime的setArgv0参数,即别名
}
if (zygote) { //判断是 zygote ,启动zygote进程 传入args参数列表
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) { //非zygote 情况下,运行RuntimeInit进程 , 传入args参数列表
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else { //否则都不是,错误:没有提供类名或 --zygote。
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时传入的参数,具体参数如下:
–zygote:表示当前进行用于承载zygote
–start-system-server:是否需要启动system server
–application:启动进入独立的程序模式
–nice-name:此进程的“别名”
对于非zygote的情况下,在上述参数的末尾会跟上main class的名称,而后的其它参数则属于这个class的主函数传入的参数;
对于zygote的情况,所有参数则会作为它的主函数传入参数使用;
在前面的 init.zegote64_32.rc中指定了 “–zygote“选项,因而app_process接下来将启动“com.android.internal.os.ZygoteInit ”,并传入“start-system-server”。之后ZygoteInit会运行于Java虚拟机上,那为什么呢?
原因就是runtime这个变量,它实际上是一个AndroidRuntime对象,其start函数源码如下:
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote) { ALOGD(">>>>>> START %s uid %d <<<<< FindClass("java/lang/String"); //找 string 类 assert(stringClass != NULL); strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); //新建string类的对象的队列 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); } char* slashClassName = toSlashClassName(className != NULL ? className : ""); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'n", slashClassName); } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'n", className); } else { env->CallStaticVoidMethod(startClass, startMeth, strArray); #if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName); ALOGD("Shutting down VMn"); if (mJavaVM->DetachCurrentThread() != JNI_OK) ALOGW("Warning: unable to detach main threadn"); if (mJavaVM->DestroyJavaVM() != 0) ALOGW("Warning: VM did not shut down cleanlyn"); }
对于虚拟机的具体启动和运行过程,等作者学习了后续在出相关文章讲解。
按照正常的启动流程,VM成功启动,并进入ZygoteInit执行;ZygoteInit执行源码如下:
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
// Mark zygote start. This ensures that thread creation will throw
// an error.
// 标记zygote开始。 这确保线程创建将引发错误。
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
// Zygote 进入它自己的进程组。
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
// 向 tron 报告 Zygote 启动时间,除非是运行时重启
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
MetricsLogger.histogram(null, "boot_zygote_init",
(int) SystemClock.elapsedRealtime());
}
String bootTimetag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimetag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.enableDdms();
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])) {
startSystemServer = true; //标记,需要启动 SystemServer
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true; //标记, 使能 LazyPreload
} else if (argv[i].startsWith(ABI_LIST_ARG)) { //abi列表参数
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) { //socket名参数
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
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());
preload(bootTimingsTraceLog); //预加载资源
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
// 启动后执行初始 gc 清理
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
// 禁用跟踪,以便分叉的进程不会从 Zygote 继承陈旧的跟踪标记。
Trace.setTracingEnabled(false, 0);
Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) { //启动一个 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.
// {@code r == null} 在父 (zygote) 进程中,{@code r != null} 在子 (system_server) 进程中。
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.
// select 循环在 fork 之后的子进程中尽早返回,并在 zygote 中永远循环。
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();
}
}
ZygoteInit的主函数主要完成以下几点:
—Socket—
zygote是一个孵化器,一旦有新程序需要运行时,系统会通过这个socket(完整名称为:zygote 、Zygote.PRIMARY_SOCKET_NAME))第一时间通知“总管事”Zygote,并由它负责实际的进程孵化过程。
—Preload预加载资源—
通过enableLazyPreload判断是否预加载资源。加载虚拟机运行时所需的资源,包括:
preloadClasses();
cacheNonBootClasspathClassLoaders();
preloadResources();
nativePreloadAppProcessHALs();
maybePreloadGraphicsDriver();
preloadSharedLibraries();
preloadTextResources();
从对应的名称可看出每个preload函数的作用。以preloadClasses为例,它负责加载和初始化常用的一些classes。这些需要预加载的classes被记录在"out/target/product/msmnile_au/system/etc/preloaded-classes"中,在高通8155的LA.1.1中如下所示:
# Preloaded-classes filter file for phones. # Classes in this file will be allocated into the boot image, and forcibly initialized in # the zygote during initialization. This is a trade-off, using virtual address space to share # common heap between apps. # This file has been derived for mainline phone (and tablet) usage. # android.R$styleable android.accessibilityservice.AccessibilityServiceInfo$1 android.accessibilityservice.AccessibilityServiceInfo android.accounts.Account$1 android.accounts.Account android.accounts.AccountManager$10 android.accounts.AccountManager$11 android.accounts.AccountManager$18 android.accounts.AccountManager$1 android.accounts.AccountManager$20 android.accounts.AccountManager$2 android.accounts.AccountManager$AmsTask$1 android.accounts.AccountManager$AmsTask$Response android.accounts.AccountManager$AmsTask android.accounts.AccountManager$baseFutureTask$1 android.accounts.AccountManager$baseFutureTask$Response ....略
从输出文件中可以看出preloaded-classes中包含多达上千个classes,而且包括了libcore里面的重要基础资源,如:android.system、android.util等。另外,这个记录表通过“frameworks/base/tools/preload/WritePreloadedClassFile.java”生成。
—启动System Server—
如果app_process的调用参数中带有“start-system-server”,那么此时就会通过“forkSystemServer”来启动“SystemServer”。
Zygote在前期主要担当启动系统服务的工作,后期则又负责“程序孵化”的任务。但是Zygote只在init.rc中被启动一次,它又是如何协调这两者的工作关系呢?从前面的过程中,大致推断,上述中的“forkSystemServer”应该会新建一个专门的进程来承载系统服务的运行,而后app_process所在的进程转化为Zygote的“孵化器”守护进程。那么具体如何呢?接下来分析“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
);
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);
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;
}
pid = Zygote.forkSystemServer( //此处fork一个新的进程(子进程)
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
if (pid == 0) { //子进程,即System Server所承载进程
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs); //启动各个 System Server
}
return null;
}
上述代码段中又出现了我们熟悉的fork流程,forkSystemServer在内部利用UNIX的fork机制创建了一个新的进程;而这个“新生儿”(即pid=0分支)会在随后的执行过程中通过“handleSystemServerProcess”来启动各种支撑系统运行的 System Server。
在跟踪 System Server的具体启动过程之前,先来为Zygote接下来的工作做个分析,与我们之前所见的fork处理流程不同的是,ZygoteInit.java-》main()-》forkSystemServer()函数中并没有为父进程专门开辟一个代码分支,因而这个函数最后会通过return handleSystemServerProcess(parsedArgs) 返回到ZygoteInit.java-》main()中。在main()接着就是语句:
caller = zygoteServer.runSelectLoop(abiList);
从runSelectLoop的函数名可猜到,这很可能会是一个“死循环”,除非Zygote退出或者出现异常才会跳出循环,runSelectLoop函数内容如下:
**
* 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.
*/
Runnable runSelectLoop(String abiList) {
ArrayList socketFDs = new ArrayList();
ArrayList peers = new ArrayList();
socketFDs.add(mZygoteSocket.getFileDescriptor());
peers.add(null); //添加null是为了保存fds和peers的一致性
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).
// 为轮询结构分配足够的空间,同时考虑此 Zygote 的 USAP 池的状态
//(可以是常规 Zygote、WebView Zygote 或 AppZygote)。
if (mUsapPoolEnabled) {
usapPipeFDs = Zygote.getUsapPipeFDs();
pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
} else {
pollFDs = new StructPollfd[socketFDs.size()];
}
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
// Zygote 服务器套接字
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
} else if (pollIndex < usapPoolEventFDIndex) { //已建立的连接中有客户端发过来的数据需要处理
// Session socket accepted from the Zygote server socket
// 从 Zygote 服务器套接字接受的会话套接字
try {
ZygoteConnection connection = peers.get(pollIndex);
final Runnable command = connection.processOneCommand(this);
// TODO (chriswailes): Is this extra check necessary?
// TODO (chriswailes): 这个额外的检查是必要的吗?
if (mIsForkChild) {
// We're in the child. We should always have a command to run at this
// stage if processoneCommand hasn't called "exec".
// 我们在孩子里面。 如果 processoneCommand 没有调用“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.
// 在我们尝试从 processoneCommand 中读取之前,我们不知道套接字的远程端是否已关闭。
// 这在我们的常规处理循环中显示为常规 POLLIN 事件。
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.
// 我们在服务器中,所以这里的任何异常都是在处理命令或从控制套接字读取/写入时发生的 pre-fork。
// 对任何此类异常大声喧哗,以便我们确切知道失败的原因和原因。
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.
// 我们在子进程中,所以这里捕获的任何异常都发生在分叉后和
// 执行 ActivityThread.main(或任何其他 main() 方法)之前。 记录异常的详细信息并关闭该过程。
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.
// 如果子进程是子zygote,则重置子标志。 在 Runnable 返回后,将不会在此循环过程中查询标志。
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.
// USAP 池事件 FD 或 USAP 报告管道。
// 如果这是事件 FD,则有效载荷将是移除的 USAP 的数量。
// 如果这是一个报告管道 FD,则有效载荷将是刚刚专门化的 USAP 的 PID。
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.
// 检查是否需要重新填充 USAP 池。
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;
}
}
}
}
}
从上述程序可看到,runSelectLoop函数的主体部分确实是一和while循环,它将作为zygoted 守护体存在。因为zygote此时运行在虚拟机环境中,所以需要处理USAP 池的分配与管理的问题。
在mZygoteSocket.getFileDescriptor()获取的是Server socket的文件描述符,并添加到 ArrayList
try {
Os.poll(pollFDs, -1); //判断 当前轮训到的 pollFDs中的socketFD 是否处于可读状态
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
当pollFDs中的socketFD指示的某个文件有可读数据时,pollFDs的revents成员的值会被修改。
pollIndex表示当前轮训的第几个socket,从最大值开始轮训。pollIndex大于或等于0时进入循环。
首先判断当前socket的事件 是否 时进入轮训状态,是的话进入重新循环,否则继续运行下面的判断。
if ((pollFDs[pollIndex].revents & POLLIN) == 0) { //轮训
continue;
}
pollIndex = 0
此时表示有新的客户端连接,需要通过“ZygoteConnection newPeer = acceptCommandPeer(abiList);”接受来自客户端的连接,产生一个新的ZygoteConnection,然后分别更新peers和socketFDs,为了保证这两个列表中的对象序列号保存一致,可以看到peer在初始化时专门添加了一个null,对应的是Zygote Server Socket 这个“监听者”。
pollIndex < usapPoolEventFDIndex
usapPoolEventFDIndex是记录了socketFDs的数量的。此时说明已经建立Socket连接中有来自客户端的数据需要处理,完成具体工作的是connection.processoneCommand(this),该函数具体内容如下:
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
ZygoteArguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = Zygote.readArgumentList(mSocketReader);
// TODO (chriswailes): Remove this and add an assert.
// TODO (chriswailes): 删除它并添加一个断言。
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.
// readArgumentList 仅在到达 EOF 且没有可读取的数据时才返回 null。 这只会在远程套接字断开连接时发生。
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);
}
}
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;
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(); //mIsForkChild 值为true
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.
// 在父级中。 pid < 0 表示失败,将在 handleParentProc 中处理。
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
函数processOneCommand比较长,其中重点内容是函数的后面部分,我们重点关注两点:
创建承载应用程序的新进程
这个是在意料之中的,zygote需要为每个新启动的应用程序生成自己独立的进程。不过processOneCommand中并没有直接使用fork来父进程的“收尾”工作
执行完上述的任务后,父进程还需要做一些清尾工作才算“大功告成”。包括:将子进程加入进程组;正确关闭文件;调用方返回结果值等。
Specialize的字面意思是“专门化”,表达了“forkAndSpecialize”在“孵化”的同时也把它转变为Android应用程序的目标。函数forkAndSpecialize的处理分为3个阶段,即 preFork、nativeForkAndSpecialize以及postForkCommon。函数内容如下:
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
int targetSdkVersion) {
ZygoteHooks.preFork();
// Resets nice priority for zygote process.
// 重置 zygote 进程的优先级。
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
// 尽快为子进程启用跟踪。
if (pid == 0) {
Zygote.disableExecuteOnly(targetSdkVersion);
Trace.setTracingEnabled(true, runtimeFlags);
// Note that this event ends at the end of handleChildProc,
// 注意这个事件在handleChildProc结束时结束,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
ZygoteHooks.postForkCommon();
return pid;
}
preFork分析
preFork在Zygote中对应的实现如下:
static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";
runtime->PreZygoteFork();
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
// 在 fork 之前抓取线程可能会使 Thread::pthread_key_self_ 不可用。
return reinterpret_cast(ThreadForEnv(env));
}
这里的Runtime实例具体而言指的是 Zygote进程中的运行环境(后续如果讲解虚拟机时在详细讲解)。而runtime->PreZygoteFork()又会间接调用Heap::PreZygoteFrok,从而完成堆栈空间的初始化。
函数nativeForkAndSpecialize是一个native方法。具体对应的实现是com_android_internal_os_Zygote_nativeForkAndSpecialize,后者有进一步调用了 ForkCommon 和 SpecializeCommon。
com_android_internal_os_Zygote_nativeForkAndSpecialize 函数内容如下:
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector.");
}
std::vector fds_to_close =
ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_close).value();
std::vector fds_to_ignore =
ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_ignore)
.value_or(std::vector());
std::vector usap_pipes = MakeUsapPipeReadFDVector();
fds_to_close.insert(fds_to_close.end(), usap_pipes.begin(), usap_pipes.end());
fds_to_ignore.insert(fds_to_ignore.end(), usap_pipes.begin(), usap_pipes.end());
fds_to_close.push_back(gUsapPoolSocketFD);
if (gUsapPoolEventFD != -1) {
fds_to_close.push_back(gUsapPoolEventFD);
fds_to_ignore.push_back(gUsapPoolEventFD);
}
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir);
}
return pid;
}
ForkCommon函数的内容如下:
// Utility routine to fork a process from the zygote.
// 从 zygote 派生一个进程的实用程序。
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector& fds_to_close,
const std::vector& fds_to_ignore) {
SetSignalHandlers();
// Curry a failure function.
// Curry 一个失败函数。
auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
nullptr, _1);
// Temporarily block SIGCHLD during forks. The SIGCHLD handler might
// log, which would result in the logging FDs we close being reopened.
// This would cause failures because the FDs are not whitelisted.
//
// Note that the zygote process is single threaded at this point.
// 在分叉期间临时阻塞 SIGCHLD。 SIGCHLD 处理程序可能会记录日志,
// 这将导致我们关闭的日志记录 FD 被重新打开。 这将导致失败,因为 FD 未列入白名单。
// 注意此时zygote进程是单线程的。
BlockSignal(SIGCHLD, fail_fn);
// Close any logging related FDs before we start evaluating the list of
// file descriptors.
// 在我们开始评估文件描述符列表之前,关闭所有与日志记录相关的 FD。
__android_log_close();
stats_log_close();
// If this is the first fork for this zygote, create the open FD table. If
// it isn't, we just need to check whether the list of open files has changed
// (and it shouldn't in the normal case).
// 如果这是此 zygote 的第一个分叉,则创建打开的 FD 表。
// 如果不是,我们只需要检查打开文件列表是否发生了变化(正常情况下不应该)。
if (gOpenFdTable == nullptr) {
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
} else {
gOpenFdTable->Restat(fds_to_ignore, fail_fn);
}
android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
pid_t pid = fork(); //这里真正孵化出一个新的进程
if (pid == 0) {
// The child process.
// 子进程。
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
// 清理所有必须立即关闭的描述符
DetachDescriptors(env, fds_to_close, fail_fn);
// Invalidate the entries in the USAP table.
// 使 USAP 表中的条目无效。
ClearUsapTable();
// Re-open all remaining open file descriptors so that they aren't shared
// with the zygote across a fork.
// 重新打开所有剩余的打开文件描述符,这样它们就不会通过 fork 与 zygote 共享。
gOpenFdTable->ReopenOrDetach(fail_fn);
// Turn fdsan back on.
// 重新打开 fdsan。
android_fdsan_set_error_level(fdsan_error_level);
} else {
//父进程不做任何事,应为在 “frameworks/base/core/java/com/android/internal/os/ZygoteServer.java”中 connection.processoneCommand(this)里有处理
ALOGD("Forked child process %d", pid);
}
// We blocked SIGCHLD prior to a fork, we unblock it here.
// 我们在分叉之前阻塞了 SIGCHLD,我们在这里解除阻塞。
UnblockSignal(SIGCHLD, fail_fn);
return pid;
}
SpecializeCommon的内容如下:
// Utility routine to specialize a zygote child process.
// 用于专门化 zygote 子进程的实用程序。
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong effective_capabilities,
jint mount_external, jstring managed_se_info,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
auto se_info = extract_fn(managed_se_info);
auto nice_name = extract_fn(managed_nice_name);
auto instruction_set = extract_fn(managed_instruction_set);
auto app_data_dir = extract_fn(managed_app_data_dir);
// Keep capabilities across UID change, unless we're staying root.
// 保持跨 UID 更改的功能,除非我们保持 root。
if (uid != 0) {
EnableKeepCapabilities(fail_fn);
}
SetInheritable(permitted_capabilities, fail_fn);
DropCapabilitiesBoundingSet(fail_fn);
bool use_native_bridge = !is_system_server &&
instruction_set.has_value() &&
android::NativeBridgeAvailable() &&
android::NeedsNativeBridge(instruction_set.value().c_str());
if (use_native_bridge && !app_data_dir.has_value()) {
// The app_data_dir variable should never be empty if we need to use a
// native bridge. In general, app_data_dir will never be empty for normal
// applications. It can only happen in special cases (for isolated
// processes which are not associated with any app). These are launched by
// the framework and should not be emulated anyway.
// 如果我们需要使用本地网桥,app_data_dir 变量永远不应该为空。
// 通常,对于普通应用程序,app_data_dir 永远不会为空。
// 它只能在特殊情况下发生(对于与任何应用程序无关的隔离进程)。
// 这些是由框架启动的,无论如何都不应该被模拟。
use_native_bridge = false;
ALOGW("Native bridge will not be used because managed_app_data_dir == nullptr.");
}
MountEmulatedStorage(uid, mount_external, use_native_bridge, fail_fn);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
// 如果这个 zygote 不是 root,它将无法创建进程组,因为该目录是由 root 拥有的。
if (!is_system_server && getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else if (rc != 0) {
ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0, strerror(-rc));
}
}
SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
if (use_native_bridge) {
// Due to the logic behind use_native_bridge we know that both app_data_dir
// and instruction_set contain values.
// 由于 use_native_bridge 背后的逻辑,我们知道 app_data_dir 和 instruction_set 都包含值。
android::PreInitializeNativeBridge(app_data_dir.value().c_str(),
instruction_set.value().c_str());
}
if (setresgid(gid, gid, gid) == -1) {
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
}
// Must be called when the new process still has CAP_SYS_ADMIN, in this case,
// before changing uid from 0, which clears capabilities. The other
// alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
// breaks SELinux domain transition (see b/71859146). As the result,
// privileged syscalls used below still need to be accessible in app process.
// 必须在新进程仍有 CAP_SYS_ADMIN 时调用,在这种情况下,在将 uid 从 0 更改为清除功能之前。
// 另一种选择是在之后调用 prctl(PR_SET_NO_NEW_PRIVS, 1),但这会中断 SELinux 域转换(参见 b/71859146)。
// 因此,下面使用的特权系统调用仍然需要在应用程序进程中访问。
SetUpSeccompFilter(uid, is_child_zygote);
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
}
// The "dumpable" flag of a process, which controls core dump generation, is
// overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
// user or group ID changes. See proc(5) for possible values. In most cases,
// the value is 0, so core dumps are disabled for zygote children. However,
// when running in a Chrome OS container, the value is already set to 2,
// which allows the external crash reporter to collect all core dumps. Since
// only system crashes are interested, core dump is disabled for app
// processes. This also ensures compliance with CTS.
// 当有效用户或组 ID 更改时,控制核心转储生成的进程的“dumpable”标志被 /proc/sys/fs/suid_dumpable 中的值覆盖。
// 有关可能的值,请参见 proc(5)。 在大多数情况下,该值为 0,因此对 zygote 子项禁用核心转储。
// 但是,在 Chrome OS 容器中运行时,该值已设置为 2,这允许外部崩溃报告器收集所有核心转储。
// 由于只对系统崩溃感兴趣,因此对应用程序进程禁用核心转储。 这也确保符合 CTS。
int dumpable = prctl(PR_GET_DUMPABLE);
if (dumpable == -1) {
ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
}
if (dumpable == 2 && uid >= AID_APP) {
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
}
}
// Set process properties to enable debugging if required.
// 如果需要,设置进程属性以启用调试。
if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
EnableDebugger();
}
if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
// simpleperf needs the process to be dumpable to profile it.
// simpleperf 需要进程可转储以对其进行分析。
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
}
}
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
// 解决 ARM 内核 ASLR 丢失问题 (http://b/5817320)。
int old_personality = personality(0xffffffff);
int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
if (new_personality == -1) {
ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
}
}
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
SetSchedulerPolicy(fail_fn);
__android_log_close();
stats_log_close();
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, "%s", "%s") failed",
uid, is_system_server, se_info_ptr, nice_name_ptr));
}
// Make it easier to debug audit logs by setting the main thread's name to the
// nice name rather than "app_process".
// 通过将主线程的名称设置为好听的名称而不是“app_process”,可以更轻松地调试审计日志。
if (nice_name.has_value()) {
SetThreadName(nice_name.value());
} else if (is_system_server) {
SetThreadName("system_server");
}
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
// 取消设置 SIGCHLD 处理程序,但继续忽略 SIGHUP(SetSignalHandlers 中的基本原理)。
UnsetChldSignalHandler();
if (is_system_server) {
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks);
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork system server hooks.");
}
// Prefetch the classloader for the system server. This is done early to
// allow a tie-down of the proper system server selinux domain.
// 预取系统服务器的类加载器。 这是尽早完成以允许绑定适当的系统服务器 selinux 域。
env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
if (env->ExceptionCheck()) {
// Be robust here. The Java code will attempt to create the classloader
// at a later point (but may not have rights to use AoT artifacts).
// 这里要健壮。 Java 代码稍后将尝试创建类加载器(但可能无权使用 AoT 工件)。
env->ExceptionClear();
}
// TODO(oth): Remove hardcoded label here (b/117874058).
// TODO(oth):在此处删除硬编码标签 (b/117874058)。
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
}
}
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork hooks.");
}
}
首先在ForkCommon函数内,会先fork一个新进程,并在pid=0这一分支中为孵化出的进程完成一系列初始化操作,而后在SpecializeCommon函数内执行CallStaticVoidMethod函数。CallStaticVoidMethod函数的参数 gZygoteClass对应的是“com/android/internal/os/Zygote”,而gCallPostForkChildHooks则是Zygote这个类中的成员函数callPostForkChildHooks,从名称可看出其是用于执行孵化后的一些处理工作。
ForkCommon和SpecializeCommon都没有涉及与应用程序相关的具体业务,这部分由 “frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java”->processOneCommand中的handleChildProc()来完成。
函数handleChildProc的内容如下:
private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, boolean isZygote) {
closeSocket();
if (descriptors != null) {
try {
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.mNiceName != null) { //存在子进程的别名
Process.setArgV0(parsedArgs.mNiceName); //设置子进程的别名
}
// End of the postFork event.
// postFork 事件结束。
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith, // 执行应用程序,正常情况下不返回
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
// Should not get here.
// 不应该到这里。
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned"); //WrapperInit.execApplication 意外返回
} else { //否则重新初始化zygote
if (!isZygote) {
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null );
} else {
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mRemainingArgs, null );
}
}
}
这个函数的任务是,处理参数后调用WrapperInit.execApplication(),执行应用程序。Android系统中所有应用程序理论上都是由zygote启动的, 从前面的分析来看,不难发现zygote会为新启动的应用程序fork一个进程。不过和传统的内核中的fork+exec的作法不同的地方是,Zygote中并不会执行exec()。在这种情况下就会造成一些障碍,比如无法使用valgrind来监控程序的内存泄漏情况。android系统提供了一种Wrapper实现,并通过parsedArgs.mInvokeWith 来加以控制。有兴趣的读者可查阅相关资料。
在高通的这个基线代码中execApplication函数主要是分析参数后,调用shell执行运行程序的命令。execApplication的内容如下:
public static void execApplication(String invokeWith, String niceName,
int targetSdkVersion, String instructionSet, FileDescriptor pipeFd,
String[] args) {
StringBuilder command = new StringBuilder(invokeWith);
final String appProcess;
if (VMRuntime.is64BitInstructionSet(instructionSet)) {
appProcess = "/system/bin/app_process64";
} else {
appProcess = "/system/bin/app_process32";
}
command.append(' ');
command.append(appProcess);
// Generate bare minimum of debug information to be able to backtrace through JITed code.
// We assume that if the invoke wrapper is used, backtraces are desirable:
// * The wrap.sh script can only be used by debuggable apps, which would enable this flag
// without the script anyway (the fork-zygote path). So this makes the two consistent.
// * The wrap.* property can only be used on userdebug builds and is likely to be used by
// developers (e.g. enable debug-malloc), in which case backtraces are also useful.
// 生成最少的调试信息,以便能够通过 JITed 代码进行回溯。
// 我们假设如果使用调用包装器,则需要回溯: wrap.sh 脚本只能由可调试的应用程序使用,
// 这将在没有脚本的情况下启用此标志(fork-zygote 路径)。 所以这使得两者一致。
// wrap.* 属性只能在 userdebug 构建中使用,并且很可能被开发人员使用(例如启用 debug-malloc),在这种情况下回溯也很有用。
command.append(" -Xcompiler-option --generate-mini-debug-info");
command.append(" /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
}
command.append(" com.android.internal.os.WrapperInit ");
command.append(pipeFd != null ? pipeFd.getInt$() : 0);
command.append(' ');
command.append(targetSdkVersion);
Zygote.appendQuotedShellArgs(command, args);
preserveCapabilities();
Zygote.execShell(command.toString());
}
在前面的handleChildProc这个函数中,会有一些会参数的处理,详细的了解需要对应用程序的启动由一个全局的认识。所以可以穿插阅读其它有关文档文章(后续作者也会持续更新)。这里假设运行的流程已经到了ActivityManagerService。
由于文章篇幅太长,本部分先讲解到这。后面文章将紧接这部分讲解分析流程。



