Android 8.1 源码_启动篇(二) -- 深入研究 zygote(转 Android 9.0 分析)
前言
在Android中,zygote是整个系统创建新进程的核心进程。zygote进程在内部会先启动Dalvik虚拟机,继而加载一些必要的系统资源和系统类,最后进入一种监听状态。在之后的运作中,当其他系统模块(比如 AMS)希望创建新进程时,只需向zygote进程发出请求,zygote进程监听到该请求后,会相应地fork出新的进程,于是这个新进程在初生之时,就先天具有了自己的Dalvik虚拟机以及系统资源。
开篇
核心源码
关键类 | 路径 |
---|---|
init.rc | system/core/rootdir/init.rc |
init.cpp | system/core/init/init.cpp |
init.zygote64.rc | system/core/rootdir/init.zygote64.rc |
builtins.cpp | system/core/init/builtins.cpp |
service.cpp | system/core/init/service.cpp |
app_main.cpp | frameworks/base/cmds/app_process/app_main.cpp |
AndroidRuntime.cpp | frameworks/base/core/jni/AndroidRuntime.cpp |
JniInvocation.cpp | libnativehelper/JniInvocation.cpp |
ZygoteInit.java | frameworks/base/core/java/com/android/internal/os/ZygoteInit.java |
ZygoteServer.java | frameworks/base/core/java/com/android/internal/os/ZygoteServer.java |
Zygote简介
在Android系统中,JavaVM(Java虚拟机)、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建JavaVM,因此通过fock而创建的应用程序进程和SystemServer进程可以在内部获取一个JavaVM的实例拷贝。
Read The Fucking Code
Zygote触发
在分析init进程时,我们知道init进程启动后,会解析init.rc文件,然后创建和加载service字段指定的进程。zygote进程就是以这种方式,被init进程加载的。
在system/core/rootdir/init.rc的开始部分,可以看到:
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // ${ro.zygote}由厂商定义,与平台相关
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
init.zygoteXX.rc
从之前分析的init篇中我们知道,在不同的平台(32、64及64_32)上,init.rc将包含不同的zygote.rc文件。在system/core/rootdir目录下,有init.zygote32_64.rc、init.zyote64.rc、 init.zyote32.rc、init.zygote64_32.rc。
✨ init.zygote32.rc:zygote 进程对应的执行程序是 app_process (纯 32bit 模式)
✨ init.zygote64.rc:zygote 进程对应的执行程序是 app_process64 (纯 64bit 模式)
✨ init.zygote32_64.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64
✨ init.zygote64_32.rc:启动两个 zygote 进程 (名为 zygote 和 zygote_secondary),对应的执行程序分别是 app_process64 (主模式)、app_process32
为什么要定义这么多种情况呢?直接定义一个不就好了,这主要是因为Android 5.0以后开始支持64位程序,为了兼容32位和64位才这样定义。不同的zygote.rc内容大致相同,主要区别体现在启动的是32位,还是64位的进程。init.zygote32_64.rc和init.zygote64_32.rc会启动两个进程,且存在主次之分。
这里拿64位处理器为例,init.zygote64_32.rc的代码如下所示:
// 进程名称是zygote,运行的二进制文件在/system/bin/app_process64
// 启动参数是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
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
socket zygote stream 660 root system // 创建一个socket,名字叫zygote,以tcp形式
onrestart write /sys/android_power/request_state wake // onrestart 指当进程重启时执行后面的命令
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 // 创建子进程时,向 /dev/cpuset/foreground/tasks 写入pid
// 另一个service ,名字 zygote_secondary
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
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
start zygote
定义了service,肯定有地方调用 start zygote。在之前init解析的博客中,我们分析过init进程的启动。init进程启动的最后,会产生”late-init”事件。
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
对应于init.rc配置文件中,我们找到如下代码:
# Mount filesystems and start core system services.
on late-init
trigger early-fs
... ...
# Now we can start zygote for devices with file based encryption
trigger zygote-start // 触发了zygote-start事件后,就会启动zygote进程
... ...
对应于init.rc配置文件中,我们找到如下代码:
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd // start对应的映射关系定义于system/core/init/builtins.cpp中
start zygote // 调用start对应的处理函数,启动名为zygote的服务(传入前文init.zygote.rc中定义的参数)
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
start命令有一个对应的执行函数do_start,定义在platform/system/core/init/builtins.cpp中
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
... ...
{"start", {1, 1, do_start}},
... ...
};
// clang-format on
return builtin_functions;
}
我们来看下do_start():
static int do_start(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]); // 找到zygote service对应信息
if (!svc) {
LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start()) // 启动对应的进程
return -1;
return 0;
}
do_start首先是通过FindServiceByName去service数组中遍历,根据名字匹配出对应的service,然后调用service的Start函数。
最后,我们来看看service.cpp中定义Start函数:
bool Service::Start() {
... ...
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork(); // 从init进程中,fork出zygote进程
}
... ...
}
Start函数主要是fork出一个新进程,然后执行service对应的二进制文件,并将参数传递进去。那么下面我们以init.zygote64.rc为例进行分析。
app_process
从上面我们分析的init.zygote64.rc可以看出,zygote64启动文件的地址为app_process64。app_process64对应的代码定义在frameworks/base/cmds/app_process中,
我们来看看对应的Android.mk: frameworks/base/cmds/app_process
LOCAL_PATH:= $(call my-dir)
... ...
app_process_src_files := \
app_main.cpp \
... ...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
其实不管是app_process、app_process32还是app_process64,对应的源文件都是app_main.cpp。
接下来我们就看看app_process对应的main函数,该函数定义于app_main.cpp中。
在app_main.cpp的main函数中,主要做的事情就是参数解析. 这个函数有两种启动模式:
✨ 一种是zygote模式,也就是初始化zygote进程,传递的参数有--start-system-server --socket-name=zygote,前者表示启动SystemServer,后者指定socket的名称(Zygote64_32)。
✨ 一种是application模式,也就是启动普通应用程序,传递的参数有class名字以及class带的参数。
两者最终都是调用AppRuntime对象的start函数,加载ZygoteInit或RuntimeInit两个Java类,并将之前整理的参数传入进去。
我们这里暂时只讲解ZygoteInit的加载流程。
int main(int argc, char* const argv[])
{
// 将参数argv放到argv_String字符串中,然后打印出来
// 之前start zygote传入的参数是 -Xzygote /system/bin --zygote --start-system-server
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定义于app_main.cpp中,继承自AndroidRuntime
// 就是对Android运行环境的一种抽象,类似于java虚拟机对Java程序的作用
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// 这两个参数是Java程序需要依赖的Jar包,相当于import
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++) {
// 将spaced_commands中的参数额外加入VM
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) {
// 比较参数是否是spaced_commands中的参数
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
// 如果参数第一个字符是'-',直接跳出循环,之前传入的第一个参数是 -Xzygote,所以执行到这儿就跳出了
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
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]);
}
// Parse runtime arguments. Stop at first unrecognized option.
// 从这里其实可以看出,通过app_main可以启动zygote、system-server及普通apk进程
// 这个可以通过init.rc来配置
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName; // app_process的名称改为zygote
String8 className; // 启动apk进程时,对应的类名
++i; // Skip unused "parent dir" argument.
// 跳过一个参数,之前跳过了-Xzygote,这里继续跳过 /system/bin ,也就是所谓的 "parent dir"
while (i < argc) { // 开始解析输入参数
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) { // 表示是zygote启动模式
zygote = true;
niceName = ZYGOTE_NICE_NAME; // 这个值根据平台可能是zygote64或zygote
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true; // init.zygote.rc中定义了该字段,启动zygote后会启动system-server
} else if (strcmp(arg, "--application") == 0) {
application = true; // 表示是application启动模式,也就是普通应用程序
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12); // 进程别名,可以自己指定进程名
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg); // 与--application配置,启动指定的类,application启动的class
break;
} else {
--i;
break;
}
}
// 准备参数
Vector<String8> args;
if (!className.isEmpty()) { // className不为空,说明是application启动模式
// 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); // 将className和参数设置给runtime
... ...
} else { // zygote启动模式
// We're in zygote mode.
maybeCreateDalvikCache(); // 创建Dalvik的缓存目录并定义权限
if (startSystemServer) { // 增加start-system-server参数,默认启动zygote后,就会启动system_server
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX]; // 获取平台对应的abi信息
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="); // 参数需要制定abi
abiFlag.append(prop);
args.add(abiFlag); // 加入--abi-list=参数
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i])); // 将剩下的参数加入args
}
}
if (!niceName.isEmpty()) { // 将app_process的进程名,替换为niceName
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) { // 调用Runtime的start函数, 启动ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) { // 启动zygote没有进入这个分支
// 但这个分支说明,通过配置init.rc文件,其实是可以不通过zygote来启动一个进程
// 如果是application启动模式,则加载RuntimeInit
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
// error情况
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
AndroidRuntime
由于AppRuntime继承自AndroidRuntime,且没有重写start方法,因此zygote的流程进入到了AndroidRuntime.cpp。
接下来,我们来看看AndroidRuntime的start函数的流程。
创建Java虚拟机
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ... // 打印一些日志,获取ANDROID_ROOT环境变量
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL); // 初始化JNI,加载libart.so
JNIEnv* env;
// 创建虚拟机,其中大多数参数由系统属性决定
// 最终,startVm利用JNI_CreateJavaVM创建出虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 回调AppRuntime的onVmCreated函数
// 对于zygote进程的启动流程而言,无实际操作,表示虚拟创建完成,但是里面是空实现
onVmCreated(env);
... ...
}
这边我们跟一下jni_invocation.Init():libnativehelper/JniInvocation.cpp
Init函数主要作用是初始化JNI,具体工作是首先通过dlopen加载libart.so获得其句柄,然后调用dlsym从libart.so中找到JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs三个函数地址,赋值给对应成员属性,这三个函数会在后续虚拟机创建中调用。
bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
library = GetLibrary(library, buffer); // 默认返回 libart.so
// Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
// This is due to the fact that it is possible that some threads might have yet to finish
// exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
// unloaded.
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
/*
* 1.dlopen功能是以指定模式打开指定的动态链接库文件,并返回一个句柄
* 2.RTLD_NOW表示需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL
* 3.RTLD_NODELETE表示在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量
*/
handle_ = dlopen(library, kDlopenFlags); // 获取libart.so的句柄
if (handle_ == NULL) { // 获取失败打印错误日志并尝试再次打开libart.so
if (strcmp(library, kLibraryFallback) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
// Note that this is enough to get something like the zygote
// running, we can't property_set here to fix this for the future
// because we are root and not the system user. See
// RuntimeInit.commonInit for where we fix up the property to
// avoid future fallbacks. http://b/11463182
ALOGW("Falling back from %s to %s after dlopen error: %s",
library, kLibraryFallback, dlerror());
library = kLibraryFallback;
handle_ = dlopen(library, kDlopenFlags);
if (handle_ == NULL) {
ALOGE("Failed to dlopen %s: %s", library, dlerror());
return false;
}
}
/*
* 1.FindSymbol函数内部实际调用的是dlsym
* 2.dlsym作用是根据 动态链接库 操作句柄(handle)与符号(symbol),返回符号对应的地址
* 3.这里实际就是从libart.so中将JNI_GetDefaultJavaVMInitArgs等对应的地址存入&JNI_GetDefaultJavaVMInitArgs_中
*/
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
return true;
}
其次,我们再跟一下startVm():
这个函数特别长,但是里面做的事情很单一,其实就是从各种系统属性中读取一些参数,然后通过addOption设置到AndroidRuntime的mOptions数组中存起来,另外就是调用之前从libart.so中找到JNI_CreateJavaVM函数,并将这些参数传入,由于本篇主要讲zygote启动流程,因此关于虚拟机的实现就不深入探究了。
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
... ...
/* route exit() to our handler */
addOption("exit", (void*) runtime_exit); // 将参数放入mOptions数组中
... ...
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray(); // 将mOptions赋值给initArgs
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { // 调用libart.so的JNI_CreateJavaVM函数
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
return 0;
}
注册JNI函数
我们回到AndroidRuntime的start函数。初始化JVM后,接下来就会调用startReg函数。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ...
/* 01. 创建Java虚拟机*/
/*
* Register android functions.
*/
if (startReg(env) < 0) { // 注册JNI函数
ALOGE("Unable to register all android natives\n");
return;
}
... ...
}
startReg首先是设置了Android创建线程的处理函数,然后创建了一个200容量的局部引用作用域,用于确保不会出现OutOfMemoryException,最后就是调用register_jni_procs进行JNI注册。
我们跟进startReg():
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
/*
* This hook causes all future threads created in this process to be
* attached to the JavaVM. (This needs to go away in favor of JNI
* Attach calls.)
*/
// 定义Android创建线程的func:javaCreateThreadEtc,这个函数内部是通过Linux的clone来创建线程的
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
ALOGV("--- registering native functions ---\n");
/*
* Every "register" function calls one or more things that return
* a local reference (e.g. FindClass). Because we haven't really
* started the VM yet, they're all getting stored in the base frame
* and never released. Use Push/Pop to manage the storage.
*/
env->PushLocalFrame(200); // 创建一个200容量的局部引用作用域,这个局部引用其实就是局部变量
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { // 注册JNI函数
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL); // 释放局部引用作用域
//createJavaThread("fubar", quickTest, (void*) "hello");
return 0;
}
从上述代码可以看出,startReg函数中主要是通过register_jni_procs来注册JNI函数。其中,gRegJNI是一个全局数组,该数组的定义如下:
static const RegJNIRec gRegJNI[] = { // 里面就是一堆的函数指针
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
... ...
};
我们挑一个register_com_android_internal_os_ZygoteInit_nativeZygoteInit,这实际上是自定义JNI函数并进行动态注册的标准写法,
内部是调用JNI的RegisterNatives,这样注册后,Java类ZygoteInit的native方法nativeZygoteInit就会调用com_android_internal_os_ZygoteInit_nativeZygoteInit函数。
int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
{ "nativeZygoteInit", "()V",
(void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
};
return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
methods, NELEM(methods));
}
REG_JNI对应的宏定义及RegJNIRec结构体的定义为:
#ifdef NDEBUG
#define REG_JNI(name) { name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
#else
#define REG_JNI(name) { name, #name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
const char* mName;
};
#endif
根据宏定义可以看出,宏REG_JNI将得到函数名;定义RegJNIRec数组时,函数名被赋值给RegJNIRec结构体,于是每个函数名被强行转换为函数指针。
因此,register_jni_procs的参数就是一个函数指针数组,数组的大小和JNIEnv。
我们来跟进一下register_jni_procs函数:
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) { // 调用mProc
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
结合前面的分析,容易知道register_jni_procs函数,实际上就是调用函数指针(mProc)对应的函数,以进行实际的JNI函数注册。
反射启动ZygoteInit
继续分析AndroidRuntime.cpp的start函数:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
... ...
/* 01. 创建Java虚拟机*/
/* 02. 注册JNI函数 */
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
// 替换string为实际路径
// 例如:将 "com.android.internal.os.ZygoteInit" 替换为 "com/android/internal/os/ZygoteInit"
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName); // 找到class文件
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V"); // 通过反射找到ZygoteInit的main函数
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray); // 调用ZygoteInit的main函数
... ...
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK) // 退出当前线程
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0) // 创建一个线程,该线程会等待所有子线程结束后关闭虚拟机
ALOGW("Warning: VM did not shut down cleanly\n");
}
可以看到,在AndroidRuntime的最后,将通过反射调用ZygoteInit的main函数。至此,zygote进程进入了java世界。
其实我们仔细想一想,就会觉得zygote的整个流程实际上是非常符合实际情况的。
✨✨ 在Android中,每个进程都运行在对应的虚拟机上,因此zygote首先就负责创建出虚拟机。
✨✨ 然后,为了反射调用java代码,必须有对应的JNI函数,于是zygote进行了JNI函数的注册。
✨✨ 当一切准备妥当后,zygote进程才进入到了java世界。
ZygoteInit
现在我们跟进ZygoteInit.java的main函数。
public static void main(String argv[]) {
//创建ZygoteServer对象
ZygoteServer zygoteServer = new ZygoteServer();
// Mark zygote start. This ensures that thread creation will throw
// an error.
// 调用native函数,确保当前没有其它线程在运行
// 主要还是处于安全的考虑
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
final Runnable caller;
try {
... ...
RuntimeInit.enableDdms();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
// 解析参数,得到上述变量的值
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
zygoteServer.registerServerSocket(socketName); // 注册server socket
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
... ...
preload(bootTimingsTraceLog); // 默认情况,预加载信息
... ...
} else {
// 如注释,延迟预加载
// 变更Zygote进程优先级为NORMAL级别
// 第一次fork时才会preload
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize(); // 如果预加载了,很有必要GC一波
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
... ...
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
// Set seccomp policy
// 加载seccomp的过滤规则
// 所有 Android 软件都使用系统调用(简称为 syscall)与 Linux 内核进行通信
// 内核提供许多特定于设备和SOC的系统调用,让用户空间进程(包括应用)可以直接与内核进行交互
// 不过,其中许多系统调用Android未予使用或未予正式支持
// 通过seccomp,Android可使应用软件无法访问未使用的内核系统调用
// 由于应用无法访问这些系统调用,因此,它们不会被潜在的有害应用利用
// 该过滤器安装到zygote进程中,由于所有Android应用均衍生自该进程
// 因而会影响到所有应用
Seccomp.setPolicy();
/// M: Added for BOOTPROF
addBootEvent("Zygote:Preload End");
/// @}
ZygoteHooks.stopZygoteNoThreadCreation(); // 允许有其它线程了
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // fork出system server
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList); // zygote进程进入无限循环,处理请求
} catch (Throwable ex) {
... ...
} finally {
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的main函数的主干部分,除了安全相关的内容外,最主要的工作就是注册server socket、预加载、启动system server及进入无限循环处理请求消息。
接下来我们分四部分分别讨论!
创建server socket
Android O将server socket相关的工作抽象到ZygoteServer.java中了。我们来看看其中的registerZygoteSocket函数:
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
// ANDROID_SOCKET_PREFIX为"ANDROID_SOCKET_"
// 此处的socket name,就是zygote
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
// 还记得么?在init.zygote.rc被加载时,指定了名为zygote的socket
// 在进程被创建时,就会创建对应的文件描述符,并加入到环境变量中
// 因此,此时可以取出对应的环境变量
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc); // 获取zygote socket的文件描述符
mServerSocket = new LocalServerSocket(fd); // 将socket包装成一个server socket
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
我们跟踪LocalServerSocket():
public LocalServerSocket(String name) throws IOException
{
impl = new LocalSocketImpl();
impl.create(LocalSocket.SOCKET_STREAM); // 创建SOCKET_STREAM类型的AF_UNIX socket
localAddress = new LocalSocketAddress(name);
impl.bind(localAddress); // 绑定到指定地址
impl.listen(LISTEN_BACKLOG); // 开始监听
}
预加载
我们看看预加载的内容:
static void preload(TimingsTraceLog bootTimingsTraceLog) {
... ...
beginIcuCachePinning(); // Pin ICU Data, 获取字符集转换资源等
... ...
preloadClasses(); // 读取文件system/etc/preloaded-classes,然后通过反射加载对应的类
// 一般由厂商来定义,有时需要加载数千个类,启动慢的原因之一
... ...
preloadResources(); // 负责加载一些常用的系统资源
... ...
nativePreloadAppProcessHALs();
... ...
preloadOpenGL(); // 图形相关
... ...
preloadSharedLibraries(); // 一些必要库
preloadTextResources(); // 语言相关的字符信息
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders(); // 安全相关的
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
为了让系统实际运行时更加流畅,在zygote启动时候,调用preload函数进行了一些预加载操作。Android 通过zygote fork的方式创建子进程。zygote进程预加载这些类和资源,在fork子进程时,仅需要做一个复制即可。
这样可以节约子进程的启动时间。同时,根据fork的copy-on-write机制可知,有些类如果不做改变,甚至都不用复制,子进程可以和父进程共享这部分数据,从而省去不少内存的占用。
启动SystemServer进程
再来看看启动System Server的流程:
/**
* Prepare the arguments and forks for the system server process.
*
* Returns an {@code Runnable} that provides an entrypoint into system_server code in the
* child process, and {@code null} in the parent.
*/
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
);
/* Containers run without this capability, so avoid setting it in that case */
if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER, false)) {
capabilities |= posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
}
/* 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,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args); // 将上面准备的参数,按照ZygoteConnection的风格进行封装
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer( // 通过fork"分裂"出system_server
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
// 处理32_64和64_32的情况
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
// fork时会copy socket,system server需要主动关闭
zygoteServer.closeServerSocket();
// system server进程处理自己的工作
return handleSystemServerProcess(parsedArgs);
}
return null;
}
处理请求信息
创建出SystemServer进程后,zygote进程调用ZygoteServer中的函数runSelectLoop,处理server socket收到的命令。
/**
* 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<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
// 首先将server socket加入到fds
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
// 每次循环,都重新创建需要监听的pollFds
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
// 关注事件到来
pollFds[i].events = (short) POLLIN;
}
try {
// 等待事件到来
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
// 注意这里是倒序的,即优先处理已建立链接的信息,后处理新建链接的请求
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
// server socket最先加入fds, 因此这里是server socket收到数据
if (i == 0) {
// 收到新的建立通信的请求,建立通信连接
ZygoteConnection newPeer = acceptCommandPeer(abiList);
// 加入到peers和fds, 即下一次也开始监听
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//其它通信连接收到数据
... ...
}
}
}
}
从上面代码可知,初始时fds中仅有server socket,因此当有数据到来时,将执行i等于0的分支。此时,显然是需要创建新的通信连接,因此acceptCommandPeer将被调用。
我们看看acceptCommandPeer函数:
/**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
// socket编程中,accept()调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET
// 它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符
// 新建立的套接字不在监听状态,原来所监听的套接字的状态也不受accept()调用的影响
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
从上面的代码,可以看出acceptCommandPeer调用了server socket的accpet函数。于是当新的连接建立时,zygote将会创建出一个新的socket与其通信,并将该socket加入到fds中。因此,一旦通信连接建立后,fds中将会包含有多个socket。
当poll监听到这一组sockets上有数据到来时,就会从阻塞中恢复。于是,我们需要判断到底是哪个socket收到了数据。
在runSelectLoop中采用倒序的方式轮询。由于server socket第一个被加入到fds,因此最后轮询到的socket才需要处理新建连接的操作;其它socket收到数据时,仅需要调用zygoteConnection的runonce函数执行数据对应的操作。若一个连接处理完所有对应消息后,该连接对应的socket和连接等将被移除。
完结
Zygote启动流程到此结束,Zygote进程共做了如下几件事:
Android 8.1 源码_启动篇(二) -- 深入研究 zygote(转 Android 9.0 分析)的更多相关文章
- Android 8.1 源码_启动篇(一) -- 深入研究 init(转 Android 9.0 分析)
前言 init进程,它是一个由内核启动的用户级进程,当Linux内核启动之后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,所以它的进程号是1 ...
- JDK源码学习--String篇(二) 关于String采用final修饰的思考
JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...
- Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- 第一部分:开发前的准备-第八章 Android SDK与源码下载
第8章 Android SDK与源码下载 如果你是新下载的SDK,请阅读一下步骤了解如何设置SDK.如果你已经下载使用过SDK,那么你应该使用AVD Manager,来更新即可. 下面是构建Andro ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
- jQuery2.x源码解析(回调篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...
- Android 开源项目源码解析(第二期)
Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...
随机推荐
- mongodb常用语句
一.查询 find方法 db.collection_name.find(); 查询所有的结果: select * from users; db.users.find(); 指定返回那些列(键): se ...
- centOS7虚拟环境搭建
今天来记录一下使用WMware虚拟机来搭建centOS虚拟机的过程. 本次使用工具为VMware Workstation 14 Pro,可以从https://www.vmware.com/来获取所需工 ...
- QT中对内存的管理
在QT中,一切继承自QT自有类的类,如果存在parent指针,那么当parent指针delete时,该类中的指针(它们都属于parent指针对应的child指针)也会被delete.综上,如果我们的窗 ...
- Hibernate Session总结
现在我们可以在 IDEA 下新建一个 Hibernate 项目,接着上次内容这次主要总结一下 Hibernate 的 Session,及其核心方法. Session 概述 Session 接口是 Hi ...
- Python学习 Part4:模块
Python学习 Part4:模块 1. 模块是将定义保存在一个文件中的方法,然后在脚本中或解释器的交互实例中使用.模块中的定义可以被导入到其他模块或者main模块. 模块就是一个包含Python定义 ...
- JavaScript 之函数
刚开 始学习 JS 时,挺不习惯它函数的用法,就比如一个 function 里面会嵌套一个 function,对于函数里创建变量的作用域也感到很迷惑,这个的语法和 JAVA 相差太多,为此,阅读了&l ...
- 一分钟告诉你究竟DevOps是什么鬼?
历史回顾 为了能够更好的理解什么是DevOps,我们很有必要对当时还只有程序员(此前还没有派生出开发者,前台工程师,后台工程师之类)这个称号存在的历史进行一下回顾. 如编程之道中所言: 老一辈的程序员 ...
- 配置teredo,启用ipv6,xx-net
最近使用XX-NET科学上网 ,提示要配置使用IPv6.根据github的上win10配置ipv6方式(https://github.com/XX-net/XX-Net/wiki/IPv6-Win10 ...
- Object类的equals()方法总结
1.equals()是Object中的方法,作用在于检测一个对象是否等于另一个对象. 2.在Object类中equals()方法是判断两个对象是否有相同的引用 ,即是否是相同的对象. 3.String ...
- java正则表达式验证金额
String reg_money = "\\d+(\\.\\d{1,2})?";// 金额正则,可以没有小数,小数最多不超过两位 Pattern pattern = Pattern ...